Page Manipulation
Changing an existing document always means reading it with a PdfReader and writing a new
one with a PdfWriter; there is no in-place edit. Which of the three routes from reader to
writer you take decides what survives the round trip, and that is the decision worth
getting right before writing any code.
| Route | What you control | What you keep |
|---|---|---|
reader.ToWriter(opts) | Each page, through opts.PageProcessCallback. Page order is fixed. | Everything: document info, metadata, outlines, AcroForm, page labels, named destinations, optional content, viewer preferences, structure tree, global rotation. |
model.NewPdfWriter() plus AddPage | Which pages, and in what order. | Pages only. Anything document-level you want has to be copied across by hand. |
creator plus NewBlockFromPage | Geometry: scale, rotate, place several source pages on one output page. | Page content and its resources. Annotations, links and form fields are dropped. |
ToWriter is the default. The callback runs once per page, in order, before the page is
added to the writer, and returning an error from it aborts the whole conversion:
pdfWriter, err := pdfReader.ToWriter(&model.ReaderToWriterOpts{
PageProcessCallback: func(pageNum int, page *model.PdfPage) error {
page.Rotate = nil // for example
return nil
},
})
if err != nil {
return err
}
err = pdfWriter.WriteToFile(outputPath)ReaderToWriterOpts also carries a Skip* flag for each document-level part, so you can
drop outlines or the AcroForm deliberately rather than by accident.
Inherited page attributes
Resources, MediaBox, CropBox and Rotate are inheritable: a page dictionary that
omits them takes the value from its parent node in the page tree. This shows up in two
places.
When reading, the struct field is nil if the value is inherited. page.MediaBox and
page.Rotate are only set when the page carries them itself, so use page.GetMediaBox()
and page.GetRotate(), which walk up the tree. page.Size() is built on both and also
accounts for rotation, swapping width and height at 90 and 270 degrees.
When writing, PdfWriter.AddPage copies all four inherited values down onto the page
dictionary itself. This is what makes a page independent of its original page tree, and
it is also why PdfWriter.SetRotation often appears to do nothing - see
rotate PDF pages.
Page boxes
A page has up to five rectangles. Only two matter for most work.
MediaBox is the physical sheet and is the only required one. CropBox is the region a
viewer displays and defaults to the media box when absent. BleedBox, TrimBox and
ArtBox are for print production and are ignored on screen.
Because the crop box wins for display when it is present, changing only the media box on a page that has a crop box will not change what anyone sees. Crop page covers this.
Where to look
| Guide | Covers |
|---|---|
| Get page info | Page size, rotation and boxes, and inherited values. |
| Crop PDF pages | Shrinking the visible area of every page. |
| Rotate PDF pages | Global rotation, per-page rotation, and why the two conflict. |
| Flatten page rotation | Baking rotation into the content so the origin is the top left corner. |
| Merge PDFs | Combining files, with and without form fields. |
| Reorder pages | Writing pages out in a different order, or dropping some. |
| Add a side note to a page margin | Drawing in the margin of every page at generation time. |
For splitting a document into page ranges, pdfutil.ExtractPageRange is the one call
version; see reorder pages for the pattern it uses. For stamping text or
images over existing pages, see watermarks.