From b840d2a6aa91a358119230410ba95d78cc7de904 Mon Sep 17 00:00:00 2001 From: Jeroen Bobbeldijk Date: Sun, 13 Feb 2022 19:16:26 +0100 Subject: [PATCH] Implement struct tree --- README.md | 10 +- check_implementation/main.go | 5 + internal/commons/generated.go | 377 +++++++++++++ internal/implementation/dest.go | 3 +- internal/implementation/fpdf_structtree.go | 253 +++++++++ .../fpdf_structtree_experimental.go | 107 ++++ .../fpdf_structtree_no_experimental.go | 28 + internal/implementation/handles.go | 22 + internal/implementation/implementation.go | 38 ++ internal/implementation/struct_tree.go | 38 ++ multi_threaded/generated.go | 104 ++++ pdfium.go | 48 ++ references/references.go | 6 + requests/fpdf_structtree.go | 57 ++ responses/fpdf_structtree.go | 53 ++ shared_tests/fpdf_structtree.go | 526 ++++++++++++++++++ shared_tests/fpdf_structtree_experimental.go | 232 ++++++++ single_threaded/generated.go | 182 ++++++ 18 files changed, 2083 insertions(+), 6 deletions(-) create mode 100644 internal/implementation/fpdf_structtree_experimental.go create mode 100644 internal/implementation/fpdf_structtree_no_experimental.go create mode 100644 internal/implementation/struct_tree.go create mode 100644 shared_tests/fpdf_structtree.go create mode 100644 shared_tests/fpdf_structtree_experimental.go diff --git a/README.md b/README.md index 99a56011..a9c72dd1 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ * Option between single-threaded and multi-threaded (through subprocesses), while keeping the same interface * This library will handle all complicated cgo gymnastics for you -* The goal is to implement all PDFium public API methods (including [experimental](#experimental)), current progress: 46% +* The goal is to implement all PDFium public API methods (including [experimental](#experimental)), current progress: 50% * Current PDFium methods exposed, no cgo required * PDFium instance configuration (sandbox policy, fonts) * Document loading (from bytes, path or io.ReadSeeker) @@ -39,11 +39,11 @@ * Transformations (page boxes, clip paths) * Progressive rendering * Document loading through data availability (loading data as needed) -* Methods to be implemented: - * Page/Page object editing - * Form filling - * Annotations * Struct trees +* Methods to be implemented: + * Page/Page object editing (fpdf_edit.h) + * Form filling (fpdf_formfill.h) + * Annotations (fpdf_annot.h) * Methods that won't be implemented for now: * fpdf_sysfontinfo.h (probably too complicated) * Useful helpers to make your life easier: diff --git a/check_implementation/main.go b/check_implementation/main.go index bd47d072..96298472 100644 --- a/check_implementation/main.go +++ b/check_implementation/main.go @@ -65,6 +65,11 @@ func main() { methodParts := strings.SplitN(method, "(", 2) method = methodParts[0] + // Skip methods that should never be implemented. + if method == "FPDF_InitLibrary" || method == "FPDF_InitLibraryWithConfig" || method == "FPDF_DestroyLibrary" { + continue + } + methodCount++ if _, ok := implementedMethods[method]; !ok { fmt.Println(method) diff --git a/internal/commons/generated.go b/internal/commons/generated.go index df95a718..e829f1cf 100644 --- a/internal/commons/generated.go +++ b/internal/commons/generated.go @@ -190,6 +190,19 @@ type Pdfium interface { FPDF_SaveWithVersion(*requests.FPDF_SaveWithVersion) (*responses.FPDF_SaveWithVersion, error) FPDF_SetPrintMode(*requests.FPDF_SetPrintMode) (*responses.FPDF_SetPrintMode, error) FPDF_SetSandBoxPolicy(*requests.FPDF_SetSandBoxPolicy) (*responses.FPDF_SetSandBoxPolicy, error) + FPDF_StructElement_CountChildren(*requests.FPDF_StructElement_CountChildren) (*responses.FPDF_StructElement_CountChildren, error) + FPDF_StructElement_GetAltText(*requests.FPDF_StructElement_GetAltText) (*responses.FPDF_StructElement_GetAltText, error) + FPDF_StructElement_GetChildAtIndex(*requests.FPDF_StructElement_GetChildAtIndex) (*responses.FPDF_StructElement_GetChildAtIndex, error) + FPDF_StructElement_GetID(*requests.FPDF_StructElement_GetID) (*responses.FPDF_StructElement_GetID, error) + FPDF_StructElement_GetLang(*requests.FPDF_StructElement_GetLang) (*responses.FPDF_StructElement_GetLang, error) + FPDF_StructElement_GetMarkedContentID(*requests.FPDF_StructElement_GetMarkedContentID) (*responses.FPDF_StructElement_GetMarkedContentID, error) + FPDF_StructElement_GetStringAttribute(*requests.FPDF_StructElement_GetStringAttribute) (*responses.FPDF_StructElement_GetStringAttribute, error) + FPDF_StructElement_GetTitle(*requests.FPDF_StructElement_GetTitle) (*responses.FPDF_StructElement_GetTitle, error) + FPDF_StructElement_GetType(*requests.FPDF_StructElement_GetType) (*responses.FPDF_StructElement_GetType, error) + FPDF_StructTree_Close(*requests.FPDF_StructTree_Close) (*responses.FPDF_StructTree_Close, error) + FPDF_StructTree_CountChildren(*requests.FPDF_StructTree_CountChildren) (*responses.FPDF_StructTree_CountChildren, error) + FPDF_StructTree_GetChildAtIndex(*requests.FPDF_StructTree_GetChildAtIndex) (*responses.FPDF_StructTree_GetChildAtIndex, error) + FPDF_StructTree_GetForPage(*requests.FPDF_StructTree_GetForPage) (*responses.FPDF_StructTree_GetForPage, error) FPDF_VIEWERREF_GetDuplex(*requests.FPDF_VIEWERREF_GetDuplex) (*responses.FPDF_VIEWERREF_GetDuplex, error) FPDF_VIEWERREF_GetName(*requests.FPDF_VIEWERREF_GetName) (*responses.FPDF_VIEWERREF_GetName, error) FPDF_VIEWERREF_GetNumCopies(*requests.FPDF_VIEWERREF_GetNumCopies) (*responses.FPDF_VIEWERREF_GetNumCopies, error) @@ -1999,6 +2012,136 @@ func (g *PdfiumRPC) FPDF_SetSandBoxPolicy(request *requests.FPDF_SetSandBoxPolic return resp, nil } +func (g *PdfiumRPC) FPDF_StructElement_CountChildren(request *requests.FPDF_StructElement_CountChildren) (*responses.FPDF_StructElement_CountChildren, error) { + resp := &responses.FPDF_StructElement_CountChildren{} + err := g.client.Call("Plugin.FPDF_StructElement_CountChildren", request, resp) + if err != nil { + return nil, err + } + + return resp, nil +} + +func (g *PdfiumRPC) FPDF_StructElement_GetAltText(request *requests.FPDF_StructElement_GetAltText) (*responses.FPDF_StructElement_GetAltText, error) { + resp := &responses.FPDF_StructElement_GetAltText{} + err := g.client.Call("Plugin.FPDF_StructElement_GetAltText", request, resp) + if err != nil { + return nil, err + } + + return resp, nil +} + +func (g *PdfiumRPC) FPDF_StructElement_GetChildAtIndex(request *requests.FPDF_StructElement_GetChildAtIndex) (*responses.FPDF_StructElement_GetChildAtIndex, error) { + resp := &responses.FPDF_StructElement_GetChildAtIndex{} + err := g.client.Call("Plugin.FPDF_StructElement_GetChildAtIndex", request, resp) + if err != nil { + return nil, err + } + + return resp, nil +} + +func (g *PdfiumRPC) FPDF_StructElement_GetID(request *requests.FPDF_StructElement_GetID) (*responses.FPDF_StructElement_GetID, error) { + resp := &responses.FPDF_StructElement_GetID{} + err := g.client.Call("Plugin.FPDF_StructElement_GetID", request, resp) + if err != nil { + return nil, err + } + + return resp, nil +} + +func (g *PdfiumRPC) FPDF_StructElement_GetLang(request *requests.FPDF_StructElement_GetLang) (*responses.FPDF_StructElement_GetLang, error) { + resp := &responses.FPDF_StructElement_GetLang{} + err := g.client.Call("Plugin.FPDF_StructElement_GetLang", request, resp) + if err != nil { + return nil, err + } + + return resp, nil +} + +func (g *PdfiumRPC) FPDF_StructElement_GetMarkedContentID(request *requests.FPDF_StructElement_GetMarkedContentID) (*responses.FPDF_StructElement_GetMarkedContentID, error) { + resp := &responses.FPDF_StructElement_GetMarkedContentID{} + err := g.client.Call("Plugin.FPDF_StructElement_GetMarkedContentID", request, resp) + if err != nil { + return nil, err + } + + return resp, nil +} + +func (g *PdfiumRPC) FPDF_StructElement_GetStringAttribute(request *requests.FPDF_StructElement_GetStringAttribute) (*responses.FPDF_StructElement_GetStringAttribute, error) { + resp := &responses.FPDF_StructElement_GetStringAttribute{} + err := g.client.Call("Plugin.FPDF_StructElement_GetStringAttribute", request, resp) + if err != nil { + return nil, err + } + + return resp, nil +} + +func (g *PdfiumRPC) FPDF_StructElement_GetTitle(request *requests.FPDF_StructElement_GetTitle) (*responses.FPDF_StructElement_GetTitle, error) { + resp := &responses.FPDF_StructElement_GetTitle{} + err := g.client.Call("Plugin.FPDF_StructElement_GetTitle", request, resp) + if err != nil { + return nil, err + } + + return resp, nil +} + +func (g *PdfiumRPC) FPDF_StructElement_GetType(request *requests.FPDF_StructElement_GetType) (*responses.FPDF_StructElement_GetType, error) { + resp := &responses.FPDF_StructElement_GetType{} + err := g.client.Call("Plugin.FPDF_StructElement_GetType", request, resp) + if err != nil { + return nil, err + } + + return resp, nil +} + +func (g *PdfiumRPC) FPDF_StructTree_Close(request *requests.FPDF_StructTree_Close) (*responses.FPDF_StructTree_Close, error) { + resp := &responses.FPDF_StructTree_Close{} + err := g.client.Call("Plugin.FPDF_StructTree_Close", request, resp) + if err != nil { + return nil, err + } + + return resp, nil +} + +func (g *PdfiumRPC) FPDF_StructTree_CountChildren(request *requests.FPDF_StructTree_CountChildren) (*responses.FPDF_StructTree_CountChildren, error) { + resp := &responses.FPDF_StructTree_CountChildren{} + err := g.client.Call("Plugin.FPDF_StructTree_CountChildren", request, resp) + if err != nil { + return nil, err + } + + return resp, nil +} + +func (g *PdfiumRPC) FPDF_StructTree_GetChildAtIndex(request *requests.FPDF_StructTree_GetChildAtIndex) (*responses.FPDF_StructTree_GetChildAtIndex, error) { + resp := &responses.FPDF_StructTree_GetChildAtIndex{} + err := g.client.Call("Plugin.FPDF_StructTree_GetChildAtIndex", request, resp) + if err != nil { + return nil, err + } + + return resp, nil +} + +func (g *PdfiumRPC) FPDF_StructTree_GetForPage(request *requests.FPDF_StructTree_GetForPage) (*responses.FPDF_StructTree_GetForPage, error) { + resp := &responses.FPDF_StructTree_GetForPage{} + err := g.client.Call("Plugin.FPDF_StructTree_GetForPage", request, resp) + if err != nil { + return nil, err + } + + return resp, nil +} + func (g *PdfiumRPC) FPDF_VIEWERREF_GetDuplex(request *requests.FPDF_VIEWERREF_GetDuplex) (*responses.FPDF_VIEWERREF_GetDuplex, error) { resp := &responses.FPDF_VIEWERREF_GetDuplex{} err := g.client.Call("Plugin.FPDF_VIEWERREF_GetDuplex", request, resp) @@ -5463,6 +5606,240 @@ func (s *PdfiumRPCServer) FPDF_SetSandBoxPolicy(request *requests.FPDF_SetSandBo return nil } +func (s *PdfiumRPCServer) FPDF_StructElement_CountChildren(request *requests.FPDF_StructElement_CountChildren, resp *responses.FPDF_StructElement_CountChildren) (err error) { + defer func() { + if panicError := recover(); panicError != nil { + err = fmt.Errorf("panic occurred in %s: %v", "FPDF_StructElement_CountChildren", panicError) + } + }() + + implResp, err := s.Impl.FPDF_StructElement_CountChildren(request) + if err != nil { + return err + } + + // Overwrite the target address of resp to the target address of implResp. + *resp = *implResp + + return nil +} + +func (s *PdfiumRPCServer) FPDF_StructElement_GetAltText(request *requests.FPDF_StructElement_GetAltText, resp *responses.FPDF_StructElement_GetAltText) (err error) { + defer func() { + if panicError := recover(); panicError != nil { + err = fmt.Errorf("panic occurred in %s: %v", "FPDF_StructElement_GetAltText", panicError) + } + }() + + implResp, err := s.Impl.FPDF_StructElement_GetAltText(request) + if err != nil { + return err + } + + // Overwrite the target address of resp to the target address of implResp. + *resp = *implResp + + return nil +} + +func (s *PdfiumRPCServer) FPDF_StructElement_GetChildAtIndex(request *requests.FPDF_StructElement_GetChildAtIndex, resp *responses.FPDF_StructElement_GetChildAtIndex) (err error) { + defer func() { + if panicError := recover(); panicError != nil { + err = fmt.Errorf("panic occurred in %s: %v", "FPDF_StructElement_GetChildAtIndex", panicError) + } + }() + + implResp, err := s.Impl.FPDF_StructElement_GetChildAtIndex(request) + if err != nil { + return err + } + + // Overwrite the target address of resp to the target address of implResp. + *resp = *implResp + + return nil +} + +func (s *PdfiumRPCServer) FPDF_StructElement_GetID(request *requests.FPDF_StructElement_GetID, resp *responses.FPDF_StructElement_GetID) (err error) { + defer func() { + if panicError := recover(); panicError != nil { + err = fmt.Errorf("panic occurred in %s: %v", "FPDF_StructElement_GetID", panicError) + } + }() + + implResp, err := s.Impl.FPDF_StructElement_GetID(request) + if err != nil { + return err + } + + // Overwrite the target address of resp to the target address of implResp. + *resp = *implResp + + return nil +} + +func (s *PdfiumRPCServer) FPDF_StructElement_GetLang(request *requests.FPDF_StructElement_GetLang, resp *responses.FPDF_StructElement_GetLang) (err error) { + defer func() { + if panicError := recover(); panicError != nil { + err = fmt.Errorf("panic occurred in %s: %v", "FPDF_StructElement_GetLang", panicError) + } + }() + + implResp, err := s.Impl.FPDF_StructElement_GetLang(request) + if err != nil { + return err + } + + // Overwrite the target address of resp to the target address of implResp. + *resp = *implResp + + return nil +} + +func (s *PdfiumRPCServer) FPDF_StructElement_GetMarkedContentID(request *requests.FPDF_StructElement_GetMarkedContentID, resp *responses.FPDF_StructElement_GetMarkedContentID) (err error) { + defer func() { + if panicError := recover(); panicError != nil { + err = fmt.Errorf("panic occurred in %s: %v", "FPDF_StructElement_GetMarkedContentID", panicError) + } + }() + + implResp, err := s.Impl.FPDF_StructElement_GetMarkedContentID(request) + if err != nil { + return err + } + + // Overwrite the target address of resp to the target address of implResp. + *resp = *implResp + + return nil +} + +func (s *PdfiumRPCServer) FPDF_StructElement_GetStringAttribute(request *requests.FPDF_StructElement_GetStringAttribute, resp *responses.FPDF_StructElement_GetStringAttribute) (err error) { + defer func() { + if panicError := recover(); panicError != nil { + err = fmt.Errorf("panic occurred in %s: %v", "FPDF_StructElement_GetStringAttribute", panicError) + } + }() + + implResp, err := s.Impl.FPDF_StructElement_GetStringAttribute(request) + if err != nil { + return err + } + + // Overwrite the target address of resp to the target address of implResp. + *resp = *implResp + + return nil +} + +func (s *PdfiumRPCServer) FPDF_StructElement_GetTitle(request *requests.FPDF_StructElement_GetTitle, resp *responses.FPDF_StructElement_GetTitle) (err error) { + defer func() { + if panicError := recover(); panicError != nil { + err = fmt.Errorf("panic occurred in %s: %v", "FPDF_StructElement_GetTitle", panicError) + } + }() + + implResp, err := s.Impl.FPDF_StructElement_GetTitle(request) + if err != nil { + return err + } + + // Overwrite the target address of resp to the target address of implResp. + *resp = *implResp + + return nil +} + +func (s *PdfiumRPCServer) FPDF_StructElement_GetType(request *requests.FPDF_StructElement_GetType, resp *responses.FPDF_StructElement_GetType) (err error) { + defer func() { + if panicError := recover(); panicError != nil { + err = fmt.Errorf("panic occurred in %s: %v", "FPDF_StructElement_GetType", panicError) + } + }() + + implResp, err := s.Impl.FPDF_StructElement_GetType(request) + if err != nil { + return err + } + + // Overwrite the target address of resp to the target address of implResp. + *resp = *implResp + + return nil +} + +func (s *PdfiumRPCServer) FPDF_StructTree_Close(request *requests.FPDF_StructTree_Close, resp *responses.FPDF_StructTree_Close) (err error) { + defer func() { + if panicError := recover(); panicError != nil { + err = fmt.Errorf("panic occurred in %s: %v", "FPDF_StructTree_Close", panicError) + } + }() + + implResp, err := s.Impl.FPDF_StructTree_Close(request) + if err != nil { + return err + } + + // Overwrite the target address of resp to the target address of implResp. + *resp = *implResp + + return nil +} + +func (s *PdfiumRPCServer) FPDF_StructTree_CountChildren(request *requests.FPDF_StructTree_CountChildren, resp *responses.FPDF_StructTree_CountChildren) (err error) { + defer func() { + if panicError := recover(); panicError != nil { + err = fmt.Errorf("panic occurred in %s: %v", "FPDF_StructTree_CountChildren", panicError) + } + }() + + implResp, err := s.Impl.FPDF_StructTree_CountChildren(request) + if err != nil { + return err + } + + // Overwrite the target address of resp to the target address of implResp. + *resp = *implResp + + return nil +} + +func (s *PdfiumRPCServer) FPDF_StructTree_GetChildAtIndex(request *requests.FPDF_StructTree_GetChildAtIndex, resp *responses.FPDF_StructTree_GetChildAtIndex) (err error) { + defer func() { + if panicError := recover(); panicError != nil { + err = fmt.Errorf("panic occurred in %s: %v", "FPDF_StructTree_GetChildAtIndex", panicError) + } + }() + + implResp, err := s.Impl.FPDF_StructTree_GetChildAtIndex(request) + if err != nil { + return err + } + + // Overwrite the target address of resp to the target address of implResp. + *resp = *implResp + + return nil +} + +func (s *PdfiumRPCServer) FPDF_StructTree_GetForPage(request *requests.FPDF_StructTree_GetForPage, resp *responses.FPDF_StructTree_GetForPage) (err error) { + defer func() { + if panicError := recover(); panicError != nil { + err = fmt.Errorf("panic occurred in %s: %v", "FPDF_StructTree_GetForPage", panicError) + } + }() + + implResp, err := s.Impl.FPDF_StructTree_GetForPage(request) + if err != nil { + return err + } + + // Overwrite the target address of resp to the target address of implResp. + *resp = *implResp + + return nil +} + func (s *PdfiumRPCServer) FPDF_VIEWERREF_GetDuplex(request *requests.FPDF_VIEWERREF_GetDuplex, resp *responses.FPDF_VIEWERREF_GetDuplex) (err error) { defer func() { if panicError := recover(); panicError != nil { diff --git a/internal/implementation/dest.go b/internal/implementation/dest.go index 598c6c91..3ecfdc3d 100644 --- a/internal/implementation/dest.go +++ b/internal/implementation/dest.go @@ -4,10 +4,11 @@ package implementation // #include "fpdf_doc.h" import "C" import ( - "github.com/google/uuid" "github.com/klippa-app/go-pdfium/references" "github.com/klippa-app/go-pdfium/requests" "github.com/klippa-app/go-pdfium/responses" + + "github.com/google/uuid" ) func (p *PdfiumImplementation) registerDest(dest C.FPDF_DEST, documentHandle *DocumentHandle) *DestHandle { diff --git a/internal/implementation/fpdf_structtree.go b/internal/implementation/fpdf_structtree.go index dc9aabbc..465d8295 100644 --- a/internal/implementation/fpdf_structtree.go +++ b/internal/implementation/fpdf_structtree.go @@ -1 +1,254 @@ package implementation + +// #cgo pkg-config: pdfium +// #include "fpdf_structtree.h" +import "C" +import ( + "errors" + "unsafe" + + "github.com/klippa-app/go-pdfium/requests" + "github.com/klippa-app/go-pdfium/responses" +) + +// FPDF_StructTree_GetForPage returns the structure tree for a page. +func (p *PdfiumImplementation) FPDF_StructTree_GetForPage(request *requests.FPDF_StructTree_GetForPage) (*responses.FPDF_StructTree_GetForPage, error) { + p.Lock() + defer p.Unlock() + + pageHandle, err := p.loadPage(request.Page) + if err != nil { + return nil, err + } + + documentHandle, err := p.getDocumentHandle(pageHandle.documentRef) + if err != nil { + return nil, err + } + + structTree := C.FPDF_StructTree_GetForPage(pageHandle.handle) + if structTree == nil { + return nil, errors.New("could not load struct tree") + } + + structTreeHandle := p.registerStructTree(structTree, documentHandle) + + return &responses.FPDF_StructTree_GetForPage{ + StructTree: structTreeHandle.nativeRef, + }, nil +} + +// FPDF_StructTree_Close releases a resource allocated by FPDF_StructTree_GetForPage(). +func (p *PdfiumImplementation) FPDF_StructTree_Close(request *requests.FPDF_StructTree_Close) (*responses.FPDF_StructTree_Close, error) { + p.Lock() + defer p.Unlock() + + structTreeHandle, err := p.getStructTreeHandle(request.StructTree) + if err != nil { + return nil, err + } + + C.FPDF_StructTree_Close(structTreeHandle.handle) + + delete(p.structTreeRefs, structTreeHandle.nativeRef) + + documentHandle, err := p.getDocumentHandle(structTreeHandle.documentRef) + if err != nil { + return nil, err + } + + delete(documentHandle.structTreeRefs, structTreeHandle.nativeRef) + + return &responses.FPDF_StructTree_Close{}, nil +} + +// FPDF_StructTree_CountChildren counts the number of children for the structure tree. +func (p *PdfiumImplementation) FPDF_StructTree_CountChildren(request *requests.FPDF_StructTree_CountChildren) (*responses.FPDF_StructTree_CountChildren, error) { + p.Lock() + defer p.Unlock() + + structTreeHandle, err := p.getStructTreeHandle(request.StructTree) + if err != nil { + return nil, err + } + + count := C.FPDF_StructTree_CountChildren(structTreeHandle.handle) + + return &responses.FPDF_StructTree_CountChildren{ + Count: int(count), + }, nil +} + +// FPDF_StructTree_GetChildAtIndex returns a child in the structure tree. +func (p *PdfiumImplementation) FPDF_StructTree_GetChildAtIndex(request *requests.FPDF_StructTree_GetChildAtIndex) (*responses.FPDF_StructTree_GetChildAtIndex, error) { + p.Lock() + defer p.Unlock() + + structTreeHandle, err := p.getStructTreeHandle(request.StructTree) + if err != nil { + return nil, err + } + + child := C.FPDF_StructTree_GetChildAtIndex(structTreeHandle.handle, C.int(request.Index)) + if child == nil { + return nil, errors.New("could not load struct tree child") + } + + documentHandle, err := p.getDocumentHandle(structTreeHandle.documentRef) + if err != nil { + return nil, err + } + + structElementHandle := p.registerStructElement(child, documentHandle) + + return &responses.FPDF_StructTree_GetChildAtIndex{ + StructElement: structElementHandle.nativeRef, + }, nil +} + +// FPDF_StructElement_GetAltText returns the alt text for a given element. +func (p *PdfiumImplementation) FPDF_StructElement_GetAltText(request *requests.FPDF_StructElement_GetAltText) (*responses.FPDF_StructElement_GetAltText, error) { + p.Lock() + defer p.Unlock() + + structElementHandle, err := p.getStructElementHandle(request.StructElement) + if err != nil { + return nil, err + } + + altTextLength := C.FPDF_StructElement_GetAltText(structElementHandle.handle, nil, 0) + if altTextLength == 0 { + return nil, errors.New("Could not get alt text") + } + + charData := make([]byte, altTextLength) + C.FPDF_StructElement_GetAltText(structElementHandle.handle, unsafe.Pointer(&charData[0]), C.ulong(len(charData))) + + transformedText, err := p.transformUTF16LEToUTF8(charData) + if err != nil { + return nil, err + } + + return &responses.FPDF_StructElement_GetAltText{ + AltText: transformedText, + }, nil +} + +// FPDF_StructElement_GetMarkedContentID returns the marked content ID for a given element. +func (p *PdfiumImplementation) FPDF_StructElement_GetMarkedContentID(request *requests.FPDF_StructElement_GetMarkedContentID) (*responses.FPDF_StructElement_GetMarkedContentID, error) { + p.Lock() + defer p.Unlock() + + structElementHandle, err := p.getStructElementHandle(request.StructElement) + if err != nil { + return nil, err + } + + markedContentID := C.FPDF_StructElement_GetMarkedContentID(structElementHandle.handle) + + return &responses.FPDF_StructElement_GetMarkedContentID{ + MarkedContentID: int(markedContentID), + }, nil +} + +// FPDF_StructElement_GetType returns the type (/S) for a given element. +func (p *PdfiumImplementation) FPDF_StructElement_GetType(request *requests.FPDF_StructElement_GetType) (*responses.FPDF_StructElement_GetType, error) { + p.Lock() + defer p.Unlock() + + structElementHandle, err := p.getStructElementHandle(request.StructElement) + if err != nil { + return nil, err + } + + typeLength := C.FPDF_StructElement_GetType(structElementHandle.handle, nil, 0) + if typeLength == 0 { + return nil, errors.New("Could not get type") + } + + charData := make([]byte, typeLength) + C.FPDF_StructElement_GetType(structElementHandle.handle, unsafe.Pointer(&charData[0]), C.ulong(len(charData))) + + transformedText, err := p.transformUTF16LEToUTF8(charData) + if err != nil { + return nil, err + } + + return &responses.FPDF_StructElement_GetType{ + Type: transformedText, + }, nil +} + +// FPDF_StructElement_GetTitle returns the title (/T) for a given element. +func (p *PdfiumImplementation) FPDF_StructElement_GetTitle(request *requests.FPDF_StructElement_GetTitle) (*responses.FPDF_StructElement_GetTitle, error) { + p.Lock() + defer p.Unlock() + + structElementHandle, err := p.getStructElementHandle(request.StructElement) + if err != nil { + return nil, err + } + + titleLength := C.FPDF_StructElement_GetTitle(structElementHandle.handle, nil, 0) + if titleLength == 0 { + return nil, errors.New("Could not get title") + } + + charData := make([]byte, titleLength) + C.FPDF_StructElement_GetTitle(structElementHandle.handle, unsafe.Pointer(&charData[0]), C.ulong(len(charData))) + + transformedText, err := p.transformUTF16LEToUTF8(charData) + if err != nil { + return nil, err + } + + return &responses.FPDF_StructElement_GetTitle{ + Title: transformedText, + }, nil +} + +// FPDF_StructElement_CountChildren counts the number of children for the structure element. +func (p *PdfiumImplementation) FPDF_StructElement_CountChildren(request *requests.FPDF_StructElement_CountChildren) (*responses.FPDF_StructElement_CountChildren, error) { + p.Lock() + defer p.Unlock() + + structElementHandle, err := p.getStructElementHandle(request.StructElement) + if err != nil { + return nil, err + } + + count := C.FPDF_StructElement_CountChildren(structElementHandle.handle) + + return &responses.FPDF_StructElement_CountChildren{ + Count: int(count), + }, nil +} + +// FPDF_StructElement_GetChildAtIndex returns a child in the structure element. +// If the child exists but is not an element, then this function will +// return an error. This will also return an error for out of bounds indices. +func (p *PdfiumImplementation) FPDF_StructElement_GetChildAtIndex(request *requests.FPDF_StructElement_GetChildAtIndex) (*responses.FPDF_StructElement_GetChildAtIndex, error) { + p.Lock() + defer p.Unlock() + + parentStructElementHandle, err := p.getStructElementHandle(request.StructElement) + if err != nil { + return nil, err + } + + child := C.FPDF_StructElement_GetChildAtIndex(parentStructElementHandle.handle, C.int(request.Index)) + if child == nil { + return nil, errors.New("could not load struct element child") + } + + documentHandle, err := p.getDocumentHandle(parentStructElementHandle.documentRef) + if err != nil { + return nil, err + } + + structElementHandle := p.registerStructElement(child, documentHandle) + + return &responses.FPDF_StructElement_GetChildAtIndex{ + StructElement: structElementHandle.nativeRef, + }, nil +} diff --git a/internal/implementation/fpdf_structtree_experimental.go b/internal/implementation/fpdf_structtree_experimental.go new file mode 100644 index 00000000..3509358f --- /dev/null +++ b/internal/implementation/fpdf_structtree_experimental.go @@ -0,0 +1,107 @@ +//go:build pdfium_experimental +// +build pdfium_experimental + +package implementation + +// #cgo pkg-config: pdfium +// #include "fpdf_structtree.h" +// #include +import "C" +import ( + "errors" + "unsafe" + + "github.com/klippa-app/go-pdfium/requests" + "github.com/klippa-app/go-pdfium/responses" +) + +// FPDF_StructElement_GetID returns the ID for a given element. +// Experimental API. +func (p *PdfiumImplementation) FPDF_StructElement_GetID(request *requests.FPDF_StructElement_GetID) (*responses.FPDF_StructElement_GetID, error) { + p.Lock() + defer p.Unlock() + + structElementHandle, err := p.getStructElementHandle(request.StructElement) + if err != nil { + return nil, err + } + + idLength := C.FPDF_StructElement_GetID(structElementHandle.handle, nil, 0) + if idLength == 0 { + return nil, errors.New("Could not get ID") + } + + charData := make([]byte, idLength) + C.FPDF_StructElement_GetID(structElementHandle.handle, unsafe.Pointer(&charData[0]), C.ulong(len(charData))) + + transformedText, err := p.transformUTF16LEToUTF8(charData) + if err != nil { + return nil, err + } + + return &responses.FPDF_StructElement_GetID{ + ID: transformedText, + }, nil +} + +// FPDF_StructElement_GetLang returns the case-insensitive IETF BCP 47 language code for an element. +// Experimental API. +func (p *PdfiumImplementation) FPDF_StructElement_GetLang(request *requests.FPDF_StructElement_GetLang) (*responses.FPDF_StructElement_GetLang, error) { + p.Lock() + defer p.Unlock() + + structElementHandle, err := p.getStructElementHandle(request.StructElement) + if err != nil { + return nil, err + } + + langLength := C.FPDF_StructElement_GetLang(structElementHandle.handle, nil, 0) + if langLength == 0 { + return nil, errors.New("Could not get lang") + } + + charData := make([]byte, langLength) + C.FPDF_StructElement_GetLang(structElementHandle.handle, unsafe.Pointer(&charData[0]), C.ulong(len(charData))) + + transformedText, err := p.transformUTF16LEToUTF8(charData) + if err != nil { + return nil, err + } + + return &responses.FPDF_StructElement_GetLang{ + Lang: transformedText, + }, nil +} + +// FPDF_StructElement_GetStringAttribute returns a struct element attribute of type "name" or "string" +// Experimental API. +func (p *PdfiumImplementation) FPDF_StructElement_GetStringAttribute(request *requests.FPDF_StructElement_GetStringAttribute) (*responses.FPDF_StructElement_GetStringAttribute, error) { + p.Lock() + defer p.Unlock() + + structElementHandle, err := p.getStructElementHandle(request.StructElement) + if err != nil { + return nil, err + } + + attributeName := C.CString(request.AttributeName) + defer C.free(unsafe.Pointer(attributeName)) + + attributeLength := C.FPDF_StructElement_GetStringAttribute(structElementHandle.handle, attributeName, nil, 0) + if attributeLength == 0 { + return nil, errors.New("could not get attribute") + } + + charData := make([]byte, attributeLength) + C.FPDF_StructElement_GetStringAttribute(structElementHandle.handle, attributeName, unsafe.Pointer(&charData[0]), C.ulong(len(charData))) + + transformedText, err := p.transformUTF16LEToUTF8(charData) + if err != nil { + return nil, err + } + + return &responses.FPDF_StructElement_GetStringAttribute{ + Attribute: request.AttributeName, + Value: transformedText, + }, nil +} diff --git a/internal/implementation/fpdf_structtree_no_experimental.go b/internal/implementation/fpdf_structtree_no_experimental.go new file mode 100644 index 00000000..ec31d1d5 --- /dev/null +++ b/internal/implementation/fpdf_structtree_no_experimental.go @@ -0,0 +1,28 @@ +//go:build !pdfium_experimental +// +build !pdfium_experimental + +package implementation + +import ( + pdfium_errors "github.com/klippa-app/go-pdfium/errors" + "github.com/klippa-app/go-pdfium/requests" + "github.com/klippa-app/go-pdfium/responses" +) + +// FPDF_StructElement_GetID returns the ID for a given element. +// Experimental API. +func (p *PdfiumImplementation) FPDF_StructElement_GetID(request *requests.FPDF_StructElement_GetID) (*responses.FPDF_StructElement_GetID, error) { + return nil, pdfium_errors.ErrExperimentalUnsupported +} + +// FPDF_StructElement_GetLang returns the case-insensitive IETF BCP 47 language code for an element. +// Experimental API. +func (p *PdfiumImplementation) FPDF_StructElement_GetLang(request *requests.FPDF_StructElement_GetLang) (*responses.FPDF_StructElement_GetLang, error) { + return nil, pdfium_errors.ErrExperimentalUnsupported +} + +// FPDF_StructElement_GetStringAttribute returns a struct element attribute of type "name" or "string" +// Experimental API. +func (p *PdfiumImplementation) FPDF_StructElement_GetStringAttribute(request *requests.FPDF_StructElement_GetStringAttribute) (*responses.FPDF_StructElement_GetStringAttribute, error) { + return nil, pdfium_errors.ErrExperimentalUnsupported +} diff --git a/internal/implementation/handles.go b/internal/implementation/handles.go index 9adc803e..f7a0f9e8 100644 --- a/internal/implementation/handles.go +++ b/internal/implementation/handles.go @@ -38,6 +38,8 @@ type DocumentHandle struct { attachmentRefs map[references.FPDF_ATTACHMENT]*AttachmentHandle javaScriptActionRefs map[references.FPDF_JAVASCRIPT_ACTION]*JavaScriptActionHandle searchRefs map[references.FPDF_SCHHANDLE]*SearchHandle + structTreeRefs map[references.FPDF_STRUCTTREE]*StructTreeHandle + structElementRefs map[references.FPDF_STRUCTELEMENT]*StructElementHandle } func (d *DocumentHandle) getPageHandle(pageRef references.FPDF_PAGE) (*PageHandle, error) { @@ -145,6 +147,14 @@ func (d *DocumentHandle) Close() error { delete(d.searchRefs, i) } + for i := range d.structTreeRefs { + delete(d.structTreeRefs, i) + } + + for i := range d.structElementRefs { + delete(d.structElementRefs, i) + } + C.FPDF_CloseDocument(d.handle) d.handle = nil @@ -290,3 +300,15 @@ type PathSegmentHandle struct { handle C.FPDF_PATHSEGMENT nativeRef references.FPDF_PATHSEGMENT // A string that is our reference inside the process. We need this to close the references in DestroyLibrary. } + +type StructTreeHandle struct { + handle C.FPDF_STRUCTTREE + documentRef references.FPDF_DOCUMENT + nativeRef references.FPDF_STRUCTTREE // A string that is our reference inside the process. We need this to close the references in DestroyLibrary. +} + +type StructElementHandle struct { + handle C.FPDF_STRUCTELEMENT + documentRef references.FPDF_DOCUMENT + nativeRef references.FPDF_STRUCTELEMENT // A string that is our reference inside the process. We need this to close the references in DestroyLibrary. +} diff --git a/internal/implementation/implementation.go b/internal/implementation/implementation.go index 25932f56..36d5db01 100644 --- a/internal/implementation/implementation.go +++ b/internal/implementation/implementation.go @@ -186,6 +186,8 @@ func (p *mainPdfium) GetInstance() *PdfiumImplementation { searchRefs: map[references.FPDF_SCHHANDLE]*SearchHandle{}, pathSegmentRefs: map[references.FPDF_PATHSEGMENT]*PathSegmentHandle{}, dataAvailRefs: map[references.FPDF_AVAIL]*DataAvailHandle{}, + structTreeRefs: map[references.FPDF_STRUCTTREE]*StructTreeHandle{}, + structElementRefs: map[references.FPDF_STRUCTELEMENT]*StructElementHandle{}, } newInstance.instanceRef = len(p.instanceRefs) @@ -224,6 +226,8 @@ type PdfiumImplementation struct { searchRefs map[references.FPDF_SCHHANDLE]*SearchHandle pathSegmentRefs map[references.FPDF_PATHSEGMENT]*PathSegmentHandle dataAvailRefs map[references.FPDF_AVAIL]*DataAvailHandle + structTreeRefs map[references.FPDF_STRUCTTREE]*StructTreeHandle + structElementRefs map[references.FPDF_STRUCTELEMENT]*StructElementHandle // We need to keep track of our own instance. instanceRef int @@ -269,6 +273,8 @@ func (p *PdfiumImplementation) OpenDocument(request *requests.OpenDocument) (*re attachmentRefs: map[references.FPDF_ATTACHMENT]*AttachmentHandle{}, javaScriptActionRefs: map[references.FPDF_JAVASCRIPT_ACTION]*JavaScriptActionHandle{}, searchRefs: map[references.FPDF_SCHHANDLE]*SearchHandle{}, + structTreeRefs: map[references.FPDF_STRUCTTREE]*StructTreeHandle{}, + structElementRefs: map[references.FPDF_STRUCTELEMENT]*StructElementHandle{}, } var doc C.FPDF_DOCUMENT @@ -468,6 +474,14 @@ func (p *PdfiumImplementation) Close() error { delete(p.dataAvailRefs, i) } + for i := range p.structTreeRefs { + delete(p.structTreeRefs, i) + } + + for i := range p.structElementRefs { + delete(p.structElementRefs, i) + } + delete(Pdfium.instanceRefs, p.instanceRef) return nil @@ -688,3 +702,27 @@ func (p *PdfiumImplementation) getDataAvailHandle(dataAvail references.FPDF_AVAI return nil, errors.New("could not find dataAvail handle, perhaps the dataAvail was already closed or you tried to share dataAvails between instances") } + +func (p *PdfiumImplementation) getStructTreeHandle(structTree references.FPDF_STRUCTTREE) (*StructTreeHandle, error) { + if structTree == "" { + return nil, errors.New("structTree not given") + } + + if val, ok := p.structTreeRefs[structTree]; ok { + return val, nil + } + + return nil, errors.New("could not find structTree handle, perhaps the structTree was already closed or you tried to share structTrees between instances") +} + +func (p *PdfiumImplementation) getStructElementHandle(structElement references.FPDF_STRUCTELEMENT) (*StructElementHandle, error) { + if structElement == "" { + return nil, errors.New("structElement not given") + } + + if val, ok := p.structElementRefs[structElement]; ok { + return val, nil + } + + return nil, errors.New("could not find structElement handle, perhaps the structElement was already closed or you tried to share structElements between instances") +} diff --git a/internal/implementation/struct_tree.go b/internal/implementation/struct_tree.go new file mode 100644 index 00000000..3502a1fb --- /dev/null +++ b/internal/implementation/struct_tree.go @@ -0,0 +1,38 @@ +package implementation + +// #cgo pkg-config: pdfium +// #include "fpdf_structtree.h" +import "C" +import ( + "github.com/klippa-app/go-pdfium/references" + + "github.com/google/uuid" +) + +func (p *PdfiumImplementation) registerStructTree(structTree C.FPDF_STRUCTTREE, documentHandle *DocumentHandle) *StructTreeHandle { + ref := uuid.New() + handle := &StructTreeHandle{ + handle: structTree, + nativeRef: references.FPDF_STRUCTTREE(ref.String()), + documentRef: documentHandle.nativeRef, + } + + documentHandle.structTreeRefs[handle.nativeRef] = handle + p.structTreeRefs[handle.nativeRef] = handle + + return handle +} + +func (p *PdfiumImplementation) registerStructElement(structElement C.FPDF_STRUCTELEMENT, documentHandle *DocumentHandle) *StructElementHandle { + ref := uuid.New() + handle := &StructElementHandle{ + handle: structElement, + nativeRef: references.FPDF_STRUCTELEMENT(ref.String()), + documentRef: documentHandle.nativeRef, + } + + documentHandle.structElementRefs[handle.nativeRef] = handle + p.structElementRefs[handle.nativeRef] = handle + + return handle +} diff --git a/multi_threaded/generated.go b/multi_threaded/generated.go index e2d5679e..49188858 100644 --- a/multi_threaded/generated.go +++ b/multi_threaded/generated.go @@ -1395,6 +1395,110 @@ func (i *pdfiumInstance) FPDF_SetSandBoxPolicy(request *requests.FPDF_SetSandBox return i.worker.plugin.FPDF_SetSandBoxPolicy(request) } +func (i *pdfiumInstance) FPDF_StructElement_CountChildren(request *requests.FPDF_StructElement_CountChildren) (*responses.FPDF_StructElement_CountChildren, error) { + if i.closed { + return nil, errors.New("instance is closed") + } + + return i.worker.plugin.FPDF_StructElement_CountChildren(request) +} + +func (i *pdfiumInstance) FPDF_StructElement_GetAltText(request *requests.FPDF_StructElement_GetAltText) (*responses.FPDF_StructElement_GetAltText, error) { + if i.closed { + return nil, errors.New("instance is closed") + } + + return i.worker.plugin.FPDF_StructElement_GetAltText(request) +} + +func (i *pdfiumInstance) FPDF_StructElement_GetChildAtIndex(request *requests.FPDF_StructElement_GetChildAtIndex) (*responses.FPDF_StructElement_GetChildAtIndex, error) { + if i.closed { + return nil, errors.New("instance is closed") + } + + return i.worker.plugin.FPDF_StructElement_GetChildAtIndex(request) +} + +func (i *pdfiumInstance) FPDF_StructElement_GetID(request *requests.FPDF_StructElement_GetID) (*responses.FPDF_StructElement_GetID, error) { + if i.closed { + return nil, errors.New("instance is closed") + } + + return i.worker.plugin.FPDF_StructElement_GetID(request) +} + +func (i *pdfiumInstance) FPDF_StructElement_GetLang(request *requests.FPDF_StructElement_GetLang) (*responses.FPDF_StructElement_GetLang, error) { + if i.closed { + return nil, errors.New("instance is closed") + } + + return i.worker.plugin.FPDF_StructElement_GetLang(request) +} + +func (i *pdfiumInstance) FPDF_StructElement_GetMarkedContentID(request *requests.FPDF_StructElement_GetMarkedContentID) (*responses.FPDF_StructElement_GetMarkedContentID, error) { + if i.closed { + return nil, errors.New("instance is closed") + } + + return i.worker.plugin.FPDF_StructElement_GetMarkedContentID(request) +} + +func (i *pdfiumInstance) FPDF_StructElement_GetStringAttribute(request *requests.FPDF_StructElement_GetStringAttribute) (*responses.FPDF_StructElement_GetStringAttribute, error) { + if i.closed { + return nil, errors.New("instance is closed") + } + + return i.worker.plugin.FPDF_StructElement_GetStringAttribute(request) +} + +func (i *pdfiumInstance) FPDF_StructElement_GetTitle(request *requests.FPDF_StructElement_GetTitle) (*responses.FPDF_StructElement_GetTitle, error) { + if i.closed { + return nil, errors.New("instance is closed") + } + + return i.worker.plugin.FPDF_StructElement_GetTitle(request) +} + +func (i *pdfiumInstance) FPDF_StructElement_GetType(request *requests.FPDF_StructElement_GetType) (*responses.FPDF_StructElement_GetType, error) { + if i.closed { + return nil, errors.New("instance is closed") + } + + return i.worker.plugin.FPDF_StructElement_GetType(request) +} + +func (i *pdfiumInstance) FPDF_StructTree_Close(request *requests.FPDF_StructTree_Close) (*responses.FPDF_StructTree_Close, error) { + if i.closed { + return nil, errors.New("instance is closed") + } + + return i.worker.plugin.FPDF_StructTree_Close(request) +} + +func (i *pdfiumInstance) FPDF_StructTree_CountChildren(request *requests.FPDF_StructTree_CountChildren) (*responses.FPDF_StructTree_CountChildren, error) { + if i.closed { + return nil, errors.New("instance is closed") + } + + return i.worker.plugin.FPDF_StructTree_CountChildren(request) +} + +func (i *pdfiumInstance) FPDF_StructTree_GetChildAtIndex(request *requests.FPDF_StructTree_GetChildAtIndex) (*responses.FPDF_StructTree_GetChildAtIndex, error) { + if i.closed { + return nil, errors.New("instance is closed") + } + + return i.worker.plugin.FPDF_StructTree_GetChildAtIndex(request) +} + +func (i *pdfiumInstance) FPDF_StructTree_GetForPage(request *requests.FPDF_StructTree_GetForPage) (*responses.FPDF_StructTree_GetForPage, error) { + if i.closed { + return nil, errors.New("instance is closed") + } + + return i.worker.plugin.FPDF_StructTree_GetForPage(request) +} + func (i *pdfiumInstance) FPDF_VIEWERREF_GetDuplex(request *requests.FPDF_VIEWERREF_GetDuplex) (*responses.FPDF_VIEWERREF_GetDuplex, error) { if i.closed { return nil, errors.New("instance is closed") diff --git a/pdfium.go b/pdfium.go index 41033f28..59ccb215 100644 --- a/pdfium.go +++ b/pdfium.go @@ -981,4 +981,52 @@ type Pdfium interface { FPDFAvail_IsLinearized(request *requests.FPDFAvail_IsLinearized) (*responses.FPDFAvail_IsLinearized, error) // End fpdf_dataavail.h + + // Start fpdf_structtree.h + + // FPDF_StructTree_GetForPage returns the structure tree for a page. + FPDF_StructTree_GetForPage(request *requests.FPDF_StructTree_GetForPage) (*responses.FPDF_StructTree_GetForPage, error) + + // FPDF_StructTree_Close releases a resource allocated by FPDF_StructTree_GetForPage(). + FPDF_StructTree_Close(request *requests.FPDF_StructTree_Close) (*responses.FPDF_StructTree_Close, error) + + // FPDF_StructTree_CountChildren counts the number of children for the structure tree. + FPDF_StructTree_CountChildren(request *requests.FPDF_StructTree_CountChildren) (*responses.FPDF_StructTree_CountChildren, error) + + // FPDF_StructTree_GetChildAtIndex returns a child in the structure tree. + FPDF_StructTree_GetChildAtIndex(request *requests.FPDF_StructTree_GetChildAtIndex) (*responses.FPDF_StructTree_GetChildAtIndex, error) + + // FPDF_StructElement_GetAltText returns the alt text for a given element. + FPDF_StructElement_GetAltText(request *requests.FPDF_StructElement_GetAltText) (*responses.FPDF_StructElement_GetAltText, error) + + // FPDF_StructElement_GetID returns the ID for a given element. + // Experimental API. + FPDF_StructElement_GetID(request *requests.FPDF_StructElement_GetID) (*responses.FPDF_StructElement_GetID, error) + + // FPDF_StructElement_GetLang returns the case-insensitive IETF BCP 47 language code for an element. + // Experimental API. + FPDF_StructElement_GetLang(request *requests.FPDF_StructElement_GetLang) (*responses.FPDF_StructElement_GetLang, error) + + // FPDF_StructElement_GetStringAttribute returns a struct element attribute of type "name" or "string" + // Experimental API. + FPDF_StructElement_GetStringAttribute(request *requests.FPDF_StructElement_GetStringAttribute) (*responses.FPDF_StructElement_GetStringAttribute, error) + + // FPDF_StructElement_GetMarkedContentID returns the marked content ID for a given element. + FPDF_StructElement_GetMarkedContentID(request *requests.FPDF_StructElement_GetMarkedContentID) (*responses.FPDF_StructElement_GetMarkedContentID, error) + + // FPDF_StructElement_GetType returns the type (/S) for a given element. + FPDF_StructElement_GetType(request *requests.FPDF_StructElement_GetType) (*responses.FPDF_StructElement_GetType, error) + + // FPDF_StructElement_GetTitle returns the title (/T) for a given element. + FPDF_StructElement_GetTitle(request *requests.FPDF_StructElement_GetTitle) (*responses.FPDF_StructElement_GetTitle, error) + + // FPDF_StructElement_CountChildren counts the number of children for the structure element. + FPDF_StructElement_CountChildren(request *requests.FPDF_StructElement_CountChildren) (*responses.FPDF_StructElement_CountChildren, error) + + // FPDF_StructElement_GetChildAtIndex returns a child in the structure element. + // If the child exists but is not an element, then this function will + // return an error. This will also return an error for out of bounds indices. + FPDF_StructElement_GetChildAtIndex(request *requests.FPDF_StructElement_GetChildAtIndex) (*responses.FPDF_StructElement_GetChildAtIndex, error) + + // End fpdf_structtree.h } diff --git a/references/references.go b/references/references.go index b6c2ff7d..c82985fd 100644 --- a/references/references.go +++ b/references/references.go @@ -65,3 +65,9 @@ type FPDF_PATHSEGMENT string // FPDF_AVAIL is an internal reference to a C.FPDF_AVAIL handle. type FPDF_AVAIL string + +// FPDF_STRUCTTREE is an internal reference to a C.FPDF_STRUCTTREE handle. +type FPDF_STRUCTTREE string + +// FPDF_STRUCTELEMENT is an internal reference to a C.FPDF_STRUCTELEMENT handle. +type FPDF_STRUCTELEMENT string diff --git a/requests/fpdf_structtree.go b/requests/fpdf_structtree.go index 523a338a..34ebdd68 100644 --- a/requests/fpdf_structtree.go +++ b/requests/fpdf_structtree.go @@ -1 +1,58 @@ package requests + +import "github.com/klippa-app/go-pdfium/references" + +type FPDF_StructTree_GetForPage struct { + Page Page +} + +type FPDF_StructTree_Close struct { + StructTree references.FPDF_STRUCTTREE +} + +type FPDF_StructTree_CountChildren struct { + StructTree references.FPDF_STRUCTTREE +} + +type FPDF_StructTree_GetChildAtIndex struct { + StructTree references.FPDF_STRUCTTREE + Index int +} + +type FPDF_StructElement_GetAltText struct { + StructElement references.FPDF_STRUCTELEMENT +} + +type FPDF_StructElement_GetID struct { + StructElement references.FPDF_STRUCTELEMENT +} + +type FPDF_StructElement_GetLang struct { + StructElement references.FPDF_STRUCTELEMENT +} + +type FPDF_StructElement_GetStringAttribute struct { + StructElement references.FPDF_STRUCTELEMENT + AttributeName string // The name of the attribute to retrieve. +} + +type FPDF_StructElement_GetMarkedContentID struct { + StructElement references.FPDF_STRUCTELEMENT +} + +type FPDF_StructElement_GetType struct { + StructElement references.FPDF_STRUCTELEMENT +} + +type FPDF_StructElement_GetTitle struct { + StructElement references.FPDF_STRUCTELEMENT +} + +type FPDF_StructElement_CountChildren struct { + StructElement references.FPDF_STRUCTELEMENT +} + +type FPDF_StructElement_GetChildAtIndex struct { + StructElement references.FPDF_STRUCTELEMENT + Index int // The index for the child, 0-based. +} diff --git a/responses/fpdf_structtree.go b/responses/fpdf_structtree.go index 66522789..1934fb2a 100644 --- a/responses/fpdf_structtree.go +++ b/responses/fpdf_structtree.go @@ -1 +1,54 @@ package responses + +import "github.com/klippa-app/go-pdfium/references" + +type FPDF_StructTree_GetForPage struct { + StructTree references.FPDF_STRUCTTREE +} + +type FPDF_StructTree_Close struct{} + +type FPDF_StructTree_CountChildren struct { + Count int +} + +type FPDF_StructTree_GetChildAtIndex struct { + StructElement references.FPDF_STRUCTELEMENT +} + +type FPDF_StructElement_GetAltText struct { + AltText string +} + +type FPDF_StructElement_GetID struct { + ID string +} + +type FPDF_StructElement_GetLang struct { + Lang string // The case-insensitive IETF BCP 47 language code for an element. +} + +type FPDF_StructElement_GetStringAttribute struct { + Attribute string + Value string +} + +type FPDF_StructElement_GetMarkedContentID struct { + MarkedContentID int // The marked content ID of the element. If no ID exists, returns -1. +} + +type FPDF_StructElement_GetType struct { + Type string +} + +type FPDF_StructElement_GetTitle struct { + Title string +} + +type FPDF_StructElement_CountChildren struct { + Count int +} + +type FPDF_StructElement_GetChildAtIndex struct { + StructElement references.FPDF_STRUCTELEMENT +} diff --git a/shared_tests/fpdf_structtree.go b/shared_tests/fpdf_structtree.go new file mode 100644 index 00000000..6dcdde10 --- /dev/null +++ b/shared_tests/fpdf_structtree.go @@ -0,0 +1,526 @@ +package shared_tests + +import ( + "github.com/klippa-app/go-pdfium/responses" + "io/ioutil" + + "github.com/klippa-app/go-pdfium/references" + "github.com/klippa-app/go-pdfium/requests" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +var _ = Describe("fpdf_structtree", func() { + BeforeEach(func() { + Locker.Lock() + }) + + AfterEach(func() { + Locker.Unlock() + }) + + Context("no page", func() { + When("is opened", func() { + It("returns an error when calling FPDF_StructTree_GetForPage", func() { + FPDF_StructTree_GetForPage, err := PdfiumInstance.FPDF_StructTree_GetForPage(&requests.FPDF_StructTree_GetForPage{}) + Expect(err).To(MatchError("either page reference or index should be given")) + Expect(FPDF_StructTree_GetForPage).To(BeNil()) + }) + }) + }) + + Context("no struct tree", func() { + When("is opened", func() { + It("returns an error when calling FPDF_StructTree_Close", func() { + FPDF_StructTree_Close, err := PdfiumInstance.FPDF_StructTree_Close(&requests.FPDF_StructTree_Close{}) + Expect(err).To(MatchError("structTree not given")) + Expect(FPDF_StructTree_Close).To(BeNil()) + }) + + It("returns an error when calling FPDF_StructTree_CountChildren", func() { + FPDF_StructTree_CountChildren, err := PdfiumInstance.FPDF_StructTree_CountChildren(&requests.FPDF_StructTree_CountChildren{}) + Expect(err).To(MatchError("structTree not given")) + Expect(FPDF_StructTree_CountChildren).To(BeNil()) + }) + + It("returns an error when calling FPDF_StructTree_GetChildAtIndex", func() { + FPDF_StructTree_GetChildAtIndex, err := PdfiumInstance.FPDF_StructTree_GetChildAtIndex(&requests.FPDF_StructTree_GetChildAtIndex{}) + Expect(err).To(MatchError("structTree not given")) + Expect(FPDF_StructTree_GetChildAtIndex).To(BeNil()) + }) + }) + }) + + Context("no struct element", func() { + When("is opened", func() { + It("returns an error when calling FPDF_StructElement_GetAltText", func() { + FPDF_StructElement_GetAltText, err := PdfiumInstance.FPDF_StructElement_GetAltText(&requests.FPDF_StructElement_GetAltText{}) + Expect(err).To(MatchError("structElement not given")) + Expect(FPDF_StructElement_GetAltText).To(BeNil()) + }) + + It("returns an error when calling FPDF_StructElement_GetMarkedContentID", func() { + FPDF_StructElement_GetMarkedContentID, err := PdfiumInstance.FPDF_StructElement_GetMarkedContentID(&requests.FPDF_StructElement_GetMarkedContentID{}) + Expect(err).To(MatchError("structElement not given")) + Expect(FPDF_StructElement_GetMarkedContentID).To(BeNil()) + }) + + It("returns an error when calling FPDF_StructElement_GetType", func() { + FPDF_StructElement_GetType, err := PdfiumInstance.FPDF_StructElement_GetType(&requests.FPDF_StructElement_GetType{}) + Expect(err).To(MatchError("structElement not given")) + Expect(FPDF_StructElement_GetType).To(BeNil()) + }) + + It("returns an error when calling FPDF_StructElement_GetTitle", func() { + FPDF_StructElement_GetTitle, err := PdfiumInstance.FPDF_StructElement_GetTitle(&requests.FPDF_StructElement_GetTitle{}) + Expect(err).To(MatchError("structElement not given")) + Expect(FPDF_StructElement_GetTitle).To(BeNil()) + }) + + It("returns an error when calling FPDF_StructElement_CountChildren", func() { + FPDF_StructElement_CountChildren, err := PdfiumInstance.FPDF_StructElement_CountChildren(&requests.FPDF_StructElement_CountChildren{}) + Expect(err).To(MatchError("structElement not given")) + Expect(FPDF_StructElement_CountChildren).To(BeNil()) + }) + + It("returns an error when calling FPDF_StructElement_GetChildAtIndex", func() { + FPDF_StructElement_GetChildAtIndex, err := PdfiumInstance.FPDF_StructElement_GetChildAtIndex(&requests.FPDF_StructElement_GetChildAtIndex{}) + Expect(err).To(MatchError("structElement not given")) + Expect(FPDF_StructElement_GetChildAtIndex).To(BeNil()) + }) + }) + }) + + Context("a normal PDF file without a struct tree", func() { + var doc references.FPDF_DOCUMENT + + BeforeEach(func() { + pdfData, err := ioutil.ReadFile(TestDataPath + "/testdata/test.pdf") + Expect(err).To(BeNil()) + + newDoc, err := PdfiumInstance.FPDF_LoadMemDocument(&requests.FPDF_LoadMemDocument{ + Data: &pdfData, + }) + Expect(err).To(BeNil()) + + doc = newDoc.Document + }) + + AfterEach(func() { + FPDF_CloseDocument, err := PdfiumInstance.FPDF_CloseDocument(&requests.FPDF_CloseDocument{ + Document: doc, + }) + Expect(err).To(BeNil()) + Expect(FPDF_CloseDocument).To(Not(BeNil())) + }) + + When("is opened", func() { + It("returns an error when calling FPDF_StructTree_GetForPage", func() { + FPDF_StructTree_GetForPage, err := PdfiumInstance.FPDF_StructTree_GetForPage(&requests.FPDF_StructTree_GetForPage{ + Page: requests.Page{ + ByIndex: &requests.PageByIndex{ + Document: doc, + Index: 0, + }, + }, + }) + Expect(err).To(MatchError("could not load struct tree")) + Expect(FPDF_StructTree_GetForPage).To(BeNil()) + }) + }) + }) + + Context("a normal PDF file with a struct tree", func() { + var doc references.FPDF_DOCUMENT + + BeforeEach(func() { + pdfData, err := ioutil.ReadFile(TestDataPath + "/testdata/tagged_alt_text.pdf") + Expect(err).To(BeNil()) + + newDoc, err := PdfiumInstance.FPDF_LoadMemDocument(&requests.FPDF_LoadMemDocument{ + Data: &pdfData, + }) + Expect(err).To(BeNil()) + + doc = newDoc.Document + }) + + AfterEach(func() { + FPDF_CloseDocument, err := PdfiumInstance.FPDF_CloseDocument(&requests.FPDF_CloseDocument{ + Document: doc, + }) + Expect(err).To(BeNil()) + Expect(FPDF_CloseDocument).To(Not(BeNil())) + }) + + When("is opened", func() { + When("a page structtree is opened", func() { + var structTree references.FPDF_STRUCTTREE + + BeforeEach(func() { + FPDF_StructTree_GetForPage, err := PdfiumInstance.FPDF_StructTree_GetForPage(&requests.FPDF_StructTree_GetForPage{ + Page: requests.Page{ + ByIndex: &requests.PageByIndex{ + Document: doc, + Index: 0, + }, + }, + }) + Expect(err).To(BeNil()) + Expect(FPDF_StructTree_GetForPage).To(Not(BeNil())) + + structTree = FPDF_StructTree_GetForPage.StructTree + }) + + AfterEach(func() { + FPDF_StructTree_Close, err := PdfiumInstance.FPDF_StructTree_Close(&requests.FPDF_StructTree_Close{ + StructTree: structTree, + }) + Expect(err).To(BeNil()) + Expect(FPDF_StructTree_Close).To(Not(BeNil())) + }) + + It("returns the correct struct tree children count", func() { + FPDF_StructTree_CountChildren, err := PdfiumInstance.FPDF_StructTree_CountChildren(&requests.FPDF_StructTree_CountChildren{ + StructTree: structTree, + }) + Expect(err).To(BeNil()) + Expect(FPDF_StructTree_CountChildren).To(Equal(&responses.FPDF_StructTree_CountChildren{ + Count: 1, + })) + }) + + It("returns an error when loading an invalid struct element", func() { + FPDF_StructTree_GetChildAtIndex, err := PdfiumInstance.FPDF_StructTree_GetChildAtIndex(&requests.FPDF_StructTree_GetChildAtIndex{ + StructTree: structTree, + Index: 30, + }) + Expect(err).To(MatchError("could not load struct tree child")) + Expect(FPDF_StructTree_GetChildAtIndex).To(BeNil()) + }) + + When("a struct tree struct element is opened", func() { + var structElement references.FPDF_STRUCTELEMENT + + BeforeEach(func() { + FPDF_StructTree_GetChildAtIndex, err := PdfiumInstance.FPDF_StructTree_GetChildAtIndex(&requests.FPDF_StructTree_GetChildAtIndex{ + StructTree: structTree, + }) + Expect(err).To(BeNil()) + Expect(FPDF_StructTree_GetChildAtIndex).To(Not(BeNil())) + + structElement = FPDF_StructTree_GetChildAtIndex.StructElement + }) + + It("returns the correct marked content ID", func() { + FPDF_StructElement_GetMarkedContentID, err := PdfiumInstance.FPDF_StructElement_GetMarkedContentID(&requests.FPDF_StructElement_GetMarkedContentID{ + StructElement: structElement, + }) + Expect(err).To(BeNil()) + Expect(FPDF_StructElement_GetMarkedContentID).To(Equal(&responses.FPDF_StructElement_GetMarkedContentID{ + MarkedContentID: -1, + })) + }) + + It("returns the correct struct element children count", func() { + FPDF_StructElement_CountChildren, err := PdfiumInstance.FPDF_StructElement_CountChildren(&requests.FPDF_StructElement_CountChildren{ + StructElement: structElement, + }) + Expect(err).To(BeNil()) + Expect(FPDF_StructElement_CountChildren).To(Equal(&responses.FPDF_StructElement_CountChildren{ + Count: 1, + })) + }) + + It("returns an error when loading an invalid subelement", func() { + FPDF_StructElement_GetChildAtIndex, err := PdfiumInstance.FPDF_StructElement_GetChildAtIndex(&requests.FPDF_StructElement_GetChildAtIndex{ + StructElement: structElement, + Index: 30, + }) + Expect(err).To(MatchError("could not load struct element child")) + Expect(FPDF_StructElement_GetChildAtIndex).To(BeNil()) + }) + + When("a struct tree struct subelement is opened", func() { + var structSubElement references.FPDF_STRUCTELEMENT + + BeforeEach(func() { + FPDF_StructElement_GetChildAtIndex, err := PdfiumInstance.FPDF_StructElement_GetChildAtIndex(&requests.FPDF_StructElement_GetChildAtIndex{ + StructElement: structElement, + }) + Expect(err).To(BeNil()) + Expect(FPDF_StructElement_GetChildAtIndex).To(Not(BeNil())) + + structSubElement = FPDF_StructElement_GetChildAtIndex.StructElement + }) + + It("returns the correct struct element children count", func() { + FPDF_StructElement_CountChildren, err := PdfiumInstance.FPDF_StructElement_CountChildren(&requests.FPDF_StructElement_CountChildren{ + StructElement: structSubElement, + }) + Expect(err).To(BeNil()) + Expect(FPDF_StructElement_CountChildren).To(Equal(&responses.FPDF_StructElement_CountChildren{ + Count: 1, + })) + }) + + It("returns the correct struct element children count", func() { + FPDF_StructElement_CountChildren, err := PdfiumInstance.FPDF_StructElement_CountChildren(&requests.FPDF_StructElement_CountChildren{ + StructElement: structSubElement, + }) + Expect(err).To(BeNil()) + Expect(FPDF_StructElement_CountChildren).To(Equal(&responses.FPDF_StructElement_CountChildren{ + Count: 1, + })) + }) + + It("returns an error when the struct element has no alt text", func() { + FPDF_StructElement_GetAltText, err := PdfiumInstance.FPDF_StructElement_GetAltText(&requests.FPDF_StructElement_GetAltText{ + StructElement: structSubElement, + }) + Expect(err).To(MatchError("Could not get alt text")) + Expect(FPDF_StructElement_GetAltText).To(BeNil()) + }) + + When("a struct tree struct subsubelement is opened", func() { + var structSubSubElement references.FPDF_STRUCTELEMENT + + BeforeEach(func() { + FPDF_StructElement_GetChildAtIndex, err := PdfiumInstance.FPDF_StructElement_GetChildAtIndex(&requests.FPDF_StructElement_GetChildAtIndex{ + StructElement: structSubElement, + }) + Expect(err).To(BeNil()) + Expect(FPDF_StructElement_GetChildAtIndex).To(Not(BeNil())) + + structSubSubElement = FPDF_StructElement_GetChildAtIndex.StructElement + }) + + It("returns the correct alt text", func() { + FPDF_StructElement_GetAltText, err := PdfiumInstance.FPDF_StructElement_GetAltText(&requests.FPDF_StructElement_GetAltText{ + StructElement: structSubSubElement, + }) + Expect(err).To(BeNil()) + Expect(FPDF_StructElement_GetAltText).To(Equal(&responses.FPDF_StructElement_GetAltText{ + AltText: "Black Image", + })) + }) + + It("returns an error when requesting element title", func() { + FPDF_StructElement_GetTitle, err := PdfiumInstance.FPDF_StructElement_GetTitle(&requests.FPDF_StructElement_GetTitle{ + StructElement: structSubSubElement, + }) + Expect(err).To(MatchError("Could not get title")) + Expect(FPDF_StructElement_GetTitle).To(BeNil()) + }) + + It("returns an error when requesting element type", func() { + FPDF_StructElement_GetType, err := PdfiumInstance.FPDF_StructElement_GetType(&requests.FPDF_StructElement_GetType{ + StructElement: structSubSubElement, + }) + Expect(err).To(BeNil()) + Expect(FPDF_StructElement_GetType).To(Equal(&responses.FPDF_StructElement_GetType{ + Type: "Figure", + })) + }) + }) + }) + }) + }) + }) + }) + + Context("a normal PDF file with a marked content ID", func() { + var doc references.FPDF_DOCUMENT + + BeforeEach(func() { + pdfData, err := ioutil.ReadFile(TestDataPath + "/testdata/marked_content_id.pdf") + Expect(err).To(BeNil()) + + newDoc, err := PdfiumInstance.FPDF_LoadMemDocument(&requests.FPDF_LoadMemDocument{ + Data: &pdfData, + }) + Expect(err).To(BeNil()) + + doc = newDoc.Document + }) + + AfterEach(func() { + FPDF_CloseDocument, err := PdfiumInstance.FPDF_CloseDocument(&requests.FPDF_CloseDocument{ + Document: doc, + }) + Expect(err).To(BeNil()) + Expect(FPDF_CloseDocument).To(Not(BeNil())) + }) + + When("is opened", func() { + When("a page structtree is opened", func() { + var structTree references.FPDF_STRUCTTREE + + BeforeEach(func() { + FPDF_StructTree_GetForPage, err := PdfiumInstance.FPDF_StructTree_GetForPage(&requests.FPDF_StructTree_GetForPage{ + Page: requests.Page{ + ByIndex: &requests.PageByIndex{ + Document: doc, + Index: 0, + }, + }, + }) + Expect(err).To(BeNil()) + Expect(FPDF_StructTree_GetForPage).To(Not(BeNil())) + + structTree = FPDF_StructTree_GetForPage.StructTree + }) + + AfterEach(func() { + FPDF_StructTree_Close, err := PdfiumInstance.FPDF_StructTree_Close(&requests.FPDF_StructTree_Close{ + StructTree: structTree, + }) + Expect(err).To(BeNil()) + Expect(FPDF_StructTree_Close).To(Not(BeNil())) + }) + + It("returns the correct struct tree children count", func() { + FPDF_StructTree_CountChildren, err := PdfiumInstance.FPDF_StructTree_CountChildren(&requests.FPDF_StructTree_CountChildren{ + StructTree: structTree, + }) + Expect(err).To(BeNil()) + Expect(FPDF_StructTree_CountChildren).To(Equal(&responses.FPDF_StructTree_CountChildren{ + Count: 1, + })) + }) + + When("a struct tree struct element is opened", func() { + var structElement references.FPDF_STRUCTELEMENT + + BeforeEach(func() { + FPDF_StructTree_GetChildAtIndex, err := PdfiumInstance.FPDF_StructTree_GetChildAtIndex(&requests.FPDF_StructTree_GetChildAtIndex{ + StructTree: structTree, + }) + Expect(err).To(BeNil()) + Expect(FPDF_StructTree_GetChildAtIndex).To(Not(BeNil())) + + structElement = FPDF_StructTree_GetChildAtIndex.StructElement + }) + + It("returns the correct struct element children count", func() { + FPDF_StructElement_CountChildren, err := PdfiumInstance.FPDF_StructElement_CountChildren(&requests.FPDF_StructElement_CountChildren{ + StructElement: structElement, + }) + Expect(err).To(BeNil()) + Expect(FPDF_StructElement_CountChildren).To(Equal(&responses.FPDF_StructElement_CountChildren{ + Count: 1, + })) + }) + + It("returns the correct marked content ID", func() { + FPDF_StructElement_GetMarkedContentID, err := PdfiumInstance.FPDF_StructElement_GetMarkedContentID(&requests.FPDF_StructElement_GetMarkedContentID{ + StructElement: structElement, + }) + Expect(err).To(BeNil()) + Expect(FPDF_StructElement_GetMarkedContentID).To(Equal(&responses.FPDF_StructElement_GetMarkedContentID{ + MarkedContentID: 0, + })) + }) + }) + }) + }) + }) + + Context("a normal PDF file with a tagged alt text", func() { + var doc references.FPDF_DOCUMENT + + BeforeEach(func() { + pdfData, err := ioutil.ReadFile(TestDataPath + "/testdata/tagged_alt_text.pdf") + Expect(err).To(BeNil()) + + newDoc, err := PdfiumInstance.FPDF_LoadMemDocument(&requests.FPDF_LoadMemDocument{ + Data: &pdfData, + }) + Expect(err).To(BeNil()) + + doc = newDoc.Document + }) + + AfterEach(func() { + FPDF_CloseDocument, err := PdfiumInstance.FPDF_CloseDocument(&requests.FPDF_CloseDocument{ + Document: doc, + }) + Expect(err).To(BeNil()) + Expect(FPDF_CloseDocument).To(Not(BeNil())) + }) + + When("is opened", func() { + When("a page structtree is opened", func() { + var structTree references.FPDF_STRUCTTREE + + BeforeEach(func() { + FPDF_StructTree_GetForPage, err := PdfiumInstance.FPDF_StructTree_GetForPage(&requests.FPDF_StructTree_GetForPage{ + Page: requests.Page{ + ByIndex: &requests.PageByIndex{ + Document: doc, + Index: 0, + }, + }, + }) + Expect(err).To(BeNil()) + Expect(FPDF_StructTree_GetForPage).To(Not(BeNil())) + + structTree = FPDF_StructTree_GetForPage.StructTree + }) + + AfterEach(func() { + FPDF_StructTree_Close, err := PdfiumInstance.FPDF_StructTree_Close(&requests.FPDF_StructTree_Close{ + StructTree: structTree, + }) + Expect(err).To(BeNil()) + Expect(FPDF_StructTree_Close).To(Not(BeNil())) + }) + + It("returns the correct struct tree children count", func() { + FPDF_StructTree_CountChildren, err := PdfiumInstance.FPDF_StructTree_CountChildren(&requests.FPDF_StructTree_CountChildren{ + StructTree: structTree, + }) + Expect(err).To(BeNil()) + Expect(FPDF_StructTree_CountChildren).To(Equal(&responses.FPDF_StructTree_CountChildren{ + Count: 1, + })) + }) + + When("a struct tree struct element is opened", func() { + var structElement references.FPDF_STRUCTELEMENT + + BeforeEach(func() { + FPDF_StructTree_GetChildAtIndex, err := PdfiumInstance.FPDF_StructTree_GetChildAtIndex(&requests.FPDF_StructTree_GetChildAtIndex{ + StructTree: structTree, + }) + Expect(err).To(BeNil()) + Expect(FPDF_StructTree_GetChildAtIndex).To(Not(BeNil())) + + structElement = FPDF_StructTree_GetChildAtIndex.StructElement + }) + + It("returns the correct struct element children count", func() { + FPDF_StructElement_CountChildren, err := PdfiumInstance.FPDF_StructElement_CountChildren(&requests.FPDF_StructElement_CountChildren{ + StructElement: structElement, + }) + Expect(err).To(BeNil()) + Expect(FPDF_StructElement_CountChildren).To(Equal(&responses.FPDF_StructElement_CountChildren{ + Count: 1, + })) + }) + + It("returns the correct element title", func() { + FPDF_StructElement_GetTitle, err := PdfiumInstance.FPDF_StructElement_GetTitle(&requests.FPDF_StructElement_GetTitle{ + StructElement: structElement, + }) + Expect(err).To(BeNil()) + Expect(FPDF_StructElement_GetTitle).To(Equal(&responses.FPDF_StructElement_GetTitle{ + Title: "TitleText", + })) + }) + }) + }) + }) + }) +}) diff --git a/shared_tests/fpdf_structtree_experimental.go b/shared_tests/fpdf_structtree_experimental.go new file mode 100644 index 00000000..320c692b --- /dev/null +++ b/shared_tests/fpdf_structtree_experimental.go @@ -0,0 +1,232 @@ +//go:build pdfium_experimental +// +build pdfium_experimental + +package shared_tests + +import ( + "github.com/klippa-app/go-pdfium/responses" + "io/ioutil" + + "github.com/klippa-app/go-pdfium/references" + "github.com/klippa-app/go-pdfium/requests" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +var _ = Describe("fpdf_structtree_experimental", func() { + BeforeEach(func() { + Locker.Lock() + }) + + AfterEach(func() { + Locker.Unlock() + }) + + Context("no struct element", func() { + When("is opened", func() { + It("returns an error when calling FPDF_StructElement_GetID", func() { + FPDF_StructElement_GetID, err := PdfiumInstance.FPDF_StructElement_GetID(&requests.FPDF_StructElement_GetID{}) + Expect(err).To(MatchError("structElement not given")) + Expect(FPDF_StructElement_GetID).To(BeNil()) + }) + + It("returns an error when calling FPDF_StructElement_GetLang", func() { + FPDF_StructElement_GetLang, err := PdfiumInstance.FPDF_StructElement_GetLang(&requests.FPDF_StructElement_GetLang{}) + Expect(err).To(MatchError("structElement not given")) + Expect(FPDF_StructElement_GetLang).To(BeNil()) + }) + + It("returns an error when calling FPDF_StructElement_GetStringAttribute", func() { + FPDF_StructElement_GetStringAttribute, err := PdfiumInstance.FPDF_StructElement_GetStringAttribute(&requests.FPDF_StructElement_GetStringAttribute{}) + Expect(err).To(MatchError("structElement not given")) + Expect(FPDF_StructElement_GetStringAttribute).To(BeNil()) + }) + }) + }) + + Context("a normal PDF file with a tagged table", func() { + var doc references.FPDF_DOCUMENT + + BeforeEach(func() { + pdfData, err := ioutil.ReadFile(TestDataPath + "/testdata/tagged_table.pdf") + Expect(err).To(BeNil()) + + newDoc, err := PdfiumInstance.FPDF_LoadMemDocument(&requests.FPDF_LoadMemDocument{ + Data: &pdfData, + }) + Expect(err).To(BeNil()) + + doc = newDoc.Document + }) + + AfterEach(func() { + FPDF_CloseDocument, err := PdfiumInstance.FPDF_CloseDocument(&requests.FPDF_CloseDocument{ + Document: doc, + }) + Expect(err).To(BeNil()) + Expect(FPDF_CloseDocument).To(Not(BeNil())) + }) + + When("is opened", func() { + When("a page structtree is opened", func() { + var structTree references.FPDF_STRUCTTREE + + BeforeEach(func() { + FPDF_StructTree_GetForPage, err := PdfiumInstance.FPDF_StructTree_GetForPage(&requests.FPDF_StructTree_GetForPage{ + Page: requests.Page{ + ByIndex: &requests.PageByIndex{ + Document: doc, + Index: 0, + }, + }, + }) + Expect(err).To(BeNil()) + Expect(FPDF_StructTree_GetForPage).To(Not(BeNil())) + + structTree = FPDF_StructTree_GetForPage.StructTree + }) + + AfterEach(func() { + FPDF_StructTree_Close, err := PdfiumInstance.FPDF_StructTree_Close(&requests.FPDF_StructTree_Close{ + StructTree: structTree, + }) + Expect(err).To(BeNil()) + Expect(FPDF_StructTree_Close).To(Not(BeNil())) + }) + + It("returns the correct struct tree children count", func() { + FPDF_StructTree_CountChildren, err := PdfiumInstance.FPDF_StructTree_CountChildren(&requests.FPDF_StructTree_CountChildren{ + StructTree: structTree, + }) + Expect(err).To(BeNil()) + Expect(FPDF_StructTree_CountChildren).To(Equal(&responses.FPDF_StructTree_CountChildren{ + Count: 1, + })) + }) + + When("a struct tree struct element is opened", func() { + var structElement references.FPDF_STRUCTELEMENT + + BeforeEach(func() { + FPDF_StructTree_GetChildAtIndex, err := PdfiumInstance.FPDF_StructTree_GetChildAtIndex(&requests.FPDF_StructTree_GetChildAtIndex{ + StructTree: structTree, + }) + Expect(err).To(BeNil()) + Expect(FPDF_StructTree_GetChildAtIndex).To(Not(BeNil())) + + structElement = FPDF_StructTree_GetChildAtIndex.StructElement + }) + + It("returns the correct struct element children count", func() { + FPDF_StructElement_CountChildren, err := PdfiumInstance.FPDF_StructElement_CountChildren(&requests.FPDF_StructElement_CountChildren{ + StructElement: structElement, + }) + Expect(err).To(BeNil()) + Expect(FPDF_StructElement_CountChildren).To(Equal(&responses.FPDF_StructElement_CountChildren{ + Count: 1, + })) + }) + + It("returns the correct element type", func() { + FPDF_StructElement_GetType, err := PdfiumInstance.FPDF_StructElement_GetType(&requests.FPDF_StructElement_GetType{ + StructElement: structElement, + }) + Expect(err).To(BeNil()) + Expect(FPDF_StructElement_GetType).To(Equal(&responses.FPDF_StructElement_GetType{ + Type: "Document", + })) + }) + + It("returns an error when not giving a name when requesting an attribute name", func() { + table, err := PdfiumInstance.FPDF_StructElement_GetChildAtIndex(&requests.FPDF_StructElement_GetChildAtIndex{ + StructElement: structElement, + }) + Expect(err).To(BeNil()) + Expect(table).To(Not(BeNil())) + + row, err := PdfiumInstance.FPDF_StructElement_GetChildAtIndex(&requests.FPDF_StructElement_GetChildAtIndex{ + StructElement: table.StructElement, + }) + Expect(err).To(BeNil()) + Expect(row).To(Not(BeNil())) + + header_cell, err := PdfiumInstance.FPDF_StructElement_GetChildAtIndex(&requests.FPDF_StructElement_GetChildAtIndex{ + StructElement: row.StructElement, + }) + Expect(err).To(BeNil()) + Expect(header_cell).To(Not(BeNil())) + + FPDF_StructElement_GetStringAttribute, err := PdfiumInstance.FPDF_StructElement_GetStringAttribute(&requests.FPDF_StructElement_GetStringAttribute{ + StructElement: header_cell.StructElement, + }) + Expect(err).To(MatchError("could not get attribute")) + Expect(FPDF_StructElement_GetStringAttribute).To(BeNil()) + }) + + It("returns the correct string attribute", func() { + table, err := PdfiumInstance.FPDF_StructElement_GetChildAtIndex(&requests.FPDF_StructElement_GetChildAtIndex{ + StructElement: structElement, + }) + Expect(err).To(BeNil()) + Expect(table).To(Not(BeNil())) + + row, err := PdfiumInstance.FPDF_StructElement_GetChildAtIndex(&requests.FPDF_StructElement_GetChildAtIndex{ + StructElement: table.StructElement, + }) + Expect(err).To(BeNil()) + Expect(row).To(Not(BeNil())) + + header_cell, err := PdfiumInstance.FPDF_StructElement_GetChildAtIndex(&requests.FPDF_StructElement_GetChildAtIndex{ + StructElement: row.StructElement, + }) + Expect(err).To(BeNil()) + Expect(header_cell).To(Not(BeNil())) + + FPDF_StructElement_GetStringAttribute, err := PdfiumInstance.FPDF_StructElement_GetStringAttribute(&requests.FPDF_StructElement_GetStringAttribute{ + StructElement: header_cell.StructElement, + AttributeName: "Scope", + }) + Expect(err).To(BeNil()) + Expect(FPDF_StructElement_GetStringAttribute).To(Equal(&responses.FPDF_StructElement_GetStringAttribute{ + Attribute: "Scope", + Value: "Row", + })) + }) + + It("returns the correct ID", func() { + table, err := PdfiumInstance.FPDF_StructElement_GetChildAtIndex(&requests.FPDF_StructElement_GetChildAtIndex{ + StructElement: structElement, + }) + Expect(err).To(BeNil()) + Expect(table).To(Not(BeNil())) + + FPDF_StructElement_GetID, err := PdfiumInstance.FPDF_StructElement_GetID(&requests.FPDF_StructElement_GetID{ + StructElement: table.StructElement, + }) + Expect(err).To(BeNil()) + Expect(FPDF_StructElement_GetID).To(Equal(&responses.FPDF_StructElement_GetID{ + ID: "node12", + })) + }) + + It("returns the correct lang", func() { + table, err := PdfiumInstance.FPDF_StructElement_GetChildAtIndex(&requests.FPDF_StructElement_GetChildAtIndex{ + StructElement: structElement, + }) + Expect(err).To(BeNil()) + Expect(table).To(Not(BeNil())) + + FPDF_StructElement_GetLang, err := PdfiumInstance.FPDF_StructElement_GetLang(&requests.FPDF_StructElement_GetLang{ + StructElement: table.StructElement, + }) + Expect(err).To(BeNil()) + Expect(FPDF_StructElement_GetLang).To(Equal(&responses.FPDF_StructElement_GetLang{ + Lang: "hu", + })) + }) + }) + }) + }) + }) +}) diff --git a/single_threaded/generated.go b/single_threaded/generated.go index 48682a1e..32749dfb 100644 --- a/single_threaded/generated.go +++ b/single_threaded/generated.go @@ -2503,6 +2503,188 @@ func (i *pdfiumInstance) FPDF_SetSandBoxPolicy(request *requests.FPDF_SetSandBox return i.pdfium.FPDF_SetSandBoxPolicy(request) } +func (i *pdfiumInstance) FPDF_StructElement_CountChildren(request *requests.FPDF_StructElement_CountChildren) (resp *responses.FPDF_StructElement_CountChildren, err error) { + if i.closed { + return nil, errors.New("instance is closed") + } + + defer func() { + if panicError := recover(); panicError != nil { + err = fmt.Errorf("panic occurred in %s: %v", "FPDF_StructElement_CountChildren", panicError) + } + }() + + return i.pdfium.FPDF_StructElement_CountChildren(request) +} + +func (i *pdfiumInstance) FPDF_StructElement_GetAltText(request *requests.FPDF_StructElement_GetAltText) (resp *responses.FPDF_StructElement_GetAltText, err error) { + if i.closed { + return nil, errors.New("instance is closed") + } + + defer func() { + if panicError := recover(); panicError != nil { + err = fmt.Errorf("panic occurred in %s: %v", "FPDF_StructElement_GetAltText", panicError) + } + }() + + return i.pdfium.FPDF_StructElement_GetAltText(request) +} + +func (i *pdfiumInstance) FPDF_StructElement_GetChildAtIndex(request *requests.FPDF_StructElement_GetChildAtIndex) (resp *responses.FPDF_StructElement_GetChildAtIndex, err error) { + if i.closed { + return nil, errors.New("instance is closed") + } + + defer func() { + if panicError := recover(); panicError != nil { + err = fmt.Errorf("panic occurred in %s: %v", "FPDF_StructElement_GetChildAtIndex", panicError) + } + }() + + return i.pdfium.FPDF_StructElement_GetChildAtIndex(request) +} + +func (i *pdfiumInstance) FPDF_StructElement_GetID(request *requests.FPDF_StructElement_GetID) (resp *responses.FPDF_StructElement_GetID, err error) { + if i.closed { + return nil, errors.New("instance is closed") + } + + defer func() { + if panicError := recover(); panicError != nil { + err = fmt.Errorf("panic occurred in %s: %v", "FPDF_StructElement_GetID", panicError) + } + }() + + return i.pdfium.FPDF_StructElement_GetID(request) +} + +func (i *pdfiumInstance) FPDF_StructElement_GetLang(request *requests.FPDF_StructElement_GetLang) (resp *responses.FPDF_StructElement_GetLang, err error) { + if i.closed { + return nil, errors.New("instance is closed") + } + + defer func() { + if panicError := recover(); panicError != nil { + err = fmt.Errorf("panic occurred in %s: %v", "FPDF_StructElement_GetLang", panicError) + } + }() + + return i.pdfium.FPDF_StructElement_GetLang(request) +} + +func (i *pdfiumInstance) FPDF_StructElement_GetMarkedContentID(request *requests.FPDF_StructElement_GetMarkedContentID) (resp *responses.FPDF_StructElement_GetMarkedContentID, err error) { + if i.closed { + return nil, errors.New("instance is closed") + } + + defer func() { + if panicError := recover(); panicError != nil { + err = fmt.Errorf("panic occurred in %s: %v", "FPDF_StructElement_GetMarkedContentID", panicError) + } + }() + + return i.pdfium.FPDF_StructElement_GetMarkedContentID(request) +} + +func (i *pdfiumInstance) FPDF_StructElement_GetStringAttribute(request *requests.FPDF_StructElement_GetStringAttribute) (resp *responses.FPDF_StructElement_GetStringAttribute, err error) { + if i.closed { + return nil, errors.New("instance is closed") + } + + defer func() { + if panicError := recover(); panicError != nil { + err = fmt.Errorf("panic occurred in %s: %v", "FPDF_StructElement_GetStringAttribute", panicError) + } + }() + + return i.pdfium.FPDF_StructElement_GetStringAttribute(request) +} + +func (i *pdfiumInstance) FPDF_StructElement_GetTitle(request *requests.FPDF_StructElement_GetTitle) (resp *responses.FPDF_StructElement_GetTitle, err error) { + if i.closed { + return nil, errors.New("instance is closed") + } + + defer func() { + if panicError := recover(); panicError != nil { + err = fmt.Errorf("panic occurred in %s: %v", "FPDF_StructElement_GetTitle", panicError) + } + }() + + return i.pdfium.FPDF_StructElement_GetTitle(request) +} + +func (i *pdfiumInstance) FPDF_StructElement_GetType(request *requests.FPDF_StructElement_GetType) (resp *responses.FPDF_StructElement_GetType, err error) { + if i.closed { + return nil, errors.New("instance is closed") + } + + defer func() { + if panicError := recover(); panicError != nil { + err = fmt.Errorf("panic occurred in %s: %v", "FPDF_StructElement_GetType", panicError) + } + }() + + return i.pdfium.FPDF_StructElement_GetType(request) +} + +func (i *pdfiumInstance) FPDF_StructTree_Close(request *requests.FPDF_StructTree_Close) (resp *responses.FPDF_StructTree_Close, err error) { + if i.closed { + return nil, errors.New("instance is closed") + } + + defer func() { + if panicError := recover(); panicError != nil { + err = fmt.Errorf("panic occurred in %s: %v", "FPDF_StructTree_Close", panicError) + } + }() + + return i.pdfium.FPDF_StructTree_Close(request) +} + +func (i *pdfiumInstance) FPDF_StructTree_CountChildren(request *requests.FPDF_StructTree_CountChildren) (resp *responses.FPDF_StructTree_CountChildren, err error) { + if i.closed { + return nil, errors.New("instance is closed") + } + + defer func() { + if panicError := recover(); panicError != nil { + err = fmt.Errorf("panic occurred in %s: %v", "FPDF_StructTree_CountChildren", panicError) + } + }() + + return i.pdfium.FPDF_StructTree_CountChildren(request) +} + +func (i *pdfiumInstance) FPDF_StructTree_GetChildAtIndex(request *requests.FPDF_StructTree_GetChildAtIndex) (resp *responses.FPDF_StructTree_GetChildAtIndex, err error) { + if i.closed { + return nil, errors.New("instance is closed") + } + + defer func() { + if panicError := recover(); panicError != nil { + err = fmt.Errorf("panic occurred in %s: %v", "FPDF_StructTree_GetChildAtIndex", panicError) + } + }() + + return i.pdfium.FPDF_StructTree_GetChildAtIndex(request) +} + +func (i *pdfiumInstance) FPDF_StructTree_GetForPage(request *requests.FPDF_StructTree_GetForPage) (resp *responses.FPDF_StructTree_GetForPage, err error) { + if i.closed { + return nil, errors.New("instance is closed") + } + + defer func() { + if panicError := recover(); panicError != nil { + err = fmt.Errorf("panic occurred in %s: %v", "FPDF_StructTree_GetForPage", panicError) + } + }() + + return i.pdfium.FPDF_StructTree_GetForPage(request) +} + func (i *pdfiumInstance) FPDF_VIEWERREF_GetDuplex(request *requests.FPDF_VIEWERREF_GetDuplex) (resp *responses.FPDF_VIEWERREF_GetDuplex, err error) { if i.closed { return nil, errors.New("instance is closed")