diff options
author | F.O. <scrotadamus@insiberia.net> | 2025-02-16 17:56:08 +0100 |
---|---|---|
committer | F.O. <scrotadamus@insiberia.net> | 2025-02-16 17:56:57 +0100 |
commit | 17fb6add26291b31f7020e3551a7c8487130a747 (patch) | |
tree | d4559a7339ed181393ff921909e6ce05b7c2cf18 /go-poppler/page.go |
genesi
Diffstat (limited to 'go-poppler/page.go')
-rw-r--r-- | go-poppler/page.go | 217 |
1 files changed, 217 insertions, 0 deletions
diff --git a/go-poppler/page.go b/go-poppler/page.go new file mode 100644 index 0000000..efd0706 --- /dev/null +++ b/go-poppler/page.go @@ -0,0 +1,217 @@ +package poppler + +// #cgo pkg-config: poppler-glib +// #include <poppler.h> +// #include <glib.h> +// #include <cairo.h> +import "C" +import "unsafe" +import "github.com/ungerik/go-cairo" + +//import "fmt" + +type Page struct { + p *C.struct__PopplerPage + openedAnnots []*Annot +} + +func (p *Page) Text() string { + return C.GoString(C.poppler_page_get_text(p.p)) +} + +func (p *Page) TextAttributes() (results []TextAttributes) { + a := C.poppler_page_get_text_attributes(p.p) + defer C.poppler_page_free_text_attributes(a) + var attr *C.PopplerTextAttributes + results = make([]TextAttributes, 0) + el := C.g_list_first(a) + for el != nil { + attr = (*C.PopplerTextAttributes)(el.data) + fn := attr.font_name + result := TextAttributes{ + FontName: toString(fn), + FontSize: float64(attr.font_size), + IsUnderlined: toBool(attr.is_underlined), + StartIndex: int(attr.start_index), + EndIndex: int(attr.end_index), + Color: Color{ + R: int(attr.color.red), + G: int(attr.color.green), + B: int(attr.color.blue), + }, + } + results = append(results, result) + el = el.next + } + return +} + +func (p *Page) Size() (width, height float64) { + var w, h C.double + C.poppler_page_get_size(p.p, &w, &h) + return float64(w), float64(h) +} + +func (p *Page) Index() int { + return int(C.poppler_page_get_index(p.p)) +} + +func (p *Page) Label() string { + return toString(C.poppler_page_get_label(p.p)) +} + +func (p *Page) Duration() float64 { + return float64(C.poppler_page_get_duration(p.p)) +} + +func (p *Page) Images() (results []Image) { + l := C.poppler_page_get_image_mapping(p.p) + defer C.poppler_page_free_image_mapping(l) + results = make([]Image, 0) + var im *C.PopplerImageMapping + for el := C.g_list_first(l); el != nil; el = el.next { + im = (*C.PopplerImageMapping)(el.data) + result := Image{ + Id: int(im.image_id), + Area: Rectangle{ + X1: float64(im.area.x1), + Y1: float64(im.area.y1), + X2: float64(im.area.x2), + Y2: float64(im.area.y2), + }, + p: p.p, + } + results = append(results, result) + } + return +} + +func (p *Page) TextLayout() (layouts []Rectangle) { + var rect *C.PopplerRectangle + var n C.guint + if toBool(C.poppler_page_get_text_layout(p.p, &rect, &n)) { + defer C.g_free((C.gpointer)(rect)) + layouts = make([]Rectangle, int(n)) + r := (*[1 << 30]C.PopplerRectangle)(unsafe.Pointer(rect))[:n:n] + for i := 0; i < int(n); i++ { + layouts[i] = Rectangle{ + X1: float64(r[i].x1), + Y1: float64(r[i].y1), + X2: float64(r[i].x2), + Y2: float64(r[i].y2), + } + } + } + return +} + +func (p *Page) TextLayoutAndAttrs() (result []TextEl) { + text := p.Text() + attrs := p.TextAttributes() + layout := p.TextLayout() + result = make([]TextEl, len(layout)) + attrsRef := make([]*TextAttributes, len(attrs)) + for i, a := range attrs { + attr := a + attrsRef[i] = &attr + } + i := 0 + for _, t := range text { + var a *TextAttributes + for _, a = range attrsRef { + if i >= a.StartIndex && i <= a.EndIndex { + break + } + } + result[i] = TextEl{ + Text: string(t), + Attrs: a, + Rect: layout[i], + } + i++ + } + return +} + +func (p *Page) Close() { + p.closeAnnotMappings() + + if p.p != nil { + C.g_object_unref(C.gpointer(p.p)) + /* avoid double free */ + p.p = nil + } +} + +// Converts a page into SVG and saves to file. +// Inspired by https://github.com/dawbarton/pdf2svg +func (p *Page) ConvertToSVG(filename string){ + width, height := p.Size() + + // Open the SVG file + surface := cairo.NewSVGSurface( filename, width, height, cairo.SVG_VERSION_1_2 ) + + // TODO Can be improved by using cairo_svg_surface_create_for_stream() instead of + // cairo_svg_surface_create() for stream processing instead of file processing. + // However, this needs to be changed in github.com/ungerik/go-cairo/surface.go + + // Get cairo context pointer + _, drawcontext := surface.Native() + + // Render the PDF file into the SVG file + C.poppler_page_render_for_printing(p.p, (*C.cairo_t)(unsafe.Pointer(drawcontext)) ); + + // Close the SVG file + surface.ShowPage() + surface.Destroy() +} + +func (p *Page) closeAnnotMappings(){ + for i := 0; i < len(p.openedAnnots); i++ { + p.openedAnnots[i].Close() + } + + p.openedAnnots = nil + +} + +func (p *Page) GetAnnots() (Annots []*Annot) { + var annots []*Annot + + annotGlist := C.poppler_page_get_annot_mapping(p.p) + defer C.g_list_free(annotGlist) + + p.closeAnnotMappings() + + for annotGlist != nil { + popplerAnnot := (*C.PopplerAnnotMapping)(annotGlist.data) + + + annot := &Annot{ + am: popplerAnnot, + } + + /* Maybe we can used openedAnnots instead of annots + openedAnnots + */ + + annots = append(annots, annot) + p.openedAnnots = append(p.openedAnnots, annot) + + + annotGlist = annotGlist.next + } + + return annots +} + +func (p *Page) AnnotText(a Annot) string { + cText := C.poppler_page_get_text_for_area(p.p, &a.am.area) + return C.GoString(cText) +} + +func (p *Page) AddAnnot(a Annot) { + C.poppler_page_add_annot(p.p, a.am.annot) +} +func (p *Page) RemoveAnnot(a Annot) { + C.poppler_page_remove_annot(p.p, a.am.annot) +} |