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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530
  1. /*
  2. * $Id$
  3. * Copyright (C) 2001-2002 The Apache Software Foundation. All rights reserved.
  4. * For details on use and redistribution please refer to the
  5. * LICENSE file included with these sources.
  6. */
  7. /* image support modified from work of BoBoGi */
  8. /* font support based on work by Takayuki Takeuchi */
  9. package org.apache.fop.pdf;
  10. import org.apache.fop.render.pdf.CIDFont;
  11. import org.apache.fop.render.pdf.fonts.LazyFont;
  12. import org.apache.fop.layout.FontMetric;
  13. import org.apache.fop.layout.FontDescriptor;
  14. // Java
  15. import java.io.IOException;
  16. import java.io.OutputStream;
  17. import java.util.ArrayList;
  18. import java.util.HashMap;
  19. import java.util.Iterator;
  20. import java.awt.geom.Rectangle2D;
  21. /**
  22. * class representing a PDF document.
  23. *
  24. * The document is built up by calling various methods and then finally
  25. * output to given filehandle using output method.
  26. *
  27. * A PDF document consists of a series of numbered objects preceded by a
  28. * header and followed by an xref table and trailer. The xref table
  29. * allows for quick access to objects by listing their character
  30. * positions within the document. For this reason the PDF document must
  31. * keep track of the character position of each object. The document
  32. * also keeps direct track of the /Root, /Info and /Resources objects.
  33. *
  34. * Modified by Mark Lillywhite, mark-fop@inomial.com. The changes
  35. * involve: ability to output pages one-at-a-time in a streaming
  36. * fashion (rather than storing them all for output at the end);
  37. * ability to write the /Pages object after writing the rest
  38. * of the document; ability to write to a stream and flush
  39. * the object list; enhanced trailer output; cleanups.
  40. *
  41. */
  42. public class PDFDocument {
  43. private static final Integer LOCATION_PLACEHOLDER = new Integer(0);
  44. /**
  45. * the version of PDF supported which is 1.4
  46. */
  47. protected static final String PDF_VERSION = "1.4";
  48. /**
  49. * the current character position
  50. */
  51. protected int position = 0;
  52. /**
  53. * the character position of each object
  54. */
  55. protected ArrayList location = new ArrayList();
  56. /** List of objects to write in the trailer */
  57. private ArrayList trailerObjects = new ArrayList();
  58. /**
  59. * the counter for object numbering
  60. */
  61. protected int objectcount = 0;
  62. /**
  63. * the objects themselves
  64. */
  65. protected ArrayList objects = new ArrayList();
  66. /**
  67. * character position of xref table
  68. */
  69. protected int xref;
  70. /**
  71. * the /Root object
  72. */
  73. protected PDFRoot root;
  74. /** The root outline object */
  75. private PDFOutline outlineRoot = null;
  76. /** The /Pages object (mark-fop@inomial.com) */
  77. private PDFPages pages;
  78. /**
  79. * the /Info object
  80. */
  81. protected PDFInfo info;
  82. /**
  83. * the /Resources object
  84. */
  85. protected PDFResources resources;
  86. /**
  87. * the colorspace (0=RGB, 1=CMYK)
  88. */
  89. protected PDFColorSpace colorspace = new PDFColorSpace(PDFColorSpace.DEVICE_RGB);
  90. /**
  91. * the counter for Pattern name numbering (e.g. 'Pattern1')
  92. */
  93. protected int patternCount = 0;
  94. /**
  95. * the counter for Shading name numbering
  96. */
  97. protected int shadingCount = 0;
  98. /**
  99. * the counter for XObject numbering
  100. */
  101. protected int xObjectCount = 0;
  102. /**
  103. * the XObjects Map.
  104. * Should be modified (works only for image subtype)
  105. */
  106. protected HashMap xObjectsMap = new HashMap();
  107. /**
  108. * the Font Map.
  109. */
  110. protected HashMap fontMap = new HashMap();
  111. /**
  112. * The filter map.
  113. */
  114. protected HashMap filterMap = new HashMap();
  115. /**
  116. * List of PDFGState objects.
  117. */
  118. protected ArrayList gstates = new ArrayList();
  119. /**
  120. * List of functions.
  121. */
  122. protected ArrayList functions = new ArrayList();
  123. /**
  124. * List of shadings.
  125. */
  126. protected ArrayList shadings = new ArrayList();
  127. /**
  128. * List of patterns.
  129. */
  130. protected ArrayList patterns = new ArrayList();
  131. /**
  132. * creates an empty PDF document <p>
  133. *
  134. * The constructor creates a /Root and /Pages object to
  135. * track the document but does not write these objects until
  136. * the trailer is written. Note that the object ID of the
  137. * pages object is determined now, and the xref table is
  138. * updated later. This allows Pages to refer to their
  139. * Parent before we write it out.
  140. *
  141. * @param prod the name of the producer of this pdf document
  142. */
  143. public PDFDocument(String prod) {
  144. /* create the /Root, /Info and /Resources objects */
  145. this.pages = makePages();
  146. // Create the Root object
  147. this.root = makeRoot(pages);
  148. // Create the Resources object
  149. this.resources = makeResources();
  150. // Make the /Info record
  151. this.info = makeInfo(prod);
  152. }
  153. /**
  154. * set the producer of the document
  155. *
  156. * @param producer string indicating application producing the PDF
  157. */
  158. public void setProducer(String producer) {
  159. this.info.setProducer(producer);
  160. }
  161. /**
  162. * Set the filter map to use for filters in this document.
  163. *
  164. * @param map the map of filter lists for each stream type
  165. */
  166. public void setFilterMap(HashMap map) {
  167. filterMap = map;
  168. }
  169. /**
  170. * Get the filter map used for filters in this document.
  171. *
  172. * @return the map of filters being used
  173. */
  174. public HashMap getFilterMap() {
  175. return filterMap;
  176. }
  177. /**
  178. * Make a /Catalog (Root) object. This object is written in
  179. * the trailer.
  180. *
  181. * @param pages the pages pdf object that the root points to
  182. * @return the new pdf root object for this document
  183. */
  184. public PDFRoot makeRoot(PDFPages pages) {
  185. /*
  186. * Make a /Pages object. This object is written in the trailer.
  187. */
  188. PDFRoot pdfRoot = new PDFRoot(++this.objectcount, pages);
  189. addTrailerObject(pdfRoot);
  190. return pdfRoot;
  191. }
  192. /**
  193. * Make a /Pages object. This object is written in the trailer.
  194. *
  195. * @return a new PDF Pages object for adding pages to
  196. */
  197. public PDFPages makePages() {
  198. PDFPages pdfPages = new PDFPages(++this.objectcount);
  199. addTrailerObject(pdfPages);
  200. return pdfPages;
  201. }
  202. /**
  203. * Make a /Resources object. This object is written in the trailer.
  204. *
  205. * @return a new PDF resources object
  206. */
  207. public PDFResources makeResources() {
  208. PDFResources pdfResources = new PDFResources(++this.objectcount);
  209. addTrailerObject(pdfResources);
  210. return pdfResources;
  211. }
  212. /**
  213. * make an /Info object
  214. *
  215. * @param prod string indicating application producing the PDF
  216. * @return the created /Info object
  217. */
  218. protected PDFInfo makeInfo(String prod) {
  219. /*
  220. * create a PDFInfo with the next object number and add to
  221. * list of objects
  222. */
  223. PDFInfo pdfInfo = new PDFInfo(++this.objectcount);
  224. // set the default producer
  225. pdfInfo.setProducer(prod);
  226. this.objects.add(pdfInfo);
  227. return pdfInfo;
  228. }
  229. /**
  230. * Get the pdf info object for this document.
  231. *
  232. * @return the PDF Info object for this document
  233. */
  234. public PDFInfo getInfo() {
  235. return info;
  236. }
  237. /**
  238. * Make a Type 0 sampled function
  239. *
  240. * @param theDomain ArrayList objects of Double objects.
  241. * This is the domain of the function.
  242. * See page 264 of the PDF 1.3 Spec.
  243. * @param theRange ArrayList objects of Double objects.
  244. * This is the Range of the function.
  245. * See page 264 of the PDF 1.3 Spec.
  246. * @param theSize A ArrayList object of Integer objects.
  247. * This is the number of samples in each input dimension.
  248. * I can't imagine there being more or less than two input dimensions,
  249. * so maybe this should be an array of length 2.
  250. *
  251. * See page 265 of the PDF 1.3 Spec.
  252. * @param theBitsPerSample An int specifying the number of bits user
  253. * to represent each sample value.
  254. * Limited to 1,2,4,8,12,16,24 or 32.
  255. * See page 265 of the 1.3 PDF Spec.
  256. * @param theOrder The order of interpolation between samples.
  257. * Default is 1 (one). Limited
  258. * to 1 (one) or 3, which means linear or cubic-spline interpolation.
  259. *
  260. * This attribute is optional.
  261. *
  262. * See page 265 in the PDF 1.3 spec.
  263. * @param theEncode ArrayList objects of Double objects.
  264. * This is the linear mapping of input values intop the domain
  265. * of the function's sample table. Default is hard to represent in
  266. * ascii, but basically [0 (Size0 1) 0 (Size1 1)...].
  267. * This attribute is optional.
  268. *
  269. * See page 265 in the PDF 1.3 spec.
  270. * @param theDecode ArrayList objects of Double objects.
  271. * This is a linear mapping of sample values into the range.
  272. * The default is just the range.
  273. *
  274. * This attribute is optional.
  275. * Read about it on page 265 of the PDF 1.3 spec.
  276. * @param theFunctionDataStream The sample values that specify
  277. * the function are provided in a stream.
  278. *
  279. * This is optional, but is almost always used.
  280. *
  281. * Page 265 of the PDF 1.3 spec has more.
  282. * @param theFilter This is a vector of String objects which
  283. * are the various filters that have are to be
  284. * applied to the stream to make sense of it.
  285. * Order matters, so watch out.
  286. *
  287. * This is not documented in the Function section of the PDF 1.3 spec,
  288. * it was deduced from samples that this is sometimes used, even if we may never
  289. * use it in FOP. It is added for completeness sake.
  290. * @param theNumber The object number of this PDF object.
  291. * @param theFunctionType This is the type of function (0,2,3, or 4).
  292. * It should be 0 as this is the constructor for sampled functions.
  293. */
  294. public PDFFunction makeFunction(int theFunctionType, ArrayList theDomain,
  295. ArrayList theRange, ArrayList theSize,
  296. int theBitsPerSample, int theOrder,
  297. ArrayList theEncode, ArrayList theDecode,
  298. StringBuffer theFunctionDataStream,
  299. ArrayList theFilter) {
  300. // Type 0 function
  301. PDFFunction function = new PDFFunction(++this.objectcount,
  302. theFunctionType, theDomain,
  303. theRange, theSize,
  304. theBitsPerSample, theOrder,
  305. theEncode, theDecode,
  306. theFunctionDataStream,
  307. theFilter);
  308. PDFFunction oldfunc = findFunction(function);
  309. if (oldfunc == null) {
  310. functions.add(function);
  311. this.objects.add(function);
  312. } else {
  313. this.objectcount--;
  314. function = oldfunc;
  315. }
  316. return (function);
  317. }
  318. /**
  319. * make a type Exponential interpolation function
  320. * (for shading usually)
  321. *
  322. * @param theDomain ArrayList objects of Double objects.
  323. * This is the domain of the function.
  324. * See page 264 of the PDF 1.3 Spec.
  325. * @param theRange ArrayList of Doubles that is the Range of the function.
  326. * See page 264 of the PDF 1.3 Spec.
  327. * @param theCZero This is a vector of Double objects which defines the function result
  328. * when x=0.
  329. *
  330. * This attribute is optional.
  331. * It's described on page 268 of the PDF 1.3 spec.
  332. * @param theCOne This is a vector of Double objects which defines the function result
  333. * when x=1.
  334. *
  335. * This attribute is optional.
  336. * It's described on page 268 of the PDF 1.3 spec.
  337. * @param theInterpolationExponentN This is the inerpolation exponent.
  338. *
  339. * This attribute is required.
  340. * PDF Spec page 268
  341. * @param theFunctionType The type of the function, which should be 2.
  342. */
  343. public PDFFunction makeFunction(int theFunctionType, ArrayList theDomain,
  344. ArrayList theRange, ArrayList theCZero,
  345. ArrayList theCOne,
  346. double theInterpolationExponentN) { // type 2
  347. PDFFunction function = new PDFFunction(++this.objectcount,
  348. theFunctionType, theDomain,
  349. theRange, theCZero, theCOne,
  350. theInterpolationExponentN);
  351. PDFFunction oldfunc = findFunction(function);
  352. if (oldfunc == null) {
  353. functions.add(function);
  354. this.objects.add(function);
  355. } else {
  356. this.objectcount--;
  357. function = oldfunc;
  358. }
  359. return (function);
  360. }
  361. private PDFFunction findFunction(PDFFunction compare) {
  362. for (Iterator iter = functions.iterator(); iter.hasNext();) {
  363. Object func = iter.next();
  364. if (compare.equals(func)) {
  365. return (PDFFunction)func;
  366. }
  367. }
  368. return null;
  369. }
  370. private PDFShading findShading(PDFShading compare) {
  371. for (Iterator iter = shadings.iterator(); iter.hasNext();) {
  372. Object shad = iter.next();
  373. if (compare.equals(shad)) {
  374. return (PDFShading)shad;
  375. }
  376. }
  377. return null;
  378. }
  379. /**
  380. * Find a previous pattern.
  381. * The problem with this is for tiling patterns the pattern
  382. * data stream is stored and may use up memory, usually this
  383. * would only be a small amount of data.
  384. */
  385. private PDFPattern findPattern(PDFPattern compare) {
  386. for (Iterator iter = patterns.iterator(); iter.hasNext();) {
  387. Object patt = iter.next();
  388. if (compare.equals(patt)) {
  389. return (PDFPattern)patt;
  390. }
  391. }
  392. return null;
  393. }
  394. /**
  395. * Make a Type 3 Stitching function
  396. *
  397. * @param theDomain ArrayList objects of Double objects.
  398. * This is the domain of the function.
  399. * See page 264 of the PDF 1.3 Spec.
  400. * @param theRange ArrayList objects of Double objects.
  401. * This is the Range of the function.
  402. * See page 264 of the PDF 1.3 Spec.
  403. * @param theFunctions An ArrayList of the PDFFunction objects
  404. * that the stitching function stitches.
  405. *
  406. * This attributed is required.
  407. * It is described on page 269 of the PDF spec.
  408. * @param theBounds This is a vector of Doubles representing
  409. * the numbers that, in conjunction with Domain
  410. * define the intervals to which each function from
  411. * the 'functions' object applies. It must be in
  412. * order of increasing magnitude, and each must be
  413. * within Domain.
  414. *
  415. * It basically sets how much of the gradient each function handles.
  416. *
  417. * This attributed is required.
  418. * It's described on page 269 of the PDF 1.3 spec.
  419. * @param theEncode ArrayList objects of Double objects.
  420. * This is the linear mapping of input values intop the domain
  421. * of the function's sample table. Default is hard to represent in
  422. * ascii, but basically [0 (Size0 1) 0 (Size1 1)...].
  423. * This attribute is required.
  424. *
  425. * See page 270 in the PDF 1.3 spec.
  426. * @param theFunctionType This is the function type. It should be 3,
  427. * for a stitching function.
  428. */
  429. public PDFFunction makeFunction(int theFunctionType, ArrayList theDomain,
  430. ArrayList theRange, ArrayList theFunctions,
  431. ArrayList theBounds,
  432. ArrayList theEncode) {
  433. // Type 3
  434. PDFFunction function = new PDFFunction(++this.objectcount,
  435. theFunctionType, theDomain,
  436. theRange, theFunctions,
  437. theBounds, theEncode);
  438. PDFFunction oldfunc = findFunction(function);
  439. if (oldfunc == null) {
  440. functions.add(function);
  441. this.objects.add(function);
  442. } else {
  443. this.objectcount--;
  444. function = oldfunc;
  445. }
  446. return (function);
  447. }
  448. /**
  449. * make a postscript calculator function
  450. *
  451. * @param theNumber
  452. * @param theFunctionType
  453. * @param theDomain
  454. * @param theRange
  455. * @param theFunctionDataStream
  456. */
  457. public PDFFunction makeFunction(int theNumber, int theFunctionType,
  458. ArrayList theDomain, ArrayList theRange,
  459. StringBuffer theFunctionDataStream) { // Type 4
  460. PDFFunction function = new PDFFunction(++this.objectcount,
  461. theFunctionType, theDomain,
  462. theRange,
  463. theFunctionDataStream);
  464. PDFFunction oldfunc = findFunction(function);
  465. if (oldfunc == null) {
  466. functions.add(function);
  467. this.objects.add(function);
  468. } else {
  469. this.objectcount--;
  470. function = oldfunc;
  471. }
  472. return (function);
  473. }
  474. /**
  475. * make a function based shading object
  476. *
  477. * @param theShadingType The type of shading object, which should be 1 for function
  478. * based shading.
  479. * @param theColorSpace The colorspace is 'DeviceRGB' or something similar.
  480. * @param theBackground An array of color components appropriate to the
  481. * colorspace key specifying a single color value.
  482. * This key is used by the f operator buy ignored by the sh operator.
  483. * @param theBBox ArrayList of double's representing a rectangle
  484. * in the coordinate space that is current at the
  485. * time of shading is imaged. Temporary clipping
  486. * boundary.
  487. * @param theAntiAlias Whether or not to anti-alias.
  488. * @param theDomain Optional vector of Doubles specifying the domain.
  489. * @param theMatrix ArrayList of Doubles specifying the matrix.
  490. * If it's a pattern, then the matrix maps it to pattern space.
  491. * If it's a shading, then it maps it to current user space.
  492. * It's optional, the default is the identity matrix
  493. * @param theFunction The PDF Function that maps an (x,y) location to a color
  494. */
  495. public PDFShading makeShading(PDFResourceContext res, int theShadingType,
  496. PDFColorSpace theColorSpace,
  497. ArrayList theBackground, ArrayList theBBox,
  498. boolean theAntiAlias, ArrayList theDomain,
  499. ArrayList theMatrix,
  500. PDFFunction theFunction) {
  501. // make Shading of Type 1
  502. String theShadingName = new String("Sh" + (++this.shadingCount));
  503. PDFShading shading = new PDFShading(++this.objectcount,
  504. theShadingName, theShadingType,
  505. theColorSpace, theBackground,
  506. theBBox, theAntiAlias, theDomain,
  507. theMatrix, theFunction);
  508. PDFShading oldshad = findShading(shading);
  509. if (oldshad == null) {
  510. shadings.add(shading);
  511. this.objects.add(shading);
  512. } else {
  513. this.objectcount--;
  514. this.shadingCount--;
  515. shading = oldshad;
  516. }
  517. // add this shading to resources
  518. if (res != null) {
  519. res.getPDFResources().addShading(shading);
  520. } else {
  521. this.resources.addShading(shading);
  522. }
  523. return (shading);
  524. }
  525. /**
  526. * Make an axial or radial shading object.
  527. *
  528. * @param theShadingType 2 or 3 for axial or radial shading
  529. * @param theColorSpace "DeviceRGB" or similar.
  530. * @param theBackground theBackground An array of color components appropriate to the
  531. * colorspace key specifying a single color value.
  532. * This key is used by the f operator buy ignored by the sh operator.
  533. * @param theBBox ArrayList of double's representing a rectangle
  534. * in the coordinate space that is current at the
  535. * time of shading is imaged. Temporary clipping
  536. * boundary.
  537. * @param theAntiAlias Default is false
  538. * @param theCoords ArrayList of four (type 2) or 6 (type 3) Double
  539. * @param theDomain ArrayList of Doubles specifying the domain
  540. * @param theFunction the Stitching (PDFfunction type 3) function,
  541. * even if it's stitching a single function
  542. * @param theExtend ArrayList of Booleans of whether to extend the
  543. * start and end colors past the start and end points
  544. * The default is [false, false]
  545. */
  546. public PDFShading makeShading(PDFResourceContext res, int theShadingType,
  547. PDFColorSpace theColorSpace,
  548. ArrayList theBackground, ArrayList theBBox,
  549. boolean theAntiAlias, ArrayList theCoords,
  550. ArrayList theDomain, PDFFunction theFunction,
  551. ArrayList theExtend) {
  552. // make Shading of Type 2 or 3
  553. String theShadingName = new String("Sh" + (++this.shadingCount));
  554. PDFShading shading = new PDFShading(++this.objectcount,
  555. theShadingName, theShadingType,
  556. theColorSpace, theBackground,
  557. theBBox, theAntiAlias, theCoords,
  558. theDomain, theFunction,
  559. theExtend);
  560. PDFShading oldshad = findShading(shading);
  561. if (oldshad == null) {
  562. shadings.add(shading);
  563. this.objects.add(shading);
  564. } else {
  565. this.objectcount--;
  566. this.shadingCount--;
  567. shading = oldshad;
  568. }
  569. if (res != null) {
  570. res.getPDFResources().addShading(shading);
  571. } else {
  572. this.resources.addShading(shading);
  573. }
  574. return (shading);
  575. }
  576. /**
  577. * Make a free-form gouraud shaded triangle mesh, coons patch mesh, or tensor patch mesh
  578. * shading object
  579. *
  580. * @param theShadingType 4, 6, or 7 depending on whether it's
  581. * Free-form gouraud-shaded triangle meshes, coons patch meshes,
  582. * or tensor product patch meshes, respectively.
  583. * @param theColorSpace "DeviceRGB" or similar.
  584. * @param theBackground theBackground An array of color components appropriate to the
  585. * colorspace key specifying a single color value.
  586. * This key is used by the f operator buy ignored by the sh operator.
  587. * @param theBBox ArrayList of double's representing a rectangle
  588. * in the coordinate space that is current at the
  589. * time of shading is imaged. Temporary clipping
  590. * boundary.
  591. * @param theAntiAlias Default is false
  592. * @param theBitsPerCoordinate 1,2,4,8,12,16,24 or 32.
  593. * @param theBitsPerComponent 1,2,4,8,12, and 16
  594. * @param theBitsPerFlag 2,4,8.
  595. * @param theDecode ArrayList of Doubles see PDF 1.3 spec pages 303 to 312.
  596. * @param theFunction the PDFFunction
  597. */
  598. public PDFShading makeShading(PDFResourceContext res, int theShadingType,
  599. PDFColorSpace theColorSpace,
  600. ArrayList theBackground, ArrayList theBBox,
  601. boolean theAntiAlias,
  602. int theBitsPerCoordinate,
  603. int theBitsPerComponent,
  604. int theBitsPerFlag, ArrayList theDecode,
  605. PDFFunction theFunction) {
  606. // make Shading of type 4,6 or 7
  607. String theShadingName = new String("Sh" + (++this.shadingCount));
  608. PDFShading shading = new PDFShading(++this.objectcount,
  609. theShadingName, theShadingType,
  610. theColorSpace, theBackground,
  611. theBBox, theAntiAlias,
  612. theBitsPerCoordinate,
  613. theBitsPerComponent,
  614. theBitsPerFlag, theDecode,
  615. theFunction);
  616. PDFShading oldshad = findShading(shading);
  617. if (oldshad == null) {
  618. shadings.add(shading);
  619. this.objects.add(shading);
  620. } else {
  621. this.objectcount--;
  622. this.shadingCount--;
  623. shading = oldshad;
  624. }
  625. if (res != null) {
  626. res.getPDFResources().addShading(shading);
  627. } else {
  628. this.resources.addShading(shading);
  629. }
  630. return (shading);
  631. }
  632. /**
  633. * make a Lattice-Form Gouraud mesh shading object
  634. *
  635. * @param theShadingType 5 for lattice-Form Gouraud shaded-triangle mesh
  636. * without spaces. "Shading1" or "Sh1" are good examples.
  637. * @param theColorSpace "DeviceRGB" or similar.
  638. * @param theBackground theBackground An array of color components appropriate to the
  639. * colorspace key specifying a single color value.
  640. * This key is used by the f operator buy ignored by the sh operator.
  641. * @param theBBox ArrayList of double's representing a rectangle
  642. * in the coordinate space that is current at the
  643. * time of shading is imaged. Temporary clipping
  644. * boundary.
  645. * @param theAntiAlias Default is false
  646. * @param theBitsPerCoordinate 1,2,4,8,12,16, 24, or 32
  647. * @param theBitsPerComponent 1,2,4,8,12,24,32
  648. * @param theDecode ArrayList of Doubles. See page 305 in PDF 1.3 spec.
  649. * @param theVerticesPerRow number of vertices in each "row" of the lattice.
  650. * @param theFunction The PDFFunction that's mapped on to this shape
  651. */
  652. public PDFShading makeShading(PDFResourceContext res, int theShadingType,
  653. PDFColorSpace theColorSpace,
  654. ArrayList theBackground, ArrayList theBBox,
  655. boolean theAntiAlias,
  656. int theBitsPerCoordinate,
  657. int theBitsPerComponent, ArrayList theDecode,
  658. int theVerticesPerRow,
  659. PDFFunction theFunction) {
  660. // make shading of Type 5
  661. String theShadingName = new String("Sh" + (++this.shadingCount));
  662. PDFShading shading = new PDFShading(++this.objectcount,
  663. theShadingName, theShadingType,
  664. theColorSpace, theBackground,
  665. theBBox, theAntiAlias,
  666. theBitsPerCoordinate,
  667. theBitsPerComponent, theDecode,
  668. theVerticesPerRow, theFunction);
  669. PDFShading oldshad = findShading(shading);
  670. if (oldshad == null) {
  671. shadings.add(shading);
  672. this.objects.add(shading);
  673. } else {
  674. this.objectcount--;
  675. this.shadingCount--;
  676. shading = oldshad;
  677. }
  678. if (res != null) {
  679. res.getPDFResources().addShading(shading);
  680. } else {
  681. this.resources.addShading(shading);
  682. }
  683. return (shading);
  684. }
  685. /**
  686. * Make a tiling pattern
  687. *
  688. * @param thePatternType the type of pattern, which is 1 for tiling.
  689. * @param theResources the resources associated with this pattern
  690. * @param thePaintType 1 or 2, colored or uncolored.
  691. * @param theTilingType 1, 2, or 3, constant spacing, no distortion, or faster tiling
  692. * @param theBBox ArrayList of Doubles: The pattern cell bounding box
  693. * @param theXStep horizontal spacing
  694. * @param theYStep vertical spacing
  695. * @param theMatrix Optional ArrayList of Doubles transformation matrix
  696. * @param theXUID Optional vector of Integers that uniquely identify the pattern
  697. * @param thePatternDataStream The stream of pattern data to be tiled.
  698. */
  699. public PDFPattern makePattern(PDFResourceContext res, int thePatternType, // 1
  700. PDFResources theResources, int thePaintType, int theTilingType,
  701. ArrayList theBBox, double theXStep,
  702. double theYStep, ArrayList theMatrix,
  703. ArrayList theXUID, StringBuffer thePatternDataStream) {
  704. String thePatternName = new String("Pa" + (++this.patternCount));
  705. // int theNumber, String thePatternName,
  706. // PDFResources theResources
  707. PDFPattern pattern = new PDFPattern(++this.objectcount,
  708. thePatternName, theResources, 1,
  709. thePaintType, theTilingType,
  710. theBBox, theXStep, theYStep,
  711. theMatrix, theXUID,
  712. thePatternDataStream);
  713. PDFPattern oldpatt = findPattern(pattern);
  714. if (oldpatt == null) {
  715. patterns.add(pattern);
  716. this.objects.add(pattern);
  717. } else {
  718. this.objectcount--;
  719. this.patternCount--;
  720. pattern = oldpatt;
  721. }
  722. if (res != null) {
  723. res.getPDFResources().addPattern(pattern);
  724. } else {
  725. this.resources.addPattern(pattern);
  726. }
  727. return (pattern);
  728. }
  729. /**
  730. * Make a smooth shading pattern
  731. *
  732. * @param thePatternType the type of the pattern, which is 2, smooth shading
  733. * @param theShading the PDF Shading object that comprises this pattern
  734. * @param theXUID optional:the extended unique Identifier if used.
  735. * @param theExtGState optional: the extended graphics state, if used.
  736. * @param theMatrix Optional:ArrayList of Doubles that specify the matrix.
  737. */
  738. public PDFPattern makePattern(PDFResourceContext res,
  739. int thePatternType, PDFShading theShading,
  740. ArrayList theXUID, StringBuffer theExtGState,
  741. ArrayList theMatrix) {
  742. String thePatternName = new String("Pa" + (++this.patternCount));
  743. PDFPattern pattern = new PDFPattern(++this.objectcount,
  744. thePatternName, 2, theShading,
  745. theXUID, theExtGState, theMatrix);
  746. PDFPattern oldpatt = findPattern(pattern);
  747. if (oldpatt == null) {
  748. patterns.add(pattern);
  749. this.objects.add(pattern);
  750. } else {
  751. this.objectcount--;
  752. this.patternCount--;
  753. pattern = oldpatt;
  754. }
  755. if (res != null) {
  756. res.getPDFResources().addPattern(pattern);
  757. } else {
  758. this.resources.addPattern(pattern);
  759. }
  760. return (pattern);
  761. }
  762. public int getColorSpace() {
  763. return (this.colorspace.getColorSpace());
  764. }
  765. public void setColorSpace(int theColorspace) {
  766. this.colorspace.setColorSpace(theColorspace);
  767. return;
  768. }
  769. public PDFPattern createGradient(PDFResourceContext res, boolean radial,
  770. PDFColorSpace theColorspace,
  771. ArrayList theColors, ArrayList theBounds,
  772. ArrayList theCoords) {
  773. PDFShading myShad;
  774. PDFFunction myfunky;
  775. PDFFunction myfunc;
  776. ArrayList theCzero;
  777. ArrayList theCone;
  778. PDFPattern myPattern;
  779. PDFColorSpace theColorSpace;
  780. double interpolation = (double)1.000;
  781. ArrayList theFunctions = new ArrayList();
  782. int currentPosition;
  783. int lastPosition = theColors.size() - 1;
  784. // if 5 elements, the penultimate element is 3.
  785. // do not go beyond that, because you always need
  786. // to have a next color when creating the function.
  787. for (currentPosition = 0; currentPosition < lastPosition;
  788. currentPosition++) { // for every consecutive color pair
  789. PDFColor currentColor =
  790. (PDFColor)theColors.get(currentPosition);
  791. PDFColor nextColor = (PDFColor)theColors.get(currentPosition
  792. + 1);
  793. // colorspace must be consistant
  794. if (this.colorspace.getColorSpace()
  795. != currentColor.getColorSpace()) {
  796. currentColor.setColorSpace(this.colorspace.getColorSpace());
  797. }
  798. if (this.colorspace.getColorSpace() != nextColor.getColorSpace()) {
  799. nextColor.setColorSpace(this.colorspace.getColorSpace());
  800. }
  801. theCzero = currentColor.getVector();
  802. theCone = nextColor.getVector();
  803. myfunc = this.makeFunction(2, null, null, theCzero, theCone,
  804. interpolation);
  805. theFunctions.add(myfunc);
  806. } // end of for every consecutive color pair
  807. myfunky = this.makeFunction(3, null, null, theFunctions, theBounds,
  808. null);
  809. if (radial) {
  810. if (theCoords.size() == 6) {
  811. myShad = this.makeShading(res, 3, this.colorspace, null, null,
  812. false, theCoords, null, myfunky,
  813. null);
  814. } else { // if the center x, center y, and radius specifiy
  815. // the gradient, then assume the same center x, center y,
  816. // and radius of zero for the other necessary component
  817. ArrayList newCoords = new ArrayList();
  818. newCoords.add(theCoords.get(0));
  819. newCoords.add(theCoords.get(1));
  820. newCoords.add(theCoords.get(2));
  821. newCoords.add(theCoords.get(0));
  822. newCoords.add(theCoords.get(1));
  823. newCoords.add(new Double(0.0));
  824. myShad = this.makeShading(res, 3, this.colorspace, null, null,
  825. false, newCoords, null, myfunky,
  826. null);
  827. }
  828. } else {
  829. myShad = this.makeShading(res, 2, this.colorspace, null, null, false,
  830. theCoords, null, myfunky, null);
  831. }
  832. myPattern = this.makePattern(res, 2, myShad, null, null, null);
  833. return (myPattern);
  834. }
  835. /**
  836. * make a /Encoding object
  837. *
  838. * @param encodingName character encoding scheme name
  839. * @return the created /Encoding object
  840. */
  841. public PDFEncoding makeEncoding(String encodingName) {
  842. /*
  843. * create a PDFEncoding with the next object number and add to the
  844. * list of objects
  845. */
  846. PDFEncoding encoding = new PDFEncoding(++this.objectcount,
  847. encodingName);
  848. this.objects.add(encoding);
  849. return encoding;
  850. }
  851. /**
  852. * Create a PDFICCStream
  853. @see PDFXObject
  854. @see org.apache.fop.image.JpegImage
  855. @see org.apache.fop.pdf.PDFColorSpace
  856. */
  857. public PDFICCStream makePDFICCStream() {
  858. PDFICCStream iccStream = new PDFICCStream(++this.objectcount);
  859. this.objects.add(iccStream);
  860. return iccStream;
  861. }
  862. /**
  863. * Get the font map for this document.
  864. *
  865. * @return the map of fonts used in this document
  866. */
  867. public HashMap getFontMap() {
  868. return fontMap;
  869. }
  870. /**
  871. * make a Type1 /Font object
  872. *
  873. * @param fontname internal name to use for this font (eg "F1")
  874. * @param basefont name of the base font (eg "Helvetica")
  875. * @param encoding character encoding scheme used by the font
  876. * @param metrics additional information about the font
  877. * @param descriptor additional information about the font
  878. * @return the created /Font object
  879. */
  880. public PDFFont makeFont(String fontname, String basefont,
  881. String encoding, FontMetric metrics,
  882. FontDescriptor descriptor) {
  883. if (fontMap.containsKey(fontname)) {
  884. return (PDFFont)fontMap.get(fontname);
  885. }
  886. /*
  887. * create a PDFFont with the next object number and add to the
  888. * list of objects
  889. */
  890. if (descriptor == null) {
  891. PDFFont font = new PDFFont(++this.objectcount, fontname,
  892. PDFFont.TYPE1, basefont, encoding);
  893. this.objects.add(font);
  894. fontMap.put(basefont, font);
  895. return font;
  896. } else {
  897. byte subtype = PDFFont.TYPE1;
  898. if (metrics instanceof org.apache.fop.render.pdf.Font) {
  899. subtype =
  900. ((org.apache.fop.render.pdf.Font)metrics).getSubType();
  901. }
  902. PDFFontDescriptor pdfdesc = makeFontDescriptor(descriptor,
  903. subtype);
  904. PDFFontNonBase14 font = null;
  905. if (subtype == PDFFont.TYPE0) {
  906. /*
  907. * Temporary commented out - customized CMaps
  908. * isn't needed until /ToUnicode support is added
  909. * PDFCMap cmap = new PDFCMap(++this.objectcount,
  910. * "fop-ucs-H",
  911. * new PDFCIDSystemInfo("Adobe",
  912. * "Identity",
  913. * 0));
  914. * cmap.addContents();
  915. * this.objects.add(cmap);
  916. */
  917. font =
  918. (PDFFontNonBase14)PDFFont.createFont(++this.objectcount,
  919. fontname, subtype,
  920. basefont,
  921. "Identity-H");
  922. } else {
  923. font =
  924. (PDFFontNonBase14)PDFFont.createFont(++this.objectcount,
  925. fontname, subtype,
  926. basefont, encoding);
  927. }
  928. this.objects.add(font);
  929. font.setDescriptor(pdfdesc);
  930. if (subtype == PDFFont.TYPE0) {
  931. CIDFont cidMetrics;
  932. if (metrics instanceof LazyFont) {
  933. cidMetrics = (CIDFont) ((LazyFont) metrics).getRealFont();
  934. } else {
  935. cidMetrics = (CIDFont)metrics;
  936. }
  937. PDFCIDSystemInfo sysInfo =
  938. new PDFCIDSystemInfo(cidMetrics.getRegistry(),
  939. cidMetrics.getOrdering(),
  940. cidMetrics.getSupplement());
  941. PDFCIDFont cidFont =
  942. new PDFCIDFont(++this.objectcount, basefont,
  943. cidMetrics.getCidType(),
  944. cidMetrics.getDefaultWidth(),
  945. cidMetrics.getWidths(), sysInfo,
  946. (PDFCIDFontDescriptor)pdfdesc);
  947. this.objects.add(cidFont);
  948. ((PDFFontType0)font).setDescendantFonts(cidFont);
  949. } else {
  950. font.setWidthMetrics(metrics.getFirstChar(),
  951. metrics.getLastChar(),
  952. makeArray(metrics.getWidths(1)));
  953. }
  954. fontMap.put(basefont, font);
  955. return font;
  956. }
  957. }
  958. /**
  959. * make a /FontDescriptor object
  960. */
  961. public PDFFontDescriptor makeFontDescriptor(FontDescriptor desc,
  962. byte subtype) {
  963. PDFFontDescriptor font = null;
  964. if (subtype == PDFFont.TYPE0) {
  965. // CID Font
  966. font = new PDFCIDFontDescriptor(++this.objectcount,
  967. desc.fontName(),
  968. desc.getFontBBox(),
  969. desc.getCapHeight(), desc.getFlags(),
  970. desc.getItalicAngle(),
  971. desc.getStemV(), null);
  972. } else {
  973. // Create normal FontDescriptor
  974. font = new PDFFontDescriptor(++this.objectcount, desc.fontName(),
  975. desc.getAscender(),
  976. desc.getDescender(),
  977. desc.getCapHeight(),
  978. desc.getFlags(),
  979. new PDFRectangle(desc.getFontBBox()),
  980. desc.getStemV(),
  981. desc.getItalicAngle());
  982. }
  983. this.objects.add(font);
  984. // Check if the font is embeddable
  985. if (desc.isEmbeddable()) {
  986. PDFStream stream = desc.getFontFile(this.objectcount + 1);
  987. if (stream != null) {
  988. this.objectcount++;
  989. font.setFontFile(desc.getSubType(), stream);
  990. this.objects.add(stream);
  991. }
  992. }
  993. return font;
  994. }
  995. /**
  996. * make an Array object (ex. Widths array for a font)
  997. */
  998. public PDFArray makeArray(int[] values) {
  999. PDFArray array = new PDFArray(++this.objectcount, values);
  1000. this.objects.add(array);
  1001. return array;
  1002. }
  1003. /**
  1004. * make an ExtGState for extra graphics options
  1005. */
  1006. public PDFGState makeGState(HashMap settings, PDFGState current) {
  1007. // try to locate a gstate that has all the settings
  1008. // or will inherit from the current gstate
  1009. // compare "DEFAULT + settings" with "current + each gstate"
  1010. PDFGState wanted = new PDFGState(0);
  1011. wanted.addValues(PDFGState.DEFAULT);
  1012. wanted.addValues(settings);
  1013. PDFGState poss;
  1014. for (Iterator iter = gstates.iterator(); iter.hasNext();) {
  1015. PDFGState avail = (PDFGState)iter.next();
  1016. poss = new PDFGState(0);
  1017. poss.addValues(current);
  1018. poss.addValues(avail);
  1019. if (poss.equals(wanted)) {
  1020. return avail;
  1021. }
  1022. }
  1023. PDFGState gstate = new PDFGState(++this.objectcount);
  1024. gstate.addValues(settings);
  1025. this.objects.add(gstate);
  1026. gstates.add(gstate);
  1027. return gstate;
  1028. }
  1029. public PDFXObject getImage(String key) {
  1030. PDFXObject xObject = (PDFXObject)xObjectsMap.get(key);
  1031. return xObject;
  1032. }
  1033. public PDFXObject addImage(PDFResourceContext res, PDFImage img) {
  1034. // check if already created
  1035. String key = img.getKey();
  1036. PDFXObject xObject = (PDFXObject)xObjectsMap.get(key);
  1037. if (xObject != null) {
  1038. if (res != null) {
  1039. res.getPDFResources().addXObject(xObject);
  1040. }
  1041. return xObject;
  1042. }
  1043. // setup image
  1044. img.setup(this);
  1045. // create a new XObject
  1046. xObject = new PDFXObject(++this.objectcount, ++this.xObjectCount,
  1047. img);
  1048. this.objects.add(xObject);
  1049. this.resources.addXObject(xObject);
  1050. if (res != null) {
  1051. res.getPDFResources().addXObject(xObject);
  1052. }
  1053. this.xObjectsMap.put(key, xObject);
  1054. return xObject;
  1055. }
  1056. /**
  1057. * make a /Page object
  1058. *
  1059. * @param resources resources object to use
  1060. * @param contents stream object with content
  1061. * @param pagewidth width of the page in points
  1062. * @param pageheight height of the page in points
  1063. *
  1064. * @return the created /Page object
  1065. */
  1066. public PDFPage makePage(PDFResources resources,
  1067. int pagewidth, int pageheight) {
  1068. /*
  1069. * create a PDFPage with the next object number, the given
  1070. * resources, contents and dimensions
  1071. */
  1072. PDFPage page = new PDFPage(this, ++this.objectcount, resources,
  1073. pagewidth, pageheight);
  1074. /* add it to the list of objects */
  1075. pages.addPage(page);
  1076. return page;
  1077. }
  1078. public void addPage(PDFPage page) {
  1079. /* add it to the list of objects */
  1080. this.objects.add(page);
  1081. }
  1082. /**
  1083. * make a link object
  1084. *
  1085. * @param rect the clickable rectangle
  1086. * @param destination the destination file
  1087. * @param linkType the link type
  1088. * @return the PDFLink object created
  1089. */
  1090. public PDFLink makeLink(Rectangle2D rect, String destination,
  1091. int linkType) {
  1092. PDFLink linkObject;
  1093. PDFAction action;
  1094. int index;
  1095. PDFLink link = new PDFLink(++this.objectcount, rect);
  1096. this.objects.add(link);
  1097. if (linkType == PDFLink.EXTERNAL) {
  1098. // check destination
  1099. if (destination.endsWith(".pdf")) { // FileSpec
  1100. PDFFileSpec fileSpec = new PDFFileSpec(++this.objectcount,
  1101. destination);
  1102. this.objects.add(fileSpec);
  1103. action = new PDFGoToRemote(++this.objectcount, fileSpec);
  1104. this.objects.add(action);
  1105. link.setAction(action);
  1106. } else if ((index = destination.indexOf(".pdf#page=")) > 0) {
  1107. String file = destination.substring(0, index + 4);
  1108. int page = Integer.parseInt(destination.substring(index + 10));
  1109. PDFFileSpec fileSpec = new PDFFileSpec(++this.objectcount, file);
  1110. this.objects.add(fileSpec);
  1111. action = new PDFGoToRemote(++this.objectcount, fileSpec, page);
  1112. this.objects.add(action);
  1113. link.setAction(action);
  1114. } else if ((index = destination.indexOf(".pdf#dest=")) > 0) {
  1115. String file = destination.substring(0, index + 4);
  1116. String dest = destination.substring(index + 10);
  1117. PDFFileSpec fileSpec = new PDFFileSpec(++this.objectcount, file);
  1118. this.objects.add(fileSpec);
  1119. action = new PDFGoToRemote(++this.objectcount, fileSpec, dest);
  1120. this.objects.add(action);
  1121. link.setAction(action);
  1122. } else { // URI
  1123. PDFUri uri = new PDFUri(destination);
  1124. link.setAction(uri);
  1125. }
  1126. } else { // linkType is internal
  1127. String goToReference = getGoToReference(destination);
  1128. PDFInternalLink internalLink = new PDFInternalLink(goToReference);
  1129. link.setAction(internalLink);
  1130. }
  1131. return link;
  1132. }
  1133. private String getGoToReference(String destination) {
  1134. String goToReference = null;
  1135. PDFGoTo gt = new PDFGoTo(++this.objectcount, destination);
  1136. goToReference = gt.referencePDF();
  1137. addTrailerObject(gt);
  1138. return goToReference;
  1139. }
  1140. public void addTrailerObject(PDFObject object) {
  1141. this.trailerObjects.add(object);
  1142. }
  1143. public PDFLink makeLink(Rectangle2D rect, String page, String dest) {
  1144. PDFLink link = new PDFLink(++this.objectcount, rect);
  1145. this.objects.add(link);
  1146. PDFGoTo gt = new PDFGoTo(++this.objectcount, page);
  1147. gt.setDestination(dest);
  1148. addTrailerObject(gt);
  1149. PDFInternalLink internalLink = new PDFInternalLink(gt.referencePDF());
  1150. link.setAction(internalLink);
  1151. return link;
  1152. }
  1153. /**
  1154. Ensure there is room in the locations xref for the number of
  1155. objects that have been created.
  1156. */
  1157. private void prepareLocations() {
  1158. while (location.size() < objectcount) {
  1159. location.add(LOCATION_PLACEHOLDER);
  1160. }
  1161. }
  1162. /**
  1163. * make a stream object
  1164. *
  1165. * @return the stream object created
  1166. */
  1167. public PDFStream makeStream(String type, boolean add) {
  1168. /*
  1169. * create a PDFStream with the next object number and add it
  1170. *
  1171. * to the list of objects
  1172. */
  1173. PDFStream obj = new PDFStream(++this.objectcount);
  1174. obj.addDefaultFilters(filterMap, type);
  1175. if (add) {
  1176. this.objects.add(obj);
  1177. }
  1178. return obj;
  1179. }
  1180. /**
  1181. * add a stream object
  1182. *
  1183. * @param obj the PDF Stream to add to this document
  1184. */
  1185. public void addStream(PDFStream obj) {
  1186. this.objects.add(obj);
  1187. }
  1188. /**
  1189. * make an annotation list object
  1190. *
  1191. * @return the annotation list object created
  1192. */
  1193. public PDFAnnotList makeAnnotList() {
  1194. /*
  1195. * create a PDFAnnotList with the next object number and add it
  1196. * to the list of objects
  1197. */
  1198. PDFAnnotList obj = new PDFAnnotList(++this.objectcount);
  1199. this.objects.add(obj);
  1200. return obj;
  1201. }
  1202. /**
  1203. * Get the root Outlines object. This method does not write
  1204. * the outline to the PDF document, it simply creates a
  1205. * reference for later.
  1206. *
  1207. * @return the PDF Outline root object
  1208. */
  1209. public PDFOutline getOutlineRoot() {
  1210. if (outlineRoot != null) {
  1211. return outlineRoot;
  1212. }
  1213. outlineRoot = new PDFOutline(++this.objectcount, null, null);
  1214. addTrailerObject(outlineRoot);
  1215. root.setRootOutline(outlineRoot);
  1216. return outlineRoot;
  1217. }
  1218. /**
  1219. * Make an outline object and add it to the given outline
  1220. *
  1221. * @param parent parent PDFOutline object which may be null
  1222. * @param label the title for the new outline object
  1223. * @param destination the reference string for the action to go to
  1224. * @return the new PDF outline object
  1225. */
  1226. public PDFOutline makeOutline(PDFOutline parent, String label,
  1227. String destination) {
  1228. String goToRef = getGoToReference(destination);
  1229. PDFOutline obj = new PDFOutline(++this.objectcount, label, goToRef);
  1230. if (parent != null) {
  1231. parent.addOutline(obj);
  1232. }
  1233. this.objects.add(obj);
  1234. return obj;
  1235. }
  1236. /**
  1237. * get the /Resources object for the document
  1238. *
  1239. * @return the /Resources object
  1240. */
  1241. public PDFResources getResources() {
  1242. return this.resources;
  1243. }
  1244. /**
  1245. * write the entire document out
  1246. *
  1247. * @param stream the OutputStream to output the document to
  1248. * @throws IOException if there is an exception writing to the output stream
  1249. */
  1250. public void output(OutputStream stream) throws IOException {
  1251. prepareLocations();
  1252. for (int count = 0; count < this.objects.size(); count++) {
  1253. /* retrieve the object with the current number */
  1254. PDFObject object = (PDFObject)this.objects.get(count);
  1255. /*
  1256. * add the position of this object to the list of object
  1257. * locations
  1258. */
  1259. location.set(object.getNumber() - 1,
  1260. new Integer(this.position));
  1261. /*
  1262. * output the object and increment the character position
  1263. * by the object's length
  1264. */
  1265. this.position += object.output(stream);
  1266. }
  1267. this.objects.clear();
  1268. }
  1269. /**
  1270. * write the PDF header <P>
  1271. *
  1272. * This method must be called prior to formatting
  1273. * and outputting AreaTrees.
  1274. *
  1275. * @param stream the OutputStream to write the header to
  1276. * @throws IOException if there is an exception writing to the output stream
  1277. */
  1278. public void outputHeader(OutputStream stream)
  1279. throws IOException {
  1280. this.position = 0;
  1281. byte[] pdf = ("%PDF-" + PDF_VERSION + "\n").getBytes();
  1282. stream.write(pdf);
  1283. this.position += pdf.length;
  1284. // output a binary comment as recommended by the PDF spec (3.4.1)
  1285. byte[] bin = {
  1286. (byte)'%', (byte)0xAA, (byte)0xAB, (byte)0xAC, (byte)0xAD,
  1287. (byte)'\n'
  1288. };
  1289. stream.write(bin);
  1290. this.position += bin.length;
  1291. }
  1292. /**
  1293. * write the trailer
  1294. *
  1295. * @param stream the OutputStream to write the trailer to
  1296. * @throws IOException if there is an exception writing to the output stream
  1297. */
  1298. public void outputTrailer(OutputStream stream)
  1299. throws IOException {
  1300. output(stream);
  1301. for (int count = 0; count < trailerObjects.size(); count++) {
  1302. PDFObject o = (PDFObject) trailerObjects.get(count);
  1303. this.location.set(o.getNumber() - 1,
  1304. new Integer(this.position));
  1305. this.position += o.output(stream);
  1306. }
  1307. /* output the xref table and increment the character position
  1308. by the table's length */
  1309. this.position += outputXref(stream);
  1310. /* construct the trailer */
  1311. String pdf = "trailer\n" + "<<\n"
  1312. + "/Size " + (this.objectcount + 1) + "\n"
  1313. + "/Root " + this.root.number + " "
  1314. + this.root.generation + " R\n" + "/Info "
  1315. + this.info.number + " " + this.info.generation
  1316. + " R\n" + ">>\n" + "startxref\n" + this.xref
  1317. + "\n" + "%%EOF\n";
  1318. /* write the trailer */
  1319. stream.write(pdf.getBytes());
  1320. }
  1321. /**
  1322. * write the xref table
  1323. *
  1324. * @param stream the OutputStream to write the xref table to
  1325. * @return the number of characters written
  1326. */
  1327. private int outputXref(OutputStream stream) throws IOException {
  1328. /* remember position of xref table */
  1329. this.xref = this.position;
  1330. /* construct initial part of xref */
  1331. StringBuffer pdf = new StringBuffer("xref\n0 "
  1332. + (this.objectcount + 1)
  1333. + "\n0000000000 65535 f \n");
  1334. for (int count = 0; count < this.location.size(); count++) {
  1335. String x = this.location.get(count).toString();
  1336. /* contruct xref entry for object */
  1337. String padding = "0000000000";
  1338. String loc = padding.substring(x.length()) + x;
  1339. /* append to xref table */
  1340. pdf = pdf.append(loc + " 00000 n \n");
  1341. }
  1342. /* write the xref table and return the character length */
  1343. byte[] pdfBytes = pdf.toString().getBytes();
  1344. stream.write(pdfBytes);
  1345. return pdfBytes.length;
  1346. }
  1347. }