Reorder pages
Page order in a PDF is the order of the entries in the page tree, so changing it means reading the pages and adding them to a new writer in the order you want. There is no call that moves a page within an existing document.
The same loop covers reordering, dropping pages and extracting a range. Which pages you
call AddPage with, and in what sequence, is the whole of it.
Doing it
pdfReader, f, err := model.NewPdfReaderFromFile(inputPath, nil)
if err != nil {
return err
}
defer f.Close()
pdfWriter := model.NewPdfWriter()
for _, pageNum := range []int{3, 1, 2} {
page, err := pdfReader.GetPage(pageNum)
if err != nil {
return err
}
if err := pdfWriter.AddPage(page); err != nil {
return err
}
}
return pdfWriter.WriteToFile(outputPath)GetPage numbers pages from 1 and returns page numbering must start at 1 for 0, or
invalid page number (page count too short) past the end. Reversing a document is the
same loop counting down; dropping pages is the same loop with a condition.
reader.ToWriter is the other route from reader to writer and it is the better one for
most edits, because it carries outlines, metadata and the AcroForm across. It cannot do
this job: its callback runs once per page in the original order, and there is no hook
for changing that order.
For a contiguous range, pdfutil.ExtractPageRange is the same loop already written:
err := pdfutil.ExtractPageRange(inputPath, outputPath, 1, 2, false)pageFrom and pageTo are inclusive and 1-based. The last argument copies the
document’s optional content properties (OCProperties) to the output, which matters
only for files with layers.
Limitations
A hand-built PdfWriter starts empty, so everything document-level is gone from the
output: outlines, document info, XMP metadata, page labels, named destinations, viewer
preferences and the AcroForm. Page contents and annotations survive because they hang
off the page dictionary. PdfWriter has a setter for most of these, AddOutlineTree,
SetForms, SetDocInfo, SetPageLabels and SetNamedDestinations among them, so
copying them across is possible but explicit.
Internal links break silently when their target is dropped. A link annotation pointing at a page that never made it into the output still writes out, and clicking it does nothing useful.
AddPage copies the inherited Resources, MediaBox, CropBox and Rotate values
down onto the page dictionary itself, then re-parents the page to the new writer. That
is what makes a page independent of its original document.
It also mutates the page object it is given rather than copying it. Adding the same
*PdfPage twice puts one object in the page tree twice, which is not valid. Use
page.Duplicate() for the second copy.
ExtractPageRange checks that pageTo is within the document but not that pageFrom
is at least 1 or that pageFrom <= pageTo. A reversed range writes a document with no
pages instead of returning an error.
Run the example
pdf_split.go is the range case: it reads the page numbers off the command line and
hands them straight to ExtractPageRange. The reordering loop above is what the
function does internally.
git clone https://github.com/unidoc/unipdf-examples.git
cd unipdf-examples/pages
go run pdf_split.go input.pdf 1 2 output.pdfIf this is your first time using UniPDF, follow the getting started guide to create an API key and set up your development environment.
View the full source
pdf_split_advanced.go
in the same folder is the identical program with keepOptionalContent set to true.