summaryrefslogtreecommitdiff
path: root/go-poppler/page.go
diff options
context:
space:
mode:
authorF.O. <scrotadamus@insiberia.net>2025-02-16 17:56:08 +0100
committerF.O. <scrotadamus@insiberia.net>2025-02-16 17:56:57 +0100
commit17fb6add26291b31f7020e3551a7c8487130a747 (patch)
treed4559a7339ed181393ff921909e6ce05b7c2cf18 /go-poppler/page.go
genesi
Diffstat (limited to 'go-poppler/page.go')
-rw-r--r--go-poppler/page.go217
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)
+}