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.

PDFRenderer.java 46KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281
  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.render.pdf;
  19. // Java
  20. import java.awt.Color;
  21. import java.awt.Point;
  22. import java.awt.Rectangle;
  23. import java.awt.geom.AffineTransform;
  24. import java.awt.geom.Point2D;
  25. import java.awt.geom.Rectangle2D;
  26. import java.io.FileNotFoundException;
  27. import java.io.IOException;
  28. import java.io.OutputStream;
  29. import java.util.Iterator;
  30. import java.util.List;
  31. import java.util.Map;
  32. import org.apache.xmlgraphics.image.loader.ImageException;
  33. import org.apache.xmlgraphics.image.loader.ImageFlavor;
  34. import org.apache.xmlgraphics.image.loader.ImageInfo;
  35. import org.apache.xmlgraphics.image.loader.ImageManager;
  36. import org.apache.xmlgraphics.image.loader.ImageSessionContext;
  37. import org.apache.xmlgraphics.image.loader.util.ImageUtil;
  38. import org.apache.fop.apps.FOPException;
  39. import org.apache.fop.apps.FOUserAgent;
  40. import org.apache.fop.apps.MimeConstants;
  41. import org.apache.fop.area.Area;
  42. import org.apache.fop.area.Block;
  43. import org.apache.fop.area.BookmarkData;
  44. import org.apache.fop.area.CTM;
  45. import org.apache.fop.area.DestinationData;
  46. import org.apache.fop.area.LineArea;
  47. import org.apache.fop.area.OffDocumentExtensionAttachment;
  48. import org.apache.fop.area.OffDocumentItem;
  49. import org.apache.fop.area.PageSequence;
  50. import org.apache.fop.area.PageViewport;
  51. import org.apache.fop.area.Trait;
  52. import org.apache.fop.area.inline.AbstractTextArea;
  53. import org.apache.fop.area.inline.Image;
  54. import org.apache.fop.area.inline.InlineArea;
  55. import org.apache.fop.area.inline.InlineParent;
  56. import org.apache.fop.area.inline.Leader;
  57. import org.apache.fop.area.inline.SpaceArea;
  58. import org.apache.fop.area.inline.TextArea;
  59. import org.apache.fop.area.inline.WordArea;
  60. import org.apache.fop.datatypes.URISpecification;
  61. import org.apache.fop.events.ResourceEventProducer;
  62. import org.apache.fop.fo.extensions.ExtensionAttachment;
  63. import org.apache.fop.fo.extensions.xmp.XMPMetadata;
  64. import org.apache.fop.fonts.Font;
  65. import org.apache.fop.fonts.LazyFont;
  66. import org.apache.fop.fonts.SingleByteFont;
  67. import org.apache.fop.fonts.Typeface;
  68. import org.apache.fop.pdf.PDFAMode;
  69. import org.apache.fop.pdf.PDFAction;
  70. import org.apache.fop.pdf.PDFAnnotList;
  71. import org.apache.fop.pdf.PDFDocument;
  72. import org.apache.fop.pdf.PDFEncryptionParams;
  73. import org.apache.fop.pdf.PDFFactory;
  74. import org.apache.fop.pdf.PDFGoTo;
  75. import org.apache.fop.pdf.PDFInfo;
  76. import org.apache.fop.pdf.PDFLink;
  77. import org.apache.fop.pdf.PDFNumber;
  78. import org.apache.fop.pdf.PDFOutline;
  79. import org.apache.fop.pdf.PDFPage;
  80. import org.apache.fop.pdf.PDFResourceContext;
  81. import org.apache.fop.pdf.PDFResources;
  82. import org.apache.fop.pdf.PDFState;
  83. import org.apache.fop.pdf.PDFTextUtil;
  84. import org.apache.fop.pdf.PDFXMode;
  85. import org.apache.fop.pdf.PDFXObject;
  86. import org.apache.fop.render.AbstractPathOrientedRenderer;
  87. import org.apache.fop.render.Graphics2DAdapter;
  88. import org.apache.fop.render.RendererContext;
  89. import org.apache.fop.traits.RuleStyle;
  90. import org.apache.fop.util.CharUtilities;
  91. import org.apache.fop.util.ColorUtil;
  92. /**
  93. * Renderer that renders areas to PDF.
  94. */
  95. public class PDFRenderer extends AbstractPathOrientedRenderer implements PDFConfigurationConstants {
  96. /** The MIME type for PDF */
  97. public static final String MIME_TYPE = MimeConstants.MIME_PDF;
  98. /** Normal PDF resolution (72dpi) */
  99. public static final int NORMAL_PDF_RESOLUTION = 72;
  100. /** Controls whether comments are written to the PDF stream. */
  101. protected static final boolean WRITE_COMMENTS = true;
  102. /**
  103. * the PDF Document being created
  104. */
  105. protected PDFDocument pdfDoc;
  106. /**
  107. * Utility class which enables all sorts of features that are not directly connected to the
  108. * normal rendering process.
  109. */
  110. protected PDFRenderingUtil pdfUtil;
  111. /**
  112. * Map of pages using the PageViewport as the key
  113. * this is used for prepared pages that cannot be immediately
  114. * rendered
  115. */
  116. protected Map pages = null;
  117. /**
  118. * Maps unique PageViewport key to PDF page reference
  119. */
  120. protected Map pageReferences = new java.util.HashMap();
  121. /**
  122. * Maps unique PageViewport key back to PageViewport itself
  123. */
  124. //protected Map pvReferences = new java.util.HashMap();
  125. /**
  126. * Maps XSL-FO element IDs to their on-page XY-positions
  127. * Must be used in conjunction with the page reference to fully specify the PDFGoTo details
  128. */
  129. protected Map idPositions = new java.util.HashMap();
  130. /**
  131. * Maps XSL-FO element IDs to PDFGoTo objects targeting the corresponding areas
  132. * These objects may not all be fully filled in yet
  133. */
  134. protected Map idGoTos = new java.util.HashMap();
  135. /**
  136. * The PDFGoTos in idGoTos that are not complete yet
  137. */
  138. protected List unfinishedGoTos = new java.util.ArrayList();
  139. // can't use a Set because PDFGoTo.equals returns true if the target is the same,
  140. // even if the object number differs
  141. /**
  142. * The output stream to write the document to
  143. */
  144. protected OutputStream ostream;
  145. /**
  146. * the /Resources object of the PDF document being created
  147. */
  148. protected PDFResources pdfResources;
  149. /** The current content generator to produce PDF commands with */
  150. protected PDFContentGenerator generator;
  151. private PDFBorderPainter borderPainter;
  152. /**
  153. * the current annotation list to add annotations to
  154. */
  155. protected PDFResourceContext currentContext = null;
  156. /**
  157. * the current page to add annotations to
  158. */
  159. protected PDFPage currentPage;
  160. /**
  161. * the current page's PDF reference string (to avoid numerous function calls)
  162. */
  163. protected String currentPageRef;
  164. /** page height */
  165. protected int pageHeight;
  166. /** Image handler registry */
  167. private PDFImageHandlerRegistry imageHandlerRegistry = new PDFImageHandlerRegistry();
  168. /**
  169. * create the PDF renderer
  170. */
  171. public PDFRenderer() {
  172. }
  173. /** {@inheritDoc} */
  174. public void setUserAgent(FOUserAgent agent) {
  175. super.setUserAgent(agent);
  176. this.pdfUtil = new PDFRenderingUtil(getUserAgent());
  177. }
  178. PDFRenderingUtil getPDFUtil() {
  179. return this.pdfUtil;
  180. }
  181. PDFContentGenerator getGenerator() {
  182. return this.generator;
  183. }
  184. PDFState getState() {
  185. return getGenerator().getState();
  186. }
  187. /** {@inheritDoc} */
  188. public void startRenderer(OutputStream stream) throws IOException {
  189. if (userAgent == null) {
  190. throw new IllegalStateException("UserAgent must be set before starting the renderer");
  191. }
  192. ostream = stream;
  193. this.pdfDoc = pdfUtil.setupPDFDocument(stream);
  194. }
  195. /**
  196. * Checks if there are any unfinished PDFGoTos left in the list and resolves them
  197. * to a default position on the page. Logs a warning, as this should not happen.
  198. */
  199. protected void finishOpenGoTos() {
  200. int count = unfinishedGoTos.size();
  201. if (count > 0) {
  202. // TODO : page height may not be the same for all targeted pages
  203. Point2D.Float defaultPos = new Point2D.Float(0f, pageHeight / 1000f); // top-o-page
  204. while (!unfinishedGoTos.isEmpty()) {
  205. PDFGoTo gt = (PDFGoTo) unfinishedGoTos.get(0);
  206. finishIDGoTo(gt, defaultPos);
  207. }
  208. PDFEventProducer eventProducer = PDFEventProducer.Provider.get(
  209. getUserAgent().getEventBroadcaster());
  210. eventProducer.nonFullyResolvedLinkTargets(this, count);
  211. // dysfunctional if pageref is null
  212. }
  213. }
  214. /** {@inheritDoc} */
  215. public void stopRenderer() throws IOException {
  216. finishOpenGoTos();
  217. pdfDoc.getResources().addFonts(pdfDoc, fontInfo);
  218. pdfDoc.outputTrailer(ostream);
  219. this.pdfDoc = null;
  220. ostream = null;
  221. pages = null;
  222. pageReferences.clear();
  223. //pvReferences.clear();
  224. pdfResources = null;
  225. this.generator = null;
  226. //currentStream = null;
  227. currentContext = null;
  228. currentPage = null;
  229. //currentState = null;
  230. //this.textutil = null;
  231. idPositions.clear();
  232. idGoTos.clear();
  233. }
  234. /**
  235. * {@inheritDoc}
  236. */
  237. public boolean supportsOutOfOrder() {
  238. //return false;
  239. return true;
  240. }
  241. /**
  242. * {@inheritDoc}
  243. */
  244. public void processOffDocumentItem(OffDocumentItem odi) {
  245. if (odi instanceof DestinationData) {
  246. // render Destinations
  247. renderDestination((DestinationData) odi);
  248. } else if (odi instanceof BookmarkData) {
  249. // render Bookmark-Tree
  250. renderBookmarkTree((BookmarkData) odi);
  251. } else if (odi instanceof OffDocumentExtensionAttachment) {
  252. ExtensionAttachment attachment = ((OffDocumentExtensionAttachment)odi).getAttachment();
  253. if (XMPMetadata.CATEGORY.equals(attachment.getCategory())) {
  254. pdfUtil.renderXMPMetadata((XMPMetadata)attachment);
  255. }
  256. }
  257. }
  258. private void renderDestination(DestinationData dd) {
  259. String targetID = dd.getIDRef();
  260. if (targetID == null || targetID.length() == 0) {
  261. throw new IllegalArgumentException("DestinationData must contain a ID reference");
  262. }
  263. PageViewport pv = dd.getPageViewport();
  264. if (pv != null) {
  265. PDFGoTo gt = getPDFGoToForID(targetID, pv.getKey());
  266. pdfDoc.getFactory().makeDestination(
  267. dd.getIDRef(), gt.makeReference());
  268. } else {
  269. //Warning already issued by AreaTreeHandler (debug level is sufficient)
  270. log.debug("Unresolved destination item received: " + dd.getIDRef());
  271. }
  272. }
  273. /**
  274. * Renders a Bookmark-Tree object
  275. * @param bookmarks the BookmarkData object containing all the Bookmark-Items
  276. */
  277. protected void renderBookmarkTree(BookmarkData bookmarks) {
  278. for (int i = 0; i < bookmarks.getCount(); i++) {
  279. BookmarkData ext = bookmarks.getSubData(i);
  280. renderBookmarkItem(ext, null);
  281. }
  282. }
  283. private void renderBookmarkItem(BookmarkData bookmarkItem,
  284. PDFOutline parentBookmarkItem) {
  285. PDFOutline pdfOutline = null;
  286. String targetID = bookmarkItem.getIDRef();
  287. if (targetID == null || targetID.length() == 0) {
  288. throw new IllegalArgumentException("DestinationData must contain a ID reference");
  289. }
  290. PageViewport pv = bookmarkItem.getPageViewport();
  291. if (pv != null) {
  292. String pvKey = pv.getKey();
  293. PDFGoTo gt = getPDFGoToForID(targetID, pvKey);
  294. // create outline object:
  295. PDFOutline parent = parentBookmarkItem != null
  296. ? parentBookmarkItem
  297. : pdfDoc.getOutlineRoot();
  298. pdfOutline = pdfDoc.getFactory().makeOutline(parent,
  299. bookmarkItem.getBookmarkTitle(), gt, bookmarkItem.showChildItems());
  300. } else {
  301. //Warning already issued by AreaTreeHandler (debug level is sufficient)
  302. log.debug("Bookmark with IDRef \"" + targetID + "\" has a null PageViewport.");
  303. }
  304. for (int i = 0; i < bookmarkItem.getCount(); i++) {
  305. renderBookmarkItem(bookmarkItem.getSubData(i), pdfOutline);
  306. }
  307. }
  308. /** {@inheritDoc} */
  309. public Graphics2DAdapter getGraphics2DAdapter() {
  310. return new PDFGraphics2DAdapter(this);
  311. }
  312. /** {@inheritDoc} */
  313. protected void saveGraphicsState() {
  314. generator.saveGraphicsState();
  315. }
  316. /** {@inheritDoc} */
  317. protected void restoreGraphicsState() {
  318. generator.restoreGraphicsState();
  319. }
  320. /** Indicates the beginning of a text object. */
  321. protected void beginTextObject() {
  322. generator.beginTextObject();
  323. }
  324. /** Indicates the end of a text object. */
  325. protected void endTextObject() {
  326. generator.endTextObject();
  327. }
  328. /**
  329. * Start the next page sequence.
  330. * For the PDF renderer there is no concept of page sequences
  331. * but it uses the first available page sequence title to set
  332. * as the title of the PDF document, and the language of the
  333. * document.
  334. * @param pageSequence the page sequence
  335. */
  336. public void startPageSequence(PageSequence pageSequence) {
  337. super.startPageSequence(pageSequence);
  338. LineArea seqTitle = pageSequence.getTitle();
  339. if (seqTitle != null) {
  340. String str = convertTitleToString(seqTitle);
  341. PDFInfo info = this.pdfDoc.getInfo();
  342. if (info.getTitle() == null) {
  343. info.setTitle(str);
  344. }
  345. }
  346. if (pageSequence.getLanguage() != null) {
  347. String lang = pageSequence.getLanguage();
  348. String country = pageSequence.getCountry();
  349. String langCode = lang + (country != null ? "-" + country : "");
  350. if (pdfDoc.getRoot().getLanguage() == null) {
  351. //Only set if not set already (first non-null is used)
  352. //Note: No checking is performed whether the values are valid!
  353. pdfDoc.getRoot().setLanguage(langCode);
  354. }
  355. }
  356. pdfUtil.generateDefaultXMPMetadata();
  357. }
  358. /**
  359. * The pdf page is prepared by making the page.
  360. * The page is made in the pdf document without any contents
  361. * and then stored to add the contents later.
  362. * The page objects is stored using the area tree PageViewport
  363. * as a key.
  364. *
  365. * @param page the page to prepare
  366. */
  367. public void preparePage(PageViewport page) {
  368. setupPage(page);
  369. if (pages == null) {
  370. pages = new java.util.HashMap();
  371. }
  372. pages.put(page, currentPage);
  373. }
  374. private void setupPage(PageViewport page) {
  375. this.pdfResources = this.pdfDoc.getResources();
  376. Rectangle2D bounds = page.getViewArea();
  377. double w = bounds.getWidth();
  378. double h = bounds.getHeight();
  379. this.currentPage = this.pdfDoc.getFactory().makePage(
  380. this.pdfResources,
  381. (int) Math.round(w / 1000), (int) Math.round(h / 1000),
  382. page.getPageIndex());
  383. pageReferences.put(page.getKey(), currentPage.referencePDF());
  384. //pvReferences.put(page.getKey(), page);
  385. pdfUtil.generatePageLabel(page.getPageIndex(), page.getPageNumberString());
  386. }
  387. /**
  388. * This method creates a PDF stream for the current page
  389. * uses it as the contents of a new page. The page is written
  390. * immediately to the output stream.
  391. * {@inheritDoc}
  392. */
  393. public void renderPage(PageViewport page)
  394. throws IOException, FOPException {
  395. if (pages != null
  396. && (currentPage = (PDFPage) pages.get(page)) != null) {
  397. //Retrieve previously prepared page (out-of-line rendering)
  398. pages.remove(page);
  399. } else {
  400. setupPage(page);
  401. }
  402. currentPageRef = currentPage.referencePDF();
  403. Rectangle2D bounds = page.getViewArea();
  404. double h = bounds.getHeight();
  405. pageHeight = (int) h;
  406. this.generator = new PDFContentGenerator(this.pdfDoc, this.ostream, this.currentPage);
  407. this.borderPainter = new PDFBorderPainter(this.generator);
  408. // Transform the PDF's default coordinate system (0,0 at lower left) to the PDFRenderer's
  409. AffineTransform basicPageTransform = new AffineTransform(1, 0, 0, -1, 0,
  410. pageHeight / 1000f);
  411. generator.concatenate(basicPageTransform);
  412. /*
  413. currentState.concatenate(basicPageTransform);
  414. currentStream.add(CTMHelper.toPDFString(basicPageTransform, false) + " cm\n");
  415. */
  416. super.renderPage(page);
  417. this.pdfDoc.registerObject(generator.getStream());
  418. currentPage.setContents(generator.getStream());
  419. PDFAnnotList annots = currentPage.getAnnotations();
  420. if (annots != null) {
  421. this.pdfDoc.addObject(annots);
  422. }
  423. this.pdfDoc.addObject(currentPage);
  424. this.generator.flushPDFDoc();
  425. this.generator = null;
  426. }
  427. /** {@inheritDoc} */
  428. protected void startVParea(CTM ctm, Rectangle2D clippingRect) {
  429. saveGraphicsState();
  430. // Set the given CTM in the graphics state
  431. /*
  432. currentState.concatenate(
  433. new AffineTransform(CTMHelper.toPDFArray(ctm)));
  434. */
  435. if (clippingRect != null) {
  436. clipRect((float)clippingRect.getX() / 1000f,
  437. (float)clippingRect.getY() / 1000f,
  438. (float)clippingRect.getWidth() / 1000f,
  439. (float)clippingRect.getHeight() / 1000f);
  440. }
  441. // multiply with current CTM
  442. generator.concatenate(new AffineTransform(CTMHelper.toPDFArray(ctm)));
  443. //currentStream.add(CTMHelper.toPDFString(ctm) + " cm\n");
  444. }
  445. /** {@inheritDoc} */
  446. protected void endVParea() {
  447. restoreGraphicsState();
  448. }
  449. /** {@inheritDoc} */
  450. protected void concatenateTransformationMatrix(AffineTransform at) {
  451. generator.concatenate(at);
  452. /*
  453. if (!at.isIdentity()) {
  454. currentState.concatenate(at);
  455. currentStream.add(CTMHelper.toPDFString(at, false) + " cm\n");
  456. }*/
  457. }
  458. /**
  459. * Formats a float value (normally coordinates) as Strings.
  460. * @param value the value
  461. * @return the formatted value
  462. */
  463. protected static String format(float value) {
  464. return PDFNumber.doubleOut(value);
  465. }
  466. /** {@inheritDoc} */
  467. protected void drawBorderLine(float x1, float y1, float x2, float y2,
  468. boolean horz, boolean startOrBefore, int style, Color col) {
  469. PDFBorderPainter.drawBorderLine(generator, x1, y1, x2, y2, horz, startOrBefore, style, col);
  470. }
  471. /** {@inheritDoc} */
  472. protected void clipRect(float x, float y, float width, float height) {
  473. generator.add(format(x) + " " + format(y) + " "
  474. + format(width) + " " + format(height) + " re ");
  475. clip();
  476. }
  477. /**
  478. * Clip an area.
  479. */
  480. protected void clip() {
  481. generator.add("W\n" + "n\n");
  482. }
  483. /**
  484. * Moves the current point to (x, y), omitting any connecting line segment.
  485. * @param x x coordinate
  486. * @param y y coordinate
  487. */
  488. protected void moveTo(float x, float y) {
  489. generator.add(format(x) + " " + format(y) + " m ");
  490. }
  491. /**
  492. * Appends a straight line segment from the current point to (x, y). The
  493. * new current point is (x, y).
  494. * @param x x coordinate
  495. * @param y y coordinate
  496. */
  497. protected void lineTo(float x, float y) {
  498. generator.add(format(x) + " " + format(y) + " l ");
  499. }
  500. /**
  501. * Closes the current subpath by appending a straight line segment from
  502. * the current point to the starting point of the subpath.
  503. */
  504. protected void closePath() {
  505. generator.add("h ");
  506. }
  507. /**
  508. * {@inheritDoc}
  509. */
  510. protected void fillRect(float x, float y, float w, float h) {
  511. if (w != 0 && h != 0) {
  512. generator.add(format(x) + " " + format(y) + " "
  513. + format(w) + " " + format(h) + " re f\n");
  514. }
  515. }
  516. /**
  517. * Draw a line.
  518. *
  519. * @param startx the start x position
  520. * @param starty the start y position
  521. * @param endx the x end position
  522. * @param endy the y end position
  523. */
  524. private void drawLine(float startx, float starty, float endx, float endy) {
  525. generator.add(format(startx) + " " + format(starty) + " m ");
  526. generator.add(format(endx) + " " + format(endy) + " l S\n");
  527. }
  528. /**
  529. * Breaks out of the state stack to handle fixed block-containers.
  530. * @return the saved state stack to recreate later
  531. */
  532. protected List breakOutOfStateStack() {
  533. List breakOutList = new java.util.ArrayList();
  534. PDFState.Data data;
  535. while (true) {
  536. data = getState().getData();
  537. if (getState().pop() == null) {
  538. break;
  539. }
  540. if (breakOutList.size() == 0) {
  541. generator.comment("------ break out!");
  542. }
  543. breakOutList.add(0, data); //Insert because of stack-popping
  544. generator.restoreGraphicsState(false);
  545. }
  546. return breakOutList;
  547. }
  548. /**
  549. * Restores the state stack after a break out.
  550. * @param breakOutList the state stack to restore.
  551. */
  552. protected void restoreStateStackAfterBreakOut(List breakOutList) {
  553. generator.comment("------ restoring context after break-out...");
  554. PDFState.Data data;
  555. Iterator i = breakOutList.iterator();
  556. while (i.hasNext()) {
  557. data = (PDFState.Data)i.next();
  558. saveGraphicsState();
  559. AffineTransform at = data.getTransform();
  560. concatenateTransformationMatrix(at);
  561. //TODO Break-out: Also restore items such as line width and color
  562. //Left out for now because all this painting stuff is very
  563. //inconsistent. Some values go over PDFState, some don't.
  564. }
  565. generator.comment("------ done.");
  566. }
  567. /**
  568. * Returns area's id if it is the first area in the document with that id
  569. * (i.e. if the area qualifies as a link target).
  570. * Otherwise, or if the area has no id, null is returned.
  571. *
  572. * <i>NOTE</i>: area must be on currentPageViewport, otherwise result may be wrong!
  573. *
  574. * @param area the area for which to return the id
  575. * @return the area's id (null if the area has no id or
  576. * other preceding areas have the same id)
  577. */
  578. protected String getTargetableID(Area area) {
  579. String id = (String) area.getTrait(Trait.PROD_ID);
  580. if (id == null || id.length() == 0
  581. || !currentPageViewport.isFirstWithID(id)
  582. || idPositions.containsKey(id)) {
  583. return null;
  584. } else {
  585. return id;
  586. }
  587. }
  588. /**
  589. * Set XY position in the PDFGoTo and add it to the PDF trailer.
  590. *
  591. * @param gt the PDFGoTo object
  592. * @param position the X,Y position to set
  593. */
  594. protected void finishIDGoTo(PDFGoTo gt, Point2D.Float position) {
  595. gt.setPosition(position);
  596. pdfDoc.addTrailerObject(gt);
  597. unfinishedGoTos.remove(gt);
  598. }
  599. /**
  600. * Set page reference and XY position in the PDFGoTo and add it to the PDF trailer.
  601. *
  602. * @param gt the PDFGoTo object
  603. * @param pdfPageRef the PDF reference string of the target page object
  604. * @param position the X,Y position to set
  605. */
  606. protected void finishIDGoTo(PDFGoTo gt, String pdfPageRef, Point2D.Float position) {
  607. gt.setPageReference(pdfPageRef);
  608. finishIDGoTo(gt, position);
  609. }
  610. /**
  611. * Get a PDFGoTo pointing to the given id. Create one if necessary.
  612. * It is possible that the PDFGoTo is not fully resolved yet. In that case
  613. * it must be completed (and added to the PDF trailer) later.
  614. *
  615. * @param targetID the target id of the PDFGoTo
  616. * @param pvKey the unique key of the target PageViewport
  617. *
  618. * @return the PDFGoTo that was found or created
  619. */
  620. protected PDFGoTo getPDFGoToForID(String targetID, String pvKey) {
  621. // Already a PDFGoTo present for this target? If not, create.
  622. PDFGoTo gt = (PDFGoTo) idGoTos.get(targetID);
  623. if (gt == null) {
  624. String pdfPageRef = (String) pageReferences.get(pvKey);
  625. Point2D.Float position = (Point2D.Float) idPositions.get(targetID);
  626. // can the GoTo already be fully filled in?
  627. if (pdfPageRef != null && position != null) {
  628. // getPDFGoTo shares PDFGoTo objects as much as possible.
  629. // It also takes care of assignObjectNumber and addTrailerObject.
  630. gt = pdfDoc.getFactory().getPDFGoTo(pdfPageRef, position);
  631. } else {
  632. // Not complete yet, can't use getPDFGoTo:
  633. gt = new PDFGoTo(pdfPageRef);
  634. pdfDoc.assignObjectNumber(gt);
  635. // pdfDoc.addTrailerObject() will be called later, from finishIDGoTo()
  636. unfinishedGoTos.add(gt);
  637. }
  638. idGoTos.put(targetID, gt);
  639. }
  640. return gt;
  641. }
  642. /**
  643. * Saves id's absolute position on page for later retrieval by PDFGoTos
  644. *
  645. * @param id the id of the area whose position must be saved
  646. * @param pdfPageRef the PDF page reference string
  647. * @param relativeIPP the *relative* IP position in millipoints
  648. * @param relativeBPP the *relative* BP position in millipoints
  649. * @param tf the transformation to apply once the relative positions have been
  650. * converted to points
  651. */
  652. protected void saveAbsolutePosition(String id, String pdfPageRef,
  653. int relativeIPP, int relativeBPP, AffineTransform tf) {
  654. Point2D.Float position = new Point2D.Float(relativeIPP / 1000f, relativeBPP / 1000f);
  655. tf.transform(position, position);
  656. idPositions.put(id, position);
  657. // is there already a PDFGoTo waiting to be completed?
  658. PDFGoTo gt = (PDFGoTo) idGoTos.get(id);
  659. if (gt != null) {
  660. finishIDGoTo(gt, pdfPageRef, position);
  661. }
  662. /*
  663. // The code below auto-creates a named destination for every id in the document.
  664. // This should probably be controlled by a user-configurable setting, as it may
  665. // make the PDF file grow noticeably.
  666. // *** NOT YET WELL-TESTED ! ***
  667. if (true) {
  668. PDFFactory factory = pdfDoc.getFactory();
  669. if (gt == null) {
  670. gt = factory.getPDFGoTo(pdfPageRef, position);
  671. idGoTos.put(id, gt); // so others can pick it up too
  672. }
  673. factory.makeDestination(id, gt.referencePDF(), currentPageViewport);
  674. // Note: using currentPageViewport is only correct if the id is indeed on
  675. // the current PageViewport. But even if incorrect, it won't interfere with
  676. // what gets created in the PDF.
  677. // For speedup, we should also create a lookup map id -> PDFDestination
  678. }
  679. */
  680. }
  681. /**
  682. * Saves id's absolute position on page for later retrieval by PDFGoTos,
  683. * using the currently valid transformation and the currently valid PDF page reference
  684. *
  685. * @param id the id of the area whose position must be saved
  686. * @param relativeIPP the *relative* IP position in millipoints
  687. * @param relativeBPP the *relative* BP position in millipoints
  688. */
  689. protected void saveAbsolutePosition(String id, int relativeIPP, int relativeBPP) {
  690. saveAbsolutePosition(id, currentPageRef,
  691. relativeIPP, relativeBPP, getState().getTransform());
  692. }
  693. /**
  694. * If the given block area is a possible link target, its id + absolute position will
  695. * be saved. The saved position is only correct if this function is called at the very
  696. * start of renderBlock!
  697. *
  698. * @param block the block area in question
  699. */
  700. protected void saveBlockPosIfTargetable(Block block) {
  701. String id = getTargetableID(block);
  702. if (id != null) {
  703. // FIXME: Like elsewhere in the renderer code, absolute and relative
  704. // directions are happily mixed here. This makes sure that the
  705. // links point to the right location, but it is not correct.
  706. int ipp = block.getXOffset();
  707. int bpp = block.getYOffset() + block.getSpaceBefore();
  708. int positioning = block.getPositioning();
  709. if (!(positioning == Block.FIXED || positioning == Block.ABSOLUTE)) {
  710. ipp += currentIPPosition;
  711. bpp += currentBPPosition;
  712. }
  713. AffineTransform tf = positioning == Block.FIXED
  714. ? getState().getBaseTransform()
  715. : getState().getTransform();
  716. saveAbsolutePosition(id, currentPageRef, ipp, bpp, tf);
  717. }
  718. }
  719. /**
  720. * If the given inline area is a possible link target, its id + absolute position will
  721. * be saved. The saved position is only correct if this function is called at the very
  722. * start of renderInlineArea!
  723. *
  724. * @param inlineArea the inline area in question
  725. */
  726. protected void saveInlinePosIfTargetable(InlineArea inlineArea) {
  727. String id = getTargetableID(inlineArea);
  728. if (id != null) {
  729. int extraMarginBefore = 5000; // millipoints
  730. int ipp = currentIPPosition;
  731. int bpp = currentBPPosition + inlineArea.getOffset() - extraMarginBefore;
  732. saveAbsolutePosition(id, ipp, bpp);
  733. }
  734. }
  735. /**
  736. * {@inheritDoc}
  737. */
  738. protected void renderBlock(Block block) {
  739. saveBlockPosIfTargetable(block);
  740. super.renderBlock(block);
  741. }
  742. /** {@inheritDoc} */
  743. protected void renderLineArea(LineArea line) {
  744. super.renderLineArea(line);
  745. }
  746. /**
  747. * {@inheritDoc}
  748. */
  749. protected void renderInlineArea(InlineArea inlineArea) {
  750. saveInlinePosIfTargetable(inlineArea);
  751. super.renderInlineArea(inlineArea);
  752. }
  753. /**
  754. * Render inline parent area.
  755. * For pdf this handles the inline parent area traits such as
  756. * links, border, background.
  757. * @param ip the inline parent area
  758. */
  759. public void renderInlineParent(InlineParent ip) {
  760. boolean annotsAllowed = pdfDoc.getProfile().isAnnotationAllowed();
  761. // stuff we only need if a link must be created:
  762. Rectangle2D ipRect = null;
  763. PDFFactory factory = null;
  764. PDFAction action = null;
  765. if (annotsAllowed) {
  766. // make sure the rect is determined *before* calling super!
  767. int ipp = currentIPPosition;
  768. int bpp = currentBPPosition + ip.getOffset();
  769. ipRect = new Rectangle2D.Float(ipp / 1000f, bpp / 1000f,
  770. ip.getIPD() / 1000f, ip.getBPD() / 1000f);
  771. AffineTransform transform = getState().getTransform();
  772. ipRect = transform.createTransformedShape(ipRect).getBounds2D();
  773. factory = pdfDoc.getFactory();
  774. }
  775. // render contents
  776. super.renderInlineParent(ip);
  777. boolean linkTraitFound = false;
  778. // try INTERNAL_LINK first
  779. Trait.InternalLink intLink = (Trait.InternalLink) ip.getTrait(Trait.INTERNAL_LINK);
  780. if (intLink != null) {
  781. linkTraitFound = true;
  782. String pvKey = intLink.getPVKey();
  783. String idRef = intLink.getIDRef();
  784. boolean pvKeyOK = pvKey != null && pvKey.length() > 0;
  785. boolean idRefOK = idRef != null && idRef.length() > 0;
  786. if (pvKeyOK && idRefOK) {
  787. if (annotsAllowed) {
  788. action = getPDFGoToForID(idRef, pvKey);
  789. }
  790. } else {
  791. //Warnings already issued by AreaTreeHandler
  792. }
  793. }
  794. // no INTERNAL_LINK, look for EXTERNAL_LINK
  795. if (!linkTraitFound) {
  796. Trait.ExternalLink extLink = (Trait.ExternalLink) ip.getTrait(Trait.EXTERNAL_LINK);
  797. if (extLink != null) {
  798. String extDest = extLink.getDestination();
  799. if (extDest != null && extDest.length() > 0) {
  800. linkTraitFound = true;
  801. if (annotsAllowed) {
  802. action = factory.getExternalAction(extDest, extLink.newWindow());
  803. }
  804. }
  805. }
  806. }
  807. // warn if link trait found but not allowed, else create link
  808. if (linkTraitFound) {
  809. if (!annotsAllowed) {
  810. log.warn("Skipping annotation for a link due to PDF profile: "
  811. + pdfDoc.getProfile());
  812. } else if (action != null) {
  813. PDFLink pdfLink = factory.makeLink(ipRect, action);
  814. currentPage.addAnnotation(pdfLink);
  815. }
  816. }
  817. }
  818. private Typeface getTypeface(String fontName) {
  819. Typeface tf = (Typeface) fontInfo.getFonts().get(fontName);
  820. if (tf instanceof LazyFont) {
  821. tf = ((LazyFont)tf).getRealFont();
  822. }
  823. return tf;
  824. }
  825. /** {@inheritDoc} */
  826. public void renderText(TextArea text) {
  827. renderInlineAreaBackAndBorders(text);
  828. Color ct = (Color) text.getTrait(Trait.COLOR);
  829. updateColor(ct, true);
  830. beginTextObject();
  831. String fontName = getInternalFontNameForArea(text);
  832. int size = ((Integer) text.getTrait(Trait.FONT_SIZE)).intValue();
  833. // This assumes that *all* CIDFonts use a /ToUnicode mapping
  834. Typeface tf = getTypeface(fontName);
  835. PDFTextUtil textutil = generator.getTextUtil();
  836. textutil.updateTf(fontName, size / 1000f, tf.isMultiByte());
  837. // word.getOffset() = only height of text itself
  838. // currentBlockIPPosition: 0 for beginning of line; nonzero
  839. // where previous line area failed to take up entire allocated space
  840. int rx = currentIPPosition + text.getBorderAndPaddingWidthStart();
  841. int bl = currentBPPosition + text.getOffset() + text.getBaselineOffset();
  842. textutil.writeTextMatrix(new AffineTransform(1, 0, 0, -1, rx / 1000f, bl / 1000f));
  843. super.renderText(text);
  844. textutil.writeTJ();
  845. renderTextDecoration(tf, size, text, bl, rx);
  846. }
  847. /** {@inheritDoc} */
  848. public void renderWord(WordArea word) {
  849. Font font = getFontFromArea(word.getParentArea());
  850. String s = word.getWord();
  851. escapeText(s, word.getLetterAdjustArray(),
  852. font, (AbstractTextArea)word.getParentArea());
  853. super.renderWord(word);
  854. }
  855. /** {@inheritDoc} */
  856. public void renderSpace(SpaceArea space) {
  857. Font font = getFontFromArea(space.getParentArea());
  858. String s = space.getSpace();
  859. AbstractTextArea textArea = (AbstractTextArea)space.getParentArea();
  860. escapeText(s, null, font, textArea);
  861. if (space.isAdjustable()) {
  862. int tws = -((TextArea) space.getParentArea()).getTextWordSpaceAdjust()
  863. - 2 * textArea.getTextLetterSpaceAdjust();
  864. if (tws != 0) {
  865. float adjust = tws / (font.getFontSize() / 1000f);
  866. generator.getTextUtil().adjustGlyphTJ(adjust);
  867. }
  868. }
  869. super.renderSpace(space);
  870. }
  871. /**
  872. * Escapes text according to PDF rules.
  873. * @param s Text to escape
  874. * @param letterAdjust an array of widths for letter adjustment (may be null)
  875. * @param font to font in use
  876. * @param parentArea the parent text area to retrieve certain traits from
  877. */
  878. protected void escapeText(String s,
  879. int[] letterAdjust,
  880. Font font, AbstractTextArea parentArea) {
  881. escapeText(s, 0, s.length(), letterAdjust, font, parentArea);
  882. }
  883. /**
  884. * Escapes text according to PDF rules.
  885. * @param s Text to escape
  886. * @param start the start position in the text
  887. * @param end the end position in the text
  888. * @param letterAdjust an array of widths for letter adjustment (may be null)
  889. * @param font to font in use
  890. * @param parentArea the parent text area to retrieve certain traits from
  891. */
  892. protected void escapeText(String s, int start, int end,
  893. int[] letterAdjust,
  894. Font font, AbstractTextArea parentArea) {
  895. String fontName = font.getFontName();
  896. float fontSize = font.getFontSize() / 1000f;
  897. Typeface tf = getTypeface(fontName);
  898. SingleByteFont singleByteFont = null;
  899. if (tf instanceof SingleByteFont) {
  900. singleByteFont = (SingleByteFont)tf;
  901. }
  902. PDFTextUtil textutil = generator.getTextUtil();
  903. int l = s.length();
  904. for (int i = start; i < end; i++) {
  905. char orgChar = s.charAt(i);
  906. char ch;
  907. float glyphAdjust = 0;
  908. if (font.hasChar(orgChar)) {
  909. ch = font.mapChar(orgChar);
  910. if (singleByteFont != null && singleByteFont.hasAdditionalEncodings()) {
  911. int encoding = ch / 256;
  912. if (encoding == 0) {
  913. textutil.updateTf(fontName, fontSize, tf.isMultiByte());
  914. } else {
  915. textutil.updateTf(fontName + "_" + Integer.toString(encoding),
  916. fontSize, tf.isMultiByte());
  917. ch = (char)(ch % 256);
  918. }
  919. }
  920. int tls = (i < l - 1 ? parentArea.getTextLetterSpaceAdjust() : 0);
  921. glyphAdjust -= tls;
  922. } else {
  923. if (CharUtilities.isFixedWidthSpace(orgChar)) {
  924. //Fixed width space are rendered as spaces so copy/paste works in a reader
  925. ch = font.mapChar(CharUtilities.SPACE);
  926. glyphAdjust = font.getCharWidth(ch) - font.getCharWidth(orgChar);
  927. } else {
  928. ch = font.mapChar(orgChar);
  929. }
  930. }
  931. if (letterAdjust != null && i < l - 1) {
  932. glyphAdjust -= letterAdjust[i + 1];
  933. }
  934. textutil.writeTJMappedChar(ch);
  935. float adjust = glyphAdjust / fontSize;
  936. if (adjust != 0) {
  937. textutil.adjustGlyphTJ(adjust);
  938. }
  939. }
  940. }
  941. /** {@inheritDoc} */
  942. protected void updateColor(Color col, boolean fill) {
  943. generator.updateColor(col, fill, null);
  944. }
  945. /** {@inheritDoc} */
  946. public void renderImage(Image image, Rectangle2D pos) {
  947. endTextObject();
  948. String url = image.getURL();
  949. putImage(url, pos, image.getForeignAttributes());
  950. }
  951. /** {@inheritDoc} */
  952. protected void drawImage(String url, Rectangle2D pos, Map foreignAttributes) {
  953. endTextObject();
  954. putImage(url, pos, foreignAttributes);
  955. }
  956. /**
  957. * Adds a PDF XObject (a bitmap or form) to the PDF that will later be referenced.
  958. * @param uri URL of the bitmap
  959. * @param pos Position of the bitmap
  960. * @deprecated Use {@link #putImage(String, Rectangle2D, Map)} instead.
  961. */
  962. protected void putImage(String uri, Rectangle2D pos) {
  963. putImage(uri, pos, null);
  964. }
  965. /**
  966. * Adds a PDF XObject (a bitmap or form) to the PDF that will later be referenced.
  967. * @param uri URL of the bitmap
  968. * @param pos Position of the bitmap
  969. * @param foreignAttributes foreign attributes associated with the image
  970. */
  971. protected void putImage(String uri, Rectangle2D pos, Map foreignAttributes) {
  972. Rectangle posInt = new Rectangle(
  973. (int)pos.getX(),
  974. (int)pos.getY(),
  975. (int)pos.getWidth(),
  976. (int)pos.getHeight());
  977. uri = URISpecification.getURL(uri);
  978. PDFXObject xobject = pdfDoc.getXObject(uri);
  979. if (xobject != null) {
  980. float w = (float) pos.getWidth() / 1000f;
  981. float h = (float) pos.getHeight() / 1000f;
  982. placeImage((float)pos.getX() / 1000f,
  983. (float)pos.getY() / 1000f, w, h, xobject);
  984. return;
  985. }
  986. Point origin = new Point(currentIPPosition, currentBPPosition);
  987. int x = origin.x + posInt.x;
  988. int y = origin.y + posInt.y;
  989. ImageManager manager = getUserAgent().getFactory().getImageManager();
  990. ImageInfo info = null;
  991. try {
  992. ImageSessionContext sessionContext = getUserAgent().getImageSessionContext();
  993. info = manager.getImageInfo(uri, sessionContext);
  994. Map hints = ImageUtil.getDefaultHints(sessionContext);
  995. ImageFlavor[] supportedFlavors = imageHandlerRegistry.getSupportedFlavors();
  996. org.apache.xmlgraphics.image.loader.Image img = manager.getImage(
  997. info, supportedFlavors, hints, sessionContext);
  998. //First check for a dynamically registered handler
  999. PDFImageHandler handler = imageHandlerRegistry.getHandler(img.getClass());
  1000. if (handler != null) {
  1001. if (log.isDebugEnabled()) {
  1002. log.debug("Using PDFImageHandler: " + handler.getClass().getName());
  1003. }
  1004. try {
  1005. RendererContext context = createRendererContext(
  1006. x, y, posInt.width, posInt.height, foreignAttributes);
  1007. handler.generateImage(context, img, origin, posInt);
  1008. } catch (IOException ioe) {
  1009. ResourceEventProducer eventProducer = ResourceEventProducer.Provider.get(
  1010. getUserAgent().getEventBroadcaster());
  1011. eventProducer.imageWritingError(this, ioe);
  1012. return;
  1013. }
  1014. } else {
  1015. throw new UnsupportedOperationException(
  1016. "No PDFImageHandler available for image: "
  1017. + info + " (" + img.getClass().getName() + ")");
  1018. }
  1019. } catch (ImageException ie) {
  1020. ResourceEventProducer eventProducer = ResourceEventProducer.Provider.get(
  1021. getUserAgent().getEventBroadcaster());
  1022. eventProducer.imageError(this, (info != null ? info.toString() : uri), ie, null);
  1023. } catch (FileNotFoundException fe) {
  1024. ResourceEventProducer eventProducer = ResourceEventProducer.Provider.get(
  1025. getUserAgent().getEventBroadcaster());
  1026. eventProducer.imageNotFound(this, (info != null ? info.toString() : uri), fe, null);
  1027. } catch (IOException ioe) {
  1028. ResourceEventProducer eventProducer = ResourceEventProducer.Provider.get(
  1029. getUserAgent().getEventBroadcaster());
  1030. eventProducer.imageIOError(this, (info != null ? info.toString() : uri), ioe, null);
  1031. }
  1032. // output new data
  1033. try {
  1034. this.generator.flushPDFDoc();
  1035. } catch (IOException ioe) {
  1036. // ioexception will be caught later
  1037. }
  1038. }
  1039. /**
  1040. * Places a previously registered image at a certain place on the page.
  1041. * @param x X coordinate
  1042. * @param y Y coordinate
  1043. * @param w width for image
  1044. * @param h height for image
  1045. * @param xobj the image XObject
  1046. */
  1047. public void placeImage(float x, float y, float w, float h, PDFXObject xobj) {
  1048. saveGraphicsState();
  1049. generator.add(format(w) + " 0 0 "
  1050. + format(-h) + " "
  1051. + format(currentIPPosition / 1000f + x) + " "
  1052. + format(currentBPPosition / 1000f + h + y)
  1053. + " cm\n" + xobj.getName() + " Do\n");
  1054. restoreGraphicsState();
  1055. }
  1056. /** {@inheritDoc} */
  1057. protected RendererContext createRendererContext(int x, int y, int width, int height,
  1058. Map foreignAttributes) {
  1059. RendererContext context = super.createRendererContext(
  1060. x, y, width, height, foreignAttributes);
  1061. context.setProperty(PDFRendererContextConstants.PDF_DOCUMENT, pdfDoc);
  1062. context.setProperty(PDFRendererContextConstants.OUTPUT_STREAM, ostream);
  1063. context.setProperty(PDFRendererContextConstants.PDF_STATE, getState());
  1064. context.setProperty(PDFRendererContextConstants.PDF_PAGE, currentPage);
  1065. context.setProperty(PDFRendererContextConstants.PDF_CONTEXT,
  1066. currentContext == null ? currentPage : currentContext);
  1067. context.setProperty(PDFRendererContextConstants.PDF_CONTEXT, currentContext);
  1068. context.setProperty(PDFRendererContextConstants.PDF_STREAM, generator.getStream());
  1069. context.setProperty(PDFRendererContextConstants.PDF_FONT_INFO, fontInfo);
  1070. context.setProperty(PDFRendererContextConstants.PDF_FONT_NAME, "");
  1071. context.setProperty(PDFRendererContextConstants.PDF_FONT_SIZE, new Integer(0));
  1072. return context;
  1073. }
  1074. /**
  1075. * Render leader area.
  1076. * This renders a leader area which is an area with a rule.
  1077. * @param area the leader area to render
  1078. */
  1079. public void renderLeader(Leader area) {
  1080. renderInlineAreaBackAndBorders(area);
  1081. int style = area.getRuleStyle();
  1082. int ruleThickness = area.getRuleThickness();
  1083. int startx = currentIPPosition + area.getBorderAndPaddingWidthStart();
  1084. int starty = currentBPPosition + area.getOffset() + (ruleThickness / 2);
  1085. int endx = currentIPPosition
  1086. + area.getBorderAndPaddingWidthStart()
  1087. + area.getIPD();
  1088. Color col = (Color)area.getTrait(Trait.COLOR);
  1089. endTextObject();
  1090. borderPainter.drawLine(new Point(startx, starty), new Point(endx, starty),
  1091. ruleThickness, col, RuleStyle.valueOf(style));
  1092. super.renderLeader(area);
  1093. }
  1094. /** {@inheritDoc} */
  1095. public String getMimeType() {
  1096. return MIME_TYPE;
  1097. }
  1098. /**
  1099. * Sets the PDF/A mode for the PDF renderer.
  1100. * @param mode the PDF/A mode
  1101. */
  1102. public void setAMode(PDFAMode mode) {
  1103. this.pdfUtil.setAMode(mode);
  1104. }
  1105. /**
  1106. * Sets the PDF/X mode for the PDF renderer.
  1107. * @param mode the PDF/X mode
  1108. */
  1109. public void setXMode(PDFXMode mode) {
  1110. this.pdfUtil.setXMode(mode);
  1111. }
  1112. /**
  1113. * Sets the output color profile for the PDF renderer.
  1114. * @param outputProfileURI the URI to the output color profile
  1115. */
  1116. public void setOutputProfileURI(String outputProfileURI) {
  1117. this.pdfUtil.setOutputProfileURI(outputProfileURI);
  1118. }
  1119. /**
  1120. * Sets the filter map to be used by the PDF renderer.
  1121. * @param filterMap the filter map
  1122. */
  1123. public void setFilterMap(Map filterMap) {
  1124. this.pdfUtil.setFilterMap(filterMap);
  1125. }
  1126. /**
  1127. * Sets the encryption parameters used by the PDF renderer.
  1128. * @param encryptionParams the encryption parameters
  1129. */
  1130. public void setEncryptionParams(PDFEncryptionParams encryptionParams) {
  1131. this.pdfUtil.setEncryptionParams(encryptionParams);
  1132. }
  1133. }