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

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