You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

PDFDocument.java 39KB

Initial support for XMP metadata (PDF 1.4) under fo:declarations. Both xmpmeta and RDF elements can be used as root elements for XMP metadata. Extracted DOM2SAX functionality from XMLXMLHandler into utility class since it is now reused elsewhere. New DOMBuilderContentHandlerFactory is used to create ContentHandlers that build generic DOMs. New DelegatingContentHandler is a convenience base class modelled after XMLFilterImpl but as passive SAX receiver. It is used by DOMBuilderContentHandlerFactory. Refactored FOTreeBuilder. FO tree building is now in a special ContentHandler which can be replaced temporarily when handling foreign XML like SVG or XMP. Extension Elements wanting the set their own ContentHandlers (instead of using the standard FO tree building mechanism) return a non-null value in getContentHandlerFactory(). The old mechanism is still supported (MathML, Plan and Barcode4J still use that). However, SVG support is changed to use a ContentHandlerFactory. Extension elements for xmpmeta and RDF elements making use of the new DOM build-up using ContentHandlerFactory. XMP metadata is passed to the renderer as ExtensionAttachment to the document. Only PDFRenderer uses the XMP extension attachment at this time. The PDFRenderer automatically builds XMP metadata based on the basic metadata information in the PDFInfo object if no explicit XMP metadata has been added to the XSL-FO document. XMP metadata merging is not implemented because this would involve a more sophisticated XMP infrastructure. That also means that XMP metadata is not validated. It's passed into the PDF as is. The PDF library now provides the PDFMetadata class to embed XMP metadata in PDFs version >=1.4. stream contents use a special filter list which is initially empty, so non-PDF-aware XMP readers can extract (and optionally modify) the XMP metadata. git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@378482 13f79535-47bb-0310-9956-ffa450edef68
18 years ago
Initial support for XMP metadata (PDF 1.4) under fo:declarations. Both xmpmeta and RDF elements can be used as root elements for XMP metadata. Extracted DOM2SAX functionality from XMLXMLHandler into utility class since it is now reused elsewhere. New DOMBuilderContentHandlerFactory is used to create ContentHandlers that build generic DOMs. New DelegatingContentHandler is a convenience base class modelled after XMLFilterImpl but as passive SAX receiver. It is used by DOMBuilderContentHandlerFactory. Refactored FOTreeBuilder. FO tree building is now in a special ContentHandler which can be replaced temporarily when handling foreign XML like SVG or XMP. Extension Elements wanting the set their own ContentHandlers (instead of using the standard FO tree building mechanism) return a non-null value in getContentHandlerFactory(). The old mechanism is still supported (MathML, Plan and Barcode4J still use that). However, SVG support is changed to use a ContentHandlerFactory. Extension elements for xmpmeta and RDF elements making use of the new DOM build-up using ContentHandlerFactory. XMP metadata is passed to the renderer as ExtensionAttachment to the document. Only PDFRenderer uses the XMP extension attachment at this time. The PDFRenderer automatically builds XMP metadata based on the basic metadata information in the PDFInfo object if no explicit XMP metadata has been added to the XSL-FO document. XMP metadata merging is not implemented because this would involve a more sophisticated XMP infrastructure. That also means that XMP metadata is not validated. It's passed into the PDF as is. The PDF library now provides the PDFMetadata class to embed XMP metadata in PDFs version >=1.4. stream contents use a special filter list which is initially empty, so non-PDF-aware XMP readers can extract (and optionally modify) the XMP metadata. git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@378482 13f79535-47bb-0310-9956-ffa450edef68
18 years ago
Initial support for XMP metadata (PDF 1.4) under fo:declarations. Both xmpmeta and RDF elements can be used as root elements for XMP metadata. Extracted DOM2SAX functionality from XMLXMLHandler into utility class since it is now reused elsewhere. New DOMBuilderContentHandlerFactory is used to create ContentHandlers that build generic DOMs. New DelegatingContentHandler is a convenience base class modelled after XMLFilterImpl but as passive SAX receiver. It is used by DOMBuilderContentHandlerFactory. Refactored FOTreeBuilder. FO tree building is now in a special ContentHandler which can be replaced temporarily when handling foreign XML like SVG or XMP. Extension Elements wanting the set their own ContentHandlers (instead of using the standard FO tree building mechanism) return a non-null value in getContentHandlerFactory(). The old mechanism is still supported (MathML, Plan and Barcode4J still use that). However, SVG support is changed to use a ContentHandlerFactory. Extension elements for xmpmeta and RDF elements making use of the new DOM build-up using ContentHandlerFactory. XMP metadata is passed to the renderer as ExtensionAttachment to the document. Only PDFRenderer uses the XMP extension attachment at this time. The PDFRenderer automatically builds XMP metadata based on the basic metadata information in the PDFInfo object if no explicit XMP metadata has been added to the XSL-FO document. XMP metadata merging is not implemented because this would involve a more sophisticated XMP infrastructure. That also means that XMP metadata is not validated. It's passed into the PDF as is. The PDF library now provides the PDFMetadata class to embed XMP metadata in PDFs version >=1.4. stream contents use a special filter list which is initially empty, so non-PDF-aware XMP readers can extract (and optionally modify) the XMP metadata. git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@378482 13f79535-47bb-0310-9956-ffa450edef68
18 years ago
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one or more
  3. * contributor license agreements. See the NOTICE file distributed with
  4. * this work for additional information regarding copyright ownership.
  5. * The ASF licenses this file to You under the Apache License, Version 2.0
  6. * (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. /* $Id$ */
  18. package org.apache.fop.pdf;
  19. // Java
  20. import java.io.IOException;
  21. import java.io.OutputStream;
  22. import java.io.UnsupportedEncodingException;
  23. import java.security.NoSuchAlgorithmException;
  24. import java.util.ArrayList;
  25. import java.util.Collection;
  26. import java.util.Collections;
  27. import java.util.Date;
  28. import java.util.HashMap;
  29. import java.util.LinkedList;
  30. import java.util.List;
  31. import java.util.Map;
  32. import org.apache.commons.logging.Log;
  33. import org.apache.commons.logging.LogFactory;
  34. import org.apache.fop.pdf.StandardStructureAttributes.Table.Scope;
  35. import org.apache.fop.pdf.xref.CrossReferenceStream;
  36. import org.apache.fop.pdf.xref.CrossReferenceTable;
  37. import org.apache.fop.pdf.xref.TrailerDictionary;
  38. /* image support modified from work of BoBoGi */
  39. /* font support based on work by Takayuki Takeuchi */
  40. /**
  41. * Class representing a PDF document.
  42. *
  43. * The document is built up by calling various methods and then finally
  44. * output to given filehandle using output method.
  45. *
  46. * A PDF document consists of a series of numbered objects preceded by a
  47. * header and followed by an xref table and trailer. The xref table
  48. * allows for quick access to objects by listing their character
  49. * positions within the document. For this reason the PDF document must
  50. * keep track of the character position of each object. The document
  51. * also keeps direct track of the /Root, /Info and /Resources objects.
  52. *
  53. * Modified by Mark Lillywhite, mark-fop@inomial.com. The changes
  54. * involve: ability to output pages one-at-a-time in a streaming
  55. * fashion (rather than storing them all for output at the end);
  56. * ability to write the /Pages object after writing the rest
  57. * of the document; ability to write to a stream and flush
  58. * the object list; enhanced trailer output; cleanups.
  59. *
  60. */
  61. public class PDFDocument {
  62. /** the encoding to use when converting strings to PDF commands */
  63. public static final String ENCODING = "ISO-8859-1";
  64. /** the counter for object numbering */
  65. protected int objectcount;
  66. /** the logger instance */
  67. private Log log = LogFactory.getLog("org.apache.fop.pdf");
  68. /** the current character position */
  69. protected long position;
  70. /** the character position of each object */
  71. protected List<Long> indirectObjectOffsets = new ArrayList<Long>();
  72. protected List<PDFStructElem> structureTreeElements;
  73. /** List of objects to write in the trailer */
  74. protected List<PDFObject> trailerObjects = new ArrayList<PDFObject>();
  75. /** the objects themselves */
  76. protected List<PDFObject> objects = new LinkedList<PDFObject>();
  77. /** Controls the PDF version of this document */
  78. private VersionController versionController;
  79. /** Indicates which PDF profiles are active (PDF/A, PDF/X etc.) */
  80. private PDFProfile pdfProfile = new PDFProfile(this);
  81. /** the /Root object */
  82. private PDFRoot root;
  83. /** The root outline object */
  84. private PDFOutline outlineRoot;
  85. /** The /Pages object (mark-fop@inomial.com) */
  86. private PDFPages pages;
  87. /** the /Info object */
  88. private PDFInfo info;
  89. /** the /Resources object */
  90. private PDFResources resources;
  91. /** the document's encryption, if it exists */
  92. private PDFEncryption encryption;
  93. /** the colorspace (0=RGB, 1=CMYK) */
  94. private PDFDeviceColorSpace colorspace
  95. = new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_RGB);
  96. /** the counter for Pattern name numbering (e.g. 'Pattern1') */
  97. private int patternCount;
  98. /** the counter for Shading name numbering */
  99. private int shadingCount;
  100. /** the counter for XObject numbering */
  101. private int xObjectCount;
  102. protected int gStateObjectCount;
  103. /* TODO: Should be modified (works only for image subtype) */
  104. private Map<String, PDFXObject> xObjectsMap = new HashMap<String, PDFXObject>();
  105. private Map<String, PDFFont> fontMap = new HashMap<String, PDFFont>();
  106. private Map<String, List<String>> filterMap = new HashMap<String, List<String>>();
  107. private List<PDFGState> gstates = new ArrayList<PDFGState>();
  108. private List<PDFFunction> functions = new ArrayList<PDFFunction>();
  109. private List<PDFShading> shadings = new ArrayList<PDFShading>();
  110. private List<PDFPattern> patterns = new ArrayList<PDFPattern>();
  111. private List<PDFLink> links = new ArrayList<PDFLink>();
  112. private List<PDFDestination> destinations;
  113. private List<PDFFileSpec> filespecs = new ArrayList<PDFFileSpec>();
  114. private List<PDFGoToRemote> gotoremotes = new ArrayList<PDFGoToRemote>();
  115. private List<PDFGoTo> gotos = new ArrayList<PDFGoTo>();
  116. private List<PDFLaunch> launches = new ArrayList<PDFLaunch>();
  117. protected List<PDFPage> pageObjs = new ArrayList<PDFPage>();
  118. private List<PDFLayer> layers;
  119. private List<PDFNavigator> navigators;
  120. private List<PDFNavigatorAction> navigatorActions;
  121. private PDFFactory factory;
  122. private FileIDGenerator fileIDGenerator;
  123. private boolean accessibilityEnabled;
  124. private boolean mergeFontsEnabled;
  125. private boolean linearizationEnabled;
  126. protected boolean outputStarted;
  127. /**
  128. * Creates an empty PDF document.
  129. *
  130. * The constructor creates a /Root and /Pages object to
  131. * track the document but does not write these objects until
  132. * the trailer is written. Note that the object ID of the
  133. * pages object is determined now, and the xref table is
  134. * updated later. This allows Pages to refer to their
  135. * Parent before we write it out.
  136. *
  137. * @param prod the name of the producer of this pdf document
  138. */
  139. public PDFDocument(String prod) {
  140. this(prod, null);
  141. versionController = VersionController.getDynamicVersionController(Version.V1_4, this);
  142. }
  143. /**
  144. * Creates an empty PDF document.
  145. *
  146. * The constructor creates a /Root and /Pages object to
  147. * track the document but does not write these objects until
  148. * the trailer is written. Note that the object ID of the
  149. * pages object is determined now, and the xref table is
  150. * updated later. This allows Pages to refer to their
  151. * Parent before we write it out.
  152. *
  153. * @param prod the name of the producer of this pdf document
  154. * @param versionController the version controller of this PDF document
  155. */
  156. public PDFDocument(String prod, VersionController versionController) {
  157. this.factory = new PDFFactory(this);
  158. /* create the /Root, /Info and /Resources objects */
  159. this.pages = getFactory().makePages();
  160. // Create the Root object
  161. this.root = getFactory().makeRoot(this.pages);
  162. // Create the Resources object
  163. this.resources = getFactory().makeResources();
  164. // Make the /Info record
  165. this.info = getFactory().makeInfo(prod);
  166. this.versionController = versionController;
  167. }
  168. /**
  169. * Returns the current PDF version.
  170. *
  171. * @return returns the PDF version
  172. */
  173. public Version getPDFVersion() {
  174. return versionController.getPDFVersion();
  175. }
  176. /**
  177. * Sets the PDF version of this document.
  178. *
  179. * @param version the PDF version
  180. * @throws IllegalStateException if the version of this PDF is not allowed to change.
  181. */
  182. public void setPDFVersion(Version version) {
  183. versionController.setPDFVersion(version);
  184. }
  185. /** @return the String representing the current PDF version */
  186. public String getPDFVersionString() {
  187. return versionController.getPDFVersion().toString();
  188. }
  189. /** @return the PDF profile currently active. */
  190. public PDFProfile getProfile() {
  191. return this.pdfProfile;
  192. }
  193. /**
  194. * Returns the factory for PDF objects.
  195. *
  196. * @return the {@link PDFFactory} object
  197. */
  198. public PDFFactory getFactory() {
  199. return this.factory;
  200. }
  201. /**
  202. * Converts text to a byte array for writing to a PDF file.
  203. *
  204. * @param text text to convert/encode
  205. * @return the resulting <code>byte</code> array
  206. */
  207. public static byte[] encode(String text) {
  208. try {
  209. return text.getBytes(ENCODING);
  210. } catch (UnsupportedEncodingException uee) {
  211. return text.getBytes();
  212. }
  213. }
  214. /**
  215. * Flushes the given text buffer to an output stream with the right encoding and resets
  216. * the text buffer. This is used to efficiently switch between outputting text and binary
  217. * content.
  218. * @param textBuffer the text buffer
  219. * @param out the output stream to flush the text content to
  220. * @throws IOException if an I/O error occurs while writing to the output stream
  221. */
  222. public static void flushTextBuffer(StringBuilder textBuffer, OutputStream out)
  223. throws IOException {
  224. out.write(encode(textBuffer.toString()));
  225. textBuffer.setLength(0);
  226. }
  227. /**
  228. * Sets the producer of the document.
  229. *
  230. * @param producer string indicating application producing the PDF
  231. */
  232. public void setProducer(String producer) {
  233. this.info.setProducer(producer);
  234. }
  235. /**
  236. * Sets the creation date of the document.
  237. *
  238. * @param date Date to be stored as creation date in the PDF.
  239. */
  240. public void setCreationDate(Date date) {
  241. this.info.setCreationDate(date);
  242. }
  243. /**
  244. * Sets the creator of the document.
  245. *
  246. * @param creator string indicating application creating the document
  247. */
  248. public void setCreator(String creator) {
  249. this.info.setCreator(creator);
  250. }
  251. /**
  252. * Sets the filter map to use for filters in this document.
  253. *
  254. * @param map the map of filter lists for each stream type
  255. */
  256. public void setFilterMap(Map<String, List<String>> map) {
  257. this.filterMap = map;
  258. }
  259. /**
  260. * Returns the {@link PDFFilter}s map used for filters in this document.
  261. *
  262. * @return the map of filters being used
  263. */
  264. public Map<String, List<String>> getFilterMap() {
  265. return this.filterMap;
  266. }
  267. /**
  268. * Returns the {@link PDFPages} object associated with the root object.
  269. *
  270. * @return the {@link PDFPages} object
  271. */
  272. public PDFPages getPages() {
  273. return this.pages;
  274. }
  275. /**
  276. * Get the {@link PDFRoot} object for this document.
  277. *
  278. * @return the {@link PDFRoot} object
  279. */
  280. public PDFRoot getRoot() {
  281. return this.root;
  282. }
  283. /**
  284. * Get the Structural Tree Collection for this document
  285. * @return
  286. */
  287. public List<PDFStructElem> getStructureTreeElements() {
  288. return structureTreeElements;
  289. }
  290. /**
  291. * Creates and returns a StructTreeRoot object.
  292. *
  293. * @param parentTree the value of the ParenTree entry
  294. * @return the structure tree root
  295. */
  296. public PDFStructTreeRoot makeStructTreeRoot(PDFParentTree parentTree) {
  297. PDFStructTreeRoot structTreeRoot = new PDFStructTreeRoot(parentTree);
  298. assignObjectNumber(structTreeRoot);
  299. addTrailerObject(structTreeRoot);
  300. root.setStructTreeRoot(structTreeRoot);
  301. structureTreeElements = new ArrayList<PDFStructElem>();
  302. return structTreeRoot;
  303. }
  304. /**
  305. * Adds the given element to the structure tree.
  306. */
  307. public void registerStructureElement(PDFStructElem structElem) {
  308. assignObjectNumber(structElem);
  309. structureTreeElements.add(structElem);
  310. }
  311. /**
  312. * Assigns the given scope to the given element and adds it to the structure tree. The
  313. * scope may not be added if it's not compatible with this document's PDF version.
  314. */
  315. public void registerStructureElement(PDFStructElem structElem, Scope scope) {
  316. registerStructureElement(structElem);
  317. versionController.addTableHeaderScopeAttribute(structElem, scope);
  318. }
  319. /**
  320. * Get the {@link PDFInfo} object for this document.
  321. *
  322. * @return the {@link PDFInfo} object
  323. */
  324. public PDFInfo getInfo() {
  325. return this.info;
  326. }
  327. /**
  328. * Registers a {@link PDFObject} in this PDF document.
  329. * The object is assigned a new object number.
  330. *
  331. * @param obj {@link PDFObject} to add
  332. * @return the added {@link PDFObject} added (with its object number set)
  333. */
  334. public PDFObject registerObject(PDFObject obj) {
  335. assignObjectNumber(obj);
  336. addObject(obj);
  337. if (obj instanceof AbstractPDFStream) {
  338. ((AbstractPDFStream) obj).registerChildren();
  339. }
  340. return obj;
  341. }
  342. /**
  343. * Registers a {@link PDFObject} in this PDF document at end.
  344. * The object is assigned a new object number.
  345. *
  346. * @param obj {@link PDFObject} to add
  347. * @return the added {@link PDFObject} added (with its object number set)
  348. */
  349. <T extends PDFObject> T registerTrailerObject(T obj) {
  350. assignObjectNumber(obj);
  351. addTrailerObject(obj);
  352. return obj;
  353. }
  354. /**
  355. * Assigns the {@link PDFObject} an object number,
  356. * and sets the parent of the {@link PDFObject} to this document.
  357. *
  358. * @param obj {@link PDFObject} to assign a number to
  359. */
  360. public void assignObjectNumber(PDFObject obj) {
  361. if (outputStarted && isLinearizationEnabled()) {
  362. throw new IllegalStateException("Can't assign number after start of output");
  363. }
  364. if (obj == null) {
  365. throw new NullPointerException("obj must not be null");
  366. }
  367. if (obj.hasObjectNumber()) {
  368. throw new IllegalStateException(
  369. "Error registering a PDFObject: "
  370. + "PDFObject already has an object number");
  371. }
  372. PDFDocument currentParent = obj.getDocument();
  373. if (currentParent != null && currentParent != this) {
  374. throw new IllegalStateException(
  375. "Error registering a PDFObject: "
  376. + "PDFObject already has a parent PDFDocument");
  377. }
  378. obj.setObjectNumber(this);
  379. if (currentParent == null) {
  380. obj.setDocument(this);
  381. }
  382. }
  383. /**
  384. * Adds a {@link PDFObject} to this document.
  385. * The object <em>MUST</em> have an object number assigned.
  386. *
  387. * @param obj {@link PDFObject} to add
  388. */
  389. public void addObject(PDFObject obj) {
  390. if (obj == null) {
  391. throw new NullPointerException("obj must not be null");
  392. }
  393. if (!obj.hasObjectNumber()) {
  394. throw new IllegalStateException(
  395. "Error adding a PDFObject: "
  396. + "PDFObject doesn't have an object number");
  397. }
  398. //Add object to list
  399. this.objects.add(obj);
  400. //Add object to special lists where necessary
  401. if (obj instanceof PDFFunction) {
  402. this.functions.add((PDFFunction) obj);
  403. }
  404. if (obj instanceof PDFShading) {
  405. final String shadingName = "Sh" + (++this.shadingCount);
  406. ((PDFShading)obj).setName(shadingName);
  407. this.shadings.add((PDFShading) obj);
  408. }
  409. if (obj instanceof PDFPattern) {
  410. final String patternName = "Pa" + (++this.patternCount);
  411. ((PDFPattern)obj).setName(patternName);
  412. this.patterns.add((PDFPattern) obj);
  413. }
  414. if (obj instanceof PDFFont) {
  415. final PDFFont font = (PDFFont)obj;
  416. this.fontMap.put(font.getName(), font);
  417. }
  418. if (obj instanceof PDFGState) {
  419. this.gstates.add((PDFGState) obj);
  420. }
  421. if (obj instanceof PDFPage) {
  422. this.pages.notifyKidRegistered((PDFPage)obj);
  423. pageObjs.add((PDFPage) obj);
  424. }
  425. if (obj instanceof PDFLaunch) {
  426. this.launches.add((PDFLaunch) obj);
  427. }
  428. if (obj instanceof PDFLink) {
  429. this.links.add((PDFLink) obj);
  430. }
  431. if (obj instanceof PDFFileSpec) {
  432. this.filespecs.add((PDFFileSpec) obj);
  433. }
  434. if (obj instanceof PDFGoToRemote) {
  435. this.gotoremotes.add((PDFGoToRemote) obj);
  436. }
  437. if (obj instanceof PDFLayer) {
  438. if (this.layers == null) {
  439. this.layers = new ArrayList<PDFLayer>();
  440. }
  441. this.layers.add((PDFLayer) obj);
  442. }
  443. if (obj instanceof PDFNavigator) {
  444. if (this.navigators == null) {
  445. this.navigators = new ArrayList<PDFNavigator>();
  446. }
  447. this.navigators.add((PDFNavigator) obj);
  448. }
  449. if (obj instanceof PDFNavigatorAction) {
  450. if (this.navigatorActions == null) {
  451. this.navigatorActions = new ArrayList<PDFNavigatorAction>();
  452. }
  453. this.navigatorActions.add((PDFNavigatorAction) obj);
  454. }
  455. }
  456. /**
  457. * Add trailer object.
  458. * Adds an object to the list of trailer objects.
  459. *
  460. * @param obj the PDF object to add
  461. */
  462. public void addTrailerObject(PDFObject obj) {
  463. this.trailerObjects.add(obj);
  464. if (obj instanceof PDFGoTo) {
  465. this.gotos.add((PDFGoTo) obj);
  466. }
  467. }
  468. /**
  469. * Apply the encryption filter to a PDFStream if encryption is enabled.
  470. *
  471. * @param stream PDFStream to encrypt
  472. */
  473. public void applyEncryption(AbstractPDFStream stream) {
  474. if (isEncryptionActive()) {
  475. this.encryption.applyFilter(stream);
  476. }
  477. }
  478. /**
  479. * Enables PDF encryption.
  480. *
  481. * @param params The encryption parameters for the pdf file
  482. */
  483. public void setEncryption(PDFEncryptionParams params) {
  484. getProfile().verifyEncryptionAllowed();
  485. fileIDGenerator = FileIDGenerator.getRandomFileIDGenerator();
  486. this.encryption = PDFEncryptionManager.newInstance(params, this);
  487. if (this.encryption != null) {
  488. PDFObject pdfObject = (PDFObject)this.encryption;
  489. addTrailerObject(pdfObject);
  490. try {
  491. if (encryption.getPDFVersion().compareTo(versionController.getPDFVersion()) > 0) {
  492. versionController.setPDFVersion(encryption.getPDFVersion());
  493. }
  494. } catch (IllegalStateException ise) {
  495. log.warn("Configured encryption requires PDF version " + encryption.getPDFVersion()
  496. + " but version has been set to " + versionController.getPDFVersion() + ".");
  497. throw ise;
  498. }
  499. } else {
  500. log.warn("PDF encryption is unavailable. PDF will be generated without encryption.");
  501. if (params.getEncryptionLengthInBits() == 256) {
  502. log.warn("Make sure the JCE Unlimited Strength Jurisdiction Policy files are available."
  503. + "AES 256 encryption cannot be performed without them.");
  504. }
  505. }
  506. }
  507. /**
  508. * Indicates whether encryption is active for this PDF or not.
  509. *
  510. * @return boolean True if encryption is active
  511. */
  512. public boolean isEncryptionActive() {
  513. return this.encryption != null;
  514. }
  515. /**
  516. * Returns the active Encryption object.
  517. *
  518. * @return the Encryption object
  519. */
  520. public PDFEncryption getEncryption() {
  521. return this.encryption;
  522. }
  523. private Object findPDFObject(List<? extends PDFObject> list, PDFObject compare) {
  524. for (PDFObject obj : list) {
  525. if (compare.contentEquals(obj)) {
  526. return obj;
  527. }
  528. }
  529. return null;
  530. }
  531. /**
  532. * Looks through the registered functions to see if one that is equal to
  533. * a reference object exists
  534. *
  535. * @param compare reference object
  536. * @return the function if it was found, null otherwise
  537. */
  538. protected PDFFunction findFunction(PDFFunction compare) {
  539. return (PDFFunction)findPDFObject(this.functions, compare);
  540. }
  541. /**
  542. * Looks through the registered shadings to see if one that is equal to
  543. * a reference object exists
  544. *
  545. * @param compare reference object
  546. * @return the shading if it was found, null otherwise
  547. */
  548. protected PDFShading findShading(PDFShading compare) {
  549. return (PDFShading)findPDFObject(this.shadings, compare);
  550. }
  551. /**
  552. * Find a previous pattern.
  553. * The problem with this is for tiling patterns the pattern
  554. * data stream is stored and may use up memory, usually this
  555. * would only be a small amount of data.
  556. *
  557. * @param compare reference object
  558. * @return the shading if it was found, null otherwise
  559. */
  560. protected PDFPattern findPattern(PDFPattern compare) {
  561. return (PDFPattern)findPDFObject(this.patterns, compare);
  562. }
  563. /**
  564. * Finds a font.
  565. *
  566. * @param fontname name of the font
  567. * @return PDFFont the requested font, null if it wasn't found
  568. */
  569. protected PDFFont findFont(String fontname) {
  570. return this.fontMap.get(fontname);
  571. }
  572. /**
  573. * Finds a named destination.
  574. *
  575. * @param compare reference object to use as search template
  576. * @return the link if found, null otherwise
  577. */
  578. protected PDFDestination findDestination(PDFDestination compare) {
  579. int index = getDestinationList().indexOf(compare);
  580. if (index >= 0) {
  581. return getDestinationList().get(index);
  582. } else {
  583. return null;
  584. }
  585. }
  586. /**
  587. * Finds a link.
  588. *
  589. * @param compare reference object to use as search template
  590. * @return the link if found, null otherwise
  591. */
  592. protected PDFLink findLink(PDFLink compare) {
  593. return (PDFLink)findPDFObject(this.links, compare);
  594. }
  595. /**
  596. * Finds a file spec.
  597. *
  598. * @param compare reference object to use as search template
  599. * @return the file spec if found, null otherwise
  600. */
  601. protected PDFFileSpec findFileSpec(PDFFileSpec compare) {
  602. return (PDFFileSpec)findPDFObject(this.filespecs, compare);
  603. }
  604. /**
  605. * Finds a goto remote.
  606. *
  607. * @param compare reference object to use as search template
  608. * @return the goto remote if found, null otherwise
  609. */
  610. protected PDFGoToRemote findGoToRemote(PDFGoToRemote compare) {
  611. return (PDFGoToRemote)findPDFObject(this.gotoremotes, compare);
  612. }
  613. /**
  614. * Finds a goto.
  615. *
  616. * @param compare reference object to use as search template
  617. * @return the goto if found, null otherwise
  618. */
  619. protected PDFGoTo findGoTo(PDFGoTo compare) {
  620. return (PDFGoTo)findPDFObject(this.gotos, compare);
  621. }
  622. /**
  623. * Finds a launch.
  624. *
  625. * @param compare reference object to use as search template
  626. * @return the launch if found, null otherwise
  627. */
  628. protected PDFLaunch findLaunch(PDFLaunch compare) {
  629. return (PDFLaunch) findPDFObject(this.launches, compare);
  630. }
  631. /**
  632. * Looks for an existing GState to use
  633. *
  634. * @param wanted requested features
  635. * @param current currently active features
  636. * @return the GState if found, null otherwise
  637. */
  638. protected PDFGState findGState(PDFGState wanted, PDFGState current) {
  639. PDFGState poss;
  640. for (PDFGState avail : this.gstates) {
  641. poss = new PDFGState();
  642. poss.addValues(current);
  643. poss.addValues(avail);
  644. if (poss.equals(wanted)) {
  645. return avail;
  646. }
  647. }
  648. return null;
  649. }
  650. /**
  651. * Returns the PDF color space object.
  652. *
  653. * @return the color space
  654. */
  655. public PDFDeviceColorSpace getPDFColorSpace() {
  656. return this.colorspace;
  657. }
  658. /**
  659. * Returns the color space.
  660. *
  661. * @return the color space
  662. */
  663. public int getColorSpace() {
  664. return getPDFColorSpace().getColorSpace();
  665. }
  666. /**
  667. * Set the color space.
  668. * This is used when creating gradients.
  669. *
  670. * @param theColorspace the new color space
  671. */
  672. public void setColorSpace(int theColorspace) {
  673. this.colorspace.setColorSpace(theColorspace);
  674. }
  675. /**
  676. * Returns the font map for this document.
  677. *
  678. * @return the map of fonts used in this document
  679. */
  680. public Map<String, PDFFont> getFontMap() {
  681. return this.fontMap;
  682. }
  683. /**
  684. * Get an image from the image map.
  685. *
  686. * @param key the image key to look for
  687. * @return the image or PDFXObject for the key if found
  688. * @deprecated Use getXObject instead (so forms are treated in the same way)
  689. */
  690. @Deprecated
  691. public PDFImageXObject getImage(String key) {
  692. return (PDFImageXObject)this.xObjectsMap.get(key);
  693. }
  694. /**
  695. * Get an XObject from the image map.
  696. *
  697. * @param key the XObject key to look for
  698. * @return the PDFXObject for the key if found
  699. */
  700. public PDFXObject getXObject(String key) {
  701. return this.xObjectsMap.get(key);
  702. }
  703. /**
  704. * Adds a destination to the document.
  705. * @param destination the destination object
  706. */
  707. public void addDestination(PDFDestination destination) {
  708. if (this.destinations == null) {
  709. this.destinations = new ArrayList<PDFDestination>();
  710. }
  711. this.destinations.add(destination);
  712. }
  713. /**
  714. * Gets the list of named destinations.
  715. *
  716. * @return the list of named destinations.
  717. */
  718. public List<PDFDestination> getDestinationList() {
  719. if (hasDestinations()) {
  720. return this.destinations;
  721. } else {
  722. return Collections.emptyList();
  723. }
  724. }
  725. /**
  726. * Gets whether the document has named destinations.
  727. *
  728. * @return whether the document has named destinations.
  729. */
  730. public boolean hasDestinations() {
  731. return this.destinations != null && !this.destinations.isEmpty();
  732. }
  733. /**
  734. * Add an image to the PDF document.
  735. * This adds an image to the PDF objects.
  736. * If an image with the same key already exists it will return the
  737. * old {@link PDFXObject}.
  738. *
  739. * @param res the PDF resource context to add to, may be null
  740. * @param img the PDF image to add
  741. * @return the PDF XObject that references the PDF image data
  742. */
  743. public PDFImageXObject addImage(PDFResourceContext res, PDFImage img) {
  744. // check if already created
  745. String key = img.getKey();
  746. PDFImageXObject xObject = (PDFImageXObject)this.xObjectsMap.get(key);
  747. if (xObject != null) {
  748. if (res != null) {
  749. res.addXObject(xObject);
  750. }
  751. return xObject;
  752. }
  753. // setup image
  754. img.setup(this);
  755. // create a new XObject
  756. xObject = new PDFImageXObject(++this.xObjectCount, img);
  757. registerObject(xObject);
  758. this.resources.addXObject(xObject);
  759. if (res != null) {
  760. res.addXObject(xObject);
  761. }
  762. this.xObjectsMap.put(key, xObject);
  763. return xObject;
  764. }
  765. /**
  766. * Add a form XObject to the PDF document.
  767. * This adds a Form XObject to the PDF objects.
  768. * If a Form XObject with the same key already exists it will return the
  769. * old {@link PDFFormXObject}.
  770. *
  771. * @param res the PDF resource context to add to, may be null
  772. * @param cont the PDF Stream contents of the Form XObject
  773. * @param formres a reference to the PDF Resources for the Form XObject data
  774. * @param key the key for the object
  775. * @return the PDF Form XObject that references the PDF data
  776. */
  777. public PDFFormXObject addFormXObject(
  778. PDFResourceContext res,
  779. PDFStream cont,
  780. PDFReference formres,
  781. String key) {
  782. // check if already created
  783. PDFFormXObject xObject = (PDFFormXObject)xObjectsMap.get(key);
  784. if (xObject != null) {
  785. if (res != null) {
  786. res.addXObject(xObject);
  787. }
  788. return xObject;
  789. }
  790. xObject = new PDFFormXObject(
  791. ++this.xObjectCount,
  792. cont,
  793. formres);
  794. registerObject(xObject);
  795. this.resources.addXObject(xObject);
  796. if (res != null) {
  797. res.addXObject(xObject);
  798. }
  799. this.xObjectsMap.put(key, xObject);
  800. return xObject;
  801. }
  802. /**
  803. * Get the root Outlines object. This method does not write
  804. * the outline to the PDF document, it simply creates a
  805. * reference for later.
  806. *
  807. * @return the PDF Outline root object
  808. */
  809. public PDFOutline getOutlineRoot() {
  810. if (this.outlineRoot != null) {
  811. return this.outlineRoot;
  812. }
  813. this.outlineRoot = new PDFOutline(null, null, true);
  814. assignObjectNumber(this.outlineRoot);
  815. addTrailerObject(this.outlineRoot);
  816. this.root.setRootOutline(this.outlineRoot);
  817. return this.outlineRoot;
  818. }
  819. /**
  820. * Get the /Resources object for the document
  821. *
  822. * @return the /Resources object
  823. */
  824. public PDFResources getResources() {
  825. return this.resources;
  826. }
  827. public void enableAccessibility(boolean enableAccessibility) {
  828. this.accessibilityEnabled = enableAccessibility;
  829. }
  830. /**
  831. *
  832. */
  833. public PDFReference resolveExtensionReference(String id) {
  834. if (layers != null) {
  835. for (PDFLayer layer : layers) {
  836. if (layer.hasId(id)) {
  837. return layer.makeReference();
  838. }
  839. }
  840. }
  841. if (navigators != null) {
  842. for (PDFNavigator navigator : navigators) {
  843. if (navigator.hasId(id)) {
  844. return navigator.makeReference();
  845. }
  846. }
  847. }
  848. if (navigatorActions != null) {
  849. for (PDFNavigatorAction action : navigatorActions) {
  850. if (action.hasId(id)) {
  851. return action.makeReference();
  852. }
  853. }
  854. }
  855. return null;
  856. }
  857. /**
  858. * Writes out the entire document
  859. *
  860. * @param stream the OutputStream to output the document to
  861. * @throws IOException if there is an exception writing to the output stream
  862. */
  863. public void output(OutputStream stream) throws IOException {
  864. outputStarted = true;
  865. //Write out objects until the list is empty. This approach (used with a
  866. //LinkedList) allows for output() methods to create and register objects
  867. //on the fly even during serialization.
  868. while (this.objects.size() > 0) {
  869. PDFObject object = this.objects.remove(0);
  870. streamIndirectObject(object, stream);
  871. }
  872. }
  873. protected void writeTrailer(OutputStream stream, int first, int last, int size, long mainOffset, long startxref)
  874. throws IOException {
  875. TrailerOutputHelper trailerOutputHelper = mayCompressStructureTreeElements()
  876. ? new CompressedTrailerOutputHelper()
  877. : new UncompressedTrailerOutputHelper();
  878. if (structureTreeElements != null) {
  879. trailerOutputHelper.outputStructureTreeElements(stream);
  880. }
  881. TrailerDictionary trailerDictionary = createTrailerDictionary(mainOffset != 0);
  882. if (mainOffset != 0) {
  883. trailerDictionary.getDictionary().put("Prev", mainOffset);
  884. }
  885. trailerOutputHelper.outputCrossReferenceObject(stream, trailerDictionary, first, last, size);
  886. String trailer = "\nstartxref\n" + startxref + "\n%%EOF\n";
  887. stream.write(encode(trailer));
  888. }
  889. protected int streamIndirectObject(PDFObject o, OutputStream stream) throws IOException {
  890. outputStarted = true;
  891. recordObjectOffset(o);
  892. int len = outputIndirectObject(o, stream);
  893. this.position += len;
  894. return len;
  895. }
  896. private void streamIndirectObjects(Collection<? extends PDFObject> objects, OutputStream stream)
  897. throws IOException {
  898. for (PDFObject o : objects) {
  899. streamIndirectObject(o, stream);
  900. }
  901. }
  902. private void recordObjectOffset(PDFObject object) {
  903. int index = object.getObjectNumber().getNumber() - 1;
  904. while (indirectObjectOffsets.size() <= index) {
  905. indirectObjectOffsets.add(null);
  906. }
  907. indirectObjectOffsets.set(index, position);
  908. }
  909. /**
  910. * Outputs the given object, wrapped by obj/endobj, to the given stream.
  911. *
  912. * @param object an indirect object, as described in Section 3.2.9 of the PDF 1.5
  913. * Reference.
  914. * @param stream the stream to which the object must be output
  915. * @throws IllegalArgumentException if the object is not an indirect object
  916. */
  917. public static int outputIndirectObject(PDFObject object, OutputStream stream)
  918. throws IOException {
  919. if (!object.hasObjectNumber()) {
  920. throw new IllegalArgumentException("Not an indirect object");
  921. }
  922. byte[] obj = encode(object.getObjectID());
  923. stream.write(obj);
  924. int length = object.output(stream);
  925. byte[] endobj = encode("\nendobj\n");
  926. stream.write(endobj);
  927. return obj.length + length + endobj.length;
  928. }
  929. /**
  930. * Write the PDF header.
  931. *
  932. * This method must be called prior to formatting
  933. * and outputting AreaTrees.
  934. *
  935. * @param stream the OutputStream to write the header to
  936. * @throws IOException if there is an exception writing to the output stream
  937. */
  938. public void outputHeader(OutputStream stream) throws IOException {
  939. this.position = 0;
  940. getProfile().verifyPDFVersion();
  941. byte[] pdf = encode("%PDF-" + getPDFVersionString() + "\n");
  942. stream.write(pdf);
  943. this.position += pdf.length;
  944. // output a binary comment as recommended by the PDF spec (3.4.1)
  945. byte[] bin = {
  946. (byte)'%',
  947. (byte)0xAA,
  948. (byte)0xAB,
  949. (byte)0xAC,
  950. (byte)0xAD,
  951. (byte)'\n' };
  952. stream.write(bin);
  953. this.position += bin.length;
  954. }
  955. /**
  956. * Write the trailer
  957. *
  958. * @param stream the OutputStream to write the trailer to
  959. * @throws IOException if there is an exception writing to the output stream
  960. */
  961. public void outputTrailer(OutputStream stream) throws IOException {
  962. createDestinations();
  963. output(stream);
  964. outputTrailerObjectsAndXref(stream);
  965. }
  966. private void createDestinations() {
  967. if (hasDestinations()) {
  968. Collections.sort(this.destinations, new DestinationComparator());
  969. PDFDests dests = getFactory().makeDests(this.destinations);
  970. if (this.root.getNames() == null) {
  971. this.root.setNames(getFactory().makeNames());
  972. }
  973. this.root.getNames().setDests(dests);
  974. }
  975. }
  976. private void outputTrailerObjectsAndXref(OutputStream stream) throws IOException {
  977. TrailerOutputHelper trailerOutputHelper = mayCompressStructureTreeElements()
  978. ? new CompressedTrailerOutputHelper()
  979. : new UncompressedTrailerOutputHelper();
  980. if (structureTreeElements != null) {
  981. trailerOutputHelper.outputStructureTreeElements(stream);
  982. }
  983. streamIndirectObjects(trailerObjects, stream);
  984. TrailerDictionary trailerDictionary = createTrailerDictionary(true);
  985. long startxref = trailerOutputHelper.outputCrossReferenceObject(stream, trailerDictionary, 0,
  986. indirectObjectOffsets.size(), indirectObjectOffsets.size());
  987. String trailer = "\nstartxref\n" + startxref + "\n%%EOF\n";
  988. stream.write(encode(trailer));
  989. }
  990. private boolean mayCompressStructureTreeElements() {
  991. return accessibilityEnabled
  992. && versionController.getPDFVersion().compareTo(Version.V1_5) >= 0
  993. && !isLinearizationEnabled();
  994. }
  995. private TrailerDictionary createTrailerDictionary(boolean addRoot) {
  996. FileIDGenerator gen = getFileIDGenerator();
  997. TrailerDictionary trailerDictionary = new TrailerDictionary(this);
  998. if (addRoot) {
  999. trailerDictionary.setRoot(root).setInfo(info);
  1000. }
  1001. trailerDictionary.setFileID(gen.getOriginalFileID(), gen.getUpdatedFileID());
  1002. if (isEncryptionActive()) {
  1003. trailerDictionary.setEncryption(encryption);
  1004. }
  1005. return trailerDictionary;
  1006. }
  1007. public boolean isMergeFontsEnabled() {
  1008. return mergeFontsEnabled;
  1009. }
  1010. public void setMergeFontsEnabled(boolean mergeFontsEnabled) {
  1011. this.mergeFontsEnabled = mergeFontsEnabled;
  1012. if (mergeFontsEnabled) {
  1013. getResources().createFontsAsObj();
  1014. }
  1015. }
  1016. private interface TrailerOutputHelper {
  1017. void outputStructureTreeElements(OutputStream stream) throws IOException;
  1018. /**
  1019. * @return the offset of the cross-reference object (the value of startxref)
  1020. */
  1021. long outputCrossReferenceObject(OutputStream stream, TrailerDictionary trailerDictionary,
  1022. int first, int last, int size)
  1023. throws IOException;
  1024. }
  1025. private class UncompressedTrailerOutputHelper implements TrailerOutputHelper {
  1026. public void outputStructureTreeElements(OutputStream stream)
  1027. throws IOException {
  1028. streamIndirectObjects(structureTreeElements, stream);
  1029. }
  1030. public long outputCrossReferenceObject(OutputStream stream,
  1031. TrailerDictionary trailerDictionary, int first, int last, int size) throws IOException {
  1032. new CrossReferenceTable(trailerDictionary, position,
  1033. indirectObjectOffsets, first, last, size).output(stream);
  1034. return position;
  1035. }
  1036. }
  1037. private class CompressedTrailerOutputHelper implements TrailerOutputHelper {
  1038. private ObjectStreamManager structureTreeObjectStreams;
  1039. public void outputStructureTreeElements(OutputStream stream)
  1040. throws IOException {
  1041. assert structureTreeElements.size() > 0;
  1042. structureTreeObjectStreams = new ObjectStreamManager(PDFDocument.this);
  1043. for (PDFStructElem structElem : structureTreeElements) {
  1044. structureTreeObjectStreams.add(structElem);
  1045. }
  1046. }
  1047. public long outputCrossReferenceObject(OutputStream stream,
  1048. TrailerDictionary trailerDictionary, int first, int last, int size) throws IOException {
  1049. // Outputting the object streams should not have created new indirect objects
  1050. assert objects.isEmpty();
  1051. new CrossReferenceStream(PDFDocument.this, ++objectcount, trailerDictionary, position,
  1052. indirectObjectOffsets,
  1053. structureTreeObjectStreams.getCompressedObjectReferences())
  1054. .output(stream);
  1055. return position;
  1056. }
  1057. }
  1058. long getCurrentFileSize() {
  1059. return position;
  1060. }
  1061. FileIDGenerator getFileIDGenerator() {
  1062. if (fileIDGenerator == null) {
  1063. try {
  1064. fileIDGenerator = FileIDGenerator.getDigestFileIDGenerator(this);
  1065. } catch (NoSuchAlgorithmException e) {
  1066. fileIDGenerator = FileIDGenerator.getRandomFileIDGenerator();
  1067. }
  1068. }
  1069. return fileIDGenerator;
  1070. }
  1071. public boolean isLinearizationEnabled() {
  1072. return linearizationEnabled;
  1073. }
  1074. public void setLinearizationEnabled(boolean b) {
  1075. linearizationEnabled = b;
  1076. }
  1077. }