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 35KB

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
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106
  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.InputStream;
  22. import java.io.OutputStream;
  23. import java.io.UnsupportedEncodingException;
  24. import java.security.NoSuchAlgorithmException;
  25. import java.util.ArrayList;
  26. import java.util.Collection;
  27. import java.util.Collections;
  28. import java.util.Date;
  29. import java.util.HashMap;
  30. import java.util.Iterator;
  31. import java.util.LinkedList;
  32. import java.util.List;
  33. import java.util.Map;
  34. import org.apache.commons.logging.Log;
  35. import org.apache.commons.logging.LogFactory;
  36. import org.apache.fop.pdf.xref.CrossReferenceStream;
  37. import org.apache.fop.pdf.xref.CrossReferenceTable;
  38. import org.apache.fop.pdf.xref.TrailerDictionary;
  39. /* image support modified from work of BoBoGi */
  40. /* font support based on work by Takayuki Takeuchi */
  41. /**
  42. * Class representing a PDF document.
  43. *
  44. * The document is built up by calling various methods and then finally
  45. * output to given filehandle using output method.
  46. *
  47. * A PDF document consists of a series of numbered objects preceded by a
  48. * header and followed by an xref table and trailer. The xref table
  49. * allows for quick access to objects by listing their character
  50. * positions within the document. For this reason the PDF document must
  51. * keep track of the character position of each object. The document
  52. * also keeps direct track of the /Root, /Info and /Resources objects.
  53. *
  54. * Modified by Mark Lillywhite, mark-fop@inomial.com. The changes
  55. * involve: ability to output pages one-at-a-time in a streaming
  56. * fashion (rather than storing them all for output at the end);
  57. * ability to write the /Pages object after writing the rest
  58. * of the document; ability to write to a stream and flush
  59. * the object list; enhanced trailer output; cleanups.
  60. *
  61. */
  62. public class PDFDocument {
  63. /** the encoding to use when converting strings to PDF commands */
  64. public static final String ENCODING = "ISO-8859-1";
  65. /** the counter for object numbering */
  66. protected int objectcount;
  67. /** the logger instance */
  68. private Log log = LogFactory.getLog("org.apache.fop.pdf");
  69. /** the current character position */
  70. private long position;
  71. /** the character position of each object */
  72. private List<Long> indirectObjectOffsets = new ArrayList<Long>();
  73. private Collection<PDFStructElem> structureTreeElements;
  74. /** List of objects to write in the trailer */
  75. private List<PDFObject> trailerObjects = new ArrayList<PDFObject>();
  76. /** the objects themselves */
  77. private List<PDFObject> objects = new LinkedList<PDFObject>();
  78. /** Controls the PDF version of this document */
  79. private VersionController versionController;
  80. /** Indicates which PDF profiles are active (PDF/A, PDF/X etc.) */
  81. private PDFProfile pdfProfile = new PDFProfile(this);
  82. /** the /Root object */
  83. private PDFRoot root;
  84. /** The root outline object */
  85. private PDFOutline outlineRoot;
  86. /** The /Pages object (mark-fop@inomial.com) */
  87. private PDFPages pages;
  88. /** the /Info object */
  89. private PDFInfo info;
  90. /** the /Resources object */
  91. private PDFResources resources;
  92. /** the document's encryption, if it exists */
  93. private PDFEncryption encryption;
  94. /** the colorspace (0=RGB, 1=CMYK) */
  95. private PDFDeviceColorSpace colorspace
  96. = new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_RGB);
  97. /** the counter for Pattern name numbering (e.g. 'Pattern1') */
  98. private int patternCount;
  99. /** the counter for Shading name numbering */
  100. private int shadingCount;
  101. /** the counter for XObject numbering */
  102. private int xObjectCount;
  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. private PDFFactory factory;
  118. private FileIDGenerator fileIDGenerator;
  119. private boolean accessibilityEnabled;
  120. /**
  121. * Creates an empty PDF document.
  122. *
  123. * The constructor creates a /Root and /Pages object to
  124. * track the document but does not write these objects until
  125. * the trailer is written. Note that the object ID of the
  126. * pages object is determined now, and the xref table is
  127. * updated later. This allows Pages to refer to their
  128. * Parent before we write it out.
  129. *
  130. * @param prod the name of the producer of this pdf document
  131. */
  132. public PDFDocument(String prod) {
  133. this(prod, null);
  134. versionController = VersionController.getDynamicVersionController(Version.V1_4, this);
  135. }
  136. /**
  137. * Creates an empty PDF document.
  138. *
  139. * The constructor creates a /Root and /Pages object to
  140. * track the document but does not write these objects until
  141. * the trailer is written. Note that the object ID of the
  142. * pages object is determined now, and the xref table is
  143. * updated later. This allows Pages to refer to their
  144. * Parent before we write it out.
  145. *
  146. * @param prod the name of the producer of this pdf document
  147. * @param versionController the version controller of this PDF document
  148. */
  149. public PDFDocument(String prod, VersionController versionController) {
  150. this.factory = new PDFFactory(this);
  151. /* create the /Root, /Info and /Resources objects */
  152. this.pages = getFactory().makePages();
  153. // Create the Root object
  154. this.root = getFactory().makeRoot(this.pages);
  155. // Create the Resources object
  156. this.resources = getFactory().makeResources();
  157. // Make the /Info record
  158. this.info = getFactory().makeInfo(prod);
  159. this.versionController = versionController;
  160. }
  161. /**
  162. * Returns the current PDF version.
  163. *
  164. * @return returns the PDF version
  165. */
  166. public Version getPDFVersion() {
  167. return versionController.getPDFVersion();
  168. }
  169. /**
  170. * Sets the PDF version of this document.
  171. *
  172. * @param version the PDF version
  173. * @throws IllegalStateException if the version of this PDF is not allowed to change.
  174. */
  175. public void setPDFVersion(Version version) {
  176. versionController.setPDFVersion(version);
  177. }
  178. /** @return the String representing the current PDF version */
  179. public String getPDFVersionString() {
  180. return versionController.getPDFVersion().toString();
  181. }
  182. /** @return the PDF profile currently active. */
  183. public PDFProfile getProfile() {
  184. return this.pdfProfile;
  185. }
  186. /**
  187. * Returns the factory for PDF objects.
  188. *
  189. * @return the {@link PDFFactory} object
  190. */
  191. public PDFFactory getFactory() {
  192. return this.factory;
  193. }
  194. /**
  195. * Converts text to a byte array for writing to a PDF file.
  196. *
  197. * @param text text to convert/encode
  198. * @return the resulting <code>byte</code> array
  199. */
  200. public static byte[] encode(String text) {
  201. try {
  202. return text.getBytes(ENCODING);
  203. } catch (UnsupportedEncodingException uee) {
  204. return text.getBytes();
  205. }
  206. }
  207. /**
  208. * Flushes the given text buffer to an output stream with the right encoding and resets
  209. * the text buffer. This is used to efficiently switch between outputting text and binary
  210. * content.
  211. * @param textBuffer the text buffer
  212. * @param out the output stream to flush the text content to
  213. * @throws IOException if an I/O error occurs while writing to the output stream
  214. */
  215. public static void flushTextBuffer(StringBuilder textBuffer, OutputStream out)
  216. throws IOException {
  217. out.write(encode(textBuffer.toString()));
  218. textBuffer.setLength(0);
  219. }
  220. /**
  221. * Sets the producer of the document.
  222. *
  223. * @param producer string indicating application producing the PDF
  224. */
  225. public void setProducer(String producer) {
  226. this.info.setProducer(producer);
  227. }
  228. /**
  229. * Sets the creation date of the document.
  230. *
  231. * @param date Date to be stored as creation date in the PDF.
  232. */
  233. public void setCreationDate(Date date) {
  234. this.info.setCreationDate(date);
  235. }
  236. /**
  237. * Sets the creator of the document.
  238. *
  239. * @param creator string indicating application creating the document
  240. */
  241. public void setCreator(String creator) {
  242. this.info.setCreator(creator);
  243. }
  244. /**
  245. * Sets the filter map to use for filters in this document.
  246. *
  247. * @param map the map of filter lists for each stream type
  248. */
  249. public void setFilterMap(Map<String, List<String>> map) {
  250. this.filterMap = map;
  251. }
  252. /**
  253. * Returns the {@link PDFFilter}s map used for filters in this document.
  254. *
  255. * @return the map of filters being used
  256. */
  257. public Map<String, List<String>> getFilterMap() {
  258. return this.filterMap;
  259. }
  260. /**
  261. * Returns the {@link PDFPages} object associated with the root object.
  262. *
  263. * @return the {@link PDFPages} object
  264. */
  265. public PDFPages getPages() {
  266. return this.pages;
  267. }
  268. /**
  269. * Get the {@link PDFRoot} object for this document.
  270. *
  271. * @return the {@link PDFRoot} object
  272. */
  273. public PDFRoot getRoot() {
  274. return this.root;
  275. }
  276. /**
  277. * Creates and returns a StructTreeRoot object.
  278. *
  279. * @param parentTree the value of the ParenTree entry
  280. * @return the structure tree root
  281. */
  282. public PDFStructTreeRoot makeStructTreeRoot(PDFParentTree parentTree) {
  283. PDFStructTreeRoot structTreeRoot = new PDFStructTreeRoot(parentTree);
  284. assignObjectNumber(structTreeRoot);
  285. addTrailerObject(structTreeRoot);
  286. root.setStructTreeRoot(structTreeRoot);
  287. structureTreeElements = new ArrayList<PDFStructElem>();
  288. return structTreeRoot;
  289. }
  290. /**
  291. * Creates and returns a structure element.
  292. *
  293. * @param structureType the structure type of the new element (value for the
  294. * S entry)
  295. * @param parent the parent of the new structure element in the structure
  296. * hierarchy
  297. * @return a dictionary of type StructElem
  298. */
  299. public PDFStructElem makeStructureElement(PDFName structureType, PDFObject parent) {
  300. PDFStructElem structElem = new PDFStructElem(parent, structureType);
  301. assignObjectNumber(structElem);
  302. structureTreeElements.add(structElem);
  303. return structElem;
  304. }
  305. /**
  306. * Get the {@link PDFInfo} object for this document.
  307. *
  308. * @return the {@link PDFInfo} object
  309. */
  310. public PDFInfo getInfo() {
  311. return this.info;
  312. }
  313. /**
  314. * Registers a {@link PDFObject} in this PDF document.
  315. * The object is assigned a new object number.
  316. *
  317. * @param obj {@link PDFObject} to add
  318. * @return the added {@link PDFObject} added (with its object number set)
  319. */
  320. public PDFObject registerObject(PDFObject obj) {
  321. assignObjectNumber(obj);
  322. addObject(obj);
  323. return obj;
  324. }
  325. /**
  326. * Assigns the {@link PDFObject} an object number,
  327. * and sets the parent of the {@link PDFObject} to this document.
  328. *
  329. * @param obj {@link PDFObject} to assign a number to
  330. */
  331. public void assignObjectNumber(PDFObject obj) {
  332. if (obj == null) {
  333. throw new NullPointerException("obj must not be null");
  334. }
  335. if (obj.hasObjectNumber()) {
  336. throw new IllegalStateException(
  337. "Error registering a PDFObject: "
  338. + "PDFObject already has an object number");
  339. }
  340. PDFDocument currentParent = obj.getDocument();
  341. if (currentParent != null && currentParent != this) {
  342. throw new IllegalStateException(
  343. "Error registering a PDFObject: "
  344. + "PDFObject already has a parent PDFDocument");
  345. }
  346. obj.setObjectNumber(++this.objectcount);
  347. if (currentParent == null) {
  348. obj.setDocument(this);
  349. }
  350. }
  351. /**
  352. * Adds a {@link PDFObject} to this document.
  353. * The object <em>MUST</em> have an object number assigned.
  354. *
  355. * @param obj {@link PDFObject} to add
  356. */
  357. public void addObject(PDFObject obj) {
  358. if (obj == null) {
  359. throw new NullPointerException("obj must not be null");
  360. }
  361. if (!obj.hasObjectNumber()) {
  362. throw new IllegalStateException(
  363. "Error adding a PDFObject: "
  364. + "PDFObject doesn't have an object number");
  365. }
  366. //Add object to list
  367. this.objects.add(obj);
  368. //Add object to special lists where necessary
  369. if (obj instanceof PDFFunction) {
  370. this.functions.add((PDFFunction) obj);
  371. }
  372. if (obj instanceof PDFShading) {
  373. final String shadingName = "Sh" + (++this.shadingCount);
  374. ((PDFShading)obj).setName(shadingName);
  375. this.shadings.add((PDFShading) obj);
  376. }
  377. if (obj instanceof PDFPattern) {
  378. final String patternName = "Pa" + (++this.patternCount);
  379. ((PDFPattern)obj).setName(patternName);
  380. this.patterns.add((PDFPattern) obj);
  381. }
  382. if (obj instanceof PDFFont) {
  383. final PDFFont font = (PDFFont)obj;
  384. this.fontMap.put(font.getName(), font);
  385. }
  386. if (obj instanceof PDFGState) {
  387. this.gstates.add((PDFGState) obj);
  388. }
  389. if (obj instanceof PDFPage) {
  390. this.pages.notifyKidRegistered((PDFPage)obj);
  391. }
  392. if (obj instanceof PDFLaunch) {
  393. this.launches.add((PDFLaunch) obj);
  394. }
  395. if (obj instanceof PDFLink) {
  396. this.links.add((PDFLink) obj);
  397. }
  398. if (obj instanceof PDFFileSpec) {
  399. this.filespecs.add((PDFFileSpec) obj);
  400. }
  401. if (obj instanceof PDFGoToRemote) {
  402. this.gotoremotes.add((PDFGoToRemote) obj);
  403. }
  404. }
  405. /**
  406. * Add trailer object.
  407. * Adds an object to the list of trailer objects.
  408. *
  409. * @param obj the PDF object to add
  410. */
  411. public void addTrailerObject(PDFObject obj) {
  412. this.trailerObjects.add(obj);
  413. if (obj instanceof PDFGoTo) {
  414. this.gotos.add((PDFGoTo) obj);
  415. }
  416. }
  417. /**
  418. * Apply the encryption filter to a PDFStream if encryption is enabled.
  419. *
  420. * @param stream PDFStream to encrypt
  421. */
  422. public void applyEncryption(AbstractPDFStream stream) {
  423. if (isEncryptionActive()) {
  424. this.encryption.applyFilter(stream);
  425. }
  426. }
  427. /**
  428. * Enables PDF encryption.
  429. *
  430. * @param params The encryption parameters for the pdf file
  431. */
  432. public void setEncryption(PDFEncryptionParams params) {
  433. getProfile().verifyEncryptionAllowed();
  434. fileIDGenerator = FileIDGenerator.getRandomFileIDGenerator();
  435. this.encryption = PDFEncryptionManager.newInstance(++this.objectcount, params, this);
  436. if (this.encryption != null) {
  437. PDFObject pdfObject = (PDFObject)this.encryption;
  438. addTrailerObject(pdfObject);
  439. } else {
  440. log.warn(
  441. "PDF encryption is unavailable. PDF will be "
  442. + "generated without encryption.");
  443. }
  444. }
  445. /**
  446. * Indicates whether encryption is active for this PDF or not.
  447. *
  448. * @return boolean True if encryption is active
  449. */
  450. public boolean isEncryptionActive() {
  451. return this.encryption != null;
  452. }
  453. /**
  454. * Returns the active Encryption object.
  455. *
  456. * @return the Encryption object
  457. */
  458. public PDFEncryption getEncryption() {
  459. return this.encryption;
  460. }
  461. private Object findPDFObject(List<? extends PDFObject> list, PDFObject compare) {
  462. for (PDFObject obj : list) {
  463. if (compare.contentEquals(obj)) {
  464. return obj;
  465. }
  466. }
  467. return null;
  468. }
  469. /**
  470. * Looks through the registered functions to see if one that is equal to
  471. * a reference object exists
  472. *
  473. * @param compare reference object
  474. * @return the function if it was found, null otherwise
  475. */
  476. protected PDFFunction findFunction(PDFFunction compare) {
  477. return (PDFFunction)findPDFObject(this.functions, compare);
  478. }
  479. /**
  480. * Looks through the registered shadings to see if one that is equal to
  481. * a reference object exists
  482. *
  483. * @param compare reference object
  484. * @return the shading if it was found, null otherwise
  485. */
  486. protected PDFShading findShading(PDFShading compare) {
  487. return (PDFShading)findPDFObject(this.shadings, compare);
  488. }
  489. /**
  490. * Find a previous pattern.
  491. * The problem with this is for tiling patterns the pattern
  492. * data stream is stored and may use up memory, usually this
  493. * would only be a small amount of data.
  494. *
  495. * @param compare reference object
  496. * @return the shading if it was found, null otherwise
  497. */
  498. protected PDFPattern findPattern(PDFPattern compare) {
  499. return (PDFPattern)findPDFObject(this.patterns, compare);
  500. }
  501. /**
  502. * Finds a font.
  503. *
  504. * @param fontname name of the font
  505. * @return PDFFont the requested font, null if it wasn't found
  506. */
  507. protected PDFFont findFont(String fontname) {
  508. return this.fontMap.get(fontname);
  509. }
  510. /**
  511. * Finds a named destination.
  512. *
  513. * @param compare reference object to use as search template
  514. * @return the link if found, null otherwise
  515. */
  516. protected PDFDestination findDestination(PDFDestination compare) {
  517. int index = getDestinationList().indexOf(compare);
  518. if (index >= 0) {
  519. return getDestinationList().get(index);
  520. } else {
  521. return null;
  522. }
  523. }
  524. /**
  525. * Finds a link.
  526. *
  527. * @param compare reference object to use as search template
  528. * @return the link if found, null otherwise
  529. */
  530. protected PDFLink findLink(PDFLink compare) {
  531. return (PDFLink)findPDFObject(this.links, compare);
  532. }
  533. /**
  534. * Finds a file spec.
  535. *
  536. * @param compare reference object to use as search template
  537. * @return the file spec if found, null otherwise
  538. */
  539. protected PDFFileSpec findFileSpec(PDFFileSpec compare) {
  540. return (PDFFileSpec)findPDFObject(this.filespecs, compare);
  541. }
  542. /**
  543. * Finds a goto remote.
  544. *
  545. * @param compare reference object to use as search template
  546. * @return the goto remote if found, null otherwise
  547. */
  548. protected PDFGoToRemote findGoToRemote(PDFGoToRemote compare) {
  549. return (PDFGoToRemote)findPDFObject(this.gotoremotes, compare);
  550. }
  551. /**
  552. * Finds a goto.
  553. *
  554. * @param compare reference object to use as search template
  555. * @return the goto if found, null otherwise
  556. */
  557. protected PDFGoTo findGoTo(PDFGoTo compare) {
  558. return (PDFGoTo)findPDFObject(this.gotos, compare);
  559. }
  560. /**
  561. * Finds a launch.
  562. *
  563. * @param compare reference object to use as search template
  564. * @return the launch if found, null otherwise
  565. */
  566. protected PDFLaunch findLaunch(PDFLaunch compare) {
  567. return (PDFLaunch) findPDFObject(this.launches, compare);
  568. }
  569. /**
  570. * Looks for an existing GState to use
  571. *
  572. * @param wanted requested features
  573. * @param current currently active features
  574. * @return the GState if found, null otherwise
  575. */
  576. protected PDFGState findGState(PDFGState wanted, PDFGState current) {
  577. PDFGState poss;
  578. Iterator<PDFGState> iter = this.gstates.iterator();
  579. while (iter.hasNext()) {
  580. PDFGState avail = iter.next();
  581. poss = new PDFGState();
  582. poss.addValues(current);
  583. poss.addValues(avail);
  584. if (poss.equals(wanted)) {
  585. return avail;
  586. }
  587. }
  588. return null;
  589. }
  590. /**
  591. * Returns the PDF color space object.
  592. *
  593. * @return the color space
  594. */
  595. public PDFDeviceColorSpace getPDFColorSpace() {
  596. return this.colorspace;
  597. }
  598. /**
  599. * Returns the color space.
  600. *
  601. * @return the color space
  602. */
  603. public int getColorSpace() {
  604. return getPDFColorSpace().getColorSpace();
  605. }
  606. /**
  607. * Set the color space.
  608. * This is used when creating gradients.
  609. *
  610. * @param theColorspace the new color space
  611. */
  612. public void setColorSpace(int theColorspace) {
  613. this.colorspace.setColorSpace(theColorspace);
  614. }
  615. /**
  616. * Returns the font map for this document.
  617. *
  618. * @return the map of fonts used in this document
  619. */
  620. public Map<String, PDFFont> getFontMap() {
  621. return this.fontMap;
  622. }
  623. /**
  624. * Resolve a URI.
  625. *
  626. * @param uri the uri to resolve
  627. * @throws java.io.FileNotFoundException if the URI could not be resolved
  628. * @return the InputStream from the URI.
  629. */
  630. protected InputStream resolveURI(String uri)
  631. throws java.io.FileNotFoundException {
  632. try {
  633. /* TODO: Temporary hack to compile, improve later */
  634. return new java.net.URL(uri).openStream();
  635. } catch (Exception e) {
  636. throw new java.io.FileNotFoundException(
  637. "URI could not be resolved (" + e.getMessage() + "): " + uri);
  638. }
  639. }
  640. /**
  641. * Get an image from the image map.
  642. *
  643. * @param key the image key to look for
  644. * @return the image or PDFXObject for the key if found
  645. * @deprecated Use getXObject instead (so forms are treated in the same way)
  646. */
  647. @Deprecated
  648. public PDFImageXObject getImage(String key) {
  649. return (PDFImageXObject)this.xObjectsMap.get(key);
  650. }
  651. /**
  652. * Get an XObject from the image map.
  653. *
  654. * @param key the XObject key to look for
  655. * @return the PDFXObject for the key if found
  656. */
  657. public PDFXObject getXObject(String key) {
  658. return this.xObjectsMap.get(key);
  659. }
  660. /**
  661. * Adds a destination to the document.
  662. * @param destination the destination object
  663. */
  664. public void addDestination(PDFDestination destination) {
  665. if (this.destinations == null) {
  666. this.destinations = new ArrayList<PDFDestination>();
  667. }
  668. this.destinations.add(destination);
  669. }
  670. /**
  671. * Gets the list of named destinations.
  672. *
  673. * @return the list of named destinations.
  674. */
  675. public List<PDFDestination> getDestinationList() {
  676. if (hasDestinations()) {
  677. return this.destinations;
  678. } else {
  679. return Collections.emptyList();
  680. }
  681. }
  682. /**
  683. * Gets whether the document has named destinations.
  684. *
  685. * @return whether the document has named destinations.
  686. */
  687. public boolean hasDestinations() {
  688. return this.destinations != null && !this.destinations.isEmpty();
  689. }
  690. /**
  691. * Add an image to the PDF document.
  692. * This adds an image to the PDF objects.
  693. * If an image with the same key already exists it will return the
  694. * old {@link PDFXObject}.
  695. *
  696. * @param res the PDF resource context to add to, may be null
  697. * @param img the PDF image to add
  698. * @return the PDF XObject that references the PDF image data
  699. */
  700. public PDFImageXObject addImage(PDFResourceContext res, PDFImage img) {
  701. // check if already created
  702. String key = img.getKey();
  703. PDFImageXObject xObject = (PDFImageXObject)this.xObjectsMap.get(key);
  704. if (xObject != null) {
  705. if (res != null) {
  706. res.getPDFResources().addXObject(xObject);
  707. }
  708. return xObject;
  709. }
  710. // setup image
  711. img.setup(this);
  712. // create a new XObject
  713. xObject = new PDFImageXObject(++this.xObjectCount, img);
  714. registerObject(xObject);
  715. this.resources.addXObject(xObject);
  716. if (res != null) {
  717. res.getPDFResources().addXObject(xObject);
  718. }
  719. this.xObjectsMap.put(key, xObject);
  720. return xObject;
  721. }
  722. /**
  723. * Add a form XObject to the PDF document.
  724. * This adds a Form XObject to the PDF objects.
  725. * If a Form XObject with the same key already exists it will return the
  726. * old {@link PDFFormXObject}.
  727. *
  728. * @param res the PDF resource context to add to, may be null
  729. * @param cont the PDF Stream contents of the Form XObject
  730. * @param formres a reference to the PDF Resources for the Form XObject data
  731. * @param key the key for the object
  732. * @return the PDF Form XObject that references the PDF data
  733. */
  734. public PDFFormXObject addFormXObject(
  735. PDFResourceContext res,
  736. PDFStream cont,
  737. PDFReference formres,
  738. String key) {
  739. // check if already created
  740. PDFFormXObject xObject = (PDFFormXObject)xObjectsMap.get(key);
  741. if (xObject != null) {
  742. if (res != null) {
  743. res.getPDFResources().addXObject(xObject);
  744. }
  745. return xObject;
  746. }
  747. xObject = new PDFFormXObject(
  748. ++this.xObjectCount,
  749. cont,
  750. formres);
  751. registerObject(xObject);
  752. this.resources.addXObject(xObject);
  753. if (res != null) {
  754. res.getPDFResources().addXObject(xObject);
  755. }
  756. this.xObjectsMap.put(key, xObject);
  757. return xObject;
  758. }
  759. /**
  760. * Get the root Outlines object. This method does not write
  761. * the outline to the PDF document, it simply creates a
  762. * reference for later.
  763. *
  764. * @return the PDF Outline root object
  765. */
  766. public PDFOutline getOutlineRoot() {
  767. if (this.outlineRoot != null) {
  768. return this.outlineRoot;
  769. }
  770. this.outlineRoot = new PDFOutline(null, null, true);
  771. assignObjectNumber(this.outlineRoot);
  772. addTrailerObject(this.outlineRoot);
  773. this.root.setRootOutline(this.outlineRoot);
  774. return this.outlineRoot;
  775. }
  776. /**
  777. * Get the /Resources object for the document
  778. *
  779. * @return the /Resources object
  780. */
  781. public PDFResources getResources() {
  782. return this.resources;
  783. }
  784. public void enableAccessibility(boolean enableAccessibility) {
  785. this.accessibilityEnabled = enableAccessibility;
  786. }
  787. /**
  788. * Writes out the entire document
  789. *
  790. * @param stream the OutputStream to output the document to
  791. * @throws IOException if there is an exception writing to the output stream
  792. */
  793. public void output(OutputStream stream) throws IOException {
  794. //Write out objects until the list is empty. This approach (used with a
  795. //LinkedList) allows for output() methods to create and register objects
  796. //on the fly even during serialization.
  797. while (this.objects.size() > 0) {
  798. PDFObject object = this.objects.remove(0);
  799. streamIndirectObject(object, stream);
  800. }
  801. }
  802. private void streamIndirectObject(PDFObject o, OutputStream stream) throws IOException {
  803. recordObjectOffset(o);
  804. this.position += outputIndirectObject(o, stream);
  805. }
  806. private void streamIndirectObjects(Collection<? extends PDFObject> objects, OutputStream stream)
  807. throws IOException {
  808. for (PDFObject o : objects) {
  809. streamIndirectObject(o, stream);
  810. }
  811. }
  812. private void recordObjectOffset(PDFObject object) {
  813. int index = object.getObjectNumber() - 1;
  814. while (indirectObjectOffsets.size() <= index) {
  815. indirectObjectOffsets.add(null);
  816. }
  817. indirectObjectOffsets.set(index, position);
  818. }
  819. /**
  820. * Outputs the given object, wrapped by obj/endobj, to the given stream.
  821. *
  822. * @param object an indirect object, as described in Section 3.2.9 of the PDF 1.5
  823. * Reference.
  824. * @param stream the stream to which the object must be output
  825. * @throws IllegalArgumentException if the object is not an indirect object
  826. */
  827. public static int outputIndirectObject(PDFObject object, OutputStream stream)
  828. throws IOException {
  829. if (!object.hasObjectNumber()) {
  830. throw new IllegalArgumentException("Not an indirect object");
  831. }
  832. byte[] obj = encode(object.getObjectID());
  833. stream.write(obj);
  834. int length = object.output(stream);
  835. byte[] endobj = encode("\nendobj\n");
  836. stream.write(endobj);
  837. return obj.length + length + endobj.length;
  838. }
  839. /**
  840. * Write the PDF header.
  841. *
  842. * This method must be called prior to formatting
  843. * and outputting AreaTrees.
  844. *
  845. * @param stream the OutputStream to write the header to
  846. * @throws IOException if there is an exception writing to the output stream
  847. */
  848. public void outputHeader(OutputStream stream) throws IOException {
  849. this.position = 0;
  850. getProfile().verifyPDFVersion();
  851. byte[] pdf = encode("%PDF-" + getPDFVersionString() + "\n");
  852. stream.write(pdf);
  853. this.position += pdf.length;
  854. // output a binary comment as recommended by the PDF spec (3.4.1)
  855. byte[] bin = {
  856. (byte)'%',
  857. (byte)0xAA,
  858. (byte)0xAB,
  859. (byte)0xAC,
  860. (byte)0xAD,
  861. (byte)'\n' };
  862. stream.write(bin);
  863. this.position += bin.length;
  864. }
  865. /**
  866. * Write the trailer
  867. *
  868. * @param stream the OutputStream to write the trailer to
  869. * @throws IOException if there is an exception writing to the output stream
  870. */
  871. public void outputTrailer(OutputStream stream) throws IOException {
  872. createDestinations();
  873. output(stream);
  874. outputTrailerObjectsAndXref(stream);
  875. }
  876. private void createDestinations() {
  877. if (hasDestinations()) {
  878. Collections.sort(this.destinations, new DestinationComparator());
  879. PDFDests dests = getFactory().makeDests(this.destinations);
  880. if (this.root.getNames() == null) {
  881. this.root.setNames(getFactory().makeNames());
  882. }
  883. this.root.getNames().setDests(dests);
  884. }
  885. }
  886. private void outputTrailerObjectsAndXref(OutputStream stream) throws IOException {
  887. TrailerOutputHelper trailerOutputHelper = mayCompressStructureTreeElements()
  888. ? new CompressedTrailerOutputHelper()
  889. : new UncompressedTrailerOutputHelper();
  890. if (structureTreeElements != null) {
  891. trailerOutputHelper.outputStructureTreeElements(stream);
  892. }
  893. streamIndirectObjects(trailerObjects, stream);
  894. TrailerDictionary trailerDictionary = createTrailerDictionary();
  895. long startxref = trailerOutputHelper.outputCrossReferenceObject(stream, trailerDictionary);
  896. String trailer = "startxref\n" + startxref + "\n%%EOF\n";
  897. stream.write(encode(trailer));
  898. }
  899. private boolean mayCompressStructureTreeElements() {
  900. return accessibilityEnabled
  901. && versionController.getPDFVersion().compareTo(Version.V1_5) >= 0;
  902. }
  903. private TrailerDictionary createTrailerDictionary() {
  904. FileIDGenerator gen = getFileIDGenerator();
  905. TrailerDictionary trailerDictionary = new TrailerDictionary(this)
  906. .setRoot(root)
  907. .setInfo(info)
  908. .setFileID(gen.getOriginalFileID(), gen.getUpdatedFileID());
  909. if (isEncryptionActive()) {
  910. trailerDictionary.setEncryption(encryption);
  911. }
  912. return trailerDictionary;
  913. }
  914. private interface TrailerOutputHelper {
  915. void outputStructureTreeElements(OutputStream stream) throws IOException;
  916. /**
  917. * @return the offset of the cross-reference object (the value of startxref)
  918. */
  919. long outputCrossReferenceObject(OutputStream stream, TrailerDictionary trailerDictionary)
  920. throws IOException;
  921. }
  922. private class UncompressedTrailerOutputHelper implements TrailerOutputHelper {
  923. public void outputStructureTreeElements(OutputStream stream)
  924. throws IOException {
  925. streamIndirectObjects(structureTreeElements, stream);
  926. }
  927. public long outputCrossReferenceObject(OutputStream stream,
  928. TrailerDictionary trailerDictionary) throws IOException {
  929. new CrossReferenceTable(trailerDictionary, position,
  930. indirectObjectOffsets).output(stream);
  931. return position;
  932. }
  933. }
  934. private class CompressedTrailerOutputHelper implements TrailerOutputHelper {
  935. private ObjectStreamManager structureTreeObjectStreams;
  936. public void outputStructureTreeElements(OutputStream stream)
  937. throws IOException {
  938. assert structureTreeElements.size() > 0;
  939. structureTreeObjectStreams = new ObjectStreamManager(PDFDocument.this);
  940. for (PDFStructElem structElem : structureTreeElements) {
  941. structureTreeObjectStreams.add(structElem);
  942. }
  943. }
  944. public long outputCrossReferenceObject(OutputStream stream,
  945. TrailerDictionary trailerDictionary) throws IOException {
  946. // Outputting the object streams should not have created new indirect objects
  947. assert objects.isEmpty();
  948. new CrossReferenceStream(PDFDocument.this, ++objectcount, trailerDictionary, position,
  949. indirectObjectOffsets,
  950. structureTreeObjectStreams.getCompressedObjectReferences())
  951. .output(stream);
  952. return position;
  953. }
  954. }
  955. long getCurrentFileSize() {
  956. return position;
  957. }
  958. FileIDGenerator getFileIDGenerator() {
  959. if (fileIDGenerator == null) {
  960. try {
  961. fileIDGenerator = FileIDGenerator.getDigestFileIDGenerator(this);
  962. } catch (NoSuchAlgorithmException e) {
  963. fileIDGenerator = FileIDGenerator.getRandomFileIDGenerator();
  964. }
  965. }
  966. return fileIDGenerator;
  967. }
  968. }