Add low level PageLabels support (#325)

* Add reader method for retriving the PageLabels entry from the catalog
* Add writer method for setting the PageLabels entry in the catalog.
* Add creator method for adding page labels for the output file
* Add creator page labels test case
* Minor page labels test case correction
This commit is contained in:
Adrian-George Bostan 2020-04-23 00:17:33 +03:00 committed by GitHub
parent a69d788171
commit cb0166e96b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 106 additions and 0 deletions

View File

@ -13,6 +13,7 @@ import (
"strconv"
"github.com/unidoc/unipdf/v3/common"
"github.com/unidoc/unipdf/v3/core"
"github.com/unidoc/unipdf/v3/model"
)
@ -62,6 +63,10 @@ type Creator struct {
// Forms.
acroForm *model.PdfAcroForm
// Page labels.
pageLabels core.PdfObject
// Optimizer.
optimizer model.Optimizer
// Default fonts used by all components instantiated through the creator.
@ -82,6 +87,14 @@ func (c *Creator) SetOutlineTree(outlineTree *model.PdfOutlineTreeNode) {
c.externalOutline = outlineTree
}
// SetPageLabels adds the specified page labels to the PDF file generated
// by the creator. See section 12.4.2 "Page Labels" (p. 382 PDF32000_2008).
// NOTE: for existing PDF files, the page label ranges object can be obtained
// using the model.PDFReader's GetPageLabels method.
func (c *Creator) SetPageLabels(pageLabels core.PdfObject) {
c.pageLabels = pageLabels
}
// FrontpageFunctionArgs holds the input arguments to a front page drawing function.
// It is designed as a struct, so additional parameters can be added in the future with backwards
// compatibility.
@ -647,6 +660,14 @@ func (c *Creator) Write(ws io.Writer) error {
pdfWriter.AddOutlineTree(&c.outline.ToPdfOutline().PdfOutlineTreeNode)
}
// Page labels.
if c.pageLabels != nil {
if err := pdfWriter.SetPageLabels(c.pageLabels); err != nil {
common.Log.Debug("ERROR: Could not set page labels: %v", err)
return err
}
}
// Pdf Writer access hook. Can be used to encrypt, etc. via the PdfWriter instance.
if c.pdfWriterAccessFunc != nil {
err := c.pdfWriterAccessFunc(&pdfWriter)

View File

@ -2980,6 +2980,60 @@ func TestCreatorStable(t *testing.T) {
}
}
func TestPageLabels(t *testing.T) {
// Read input file.
f, err := os.Open(testPdfTemplatesFile1)
require.NoError(t, err)
defer f.Close()
reader, err := model.NewPdfReader(f)
require.NoError(t, err)
numPages, err := reader.GetNumPages()
require.NoError(t, err)
// Add input file pages to a new creator instance.
c := New()
nums := core.MakeArray()
for i := 0; i < numPages; i++ {
page, err := reader.GetPage(i + 1)
require.NoError(t, err)
err = c.AddPage(page)
require.NoError(t, err)
// Generate a page range for each page.
// If page index is even, show page label using Roman uppercase numerals.
// Otherwise, show page label using decimal Arabic numerals.
labelStyle := "R"
if i%2 != 0 {
labelStyle = "D"
}
pageRange := core.MakeDict()
pageRange.Set(*core.MakeName("S"), core.MakeName(labelStyle))
nums.Append(core.MakeInteger(int64(i)))
nums.Append(pageRange)
}
// Create page labels dictionary and add it to the creator.
genPageLabels := core.MakeDict()
genPageLabels.Set(*core.MakeName("Nums"), nums)
c.SetPageLabels(genPageLabels)
// Write output file to buffer.
outBuf := bytes.NewBuffer(nil)
err = c.Write(outBuf)
require.NoError(t, err)
// Read output file.
reader, err = model.NewPdfReader(bytes.NewReader(outBuf.Bytes()))
require.NoError(t, err)
// Retrieve page labels and compare them to the generated page labels.
pageLabels, err := reader.GetPageLabels()
require.NoError(t, err)
require.Equal(t, core.EqualObjects(genPageLabels, pageLabels), true)
}
var errRenderNotSupported = errors.New("rendering pdf is not supported on this system")
// renderPDFToPNGs uses ghostscript (gs) to render specified PDF file into a set of PNG images (one per page).

View File

@ -765,6 +765,25 @@ func (r *PdfReader) GetNamedDestinations() (core.PdfObject, error) {
return obj, nil
}
// GetPageLabels returns the PageLabels entry in the PDF catalog.
// See section 12.4.2 "Page Labels" (p. 382 PDF32000_2008).
func (r *PdfReader) GetPageLabels() (core.PdfObject, error) {
obj := core.ResolveReference(r.catalog.Get("PageLabels"))
if obj == nil {
return nil, nil
}
// Resolve references.
if !r.isLazy {
err := r.traverseObjectData(obj)
if err != nil {
return nil, err
}
}
return obj, nil
}
// Inspect inspects the object types, subtypes and content in the PDF file returning a map of
// object type to number of instances of each.
func (r *PdfReader) Inspect() (map[string]int, error) {

View File

@ -426,6 +426,18 @@ func (w *PdfWriter) SetNamedDestinations(names core.PdfObject) error {
return w.addObjects(names)
}
// SetPageLabels sets the PageLabels entry in the PDF catalog.
// See section 12.4.2 "Page Labels" (p. 382 PDF32000_2008).
func (w *PdfWriter) SetPageLabels(pageLabels core.PdfObject) error {
if pageLabels == nil {
return nil
}
common.Log.Trace("Setting catalog PageLabels...")
w.catalog.Set("PageLabels", pageLabels)
return w.addObjects(pageLabels)
}
// SetOptimizer sets the optimizer to optimize PDF before writing.
func (w *PdfWriter) SetOptimizer(optimizer Optimizer) {
w.optimizer = optimizer