Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

PDFRenderer.java 46KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336
  1. /*
  2. * $Id: PDFRenderer.java,v 1.137 2003/03/05 20:38:27 jeremias Exp $
  3. * ============================================================================
  4. * The Apache Software License, Version 1.1
  5. * ============================================================================
  6. *
  7. * Copyright (C) 1999-2003 The Apache Software Foundation. All rights reserved.
  8. *
  9. * Redistribution and use in source and binary forms, with or without modifica-
  10. * tion, are permitted provided that the following conditions are met:
  11. *
  12. * 1. Redistributions of source code must retain the above copyright notice,
  13. * this list of conditions and the following disclaimer.
  14. *
  15. * 2. Redistributions in binary form must reproduce the above copyright notice,
  16. * this list of conditions and the following disclaimer in the documentation
  17. * and/or other materials provided with the distribution.
  18. *
  19. * 3. The end-user documentation included with the redistribution, if any, must
  20. * include the following acknowledgment: "This product includes software
  21. * developed by the Apache Software Foundation (http://www.apache.org/)."
  22. * Alternately, this acknowledgment may appear in the software itself, if
  23. * and wherever such third-party acknowledgments normally appear.
  24. *
  25. * 4. The names "FOP" and "Apache Software Foundation" must not be used to
  26. * endorse or promote products derived from this software without prior
  27. * written permission. For written permission, please contact
  28. * apache@apache.org.
  29. *
  30. * 5. Products derived from this software may not be called "Apache", nor may
  31. * "Apache" appear in their name, without prior written permission of the
  32. * Apache Software Foundation.
  33. *
  34. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
  35. * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
  36. * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  37. * APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
  38. * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU-
  39. * DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
  40. * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
  41. * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  42. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  43. * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  44. * ============================================================================
  45. *
  46. * This software consists of voluntary contributions made by many individuals
  47. * on behalf of the Apache Software Foundation and was originally created by
  48. * James Tauber <jtauber@jtauber.com>. For more information on the Apache
  49. * Software Foundation, please see <http://www.apache.org/>.
  50. */
  51. package org.apache.fop.render.pdf;
  52. // Java
  53. import java.io.IOException;
  54. import java.io.OutputStream;
  55. import java.awt.Color;
  56. import java.awt.geom.Rectangle2D;
  57. import java.awt.geom.AffineTransform;
  58. import java.util.Map;
  59. import java.util.List;
  60. // XML
  61. import org.w3c.dom.Document;
  62. // Avalon
  63. import org.apache.avalon.framework.configuration.Configuration;
  64. import org.apache.avalon.framework.configuration.ConfigurationException;
  65. // FOP
  66. import org.apache.fop.apps.FOPException;
  67. import org.apache.fop.apps.FOUserAgent;
  68. import org.apache.fop.apps.Version;
  69. import org.apache.fop.area.Area;
  70. import org.apache.fop.area.Block;
  71. import org.apache.fop.area.BlockViewport;
  72. import org.apache.fop.area.CTM;
  73. import org.apache.fop.area.LineArea;
  74. import org.apache.fop.area.Page;
  75. import org.apache.fop.area.PageViewport;
  76. import org.apache.fop.area.RegionViewport;
  77. import org.apache.fop.area.Title;
  78. import org.apache.fop.area.Trait;
  79. import org.apache.fop.area.TreeExt;
  80. import org.apache.fop.area.extensions.BookmarkData;
  81. import org.apache.fop.area.inline.Character;
  82. import org.apache.fop.area.inline.TextArea;
  83. import org.apache.fop.area.inline.Viewport;
  84. import org.apache.fop.area.inline.ForeignObject;
  85. import org.apache.fop.area.inline.Image;
  86. import org.apache.fop.area.inline.Leader;
  87. import org.apache.fop.area.inline.InlineParent;
  88. import org.apache.fop.datatypes.ColorType;
  89. import org.apache.fop.fo.properties.BackgroundRepeat;
  90. import org.apache.fop.fo.properties.RuleStyle;
  91. import org.apache.fop.fonts.Typeface;
  92. import org.apache.fop.fonts.Font;
  93. import org.apache.fop.fonts.FontSetup;
  94. import org.apache.fop.fonts.FontMetrics;
  95. import org.apache.fop.image.FopImage;
  96. import org.apache.fop.image.ImageFactory;
  97. import org.apache.fop.image.XMLImage;
  98. import org.apache.fop.pdf.PDFAnnotList;
  99. import org.apache.fop.pdf.PDFColor;
  100. import org.apache.fop.pdf.PDFDocument;
  101. import org.apache.fop.pdf.PDFEncryptionManager;
  102. import org.apache.fop.pdf.PDFFilterList;
  103. import org.apache.fop.pdf.PDFInfo;
  104. import org.apache.fop.pdf.PDFLink;
  105. import org.apache.fop.pdf.PDFOutline;
  106. import org.apache.fop.pdf.PDFPage;
  107. import org.apache.fop.pdf.PDFResourceContext;
  108. import org.apache.fop.pdf.PDFResources;
  109. import org.apache.fop.pdf.PDFState;
  110. import org.apache.fop.pdf.PDFStream;
  111. import org.apache.fop.pdf.PDFText;
  112. import org.apache.fop.pdf.PDFXObject;
  113. import org.apache.fop.render.PrintRenderer;
  114. import org.apache.fop.render.RendererContext;
  115. import org.apache.fop.traits.BorderProps;
  116. /*
  117. todo:
  118. word rendering and optimistion
  119. pdf state optimisation
  120. line and border
  121. background pattern
  122. writing mode
  123. text decoration
  124. */
  125. /**
  126. * Renderer that renders areas to PDF
  127. *
  128. */
  129. public class PDFRenderer extends PrintRenderer {
  130. /**
  131. * The mime type for pdf
  132. */
  133. public static final String MIME_TYPE = "application/pdf";
  134. /**
  135. * the PDF Document being created
  136. */
  137. protected PDFDocument pdfDoc;
  138. /**
  139. * Map of pages using the PageViewport as the key
  140. * this is used for prepared pages that cannot be immediately
  141. * rendered
  142. */
  143. protected Map pages = null;
  144. /**
  145. * Page references are stored using the PageViewport as the key
  146. * when a reference is made the PageViewport is used
  147. * for pdf this means we need the pdf page reference
  148. */
  149. protected Map pageReferences = new java.util.HashMap();
  150. /** Page viewport references */
  151. protected Map pvReferences = new java.util.HashMap();
  152. /**
  153. * The output stream to write the document to
  154. */
  155. protected OutputStream ostream;
  156. /**
  157. * the /Resources object of the PDF document being created
  158. */
  159. protected PDFResources pdfResources;
  160. /**
  161. * the current stream to add PDF commands to
  162. */
  163. protected PDFStream currentStream;
  164. /**
  165. * the current annotation list to add annotations to
  166. */
  167. protected PDFResourceContext currentContext = null;
  168. /**
  169. * the current page to add annotations to
  170. */
  171. protected PDFPage currentPage;
  172. /** drawing state */
  173. protected PDFState currentState = null;
  174. /** Name of currently selected font */
  175. protected String currentFontName = "";
  176. /** Size of currently selected font */
  177. protected int currentFontSize = 0;
  178. /** page height */
  179. protected int pageHeight;
  180. /** Registry of PDF filters */
  181. protected Map filterMap;
  182. /**
  183. * true if a TJ command is left to be written
  184. */
  185. protected boolean textOpen = false;
  186. /**
  187. * the previous Y coordinate of the last word written.
  188. * Used to decide if we can draw the next word on the same line.
  189. */
  190. protected int prevWordY = 0;
  191. /**
  192. * the previous X coordinate of the last word written.
  193. * used to calculate how much space between two words
  194. */
  195. protected int prevWordX = 0;
  196. /**
  197. * The width of the previous word. Used to calculate space between
  198. */
  199. protected int prevWordWidth = 0;
  200. /**
  201. * reusable word area string buffer to reduce memory usage
  202. */
  203. private StringBuffer wordAreaPDF = new StringBuffer();
  204. /**
  205. * Offset for rendering text, taking into account borders and padding for
  206. * both region and block.
  207. */
  208. protected int BPMarginOffset = 0;
  209. /**
  210. * Offset for rendering text, taking into account borders and padding for
  211. * both the region and block.
  212. */
  213. protected int IPMarginOffset = 0;
  214. /**
  215. * create the PDF renderer
  216. */
  217. public PDFRenderer() {
  218. }
  219. /**
  220. * Configure the PDF renderer.
  221. * Get the configuration to be used for pdf stream filters,
  222. * fonts etc.
  223. * @see org.apache.avalon.framework.configuration.Configurable#configure(Configuration)
  224. */
  225. public void configure(Configuration cfg) throws ConfigurationException {
  226. //PDF filters
  227. this.filterMap = PDFFilterList.buildFilterMapFromConfiguration(cfg);
  228. //Font configuration
  229. List cfgFonts = FontSetup.buildFontListFromConfiguration(cfg);
  230. if (this.fontList == null) {
  231. this.fontList = cfgFonts;
  232. } else {
  233. this.fontList.addAll(cfgFonts);
  234. }
  235. }
  236. /**
  237. * @see org.apache.fop.render.Renderer#setUserAgent(FOUserAgent)
  238. */
  239. public void setUserAgent(FOUserAgent agent) {
  240. super.setUserAgent(agent);
  241. PDFXMLHandler xmlHandler = new PDFXMLHandler();
  242. //userAgent.setDefaultXMLHandler(MIME_TYPE, xmlHandler);
  243. String svg = "http://www.w3.org/2000/svg";
  244. addXMLHandler(userAgent, MIME_TYPE, svg, xmlHandler);
  245. }
  246. /**
  247. * @see org.apache.fop.render.Renderer#startRenderer(OutputStream)
  248. */
  249. public void startRenderer(OutputStream stream) throws IOException {
  250. ostream = stream;
  251. producer = "FOP " + Version.getVersion();
  252. this.pdfDoc = new PDFDocument(producer);
  253. setupLogger(this.pdfDoc);
  254. this.pdfDoc.setCreator(creator);
  255. this.pdfDoc.setCreationDate(creationDate);
  256. this.pdfDoc.setFilterMap(filterMap);
  257. this.pdfDoc.outputHeader(stream);
  258. //Setup encryption if necessary
  259. PDFEncryptionManager.setupPDFEncryption(userAgent, this.pdfDoc, getLogger());
  260. }
  261. /**
  262. * @see org.apache.fop.render.Renderer#stopRenderer()
  263. */
  264. public void stopRenderer() throws IOException {
  265. pdfDoc.getResources().addFonts(pdfDoc,
  266. (org.apache.fop.apps.Document) fontInfo);
  267. pdfDoc.outputTrailer(ostream);
  268. this.pdfDoc = null;
  269. ostream = null;
  270. pages = null;
  271. pageReferences.clear();
  272. pvReferences.clear();
  273. pdfResources = null;
  274. currentStream = null;
  275. currentContext = null;
  276. currentPage = null;
  277. currentState = null;
  278. currentFontName = "";
  279. wordAreaPDF = new StringBuffer();
  280. }
  281. /**
  282. * @see org.apache.fop.render.Renderer#supportsOutOfOrder()
  283. */
  284. public boolean supportsOutOfOrder() {
  285. return true;
  286. }
  287. /**
  288. * @see org.apache.fop.render.Renderer#renderExtension(TreeExt)
  289. */
  290. public void renderExtension(TreeExt ext) {
  291. // render bookmark extension
  292. if (ext instanceof BookmarkData) {
  293. renderRootExtensions((BookmarkData)ext);
  294. }
  295. }
  296. /**
  297. * Renders the root extension elements
  298. * @param bookmarks the bookmarks to render
  299. */
  300. protected void renderRootExtensions(BookmarkData bookmarks) {
  301. for (int i = 0; i < bookmarks.getCount(); i++) {
  302. BookmarkData ext = bookmarks.getSubData(i);
  303. renderOutline(ext, null);
  304. }
  305. }
  306. private void renderOutline(BookmarkData outline, PDFOutline parentOutline) {
  307. PDFOutline outlineRoot = pdfDoc.getOutlineRoot();
  308. PDFOutline pdfOutline = null;
  309. PageViewport pv = outline.getPage();
  310. if (pv != null) {
  311. Rectangle2D bounds = pv.getViewArea();
  312. double h = bounds.getHeight();
  313. float yoffset = (float)h / 1000f;
  314. String intDest = (String)pageReferences.get(pv.getKey());
  315. if (parentOutline == null) {
  316. pdfOutline = pdfDoc.getFactory().makeOutline(outlineRoot,
  317. outline.getLabel(), intDest, yoffset);
  318. } else {
  319. PDFOutline pdfParentOutline = parentOutline;
  320. pdfOutline = pdfDoc.getFactory().makeOutline(pdfParentOutline,
  321. outline.getLabel(), intDest, yoffset);
  322. }
  323. }
  324. for (int i = 0; i < outline.getCount(); i++) {
  325. renderOutline(outline.getSubData(i), pdfOutline);
  326. }
  327. }
  328. /** Saves the graphics state of the rendering engine. */
  329. protected void saveGraphicsState() {
  330. currentStream.add("q\n");
  331. }
  332. /** Restores the last graphics state of the rendering engine. */
  333. protected void restoreGraphicsState() {
  334. currentStream.add("Q\n");
  335. }
  336. /** Indicates the beginning of a text object. */
  337. protected void beginTextObject() {
  338. currentStream.add("BT\n");
  339. }
  340. /** Indicates the end of a text object. */
  341. protected void endTextObject() {
  342. currentStream.add("ET\n");
  343. }
  344. /**
  345. * Start the next page sequence.
  346. * For the pdf renderer there is no concept of page sequences
  347. * but it uses the first available page sequence title to set
  348. * as the title of the pdf document.
  349. *
  350. * @param seqTitle the title of the page sequence
  351. */
  352. public void startPageSequence(Title seqTitle) {
  353. if (seqTitle != null) {
  354. String str = convertTitleToString(seqTitle);
  355. PDFInfo info = this.pdfDoc.getInfo();
  356. info.setTitle(str);
  357. }
  358. }
  359. /**
  360. * The pdf page is prepared by making the page.
  361. * The page is made in the pdf document without any contents
  362. * and then stored to add the contents later.
  363. * The page objects is stored using the area tree PageViewport
  364. * as a key.
  365. *
  366. * @param page the page to prepare
  367. */
  368. public void preparePage(PageViewport page) {
  369. this.pdfResources = this.pdfDoc.getResources();
  370. Rectangle2D bounds = page.getViewArea();
  371. double w = bounds.getWidth();
  372. double h = bounds.getHeight();
  373. currentPage = this.pdfDoc.getFactory().makePage(
  374. this.pdfResources,
  375. (int) Math.round(w / 1000), (int) Math.round(h / 1000));
  376. if (pages == null) {
  377. pages = new java.util.HashMap();
  378. }
  379. pages.put(page, currentPage);
  380. pageReferences.put(page.getKey(), currentPage.referencePDF());
  381. pvReferences.put(page.getKey(), page);
  382. }
  383. /**
  384. * This method creates a pdf stream for the current page
  385. * uses it as the contents of a new page. The page is written
  386. * immediately to the output stream.
  387. * @see org.apache.fop.render.Renderer#renderPage(PageViewport)
  388. */
  389. public void renderPage(PageViewport page)
  390. throws IOException, FOPException {
  391. if (pages != null
  392. && (currentPage = (PDFPage) pages.get(page)) != null) {
  393. pages.remove(page);
  394. Rectangle2D bounds = page.getViewArea();
  395. double h = bounds.getHeight();
  396. pageHeight = (int) h;
  397. } else {
  398. this.pdfResources = this.pdfDoc.getResources();
  399. Rectangle2D bounds = page.getViewArea();
  400. double w = bounds.getWidth();
  401. double h = bounds.getHeight();
  402. pageHeight = (int) h;
  403. currentPage = this.pdfDoc.getFactory().makePage(
  404. this.pdfResources,
  405. (int) Math.round(w / 1000), (int) Math.round(h / 1000));
  406. pageReferences.put(page.getKey(), currentPage.referencePDF());
  407. pvReferences.put(page.getKey(), page);
  408. }
  409. currentStream =
  410. this.pdfDoc.getFactory().makeStream(PDFFilterList.CONTENT_FILTER, false);
  411. currentState = new PDFState();
  412. currentState.setTransform(new AffineTransform(1, 0, 0, -1, 0,
  413. (int) Math.round(pageHeight / 1000)));
  414. // Transform origin at top left to origin at bottom left
  415. currentStream.add("1 0 0 -1 0 "
  416. + (int) Math.round(pageHeight / 1000) + " cm\n");
  417. currentFontName = "";
  418. Page p = page.getPage();
  419. renderPageAreas(p);
  420. this.pdfDoc.registerObject(currentStream);
  421. currentPage.setContents(currentStream);
  422. PDFAnnotList annots = currentPage.getAnnotations();
  423. if (annots != null) {
  424. this.pdfDoc.addObject(annots);
  425. }
  426. this.pdfDoc.addObject(currentPage);
  427. this.pdfDoc.output(ostream);
  428. }
  429. /**
  430. * @see org.apache.fop.render.AbstractRenderer#startVParea(CTM)
  431. */
  432. protected void startVParea(CTM ctm) {
  433. // Set the given CTM in the graphics state
  434. currentState.push();
  435. currentState.setTransform(
  436. new AffineTransform(CTMHelper.toPDFArray(ctm)));
  437. saveGraphicsState();
  438. // multiply with current CTM
  439. currentStream.add(CTMHelper.toPDFString(ctm) + " cm\n");
  440. // Set clip?
  441. beginTextObject();
  442. }
  443. /**
  444. * @see org.apache.fop.render.AbstractRenderer#endVParea()
  445. */
  446. protected void endVParea() {
  447. endTextObject();
  448. restoreGraphicsState();
  449. currentState.pop();
  450. }
  451. /**
  452. * @see org.apache.fop.render.AbstractRenderer#renderBlock(Block)
  453. */
  454. protected void renderBlock(Block block) {
  455. int marginOffset = IPMarginOffset;
  456. super.renderBlock(block);
  457. // super.renderBlock() may render child blocks (with their own offsets)
  458. // so need to restore (this parent's) IPMarginOffset when finished.
  459. IPMarginOffset = marginOffset;
  460. }
  461. /**
  462. * Handle the traits for a region
  463. * This is used to draw the traits for the given page region.
  464. * (See Sect. 6.4.1.2 of XSL-FO spec.)
  465. * @param region the RegionViewport whose region is to be drawn
  466. */
  467. protected void handleRegionTraits(RegionViewport region) {
  468. currentFontName = "";
  469. float startx = 0;
  470. float starty = 0;
  471. Rectangle2D viewArea = region.getViewArea();
  472. float width = (float)(viewArea.getWidth() / 1000f);
  473. float height = (float)(viewArea.getHeight() / 1000f);
  474. if (region.getRegion().getRegionClass()
  475. == org.apache.fop.fo.pagination.Region.BODY_CODE)
  476. {
  477. BPMarginOffset = region.getBorderAndPaddingWidthBefore();
  478. IPMarginOffset = region.getBorderAndPaddingWidthStart();
  479. }
  480. drawBackAndBorders(region, startx, starty, width, height);
  481. }
  482. /**
  483. * Handle block traits.
  484. * The block could be any sort of block with any positioning
  485. * so this should render the traits such as border and background
  486. * in its position.
  487. *
  488. * @param block the block to render the traits
  489. */
  490. protected void handleBlockTraits(Block block) {
  491. /* IPMarginOffset for a particular block = region border +
  492. * region padding + parent block padding + current block padding
  493. */
  494. Integer paddingStart = (Integer) block.getTrait(Trait.PADDING_START);
  495. if (paddingStart != null) {
  496. IPMarginOffset += paddingStart.intValue();
  497. }
  498. float startx = (currentIPPosition + IPMarginOffset) / 1000f;
  499. float starty = (currentBPPosition + BPMarginOffset) / 1000f;
  500. drawBackAndBorders(block, startx, starty,
  501. block.getWidth() / 1000f, block.getHeight() / 1000f);
  502. }
  503. /**
  504. * Draw the background and borders.
  505. * This draws the background and border traits for an area given
  506. * the position.
  507. *
  508. * @param block the area to get the traits from
  509. * @param startx the start x position
  510. * @param starty the start y position
  511. * @param width the width of the area
  512. * @param height the height of the area
  513. */
  514. protected void drawBackAndBorders(Area block,
  515. float startx, float starty,
  516. float width, float height) {
  517. // draw background then border
  518. boolean started = false;
  519. Trait.Background back;
  520. back = (Trait.Background)block.getTrait(Trait.BACKGROUND);
  521. if (back != null) {
  522. started = true;
  523. closeText();
  524. endTextObject();
  525. //saveGraphicsState();
  526. if (back.getColor() != null) {
  527. updateColor(back.getColor(), true, null);
  528. currentStream.add(startx + " " + starty + " "
  529. + width + " " + height + " re\n");
  530. currentStream.add("f\n");
  531. }
  532. if (back.getURL() != null) {
  533. ImageFactory fact = ImageFactory.getInstance();
  534. FopImage fopimage = fact.getImage(back.getURL(), userAgent);
  535. if (fopimage != null && fopimage.load(FopImage.DIMENSIONS, userAgent)) {
  536. if (back.getRepeat() == BackgroundRepeat.REPEAT) {
  537. // create a pattern for the image
  538. } else {
  539. // place once
  540. Rectangle2D pos;
  541. pos = new Rectangle2D.Float((startx + back.getHoriz()) * 1000,
  542. (starty + back.getVertical()) * 1000,
  543. fopimage.getWidth() * 1000,
  544. fopimage.getHeight() * 1000);
  545. putImage(back.getURL(), pos);
  546. }
  547. }
  548. }
  549. }
  550. BorderProps bps = (BorderProps)block.getTrait(Trait.BORDER_BEFORE);
  551. if (bps != null) {
  552. float endx = startx + width;
  553. if (!started) {
  554. started = true;
  555. closeText();
  556. endTextObject();
  557. //saveGraphicsState();
  558. }
  559. float bwidth = bps.width / 1000f;
  560. updateColor(bps.color, true, null);
  561. currentStream.add(startx + " " + starty + " "
  562. + width + " " + bwidth + " re\n");
  563. currentStream.add("f\n");
  564. }
  565. bps = (BorderProps)block.getTrait(Trait.BORDER_AFTER);
  566. if (bps != null) {
  567. if (!started) {
  568. started = true;
  569. closeText();
  570. endTextObject();
  571. //saveGraphicsState();
  572. }
  573. float bwidth = bps.width / 1000f;
  574. updateColor(bps.color, true, null);
  575. currentStream.add(startx + " " + (starty + height - bwidth) + " "
  576. + width + " " + bwidth + " re\n");
  577. currentStream.add("f\n");
  578. }
  579. bps = (BorderProps)block.getTrait(Trait.BORDER_START);
  580. if (bps != null) {
  581. if (!started) {
  582. started = true;
  583. closeText();
  584. endTextObject();
  585. //saveGraphicsState();
  586. }
  587. float bwidth = bps.width / 1000f;
  588. updateColor(bps.color, true, null);
  589. currentStream.add(startx + " " + starty + " "
  590. + bwidth + " " + height + " re\n");
  591. currentStream.add("f\n");
  592. }
  593. bps = (BorderProps)block.getTrait(Trait.BORDER_END);
  594. if (bps != null) {
  595. if (!started) {
  596. started = true;
  597. closeText();
  598. endTextObject();
  599. //saveGraphicsState();
  600. }
  601. float bwidth = bps.width / 1000f;
  602. updateColor(bps.color, true, null);
  603. currentStream.add((startx + width - bwidth) + " " + starty + " "
  604. + bwidth + " " + height + " re\n");
  605. currentStream.add("f\n");
  606. }
  607. if (started) {
  608. //restoreGraphicsState();
  609. beginTextObject();
  610. // font last set out of scope in text section
  611. currentFontName = "";
  612. }
  613. }
  614. /**
  615. * Draw a line.
  616. *
  617. * @param startx the start x position
  618. * @param starty the start y position
  619. * @param endx the x end position
  620. * @param endy the y end position
  621. */
  622. private void drawLine(float startx, float starty, float endx, float endy) {
  623. currentStream.add(startx + " " + starty + " m\n");
  624. currentStream.add(endx + " " + endy + " l\n");
  625. currentStream.add("S\n");
  626. }
  627. /**
  628. * @see org.apache.fop.render.AbstractRenderer#renderBlockViewport(BlockViewport, List)
  629. */
  630. protected void renderBlockViewport(BlockViewport bv, List children) {
  631. // clip and position viewport if necessary
  632. // save positions
  633. int saveIP = currentIPPosition;
  634. int saveBP = currentBPPosition;
  635. String saveFontName = currentFontName;
  636. CTM ctm = bv.getCTM();
  637. if (bv.getPositioning() == Block.ABSOLUTE) {
  638. currentIPPosition = 0;
  639. currentBPPosition = 0;
  640. closeText();
  641. endTextObject();
  642. if (bv.getClip()) {
  643. saveGraphicsState();
  644. float x = (float)(bv.getXOffset() + containingIPPosition) / 1000f;
  645. float y = (float)(bv.getYOffset() + containingBPPosition) / 1000f;
  646. float width = (float)bv.getWidth() / 1000f;
  647. float height = (float)bv.getHeight() / 1000f;
  648. clip(x, y, width, height);
  649. }
  650. CTM tempctm = new CTM(containingIPPosition, containingBPPosition);
  651. ctm = tempctm.multiply(ctm);
  652. startVParea(ctm);
  653. handleBlockTraits(bv);
  654. renderBlocks(children);
  655. endVParea();
  656. if (bv.getClip()) {
  657. restoreGraphicsState();
  658. }
  659. beginTextObject();
  660. // clip if necessary
  661. currentIPPosition = saveIP;
  662. currentBPPosition = saveBP;
  663. } else {
  664. if (ctm != null) {
  665. currentIPPosition = 0;
  666. currentBPPosition = 0;
  667. closeText();
  668. endTextObject();
  669. double[] vals = ctm.toArray();
  670. //boolean aclock = vals[2] == 1.0;
  671. if (vals[2] == 1.0) {
  672. ctm = ctm.translate(-saveBP - bv.getHeight(), -saveIP);
  673. } else if (vals[0] == -1.0) {
  674. ctm = ctm.translate(-saveIP - bv.getWidth(), -saveBP - bv.getHeight());
  675. } else {
  676. ctm = ctm.translate(saveBP, saveIP - bv.getWidth());
  677. }
  678. }
  679. // clip if necessary
  680. if (bv.getClip()) {
  681. if (ctm == null) {
  682. closeText();
  683. endTextObject();
  684. }
  685. saveGraphicsState();
  686. float x = (float)bv.getXOffset() / 1000f;
  687. float y = (float)bv.getYOffset() / 1000f;
  688. float width = (float)bv.getWidth() / 1000f;
  689. float height = (float)bv.getHeight() / 1000f;
  690. clip(x, y, width, height);
  691. }
  692. if (ctm != null) {
  693. startVParea(ctm);
  694. }
  695. handleBlockTraits(bv);
  696. renderBlocks(children);
  697. if (ctm != null) {
  698. endVParea();
  699. }
  700. if (bv.getClip()) {
  701. restoreGraphicsState();
  702. if (ctm == null) {
  703. beginTextObject();
  704. }
  705. }
  706. if (ctm != null) {
  707. beginTextObject();
  708. }
  709. currentIPPosition = saveIP;
  710. currentBPPosition = saveBP;
  711. currentBPPosition += (int)(bv.getHeight());
  712. }
  713. currentFontName = saveFontName;
  714. }
  715. /**
  716. * Clip an area.
  717. * write a clipping operation given coordinates in the current
  718. * transform.
  719. * @param x the x coordinate
  720. * @param y the y coordinate
  721. * @param width the width of the area
  722. * @param height the height of the area
  723. */
  724. protected void clip(float x, float y, float width, float height) {
  725. currentStream.add(x + " " + y + " m\n");
  726. currentStream.add((x + width) + " " + y + " l\n");
  727. currentStream.add((x + width) + " " + (y + height) + " l\n");
  728. currentStream.add(x + " " + (y + height) + " l\n");
  729. currentStream.add("h\n");
  730. currentStream.add("W\n");
  731. currentStream.add("n\n");
  732. }
  733. /**
  734. * @see org.apache.fop.render.AbstractRenderer#renderLineArea(LineArea)
  735. */
  736. protected void renderLineArea(LineArea line) {
  737. super.renderLineArea(line);
  738. closeText();
  739. }
  740. /**
  741. * Render inline parent area.
  742. * For pdf this handles the inline parent area traits such as
  743. * links, border, background.
  744. * @param ip the inline parent area
  745. */
  746. public void renderInlineParent(InlineParent ip) {
  747. float start = currentBlockIPPosition / 1000f;
  748. float top = (ip.getOffset() + currentBPPosition) / 1000f;
  749. float width = ip.getWidth() / 1000f;
  750. float height = ip.getHeight() / 1000f;
  751. drawBackAndBorders(ip, start, top, width, height);
  752. // render contents
  753. super.renderInlineParent(ip);
  754. // place the link over the top
  755. Object tr = ip.getTrait(Trait.INTERNAL_LINK);
  756. boolean internal = false;
  757. String dest = null;
  758. float yoffset = 0;
  759. if (tr == null) {
  760. dest = (String)ip.getTrait(Trait.EXTERNAL_LINK);
  761. } else {
  762. String pvKey = (String)tr;
  763. dest = (String)pageReferences.get(pvKey);
  764. if (dest != null) {
  765. PageViewport pv = (PageViewport)pvReferences.get(pvKey);
  766. Rectangle2D bounds = pv.getViewArea();
  767. double h = bounds.getHeight();
  768. yoffset = (float)h / 1000f;
  769. internal = true;
  770. }
  771. }
  772. if (dest != null) {
  773. // add link to pdf document
  774. Rectangle2D rect = new Rectangle2D.Float(start, top, width, height);
  775. // transform rect to absolute coords
  776. AffineTransform transform = currentState.getTransform();
  777. rect = transform.createTransformedShape(rect).getBounds();
  778. int type = internal ? PDFLink.INTERNAL : PDFLink.EXTERNAL;
  779. PDFLink pdflink = pdfDoc.getFactory().makeLink(
  780. rect, dest, type, yoffset);
  781. currentPage.addAnnotation(pdflink);
  782. }
  783. }
  784. /**
  785. * @see org.apache.fop.render.Renderer#renderCharacter(Character)
  786. */
  787. public void renderCharacter(Character ch) {
  788. super.renderCharacter(ch);
  789. }
  790. /**
  791. * @see org.apache.fop.render.Renderer#renderText(Text)
  792. */
  793. public void renderText(TextArea text) {
  794. StringBuffer pdf = new StringBuffer();
  795. String name = (String) text.getTrait(Trait.FONT_NAME);
  796. int size = ((Integer) text.getTrait(Trait.FONT_SIZE)).intValue();
  797. // This assumes that *all* CIDFonts use a /ToUnicode mapping
  798. Typeface f = (Typeface) fontInfo.getFonts().get(name);
  799. boolean useMultiByte = f.isMultiByte();
  800. // String startText = useMultiByte ? "<FEFF" : "(";
  801. String startText = useMultiByte ? "<" : "(";
  802. String endText = useMultiByte ? "> " : ") ";
  803. updateFont(name, size, pdf);
  804. ColorType ct = (ColorType) text.getTrait(Trait.COLOR);
  805. if (ct != null) {
  806. updateColor(ct, true, pdf);
  807. }
  808. // word.getOffset() = only height of text itself
  809. // currentBlockIPPosition: 0 for beginning of line; nonzero
  810. // where previous line area failed to take up entire allocated space
  811. int rx = currentBlockIPPosition + IPMarginOffset;
  812. int bl = currentBPPosition + BPMarginOffset + text.getOffset();
  813. /*
  814. System.out.println("\nBlockIP Position: " + currentBlockIPPosition +
  815. "; currentBPPosition: " + currentBPPosition +
  816. "; offset: " + text.getOffset() +
  817. "; Text = " + text.getTextArea());
  818. */
  819. // Set letterSpacing
  820. //float ls = fs.getLetterSpacing() / this.currentFontSize;
  821. //pdf.append(ls).append(" Tc\n");
  822. if (!textOpen || bl != prevWordY) {
  823. closeText();
  824. pdf.append("1 0 0 -1 " + (rx / 1000f) + " "
  825. + (bl / 1000f) + " Tm [" + startText);
  826. prevWordY = bl;
  827. textOpen = true;
  828. } else {
  829. // express the space between words in thousandths of an em
  830. int space = prevWordX - rx + prevWordWidth;
  831. float emDiff = (float) space / (float) currentFontSize * 1000f;
  832. // this prevents a problem in Acrobat Reader and other viewers
  833. // where large numbers cause text to disappear or default to
  834. // a limit
  835. if (emDiff < -33000) {
  836. closeText();
  837. pdf.append("1 0 0 1 " + (rx / 1000f) + " "
  838. + (bl / 1000f) + " Tm [" + startText);
  839. textOpen = true;
  840. } else {
  841. pdf.append(Float.toString(emDiff));
  842. pdf.append(" ");
  843. pdf.append(startText);
  844. }
  845. }
  846. prevWordWidth = text.getWidth();
  847. prevWordX = rx;
  848. String s = text.getTextArea();
  849. FontMetrics metrics = fontInfo.getMetricsFor(name);
  850. Font fs = new Font(name, metrics, size);
  851. escapeText(s, fs, useMultiByte, pdf);
  852. pdf.append(endText);
  853. currentStream.add(pdf.toString());
  854. super.renderText(text);
  855. }
  856. /**
  857. * Escapes text according to PDF rules.
  858. * @param s Text to escape
  859. * @param fs Font state
  860. * @param useMultiByte Indicates the use of multi byte convention
  861. * @param pdf target buffer for the escaped text
  862. */
  863. public void escapeText(String s, Font fs,
  864. boolean useMultiByte, StringBuffer pdf) {
  865. String startText = useMultiByte ? "<" : "(";
  866. String endText = useMultiByte ? "> " : ") ";
  867. boolean kerningAvailable = false;
  868. Map kerning = fs.getKerning();
  869. if (kerning != null && !kerning.isEmpty()) {
  870. kerningAvailable = true;
  871. }
  872. int l = s.length();
  873. for (int i = 0; i < l; i++) {
  874. char ch = fs.mapChar(s.charAt(i));
  875. if (!useMultiByte) {
  876. if (ch > 127) {
  877. pdf.append("\\");
  878. pdf.append(Integer.toOctalString((int) ch));
  879. } else {
  880. switch (ch) {
  881. case '(':
  882. case ')':
  883. case '\\':
  884. pdf.append("\\");
  885. break;
  886. }
  887. pdf.append(ch);
  888. }
  889. } else {
  890. pdf.append(PDFText.toUnicodeHex(ch));
  891. }
  892. if (kerningAvailable && (i + 1) < l) {
  893. addKerning(pdf, (new Integer((int) ch)),
  894. (new Integer((int) fs.mapChar(s.charAt(i + 1)))
  895. ), kerning, startText, endText);
  896. }
  897. }
  898. }
  899. private void addKerning(StringBuffer buf, Integer ch1, Integer ch2,
  900. Map kerning, String startText, String endText) {
  901. Map kernPair = (Map) kerning.get(ch1);
  902. if (kernPair != null) {
  903. Integer width = (Integer) kernPair.get(ch2);
  904. if (width != null) {
  905. buf.append(endText).append(-width.intValue());
  906. buf.append(' ').append(startText);
  907. }
  908. }
  909. }
  910. /**
  911. * Checks to see if we have some text rendering commands open
  912. * still and writes out the TJ command to the stream if we do
  913. */
  914. protected void closeText() {
  915. if (textOpen) {
  916. currentStream.add("] TJ\n");
  917. textOpen = false;
  918. prevWordX = 0;
  919. prevWordY = 0;
  920. }
  921. }
  922. private void updateColor(ColorType col, boolean fill, StringBuffer pdf) {
  923. Color newCol = new Color(col.getRed(), col.getGreen(), col.getBlue());
  924. boolean update = false;
  925. if (fill) {
  926. update = currentState.setBackColor(newCol);
  927. } else {
  928. update = currentState.setColor(newCol);
  929. }
  930. if (update) {
  931. PDFColor color = new PDFColor((double)col.getRed(),
  932. (double)col.getGreen(),
  933. (double)col.getBlue());
  934. closeText();
  935. if (pdf != null) {
  936. pdf.append(color.getColorSpaceOut(fill));
  937. } else {
  938. currentStream.add(color.getColorSpaceOut(fill));
  939. }
  940. }
  941. }
  942. private void updateFont(String name, int size, StringBuffer pdf) {
  943. if ((!name.equals(this.currentFontName))
  944. || (size != this.currentFontSize)) {
  945. closeText();
  946. this.currentFontName = name;
  947. this.currentFontSize = size;
  948. pdf = pdf.append("/" + name + " " + ((float) size / 1000f)
  949. + " Tf\n");
  950. }
  951. }
  952. /**
  953. * @see org.apache.fop.render.AbstractRenderer#renderImage(Image, Rectangle2D)
  954. */
  955. public void renderImage(Image image, Rectangle2D pos) {
  956. String url = image.getURL();
  957. putImage(url, pos);
  958. }
  959. /**
  960. * Adds a PDF XObject (a bitmap) to the PDF that will later be referenced.
  961. * @param url URL of the bitmap
  962. * @param pos Position of the bitmap
  963. */
  964. protected void putImage(String url, Rectangle2D pos) {
  965. PDFXObject xobject = pdfDoc.getImage(url);
  966. if (xobject != null) {
  967. int w = (int) pos.getWidth() / 1000;
  968. int h = (int) pos.getHeight() / 1000;
  969. placeImage((int) pos.getX() / 1000,
  970. (int) pos.getY() / 1000, w, h, xobject.getXNumber());
  971. return;
  972. }
  973. ImageFactory fact = ImageFactory.getInstance();
  974. FopImage fopimage = fact.getImage(url, userAgent);
  975. if (fopimage == null) {
  976. return;
  977. }
  978. if (!fopimage.load(FopImage.DIMENSIONS, userAgent)) {
  979. return;
  980. }
  981. String mime = fopimage.getMimeType();
  982. if ("text/xml".equals(mime)) {
  983. if (!fopimage.load(FopImage.ORIGINAL_DATA, userAgent)) {
  984. return;
  985. }
  986. Document doc = ((XMLImage) fopimage).getDocument();
  987. String ns = ((XMLImage) fopimage).getNameSpace();
  988. renderDocument(doc, ns, pos);
  989. } else if ("image/svg+xml".equals(mime)) {
  990. if (!fopimage.load(FopImage.ORIGINAL_DATA, userAgent)) {
  991. return;
  992. }
  993. Document doc = ((XMLImage) fopimage).getDocument();
  994. String ns = ((XMLImage) fopimage).getNameSpace();
  995. renderDocument(doc, ns, pos);
  996. } else if ("image/eps".equals(mime)) {
  997. if (!fopimage.load(FopImage.ORIGINAL_DATA, userAgent)) {
  998. return;
  999. }
  1000. FopPDFImage pdfimage = new FopPDFImage(fopimage, url);
  1001. int xobj = pdfDoc.addImage(currentContext, pdfimage).getXNumber();
  1002. fact.releaseImage(url, userAgent);
  1003. } else if ("image/jpeg".equals(mime)) {
  1004. if (!fopimage.load(FopImage.ORIGINAL_DATA, userAgent)) {
  1005. return;
  1006. }
  1007. FopPDFImage pdfimage = new FopPDFImage(fopimage, url);
  1008. int xobj = pdfDoc.addImage(currentContext, pdfimage).getXNumber();
  1009. fact.releaseImage(url, userAgent);
  1010. int w = (int) pos.getWidth() / 1000;
  1011. int h = (int) pos.getHeight() / 1000;
  1012. placeImage((int) pos.getX() / 1000,
  1013. (int) pos.getY() / 1000, w, h, xobj);
  1014. } else {
  1015. if (!fopimage.load(FopImage.BITMAP, userAgent)) {
  1016. return;
  1017. }
  1018. FopPDFImage pdfimage = new FopPDFImage(fopimage, url);
  1019. int xobj = pdfDoc.addImage(currentContext, pdfimage).getXNumber();
  1020. fact.releaseImage(url, userAgent);
  1021. int w = (int) pos.getWidth() / 1000;
  1022. int h = (int) pos.getHeight() / 1000;
  1023. placeImage((int) pos.getX() / 1000,
  1024. (int) pos.getY() / 1000, w, h, xobj);
  1025. }
  1026. // output new data
  1027. try {
  1028. this.pdfDoc.output(ostream);
  1029. } catch (IOException ioe) {
  1030. // ioexception will be caught later
  1031. }
  1032. }
  1033. /**
  1034. * Places a previously registered image at a certain place on the page.
  1035. * @param x X coordinate
  1036. * @param y Y coordinate
  1037. * @param w width for image
  1038. * @param h height for image
  1039. * @param xobj object number of the referenced image
  1040. */
  1041. protected void placeImage(int x, int y, int w, int h, int xobj) {
  1042. saveGraphicsState();
  1043. currentStream.add(((float) w) + " 0 0 "
  1044. + ((float) -h) + " "
  1045. + (((float) currentBlockIPPosition) / 1000f + x) + " "
  1046. + (((float)(currentBPPosition + 1000 * h)) / 1000f
  1047. + y) + " cm\n" + "/Im" + xobj + " Do\n");
  1048. restoreGraphicsState();
  1049. }
  1050. /**
  1051. * @see org.apache.fop.render.AbstractRenderer#renderForeignObject(ForeignObject, Rectangle2D)
  1052. */
  1053. public void renderForeignObject(ForeignObject fo, Rectangle2D pos) {
  1054. Document doc = fo.getDocument();
  1055. String ns = fo.getNameSpace();
  1056. renderDocument(doc, ns, pos);
  1057. }
  1058. /**
  1059. * Renders an XML document (SVG for example).
  1060. * @param doc DOM document representing the XML document
  1061. * @param ns Namespace for the document
  1062. * @param pos Position on the page
  1063. */
  1064. public void renderDocument(Document doc, String ns, Rectangle2D pos) {
  1065. RendererContext context;
  1066. context = new RendererContext(MIME_TYPE);
  1067. context.setUserAgent(userAgent);
  1068. context.setProperty(PDFXMLHandler.PDF_DOCUMENT, pdfDoc);
  1069. context.setProperty(PDFXMLHandler.OUTPUT_STREAM, ostream);
  1070. context.setProperty(PDFXMLHandler.PDF_STATE, currentState);
  1071. context.setProperty(PDFXMLHandler.PDF_PAGE, currentPage);
  1072. context.setProperty(PDFXMLHandler.PDF_CONTEXT,
  1073. currentContext == null ? currentPage : currentContext);
  1074. context.setProperty(PDFXMLHandler.PDF_CONTEXT, currentContext);
  1075. context.setProperty(PDFXMLHandler.PDF_STREAM, currentStream);
  1076. context.setProperty(PDFXMLHandler.PDF_XPOS,
  1077. new Integer(currentBlockIPPosition + (int) pos.getX()));
  1078. context.setProperty(PDFXMLHandler.PDF_YPOS,
  1079. new Integer(currentBPPosition + (int) pos.getY()));
  1080. context.setProperty(PDFXMLHandler.PDF_FONT_INFO, fontInfo);
  1081. context.setProperty(PDFXMLHandler.PDF_FONT_NAME, currentFontName);
  1082. context.setProperty(PDFXMLHandler.PDF_FONT_SIZE,
  1083. new Integer(currentFontSize));
  1084. context.setProperty(PDFXMLHandler.PDF_WIDTH,
  1085. new Integer((int) pos.getWidth()));
  1086. context.setProperty(PDFXMLHandler.PDF_HEIGHT,
  1087. new Integer((int) pos.getHeight()));
  1088. renderXML(userAgent, context, doc, ns);
  1089. }
  1090. /**
  1091. * Render an inline viewport.
  1092. * This renders an inline viewport by clipping if necessary.
  1093. * @param viewport the viewport to handle
  1094. */
  1095. public void renderViewport(Viewport viewport) {
  1096. closeText();
  1097. float x = currentBlockIPPosition / 1000f;
  1098. float y = (currentBPPosition + viewport.getOffset()) / 1000f;
  1099. float width = viewport.getWidth() / 1000f;
  1100. float height = viewport.getHeight() / 1000f;
  1101. drawBackAndBorders(viewport, x, y, width, height);
  1102. endTextObject();
  1103. if (viewport.getClip()) {
  1104. saveGraphicsState();;
  1105. clip(x, y, width, height);
  1106. }
  1107. super.renderViewport(viewport);
  1108. if (viewport.getClip()) {
  1109. restoreGraphicsState();
  1110. }
  1111. beginTextObject();
  1112. }
  1113. /**
  1114. * Render leader area.
  1115. * This renders a leader area which is an area with a rule.
  1116. * @param area the leader area to render
  1117. */
  1118. public void renderLeader(Leader area) {
  1119. closeText();
  1120. endTextObject();
  1121. saveGraphicsState();
  1122. int style = area.getRuleStyle();
  1123. boolean alt = false;
  1124. switch(style) {
  1125. case RuleStyle.SOLID:
  1126. currentStream.add("[] 0 d\n");
  1127. break;
  1128. case RuleStyle.DOTTED:
  1129. currentStream.add("[2] 0 d\n");
  1130. break;
  1131. case RuleStyle.DASHED:
  1132. currentStream.add("[6 4] 0 d\n");
  1133. break;
  1134. case RuleStyle.DOUBLE:
  1135. case RuleStyle.GROOVE:
  1136. case RuleStyle.RIDGE:
  1137. alt = true;
  1138. break;
  1139. }
  1140. float startx = ((float) currentBlockIPPosition) / 1000f;
  1141. float starty = ((currentBPPosition + area.getOffset()) / 1000f);
  1142. float endx = (currentBlockIPPosition + area.getWidth()) / 1000f;
  1143. if (!alt) {
  1144. currentStream.add(area.getRuleThickness() / 1000f + " w\n");
  1145. drawLine(startx, starty, endx, starty);
  1146. } else {
  1147. if (style == RuleStyle.DOUBLE) {
  1148. float third = area.getRuleThickness() / 3000f;
  1149. currentStream.add(third + " w\n");
  1150. drawLine(startx, starty, endx, starty);
  1151. drawLine(startx, (starty + 2 * third), endx, (starty + 2 * third));
  1152. } else {
  1153. float half = area.getRuleThickness() / 2000f;
  1154. currentStream.add("1 g\n");
  1155. currentStream.add(startx + " " + starty + " m\n");
  1156. currentStream.add(endx + " " + starty + " l\n");
  1157. currentStream.add(endx + " " + (starty + 2 * half) + " l\n");
  1158. currentStream.add(startx + " " + (starty + 2 * half) + " l\n");
  1159. currentStream.add("h\n");
  1160. currentStream.add("f\n");
  1161. if (style == RuleStyle.GROOVE) {
  1162. currentStream.add("0 g\n");
  1163. currentStream.add(startx + " " + starty + " m\n");
  1164. currentStream.add(endx + " " + starty + " l\n");
  1165. currentStream.add(endx + " " + (starty + half) + " l\n");
  1166. currentStream.add((startx + half) + " " + (starty + half) + " l\n");
  1167. currentStream.add(startx + " " + (starty + 2 * half) + " l\n");
  1168. } else {
  1169. currentStream.add("0 g\n");
  1170. currentStream.add(endx + " " + starty + " m\n");
  1171. currentStream.add(endx + " " + (starty + 2 * half) + " l\n");
  1172. currentStream.add(startx + " " + (starty + 2 * half) + " l\n");
  1173. currentStream.add(startx + " " + (starty + half) + " l\n");
  1174. currentStream.add((endx - half) + " " + (starty + half) + " l\n");
  1175. }
  1176. currentStream.add("h\n");
  1177. currentStream.add("f\n");
  1178. }
  1179. }
  1180. restoreGraphicsState();
  1181. beginTextObject();
  1182. super.renderLeader(area);
  1183. }
  1184. }