CAM::PDF(3pm)
β¨ NAME β¨
CAM::PDF β it's the PDF manipulation library, bestie. That's the whole vibe. π
π LICENSE π
Copyright 2002-2006 Clotho Advanced Media, Inc., <http://www.clotho.com/>
Copyright 2007-2008 Chris Dolan
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
π¦ SYNOPSIS π¦
use CAM::PDF;
my $pdf = CAM::PDF->new('test1.pdf');
my $page1 = $pdf->getPageContent(1);
[ ... mess with page ... ]
$pdf->setPageContent(1, $page1);
[ ... create some new content ... ]
$pdf->appendPageContent(1, $newcontent);
my $anotherpdf = CAM::PDF->new('test2.pdf');
$pdf->appendPDF($anotherpdf);
my @prefs = $pdf->getPrefs();
$prefs[$CAM::PDF::PREF_OPASS] = 'mypassword';
$prefs[$CAM::PDF::PREF_UPASS] = 'mypassword';
$pdf->setPrefs(@prefs);
$pdf->cleanoutput('out1.pdf');
print $pdf->toPDF();
There are sooo many example programs bundled in this distribution that do genuinely useful tasks β go peek in the "bin" subdirectory, no cap. π
π DESCRIPTION π
Okay so. This package reads and writes literally any document that conforms to the PDF specification, which Adobe generously hands out at <http://partners.adobe.com/public/developer/pdf/index_reference.html> (link last checked Oct 2005, periodt).
The file format through PDF 1.5 is well-supported β slay β with one exception: the "linearized" or "optimized" output format. This module can read that, but it cannot write it. And ngl, lots of specific aspects of the document model are just not manipulable with this package (fonts, we're looking at you π), BUT if the input document is written correctly, then this module will preserve the model integrity. It's giving careful. It's giving respectful.
The PDF writing feature saves as PDF 1.4-compatible. That means we cannot write compressed object streams. The consequence? Reading a PDF 1.5+ document and then writing it back out may enlarge the resulting file by a fair margin. It's the glow-up tax, bestie. π
This library grants you some power over the PDF security model. But listen β applications editing PDF documents via this library MUST respect the security preferences of the document. Any violation of this respect is contrary to Adobe's intellectual property position, as stated in the reference manual at the URL above. So. Don't be that person. π
Technical detail regarding corrupt PDFs, and this one's important: this library adheres strictly to the PDF specification. Adobe's Acrobat Reader is more lenient, allowing some corrupted PDFs to be viewable. Therefore, it is possible that some PDFs may be readable by Acrobat that are illegible to this library. In particular, files which have had line endings converted to or from DOS/Windows style (i.e. CR-NL) may be rendered unusable even though Acrobat does not complain. Future library versions may relax the parser, but not yet. π
πΈ API πΈ
Functions intended to be used externally π
$self = CAM::PDF->new(content | filename | '-')
$self->toPDF()
$self->needsSave()
$self->save()
$self->cleansave()
$self->output(filename | '-')
$self->cleanoutput(filename | '-')
$self->previousRevision()
$self->allRevisions()
$self->preserveOrder()
$self->appendObject(olddoc, oldnum, [follow=(1|0)])
$self->replaceObject(newnum, olddoc, oldnum, [follow=(1|0)])
(olddoc can be undef in the above for adding new objects)
$self->numPages()
$self->getPageText(pagenum)
$self->getPageDimensions(pagenum)
$self->getPageContent(pagenum)
$self->setPageContent(pagenum, content)
$self->appendPageContent(pagenum, content)
$self->deletePage(pagenum)
$self->deletePages(pagenum, pagenum, ...)
$self->extractPages(pagenum, pagenum, ...)
$self->appendPDF(CAM::PDF object)
$self->prependPDF(CAM::PDF object)
$self->wrapString(string, width, fontsize, page, fontlabel)
$self->getFontNames(pagenum)
$self->addFont(page, fontname, fontlabel, [fontmetrics])
$self->deEmbedFont(page, fontname, [newfontname])
$self->deEmbedFontByBaseName(page, basename, [newfont])
$self->getPrefs()
$self->setPrefs()
$self->canPrint()
$self->canModify()
$self->canCopy()
$self->canAdd()
$self->getFormFieldList()
$self->fillFormFields(fieldname, value, [fieldname, value, ...])
or $self->fillFormFields(%values)
$self->clearFormFieldTriggers(fieldname, fieldname, ...)
Heads up bestie:'clean'as incleansave()andcleanobject()means write a fresh PDF document. The alternative (e.g.save()) reuses the existing doc and just appends to it. Also note that'clean'functions sort the objects numerically. If you prefer that the new PDF docs more closely resemble the old ones, callpreserveOrder()beforecleansave()orcleanobject(). iykyk π
Slightly less external, but useful, functions β¨
$self->toString()
$self->getPage(pagenum)
$self->getFont(pagenum, fontname)
$self->getFonts(pagenum)
$self->getStringWidth(fontdict, string)
$self->getFormField(fieldname)
$self->getFormFieldDict(object)
$self->isLinearized()
$self->decodeObject(objectnum)
$self->decodeAll(any-node)
$self->decodeOne(dict-node)
$self->encodeObject(objectnum, filter)
$self->encodeOne(any-node, filter)
$self->changeString(obj-node, hashref)
Deeper utilities π«Ά
$self->pageAddName(pagenum, name, objectnum)
$self->getPageObjnum(pagenum)
$self->getPropertyNames(pagenum)
$self->getProperty(pagenum, propname)
$self->getValue(any-node)
$self->dereference(objectnum) or $self->dereference(name,pagenum)
$self->deleteObject(objectnum)
$self->copyObject(obj-node)
$self->cacheObjects()
$self->setObjNum(obj-node, num)
$self->getRefList(obj-node)
$self->changeRefKeys(obj-node, hashref)
More rarely needed utilities π
$self->getObjValue(objectnum)
Routines that should not be called π«
$self->_startdoc()
$self->delinearlize()
$self->build*()
$self->parse*()
$self->write*()
$self->*CB()
$self->traverse()
$self->fixDecode()
$self->abbrevInlineImage()
$self->unabbrevInlineImage()
$self->cleanse()
$self->clean()
$self->createID()
Fr fr, do not call these. They're private. Stay out of the group chat. π
π₯ FUNCTIONS π₯
Object creation/manipulation π
$doc->new($package, $content)
$doc->new($package, $content, $ownerpass, $userpass)
$doc->new($package, $content, $ownerpass, $userpass, $prompt)
$doc->new($package, $content, $ownerpass, $userpass, $options)
Instantiate a brand new CAM::PDF object. $content can be a document in a string, a filename, or '-'. That last one means the document should be read from standard input. If the document is password protected, the passwords should be passed as additional arguments. If they are not known, a boolean $prompt argument lets the programmer suggest that the constructor prompt the user for a password. This is rudimentary prompting: passwords are in the clear on the console. π¬ (Yes, in the clear. We said what we said.)
This constructor takes an optional final argument which is a hash reference. This hash can contain any of the following optional parameters:
prompt_for_password=>$booleanβ This is the same as the$promptargument described above.fault_tolerant=>$booleanβ This flag causes the instance to be more lenient when reading the input PDF. Currently, this only affects PDFs which cannot be successfully decrypted.
$doc->toPDF()
Serializes the data structure as a PDF document stream and returns as in a scalar. π
$doc->toString()
Returns a serialized representation of the data structure. Implemented via Data::Dumper.
Document reading π
(all of these functions are intended for internal only)
$doc->getRootDict()
Returns the Root dictionary for the PDF.
$doc->getPagesDict()
Returns the root Pages dictionary for the PDF.
$doc->parseObj($string)
Use parseAny() instead of this, if possible.
Given a fragment of PDF page content, parse it and return an object Node. This can be called as a class method in most circumstances, but is intended as an instance method.
$doc->parseInlineImage($string)
$doc->parseInlineImage($string, $objnum)
$doc->parseInlineImage($string, $objnum, $gennum)
Given a fragment of PDF page content, parse it and return an object Node. This can be called as a class method in some cases, but is intended as an instance method.
$doc->writeInlineImage($objectnode)
This is the inverse of parseInlineImage(), intended for use only in the CAM::PDF::Content class.
$doc->parseStream($string, $objnum, $gennum, $dictnode)
This should only be used by parseObj(), or other specialized cases.
Given a fragment of PDF page content, parse it and return a stream Node. This can be called as a class method in most circumstances, but is intended as an instance method.
The dictionary Node argument is typically the body of the object Node that precedes this stream.
$doc->parseDict($string)
$doc->parseDict($string, $objnum)
$doc->parseDict($string, $objnum, $gennum)
Use parseAny() instead of this, if possible.
Given a fragment of PDF page content, parse it and return an dictionary Node. This can be called as a class method in most circumstances, but is intended as an instance method.
$doc->parseArray($string)
$doc->parseArray($string, $objnum)
$doc->parseArray($string, $objnum, $gennum)
Use parseAny() instead of this, if possible.
Given a fragment of PDF page content, parse it and return an array Node. This can be called as a class or instance method.
$doc->parseLabel($string)
$doc->parseLabel($string, $objnum)
$doc->parseLabel($string, $objnum, $gennum)
Use parseAny() instead of this, if possible.
Given a fragment of PDF page content, parse it and return a label Node. This can be called as a class or instance method.
$doc->parseRef($string)
$doc->parseRef($string, $objnum)
$doc->parseRef($string, $objnum, $gennum)
Use parseAny() instead of this, if possible.
Given a fragment of PDF page content, parse it and return a reference Node. This can be called as a class or instance method.
$doc->parseNum($string)
$doc->parseNum($string, $objnum)
$doc->parseNum($string, $objnum, $gennum)
Use parseAny() instead of this, if possible.
Given a fragment of PDF page content, parse it and return a number Node. This can be called as a class or instance method.
$doc->parseString($string)
$doc->parseString($string, $objnum)
$doc->parseString($string, $objnum, $gennum)
Use parseAny() instead of this, if possible.
Given a fragment of PDF page content, parse it and return a string Node. This can be called as a class or instance method.
$doc->parseHexString($string),$doc->parseHexString($string, $objnum),$doc->parseHexString($string, $objnum, $gennum)β bestie please just useparseAny()instead of this one if you possibly can π . Hand it a lil fragment of PDF page content and it parses that thing and gives you back a hex string Node. You can call this as a class method or an instance method, whichever vibe you're on. β¨$doc->parseBoolean($string),$doc->parseBoolean($string, $objnum),$doc->parseBoolean($string, $objnum, $gennum)β again,parseAny()is right there, use that instead if you can. Give it a fragment of PDF page content, it parses it, you get a boolean Node back. Class method or instance method, both work. π$doc->parseNull($string),$doc->parseNull($string, $objnum),$doc->parseNull($string, $objnum, $gennum)β same energy, same advice:parseAny()if possible. Feed it a fragment of PDF page content, it parses, you get a null Node. Yep, a whole Node that means nothing. Iconic. π«₯ Works as a class or instance method.$doc->parseAny($string),$doc->parseAny($string, $objnum),$doc->parseAny($string, $objnum, $gennum)β the main character of parsers π Give it a fragment of PDF page content, it parses it and returns a Node of whatever type is appropriate. Class method or instance method, your choice. This is the one you actually want, no cap.
β¨ Data Accessors β¨
$doc->getValue($object)β for INTERNAL use π Dereference a data object and return a value. Give it a node object of literally any kind and it hands back a raw scalar object:hashref,arrayref,string,number. This function follows all the references and descends into all the objects. It's thorough like that. π$doc->getObjValue($objectnum)β for INTERNAL use π Dereference a data object and return a value. Behaves exactly like thegetValue()function, but you reach for it when the only thing you know is the object number. That's the whole difference, bestie. π$doc->dereference($objectnum),$doc->dereference($name, $pagenum)β for INTERNAL use π Dereference a data object and return a PDF object as a node. This one is obsessed with the internal object cache β makes heavy use of it. Most (if not all) object requests should be routed through this function, fr fr.$nameshould look something like'/R12'. π$doc->getPropertyNames($pagenum),$doc->getProperty($pagenum, $propertyname)β every PDF page carries a list of resources it uses (images, fonts, etc.).getPropertyNames()returns an array of the names of those resources.getProperty()returns a node representing a named property (most likely a reference node). π¦$doc->getFont($pagenum, $fontname)β for INTERNAL use π Returns a dictionary for a given font identified by its label, referenced by page.$doc->getFontNames($pagenum)β for INTERNAL use π Returns a list of fonts for a given page.$doc->getFonts($pagenum)β for INTERNAL use π Returns an array of font objects for a given page.$doc->getFontByBaseName($pagenum, $fontname)β for INTERNAL use π Returns a dictionary for a given font, referenced by page and the name of the base font.$doc->getFontMetrics($properties $fontname)β for INTERNAL use π Returns a data structure representing the font metrics for the named font. The property list is the result of something like this:
$self->_buildNameTable($pagenum);
my $properties = $self->{Names}->{$pagenum};
Alternatively, if you already know the page number, it might be easier to just do:
my $font = $self->dereference($fontlabel, $pagenum);
my $fontmetrics = $font->{value}->{value};
where $fontlabel is something like '/Helv'. The getFontMetrics() method comes in clutch in the cases where you've forgotten which page number you're working on (e.g. in CAM::PDF::GS), or if your property list isn't part of any page (e.g. when you're working with form field annotation objects). π§
$doc->addFont($pagenum, $fontname, $fontlabel),$doc->addFont($pagenum, $fontname, $fontlabel, $fontmetrics)β adds a reference to the specified font to the page. πΈ
If you supply a font metrics hash (and heads up, it's required for any font other than the 14 core fonts), then it gets cloned and inserted into the new font structure. But listen: if those font metrics contain references (e.g. to the "FontDescriptor"), the referred objects are not copied β you have to do that part yourself, bestie. π
For Type1 fonts, the font metrics must minimally contain the following fields: "Subtype", "FirstChar", "LastChar", "Widths", "FontDescriptor".
$doc->deEmbedFont($pagenum, $fontname),$doc->deEmbedFont($pagenum, $fontname, $basefont)β removes embedded font data while leaving the font reference intact. Returns true if the font exists and either 1) the font is not embedded or 2) the embedded data was successfully discarded. Returns false if the font does not exist, or if the embedded data could not be discarded. π«Ά
The optional $basefont parameter lets you change the font. This is useful when some applications embed a standard font (see below) and give it a silly little name, like "SYLXNP+Helvetica". In that example, it's important to change the basename back to the standard "Helvetica" when you de-embed. π
De-embedding the font does NOT remove it from the PDF document β it just removes references to it. If you want that sweet size reduction from throwing away unused font data, you should run the following code sometime after this method.
$self->cleanse();
For reference, the standard fonts are "Times-Roman", "Helvetica", and "Courier" (and their bold, italic and bold-italic forms) plus "Symbol" and "Zapfdingbats". (Adobe PDF Reference v1.4, p.319) π
$doc->deEmbedFontByBaseName($pagenum, $fontname),$doc->deEmbedFontByBaseName($pagenum, $fontname, $basefont)β just likedeEmbedFont(), except that the font name parameter refers to the name of the current base font instead of the PDF label for the font. π$doc->wrapString($string, $width, $fontsize, $fontmetrics),$doc->wrapString($string, $width, $fontsize, $pagenum, $fontlabel)β returns an array of strings wrapped to the specified width. Cute and useful. β¨$doc->getStringWidth($fontmetrics, $string)β for INTERNAL use π Returns the width of the string, using the font metrics if possible.$doc->numPages()β returns the number of pages in the PDF document. That's it, that's the whole thing. π$doc->getPage($pagenum)β for INTERNAL use π Returns a dictionary for a given numbered page.$doc->getPageObjnum($pagenum)β for INTERNAL use π Return the number of the PDF object in which the specified page occurs.$doc->getPageText($pagenum)β extracts the text from a PDF page as a string. π¦$doc->getPageContentTree($pagenum)β retrieves a parsed page content data structure, orundefif there's a syntax error or if the page doesn't exist. No page, no tree, sorry bestie. π$doc->getPageContent($pagenum)β return a string with the layout contents of one page.$doc->getPageDimensions($pagenum)β returns an array of"x","y","width"and"height"numbers that define the dimensions of the specified page in points (1/72 inches). Technically this is the"MediaBox"dimensions, which explains why it's possible for"x"and"y"to be non-zero β but that's a rare case. π
For example, given a simple 8.5 by 11 inch page, this method will return "(0,0,612,792)".
This method will die() if the specified page number does not exist. π
$doc->getName($object)β for INTERNAL use π Given a PDF object reference, return its name, if it has one. This is useful for indirect references to images in particular. πΌοΈ$doc->getPrefs()β return an array of security information for the document:
owner password
user password
print boolean
modify boolean
copy boolean
add boolean
See the PDF reference for the intended use of the latter four booleans.
This module publishes the array indices of these values for your convenience:
$CAM::PDF::PREF_OPASS
$CAM::PDF::PREF_UPASS
$CAM::PDF::PREF_PRINT
$CAM::PDF::PREF_MODIFY
$CAM::PDF::PREF_COPY
$CAM::PDF::PREF_ADD
So, you can retrieve the value of the Copy boolean via:
my ($canCopy) = ($self->getPrefs())[$CAM::PDF::PREF_COPY];
$doc->canPrint()β return a boolean indicating whether the Print permission is enabled on the PDF. π¨οΈ$doc->canModify()β return a boolean indicating whether the Modify permission is enabled on the PDF. βοΈ$doc->canCopy()β return a boolean indicating whether the Copy permission is enabled on the PDF. π$doc->canAdd()β return a boolean indicating whether the Add permission is enabled on the PDF. β$doc->getFormFieldList()β return an array of the names of all of the PDF form fields. The names are the full hierarchical names constructed as explained in the PDF reference manual. These names are useful for thefillFormFields()function. π$doc->getFormField($name)β for INTERNAL use π Return the object containing the form field definition for the specified field name.$namecan be either the full name or the"short/alternate"name.$doc->getFormFieldDict($formfieldobject)β for INTERNAL use π Return a hash reference representing the accumulated property list for a form field, including all of its inherited properties. π
This should be treated as a read-only hash! It ONLY retrieves the properties it knows about. Don't go writing into it, bestie, I'm begging. π
π Data/Object Manipulation π
$doc->setPrefs($ownerpass, $userpass, $print?, $modify?, $copy?, $add?)β alter the document's security information. Note that modifying these parameters must be done respecting the intellectual property of the original document. See Adobe's statement in the introduction of the reference manual. π
Important Note: Most PDF readers (Acrobat, Preview.app) only offer one password field for opening documents. So, if the$ownerpassand$userpassare different, those applications cannot read the documents. (Perhaps this is a bug inCAM::PDF?) π
Note: any omitted booleans default to false. So, these two are equivalent:
$doc->setPrefs('password', 'password');
$doc->setPrefs('password', 'password', 0, 0, 0, 0);
$doc->setName($object, $name)β for INTERNAL use π Change the name of a PDF object structure. π$doc->removeName($object)β for INTERNAL use π Delete the name of a PDF object structure. Gone, poof, bye. π¦$doc->pageAddName($pagenum, $name, $objectnum)β for INTERNAL use π Append a named object to the metadata for a given page.$doc->setPageContent($pagenum, $content)$doc->setPageContent($pagenum, $tree->toString)
okay bestie so this one swaps out the content of the page you point at with a brand new version. the classic move is to call getPageContent() first, mess with the string it hands you, and then feed it right back in here. β¨
tiny but crucial: if your content is a parsed tree (aka the thing getPageContentTree gave you), you gotta serialize it with toString first. no shortcuts, fr fr. π«Ά
$doc->appendPageContent($pagenum, $content)β adds more content to the page instead of replacing it. but heads up bestie: this does NOT do any page metadata work for you β like, it will not make font objects for any newly defined fonts. that's on you. π$doc->extractPages($pages...)β nukes every page except the ones you name. likedeletePages(), the pages can be multiple arguments, comma separated lists, or ranges (open or closed). selective queen behavior π$doc->deletePages($pages...)β removes the pages you name. same deal: multiple arguments, comma separated lists, ranges (open or closed). ποΈ$doc->deletePage($pagenum)β removes that one specific page. but if the PDF has only one page? this method will fail. can't have an empty PDF, that's giving nothing. π$doc->decachePages($pagenum, $pagenum, ...)β clears out cached copies of the page data structures you list. super useful if you did some operation that changed a page and the cache is now lying to you. π$doc->addPageResources($pagenum, $resourcehash)β takes the resources from the given object and drops them into the page's resource dictionary. if the page doesn't have a resource dictionary yet, one gets created. and it avoids duplicating resources where feasible, so no messy duplicates. πΈ$doc->appendPDF($pdf)β appends pages from another PDF document onto this one. no optimization is done at all β the pieces are just appended and the internal table of contents gets updated. simple, direct, no thoughts.
β οΈ note that this can break documents with annotations. see the appendpdf.pl script for a workaround. iykyk.
$doc->prependPDF($pdf)β literally just likeappendPDF()except the new document gets inserted on page 1 instead of at the end. front of the line energy π$doc->duplicatePage($pagenum)$doc->duplicatePage($pagenum, $leaveblank)
inserts an identical copy of the page you name into the document. the new page's number will be $pagenum + 1. π
if $leaveblank is true, the new page does not get any content. so the document is broken until you subsequently call setPageContent(). don't leave it hanging bestie π
$doc->createStreamObject($content)$doc->createStreamObject($content, $filter ...)
For INTERNAL use
creates a new Stream object. this object is NOT added to the document β use the appendObject() function to do that after you call this one. π¦
$doc->uninlineImages()$doc->uninlineImages($pagenum)
searches the content of the specified page (or all pages if you leave the page number out) looking for embedded images. if it finds any, it replaces them with indirect objects. β¨
real talk: this procedure uses heuristics to detect in-line images, and it's subject to confusion in extremely rare cases of text that uses"BI"and"ID"a lot. π
$doc->appendObject($doc, $objectnum, $recurse?)$doc->appendObject($undef, $object, $recurse?)
duplicates an object from another PDF document and adds it to this document, optionally descending into the object and copying any other objects it references. π«Ά
like replaceObject(), the second form lets you append a newly-created block to the PDF. fresh object, just dropped.
$doc->replaceObject($objectnum, $doc, $objectnum, $recurse?)$doc->replaceObject($objectnum, $undef, $object)
duplicates an object from another PDF document and inserts it into this document, replacing an existing object. optionally it descends into the original object and copies any other objects it references. π
if the other document is undefined, then the object to copy is taken to be an anonymous object that isn't part of any other document. super handy when you've just created that anonymous object. π
$doc->deleteObject($objectnum)β removes an object from the document. but bestie, this function does NOT take care of dependencies on this object. you're on your own for that. π$doc->cleanse()β removes unused objects.
π¨ WARNING: this function breaks some PDF documents because it removes objects that are strictly part of the page model hierarchy, but which are required anyway (like some font definition objects). highkey risky, proceed with caution.
$doc->createID()β For INTERNAL use
generates a new document ID. contrary to the Adobe recommendation, this is a random number. not me ignoring the spec π
$doc->fillFormFields($name => $value, ...)$doc->fillFormFields($opts_hash, $name => $value, ...)
sets the default values of PDF form fields. the name should be the full hierarchical name of the field as output by the getFormFieldList() function. the argument list can be a hash if you like. a simple way to use this function is something like this: π
my %fields = (fname => 'John', lname => 'Smith', state => 'WI');
$field{zip} = 53703;
$self->fillFormFields(%fields);
if the first argument is a hash reference, it's interpreted as options for how to render the filled data:
background_color =< 'none' | $gray | [$r, $g, $b]β specify the background color for the text field. π¨max_autoscale_fontsize =< $sizemin_autoscale_fontsize =< $size
if the form field is set to auto-size the text to fit, then you may use these options to constrain the limits of that autoscaling. otherwise, for example, a very long string will become arbitrarily small to fit in the box. tiny text jumpscare π
$doc->clearFormFieldTriggers($name, $name, ...)β disables any triggers set on data entry for the specified form field names. this is useful in the case where, for example, the data entry Javascript forbids punctuation and you want to prefill with a hyphenated word. if you don't clear the trigger, the prefill may not happen. sneaky little gotcha π«Ά$doc->clearAnnotations()β removes all annotations from the document. if form fields are encountered, their text is added to the appropriate page. β¨$doc->previousRevision()β if this PDF was previously saved in append mode (that is, if"clean()"was not invoked on it), returns a new instance representing that previous version. otherwise returns void. π°οΈ
if this is an encrypted PDF, this method assumes that previous revisions were encrypted with the same password, which may be an incorrect assumption. ngl, don't count on it.
$doc->allRevisions()β accumulates CAM::PDF instances returned by"previousRevision"until there are no more previous revisions. returns a list of instances from newest to oldest, including this instance as the newest. the whole timeline bestie π
β¨ Document Writing β¨
$doc->preserveOrder()β tries to recreate the original document as much as possible. this may help in recreating documents which use undocumented tricks of saving font information in adjacent objects. respecting the original's weird little habits π$doc->isLinearized()β returns a boolean indicating whether this PDF is linearized (aka"optimized"). β β$doc->delinearize()β For INTERNAL use
undoes the tweaks used to make the document 'optimized'. this function is automatically called on every save or output since this library does not yet support linearized documents. π
$doc->clean()β caches all parts of the document and throws away its old structure. this is useful for writing PDFs anew, instead of simply appending changes to the existing documents. it's called bycleansave()andcleanoutput(). π§Όβ¨$doc->needsSave()β returns a boolean indicating whether thesave()method needs to be called. likesave(), this has nothing to do with whether the document has been saved to disk, but whether the in-memory representation of the document has been serialized. π§$doc->save()β serializes the document into a single string. all changed document elements are normalized, and a new index and an updated trailer are created. π¦
this function operates solely in memory. it DOES NOT write the document to a file. see the output() function for that. don't get it twisted bestie
$doc->cleansave()β calls theclean()function, then calls thesave()function. two steps, one vibe. π«Ά$doc->output($filename)$doc->output()
saves the document to a file. the save() function is called first to serialize the data structure. if no filename is specified, or if the filename is '-', the document is written to standard output. πΎ
note: it is the responsibility of the application to ensure that the PDF document has either the Modify or Add permission. you can do this like the following:
if ($self->canModify()) {
$self->output($outfile);
} else {
die "The PDF file denies permission to make modifications\n";
}
$doc->cleanoutput($file)$doc->cleanoutput()
calls the clean() function, then calls the output() function to write a fresh copy of the document to a file. brand new, who dis πΈ
$doc->writeObject($objnum)β returns the serialization of the specified object. π¦$doc->writeString($string)β returns the serialization of the specified string. works on normal or hex strings. if encryption is desired, the string should be encrypted before being passed here. π$doc->writeAny($node)β returns the serialization of the specified node. this handles all Node types, including object Nodes. every single one, no exceptions β¨
π Document Traversing π
$doc->traverse($dereference, $node, $callbackfunc, $callbackdata)β recursive traversal of a PDF data structure. π
in many cases, it's useful to apply one action to every node in an object tree. the routines below all use this traverse() function. one of the most important parameters is the first: the $dereference boolean. if true, the traversal follows reference Nodes. if false, it does not descend into reference Nodes. π«Ά
optionally, you can pass in a hashref as a final argument to reduce redundant traversing across multiple calls. just pass in an empty hashref the first time and pass in the same hashref each time. see "changeRefKeys()" for an example. iykyk
$doc->decodeObject($objectnum)β For INTERNAL use
removes any filters (like compression, etc) from a data stream indicated by the object number. π§Ό
$doc->decodeAll($object)β For INTERNAL use
removes any filters from any data stream in this object or any object referenced by it. deep clean energy β¨
$doc->decodeOne($object)$doc->decodeOne($object, $save?)
For INTERNAL use
okay so next up β Remove any filters from an object. The boolean flag $save (defaults to false, bestie) tells you whether this removal is gonna be permanent or just a one-time thing. If it's true, the function hands you back success or failure. If it's false, you get the defiltered content instead. Choose wisely π
$doc->fixDecode($streamdata, $filter, $params)β this is a lil utility method that does any tweaking you need after the filter gets yanked off a data stream. Cleanup crew energy β¨$doc->encodeObject($objectnum, $filter)β slaps the specified filter onto the object. That's it, that's the job π$doc->encodeOne($object, $filter)β same vibe, applies the specified filter to the object π₯$doc->setObjNum($object, $objectnum, $gennum)β descends into an object and changes all of the INTERNAL object number flags to a new number. This is purely for consistency of internal accounting, no cap π§Ύ$doc->getRefList($object)β For INTERNAL use
returns an array of all the objects referred to in this object π
$doc->changeRefKeys($object, $hashref)β For INTERNAL use
renumbers every reference inside an object π¦
$doc->abbrevInlineImage($object)β contracts all the image keywords down to their inline abbreviations. Shorthand queen π$doc->unabbrevInlineImage($object)β expands all inline image abbreviations back out. Releasing them from the acronym era πΈ$doc->changeString($object, $hashref)β alters every instance of a given string. The hashref is a dictionary of from-string and to-string. And here's the twist: if the from-string looks like"regex(...)", then it gets interpreted as a Perl regular expression and getseval'ed. Otherwise? The search-and-replace is completely literal. No surprises, just vibes π
β¨ Utility functions β¨
$doc->rangeToArray($min, $max, $list...)β converts string lists of numbers into an array. For example, this:
CAM::PDF->rangeToArray(1, 15, '1,3-5,12,9', '14-', '8 - 6, -2');
becomes:
(1,3,4,5,12,9,14,15,8,7,6,1,2)
$doc->trimstr($string)β used solely for debugging. Trims a string down to a max of 40 characters, and handles nulls and non-Unix line endings while it's at it π§$doc->copyObject($node)β clones a node viaData::Dumperandeval(). Dupe it, periodt π$doc->cacheObjects()β parses all object Nodes and stashes them in the cache. Super useful when you're planning some global manipulation and want all the data conveniently chillin' in RAM π$doc->asciify($string)β helper class/instance method to massage a string, cleaning up some non-ASCII problems. Heads up though: this is a very incomplete list. Specifically it handles:- f-i ligatures
- the (R) symbol
π COMPATIBILITY π
This library was primarily developed against the 3rd edition of the reference (PDF v1.4), with several important updates from the 4th edition (PDF v1.5). Deepest focus though? PDF v1.2 features, bestie. That said, it should be forward and backward compatible in the majority of cases π¦
β‘ PERFORMANCE β‘
This module is written with good speed and flexibility in mind β often at the expense of memory consumption. Entire PDF documents are typically slurped straight into RAM π₯€ As an example: simply calling new('PDFReference15_v15.pdf') (the 13.5 MB Adobe PDF Reference V1.5 document) pushes Perl to consume 89 MB of RAM on my development machine. That's not a typo, that's just the price of admission π
π SEE ALSO π
There are several other PDF modules on CPAN. Below is a brief description of a few of them. If these comments are out of date, please inform me π
- PDF::API2
As of v0.46.003, LGPL license.
This is the leading PDF library, in my opinion.
Excellent text and font support. This is the highest level library of the bunch, and is the most complete implementation of the Adobe PDF spec. The author is amazingly responsive and patient π
- Text::PDF
As of v0.25, Artistic license.
Excellent compression support (CAM::PDF cribs off this Text::PDF feature). This has not been developed since 2003 π¬
- PDF::Reuse
As of v0.32, Artistic/GPL license, like Perl itself.
This library is not object oriented, so it can only process one PDF at a time, while storing all data in global variables. I'm not fond of it, but it's quite popular, so don't take my word for it! π
CAM::PDF is the only one of these that has regression tests. Currently, CAM::PDF has test coverage of about 50%, as reported by Build testcover π
Additionally, PDFLib is a commercial package not on CPAN (www.pdflib.com). It is a C-based library with a Perl interface. It is designed for PDF creation, not for reuse π
π¬ INTERNALS π¬
The data structure used to represent the PDF document is composed primarily of a hierarchy of Node objects. Every node in the document tree has this structure:
type => <type>
value => <value>
objnum => <object number>
gennum => <generation number>
where the <value> depends on the <type>, and <type> is one of:
objectβ Nodestreamβ byte stringstringβ byte stringhexstringβ byte stringnumberβ numberreferenceβ integer (object number)booleanβ"true"|"false"labelβ stringarrayβ arrayref of Nodesdictionaryβ hashref of (string => Node)nullβ undef
All of these except stream are directly related to the PDF data types of the same name. Streams are treated as special cases in this library since they have a non-general syntax and placement in the document body. Internally, streams are very much like strings, except that they have filters applied to them π¦
All objects are referenced indirectly by their numbers, as defined in the PDF document. In all cases, the dereference() function should be used to deserialize objects into their internal representation. This function is also useful for looking up named objects in the page model metadata. Every node in the hierarchy contains its object and generation number. You can think of this as a sort of a pointer back to the root of each node tree. This serves in place of a "parent" link for every node, which would be harder to maintain π
The PDF document itself is represented internally as a hash reference with many components, including the document content, the document metadata (index, trailer and root node), the object cache, and several other caches, in addition to a few assorted bookkeeping structures π§Ύ
The core of the document is represented in the object cache, which is only populated as needed β thus avoiding the overhead of parsing the whole document at read time. Lazy loading but make it iconic β¨
π AUTHOR π
Chris Dolan
This module was originally developed by me at Clotho Advanced Media Inc. Now I maintain it in my spare time π«Ά
π ACKNOWLEDGMENTS π
Thanks to all the people who have submitted bug reports over the years! I've belatedly started crediting people in the CHANGES file. Apologies to contributors I've overlooked... π
aaaand that's the whole page, bestie π this one's from perl v5.36.0, dated 2022-12-08, and it's the CAM::PDF(3pm) manpage β go forth and parse some PDFs, you absolute main character ππ
π¦ where this page comes from
- debian bookworm: libcam-pdf-perl 1.60-5 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- debian trixie: libcam-pdf-perl 1.60-5 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- ubuntu noble: libcam-pdf-perl 1.60-5 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- ubuntu resolute: libcam-pdf-perl 1.60-5 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- ubuntu stonking: libcam-pdf-perl 1.60-5 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms