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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899
  1. /*
  2. * $Id$
  3. * Copyright (C) 2001-2002 The Apache Software Foundation. All rights reserved.
  4. * For details on use and redistribution please refer to the
  5. * LICENSE file included with these sources.
  6. */
  7. package org.apache.fop.render.pdf;
  8. // FOP
  9. import org.apache.fop.render.PrintRenderer;
  10. import org.apache.fop.render.XMLHandler;
  11. import org.apache.fop.render.RendererContext;
  12. import org.apache.fop.fo.FOUserAgent;
  13. import org.apache.fop.image.*;
  14. import org.apache.fop.apps.FOPException;
  15. import org.apache.fop.apps.Version;
  16. import org.apache.fop.fo.properties.*;
  17. import org.apache.fop.datatypes.*;
  18. import org.apache.fop.pdf.*;
  19. import org.apache.fop.image.*;
  20. import org.apache.fop.extensions.*;
  21. import org.apache.fop.render.pdf.fonts.LazyFont;
  22. import org.apache.fop.area.*;
  23. import org.apache.fop.area.inline.*;
  24. import org.apache.fop.area.inline.Character;
  25. import org.apache.fop.layout.FontState;
  26. import org.apache.fop.layout.FontInfo;
  27. import org.apache.fop.layout.FontMetric;
  28. import org.w3c.dom.Document;
  29. // Java
  30. import java.io.IOException;
  31. import java.io.OutputStream;
  32. import java.awt.geom.Rectangle2D;
  33. import java.awt.Rectangle;
  34. import java.awt.geom.AffineTransform;
  35. import java.util.HashMap;
  36. import java.util.List;
  37. /*
  38. TODO:
  39. word rendering and optimistion
  40. pdf state optimisation
  41. line and border
  42. leader
  43. background pattern
  44. orientation
  45. writing mode
  46. text decoration
  47. */
  48. /**
  49. * Renderer that renders areas to PDF
  50. *
  51. */
  52. public class PDFRenderer extends PrintRenderer {
  53. public static final String mimeType = "application/pdf";
  54. /**
  55. * the PDF Document being created
  56. */
  57. protected PDFDocument pdfDoc;
  58. // map of pages using the PageViewport as the key
  59. // this is used for prepared pages that cannot be immediately
  60. // rendered
  61. protected HashMap pages = null;
  62. // page references are stored using the PageViewport as the key
  63. // when a reference is made the PageViewport is used
  64. // for pdf this means we need the pdf page reference
  65. protected HashMap pageReferences = new HashMap();
  66. protected String producer;
  67. protected OutputStream ostream;
  68. /**
  69. * the /Resources object of the PDF document being created
  70. */
  71. protected PDFResources pdfResources;
  72. /**
  73. * the current stream to add PDF commands to
  74. */
  75. PDFStream currentStream;
  76. /**
  77. * the current annotation list to add annotations to
  78. */
  79. PDFAnnotList currentAnnotList;
  80. /**
  81. * the current page to add annotations to
  82. */
  83. PDFPage currentPage;
  84. // drawing state
  85. PDFState currentState = null;
  86. PDFColor currentColor;
  87. String currentFontName = "";
  88. int currentFontSize = 0;
  89. int pageHeight;
  90. /**
  91. * true if a TJ command is left to be written
  92. */
  93. boolean textOpen = false;
  94. /**
  95. * the previous Y coordinate of the last word written.
  96. * Used to decide if we can draw the next word on the same line.
  97. */
  98. int prevWordY = 0;
  99. /**
  100. * the previous X coordinate of the last word written.
  101. * used to calculate how much space between two words
  102. */
  103. int prevWordX = 0;
  104. /**
  105. * The width of the previous word. Used to calculate space between
  106. */
  107. int prevWordWidth = 0;
  108. /**
  109. * reusable word area string buffer to reduce memory usage
  110. */
  111. private StringBuffer _wordAreaPDF = new StringBuffer();
  112. /**
  113. * create the PDF renderer
  114. */
  115. public PDFRenderer() {
  116. }
  117. /**
  118. * set the PDF document's producer
  119. *
  120. * @param producer string indicating application producing PDF
  121. */
  122. public void setProducer(String prod) {
  123. producer = prod;
  124. }
  125. public void setUserAgent(FOUserAgent agent) {
  126. super.setUserAgent(agent);
  127. PDFXMLHandler xmlHandler = new PDFXMLHandler();
  128. //userAgent.setDefaultXMLHandler(mimeType, xmlHandler);
  129. String svg = "http://www.w3.org/2000/svg";
  130. userAgent.addXMLHandler(mimeType, svg, xmlHandler);
  131. }
  132. public void startRenderer(OutputStream stream) throws IOException {
  133. ostream = stream;
  134. this.pdfDoc = new PDFDocument(Version.getVersion());
  135. this.pdfDoc.setProducer(producer);
  136. pdfDoc.outputHeader(stream);
  137. }
  138. public void stopRenderer() throws IOException {
  139. FontSetup.addToResources(pdfDoc, pdfDoc.getResources(), fontInfo);
  140. pdfDoc.outputTrailer(ostream);
  141. this.pdfDoc = null;
  142. ostream = null;
  143. }
  144. public boolean supportsOutOfOrder() {
  145. return true;
  146. }
  147. public void renderExtension(TreeExt ext) {
  148. // render bookmark extension
  149. if (ext instanceof BookmarkData) {
  150. renderRootExtensions((BookmarkData)ext);
  151. }
  152. }
  153. protected void renderRootExtensions(BookmarkData bookmarks) {
  154. for (int i = 0; i < bookmarks.getCount(); i++) {
  155. BookmarkData ext = bookmarks.getSubData(i);
  156. renderOutline(ext, null);
  157. }
  158. }
  159. private void renderOutline(BookmarkData outline, PDFOutline parentOutline) {
  160. PDFOutline outlineRoot = pdfDoc.getOutlineRoot();
  161. PDFOutline pdfOutline = null;
  162. String intDest = (String)pageReferences.get(outline.getPage());
  163. if (parentOutline == null) {
  164. pdfOutline = pdfDoc.makeOutline(outlineRoot,
  165. outline.getLabel(), intDest);
  166. } else {
  167. PDFOutline pdfParentOutline = parentOutline;
  168. pdfOutline = pdfDoc.makeOutline(pdfParentOutline,
  169. outline.getLabel(), intDest);
  170. }
  171. for (int i = 0; i < outline.getCount(); i++) {
  172. renderOutline(outline.getSubData(i), pdfOutline);
  173. }
  174. }
  175. public void startPageSequence(Title seqTitle) {
  176. if (seqTitle != null) {
  177. String str = convertTitleToString(seqTitle);
  178. PDFInfo info = this.pdfDoc.getInfo();
  179. info.setTitle(str);
  180. }
  181. }
  182. /**
  183. * The pdf page is prepared by making the page.
  184. * The page is made in the pdf document without any contents
  185. * and then stored to add the contents later.
  186. * The page objects is stored using the area tree PageViewport
  187. * as a key.
  188. */
  189. public void preparePage(PageViewport page) {
  190. this.pdfResources = this.pdfDoc.getResources();
  191. Rectangle2D bounds = page.getViewArea();
  192. double w = bounds.getWidth();
  193. double h = bounds.getHeight();
  194. currentPage = this.pdfDoc.makePage(this.pdfResources,
  195. (int) Math.round(w / 1000), (int) Math.round(h / 1000));
  196. if (pages == null) {
  197. pages = new HashMap();
  198. }
  199. pages.put(page, currentPage);
  200. pageReferences.put(page, currentPage.referencePDF());
  201. }
  202. /**
  203. * This method creates a pdf stream for the current page
  204. * uses it as the contents of a new page. The page is written
  205. * immediately to the output stream.
  206. */
  207. public void renderPage(PageViewport page) throws IOException,
  208. FOPException {
  209. if (pages != null &&
  210. (currentPage = (PDFPage) pages.get(page)) != null) {
  211. pages.remove(page);
  212. } else {
  213. this.pdfResources = this.pdfDoc.getResources();
  214. Rectangle2D bounds = page.getViewArea();
  215. double w = bounds.getWidth();
  216. double h = bounds.getHeight();
  217. pageHeight = (int) h;
  218. currentPage = this.pdfDoc.makePage(this.pdfResources,
  219. (int) Math.round(w / 1000), (int) Math.round(h / 1000));
  220. pageReferences.put(page, currentPage.referencePDF());
  221. }
  222. currentStream =
  223. this.pdfDoc.makeStream(PDFStream.CONTENT_FILTER, false);
  224. currentState = new PDFState();
  225. currentState.setTransform( new AffineTransform(1, 0, 0, -1, 0,
  226. (int) Math.round(pageHeight / 1000)));
  227. // Transform origin at top left to origin at bottom left
  228. currentStream.add("1 0 0 -1 0 " +
  229. (int) Math.round(pageHeight / 1000) + " cm\n");
  230. //currentStream.add("BT\n");
  231. currentFontName = "";
  232. Page p = page.getPage();
  233. renderPageAreas(p);
  234. //currentStream.add("ET\n");
  235. this.pdfDoc.addStream(currentStream);
  236. currentPage.setContents(currentStream);
  237. this.pdfDoc.addPage(currentPage);
  238. this.pdfDoc.output(ostream);
  239. }
  240. protected void startVParea(CTM ctm) {
  241. // Set the given CTM in the graphics state
  242. currentState.push();
  243. currentState.setTransform(
  244. new AffineTransform(CTMHelper.toPDFArray(ctm)));
  245. currentStream.add("q\n");
  246. // multiply with current CTM
  247. currentStream.add(CTMHelper.toPDFString(ctm) + " cm\n");
  248. // Set clip?
  249. currentStream.add("BT\n");
  250. }
  251. protected void endVParea() {
  252. currentStream.add("ET\n");
  253. currentStream.add("Q\n");
  254. currentState.pop();
  255. }
  256. protected void renderRegion(RegionReference region) {
  257. // Draw a rectangle so we can see it!
  258. // x=0,y=0,w=ipd,h=bpd
  259. currentFontName = "";
  260. super.renderRegion(region);
  261. }
  262. protected void renderBlockViewport(BlockViewport bv, List children) {
  263. // clip and position viewport if necessary
  264. // save positions
  265. int saveIP = currentIPPosition;
  266. int saveBP = currentBPPosition;
  267. CTM ctm = bv.getCTM();
  268. closeText();
  269. if (bv.getPositioning() == Block.ABSOLUTE) {
  270. currentIPPosition = 0;
  271. currentBPPosition = 0;
  272. currentStream.add("ET\n");
  273. if (bv.getClip()) {
  274. Rectangle2D rect = bv.getBounds();
  275. currentStream.add("q\n");
  276. float x = (float)rect.getX() / 1000f;
  277. float y = (float)rect.getY() / 1000f;
  278. float width = (float)rect.getWidth() / 1000f;
  279. float height = (float)rect.getHeight() / 1000f;
  280. clip(x, y, width, height);
  281. }
  282. startVParea(ctm);
  283. renderBlocks(children);
  284. endVParea();
  285. if (bv.getClip()) {
  286. currentStream.add("Q\n");
  287. }
  288. currentStream.add("BT\n");
  289. // clip if necessary
  290. currentIPPosition = saveIP;
  291. currentBPPosition = saveBP;
  292. } else {
  293. Rectangle2D rect = bv.getBounds();
  294. if (ctm != null) {
  295. currentIPPosition = 0;
  296. currentBPPosition = 0;
  297. currentStream.add("ET\n");
  298. double[] vals = ctm.toArray();
  299. boolean aclock = vals[2] == 1.0;
  300. if (vals[2] == 1.0) {
  301. ctm = ctm.translate(-saveBP - rect.getHeight(), -saveIP);
  302. } else if (vals[0] == -1.0) {
  303. ctm = ctm.translate(-saveIP - rect.getWidth(), -saveBP - rect.getHeight());
  304. } else {
  305. ctm = ctm.translate(saveBP, saveIP - rect.getWidth());
  306. }
  307. }
  308. if (bv.getClip()) {
  309. currentStream.add("q\n");
  310. float x = (float)rect.getX() / 1000f;
  311. float y = (float)rect.getY() / 1000f;
  312. float width = (float)rect.getWidth() / 1000f;
  313. float height = (float)rect.getHeight() / 1000f;
  314. clip(x, y, width, height);
  315. }
  316. if (ctm != null) {
  317. startVParea(ctm);
  318. }
  319. renderBlocks(children);
  320. if (ctm != null) {
  321. endVParea();
  322. }
  323. if (bv.getClip()) {
  324. currentStream.add("Q\n");
  325. }
  326. if (ctm != null) {
  327. currentStream.add("BT\n");
  328. }
  329. // clip if necessary
  330. if (rect != null) {
  331. currentIPPosition = saveIP;
  332. currentBPPosition = saveBP;
  333. currentBPPosition += (int)(rect.getHeight());
  334. }
  335. }
  336. }
  337. /**
  338. * Clip an area.
  339. * write a clipping operation given coordinates in the current
  340. * transform.
  341. * @param x the x coordinate
  342. * @param y the y coordinate
  343. * @param width the width of the area
  344. * @param height the height of the area
  345. */
  346. protected void clip(float x, float y, float width, float height) {
  347. currentStream.add(x + " " + y + " m\n");
  348. currentStream.add((x + width) + " " + y + " l\n");
  349. currentStream.add((x + width) + " " + (y + height) + " l\n");
  350. currentStream.add(x + " " + (y + height) + " l\n");
  351. currentStream.add("h\n");
  352. currentStream.add("W\n");
  353. currentStream.add("n\n");
  354. }
  355. protected void renderLineArea(LineArea line) {
  356. super.renderLineArea(line);
  357. closeText();
  358. }
  359. /**
  360. * Render inline parent area.
  361. * For pdf this handles the inline parent area traits such as
  362. * links, border, background.
  363. * @param ip the inline parent area
  364. */
  365. public void renderInlineParent(InlineParent ip) {
  366. Object tr = ip.getTrait(Trait.INTERNAL_LINK);
  367. boolean internal = false;
  368. String dest = null;
  369. if (tr == null) {
  370. dest = (String)ip.getTrait(Trait.EXTERNAL_LINK);
  371. } else {
  372. PageViewport pv = (PageViewport)tr;
  373. dest = (String)pageReferences.get(pv);
  374. internal = true;
  375. }
  376. if (dest != null) {
  377. float start = currentBlockIPPosition;
  378. float top = (ip.getOffset() + currentBPPosition) / 1000f;
  379. float height = ip.getHeight() / 1000f;
  380. super.renderInlineParent(ip);
  381. float width = (currentBlockIPPosition - start) / 1000f;
  382. start = start / 1000f;
  383. // add link to pdf document
  384. Rectangle2D rect = new Rectangle2D.Float(start, top, width, height);
  385. // transform rect to absolute coords
  386. AffineTransform transform = currentState.getTransform();
  387. rect = transform.createTransformedShape(rect).getBounds();
  388. rect = new Rectangle2D.Double(rect.getX(), rect.getY() + rect.getHeight(), rect.getWidth(), rect.getHeight());
  389. int type = internal ? PDFLink.INTERNAL : PDFLink.EXTERNAL;
  390. PDFLink pdflink = pdfDoc.makeLink(rect, dest, type);
  391. currentPage.addAnnotation(pdflink);
  392. } else {
  393. super.renderInlineParent(ip);
  394. }
  395. }
  396. public void renderCharacter(Character ch) {
  397. super.renderCharacter(ch);
  398. }
  399. public void renderWord(Word word) {
  400. StringBuffer pdf = new StringBuffer();
  401. String name = (String) word.getTrait(Trait.FONT_NAME);
  402. int size = ((Integer) word.getTrait(Trait.FONT_SIZE)).intValue();
  403. // This assumes that *all* CIDFonts use a /ToUnicode mapping
  404. Font f = (Font) fontInfo.getFonts().get(name);
  405. boolean useMultiByte = f.isMultiByte();
  406. // String startText = useMultiByte ? "<FEFF" : "(";
  407. String startText = useMultiByte ? "<" : "(";
  408. String endText = useMultiByte ? "> " : ") ";
  409. updateFont(name, size, pdf);
  410. updateColor(true, pdf);
  411. int rx = currentBlockIPPosition;
  412. // int bl = pageHeight - currentBPPosition;
  413. int bl = currentBPPosition + word.getOffset();
  414. // Set letterSpacing
  415. //float ls = fs.getLetterSpacing() / this.currentFontSize;
  416. //pdf.append(ls).append(" Tc\n");
  417. if (!textOpen || bl != prevWordY) {
  418. closeText();
  419. pdf.append("1 0 0 -1 " + (rx / 1000f) + " "
  420. + (bl / 1000f) + " Tm [" + startText);
  421. prevWordY = bl;
  422. textOpen = true;
  423. } else {
  424. // express the space between words in thousandths of an em
  425. int space = prevWordX - rx + prevWordWidth;
  426. float emDiff = (float) space / (float) currentFontSize * 1000f;
  427. // this prevents a problem in Acrobat Reader and other viewers
  428. // where large numbers cause text to disappear or default to
  429. // a limit
  430. if (emDiff < -33000) {
  431. closeText();
  432. pdf.append("1 0 0 1 " + (rx / 1000f) + " "
  433. + (bl / 1000f) + " Tm [" + startText);
  434. textOpen = true;
  435. } else {
  436. pdf.append(Float.toString(emDiff));
  437. pdf.append(" ");
  438. pdf.append(startText);
  439. }
  440. }
  441. prevWordWidth = word.getWidth();
  442. prevWordX = rx;
  443. String s = word.getWord();
  444. FontMetric metrics = fontInfo.getMetricsFor(name);
  445. FontState fs = new FontState(name, metrics, size);
  446. escapeText(s, fs, useMultiByte, pdf);
  447. pdf.append(endText);
  448. currentStream.add(pdf.toString());
  449. super.renderWord(word);
  450. }
  451. public void escapeText(String s, FontState fs,
  452. boolean useMultiByte, StringBuffer pdf) {
  453. String startText = useMultiByte ? "<" : "(";
  454. String endText = useMultiByte ? "> " : ") ";
  455. boolean kerningAvailable = false;
  456. HashMap kerning = null;
  457. kerning = fs.getKerning();
  458. if (kerning != null && !kerning.isEmpty()) {
  459. kerningAvailable = true;
  460. }
  461. int l = s.length();
  462. for (int i = 0; i < l; i++) {
  463. char ch = fs.mapChar(s.charAt(i));
  464. if (!useMultiByte) {
  465. if (ch > 127) {
  466. pdf.append("\\");
  467. pdf.append(Integer.toOctalString((int) ch));
  468. } else {
  469. switch (ch) {
  470. case '(':
  471. case ')':
  472. case '\\':
  473. pdf.append("\\");
  474. break;
  475. }
  476. pdf.append(ch);
  477. }
  478. } else {
  479. pdf.append(getUnicodeString(ch));
  480. }
  481. if (kerningAvailable && (i + 1) < l) {
  482. addKerning(pdf, (new Integer((int) ch)),
  483. (new Integer((int) fs.mapChar(s.charAt(i + 1)))
  484. ), kerning, startText, endText);
  485. }
  486. }
  487. }
  488. /**
  489. * Convert a char to a multibyte hex representation
  490. */
  491. private String getUnicodeString(char c) {
  492. StringBuffer buf = new StringBuffer(4);
  493. byte[] uniBytes = null;
  494. try {
  495. char[] a = {c};
  496. uniBytes = new String(a).getBytes("UnicodeBigUnmarked");
  497. } catch (java.io.UnsupportedEncodingException e) {
  498. // This should never fail
  499. }
  500. for (int i = 0; i < uniBytes.length; i++) {
  501. int b = (uniBytes[i] < 0) ? (int)(256 + uniBytes[i]) :
  502. (int) uniBytes[i];
  503. String hexString = Integer.toHexString(b);
  504. if (hexString.length() == 1) {
  505. buf = buf.append("0" + hexString);
  506. } else {
  507. buf = buf.append(hexString);
  508. }
  509. }
  510. return buf.toString();
  511. }
  512. private void addKerning(StringBuffer buf, Integer ch1, Integer ch2,
  513. HashMap kerning, String startText, String endText) {
  514. HashMap kernPair = (HashMap) kerning.get(ch1);
  515. if (kernPair != null) {
  516. Integer width = (Integer) kernPair.get(ch2);
  517. if (width != null) {
  518. buf.append(endText).append(-
  519. (width.intValue())).append(' ').append(startText);
  520. }
  521. }
  522. }
  523. /**
  524. * Checks to see if we have some text rendering commands open
  525. * still and writes out the TJ command to the stream if we do
  526. */
  527. private void closeText() {
  528. if (textOpen) {
  529. currentStream.add("] TJ\n");
  530. textOpen = false;
  531. prevWordX = 0;
  532. prevWordY = 0;
  533. }
  534. }
  535. private void updateColor(boolean fill, StringBuffer pdf) {
  536. /*PDFColor areaColor = null;
  537. if (this.currentFill instanceof PDFColor) {
  538. areaColor = (PDFColor)this.currentFill;
  539. }
  540. if (areaColor == null || areaColor.red() != (double)area.getRed()
  541. || areaColor.green() != (double)area.getGreen()
  542. || areaColor.blue() != (double)area.getBlue()) {
  543. areaColor = new PDFColor((double)area.getRed(),
  544. (double)area.getGreen(),
  545. (double)area.getBlue());
  546. closeText();
  547. this.currentFill = areaColor;
  548. pdf.append(this.currentFill.getColorSpaceOut(true));
  549. }*/
  550. }
  551. private void updateFont(String name, int size, StringBuffer pdf) {
  552. if ((!name.equals(this.currentFontName))
  553. || (size != this.currentFontSize)) {
  554. closeText();
  555. this.currentFontName = name;
  556. this.currentFontSize = size;
  557. pdf = pdf.append("/" + name + " " + ((float) size / 1000f) +
  558. " Tf\n");
  559. }
  560. }
  561. public void renderImage(Image image, Rectangle2D pos) {
  562. String url = image.getURL();
  563. PDFXObject xobject = pdfDoc.getImage(url);
  564. if (xobject != null) {
  565. int w = (int) pos.getWidth() / 1000;
  566. int h = (int) pos.getHeight() / 1000;
  567. placeImage((int) pos.getX() / 1000,
  568. (int) pos.getY() / 1000, w, h, xobject.getXNumber());
  569. return;
  570. }
  571. ImageFactory fact = ImageFactory.getInstance();
  572. FopImage fopimage = fact.getImage(url, userAgent);
  573. if (fopimage == null) {
  574. return;
  575. }
  576. if (!fopimage.load(FopImage.DIMENSIONS, userAgent)) {
  577. return;
  578. }
  579. String mime = fopimage.getMimeType();
  580. if ("text/xml".equals(mime)) {
  581. if (!fopimage.load(FopImage.ORIGINAL_DATA, userAgent)) {
  582. return;
  583. }
  584. Document doc = ((XMLImage) fopimage).getDocument();
  585. String ns = ((XMLImage) fopimage).getNameSpace();
  586. renderDocument(doc, ns, pos);
  587. } else if ("image/svg+xml".equals(mime)) {
  588. if (!fopimage.load(FopImage.ORIGINAL_DATA, userAgent)) {
  589. return;
  590. }
  591. Document doc = ((XMLImage) fopimage).getDocument();
  592. String ns = ((XMLImage) fopimage).getNameSpace();
  593. renderDocument(doc, ns, pos);
  594. } else if ("image/eps".equals(mime)) {
  595. if (!fopimage.load(FopImage.ORIGINAL_DATA, userAgent)) {
  596. return;
  597. }
  598. FopPDFImage pdfimage = new FopPDFImage(fopimage, url);
  599. int xobj = pdfDoc.addImage(null, pdfimage).getXNumber();
  600. fact.releaseImage(url, userAgent);
  601. } else if ("image/jpeg".equals(mime)) {
  602. if (!fopimage.load(FopImage.ORIGINAL_DATA, userAgent)) {
  603. return;
  604. }
  605. FopPDFImage pdfimage = new FopPDFImage(fopimage, url);
  606. int xobj = pdfDoc.addImage(null, pdfimage).getXNumber();
  607. fact.releaseImage(url, userAgent);
  608. int w = (int) pos.getWidth() / 1000;
  609. int h = (int) pos.getHeight() / 1000;
  610. placeImage((int) pos.getX() / 1000,
  611. (int) pos.getY() / 1000, w, h, xobj);
  612. } else {
  613. if (!fopimage.load(FopImage.BITMAP, userAgent)) {
  614. return;
  615. }
  616. FopPDFImage pdfimage = new FopPDFImage(fopimage, url);
  617. int xobj = pdfDoc.addImage(null, pdfimage).getXNumber();
  618. fact.releaseImage(url, userAgent);
  619. int w = (int) pos.getWidth() / 1000;
  620. int h = (int) pos.getHeight() / 1000;
  621. placeImage((int) pos.getX() / 1000,
  622. (int) pos.getY() / 1000, w, h, xobj);
  623. }
  624. // output new data
  625. try {
  626. this.pdfDoc.output(ostream);
  627. } catch (IOException ioe) {
  628. // ioexception will be caught later
  629. }
  630. }
  631. protected void placeImage(int x, int y, int w, int h, int xobj) {
  632. currentStream.add("q\n" + ((float) w) + " 0 0 "
  633. + ((float) - h) + " "
  634. + (((float) currentBlockIPPosition) / 1000f + x) + " "
  635. + (((float)(currentBPPosition + 1000 * h)) / 1000f
  636. + y) + " cm\n" + "/Im" + xobj + " Do\nQ\n");
  637. }
  638. public void renderForeignObject(ForeignObject fo, Rectangle2D pos) {
  639. Document doc = fo.getDocument();
  640. String ns = fo.getNameSpace();
  641. renderDocument(doc, ns, pos);
  642. }
  643. public void renderDocument(Document doc, String ns, Rectangle2D pos) {
  644. RendererContext context;
  645. context = new RendererContext(mimeType);
  646. context.setUserAgent(userAgent);
  647. context.setProperty(PDFXMLHandler.PDF_DOCUMENT, pdfDoc);
  648. context.setProperty(PDFXMLHandler.OUTPUT_STREAM, ostream);
  649. context.setProperty(PDFXMLHandler.PDF_STATE, currentState);
  650. context.setProperty(PDFXMLHandler.PDF_PAGE, currentPage);
  651. context.setProperty(PDFXMLHandler.PDF_STREAM, currentStream);
  652. context.setProperty(PDFXMLHandler.PDF_XPOS,
  653. new Integer(currentBlockIPPosition + (int) pos.getX()));
  654. context.setProperty(PDFXMLHandler.PDF_YPOS,
  655. new Integer(currentBPPosition + (int) pos.getY()));
  656. context.setProperty(PDFXMLHandler.PDF_FONT_INFO, fontInfo);
  657. context.setProperty(PDFXMLHandler.PDF_FONT_NAME, currentFontName);
  658. context.setProperty(PDFXMLHandler.PDF_FONT_SIZE,
  659. new Integer(currentFontSize));
  660. context.setProperty(PDFXMLHandler.PDF_WIDTH,
  661. new Integer((int) pos.getWidth()));
  662. context.setProperty(PDFXMLHandler.PDF_HEIGHT,
  663. new Integer((int) pos.getHeight()));
  664. userAgent.renderXML(context, doc, ns);
  665. }
  666. /**
  667. * Render an inline viewport.
  668. * This renders an inline viewport by clipping if necessary.
  669. * @param viewport the viewport to handle
  670. */
  671. public void renderViewport(Viewport viewport) {
  672. closeText();
  673. currentStream.add("ET\n");
  674. if (viewport.getClip()) {
  675. currentStream.add("q\n");
  676. float x = currentBlockIPPosition / 1000f;
  677. float y = (currentBPPosition + viewport.getOffset()) / 1000f;
  678. float width = viewport.getWidth() / 1000f;
  679. float height = viewport.getHeight() / 1000f;
  680. clip(x, y, width, height);
  681. }
  682. super.renderViewport(viewport);
  683. if (viewport.getClip()) {
  684. currentStream.add("Q\n");
  685. }
  686. currentStream.add("BT\n");
  687. }
  688. /**
  689. * Render leader area.
  690. * This renders a leader area which is an area with a rule.
  691. * @param area the leader area to render
  692. */
  693. public void renderLeader(Leader area) {
  694. closeText();
  695. currentStream.add("ET\n");
  696. currentStream.add("q\n");
  697. int style = area.getRuleStyle();
  698. boolean alt = false;
  699. switch(style) {
  700. case RuleStyle.SOLID:
  701. currentStream.add("[] 0 d\n");
  702. break;
  703. case RuleStyle.DOTTED:
  704. currentStream.add("[2] 0 d\n");
  705. break;
  706. case RuleStyle.DASHED:
  707. currentStream.add("[6 4] 0 d\n");
  708. break;
  709. case RuleStyle.DOUBLE:
  710. case RuleStyle.GROOVE:
  711. case RuleStyle.RIDGE:
  712. alt = true;
  713. break;
  714. }
  715. float startx = ((float) currentBlockIPPosition) / 1000f;
  716. float starty = ((currentBPPosition + area.getOffset()) / 1000f);
  717. float endx = (currentBlockIPPosition + area.getWidth()) / 1000f;
  718. if (!alt) {
  719. currentStream.add(area.getRuleThickness() / 1000f + " w\n");
  720. currentStream.add(startx + " " + starty + " m\n");
  721. currentStream.add(endx + " " + starty + " l\n");
  722. currentStream.add("S\n");
  723. } else {
  724. if (style == RuleStyle.DOUBLE) {
  725. float third = area.getRuleThickness() / 3000f;
  726. currentStream.add(third + " w\n");
  727. currentStream.add(startx + " " + starty + " m\n");
  728. currentStream.add(endx + " " + starty + " l\n");
  729. currentStream.add("S\n");
  730. currentStream.add(startx + " " + (starty + 2 * third) + " m\n");
  731. currentStream.add(endx + " " + (starty + 2 * third) + " l\n");
  732. currentStream.add("S\n");
  733. } else {
  734. float half = area.getRuleThickness() / 2000f;
  735. currentStream.add("1 g\n");
  736. currentStream.add(startx + " " + starty + " m\n");
  737. currentStream.add(endx + " " + starty + " l\n");
  738. currentStream.add(endx + " " + (starty + 2 * half) + " l\n");
  739. currentStream.add(startx + " " + (starty + 2 * half) + " l\n");
  740. currentStream.add("h\n");
  741. currentStream.add("f\n");
  742. if (style == RuleStyle.GROOVE) {
  743. currentStream.add("0 g\n");
  744. currentStream.add(startx + " " + starty + " m\n");
  745. currentStream.add(endx + " " + starty + " l\n");
  746. currentStream.add(endx + " " + (starty + half) + " l\n");
  747. currentStream.add((startx + half) + " " + (starty + half) + " l\n");
  748. currentStream.add(startx + " " + (starty + 2 * half) + " l\n");
  749. } else {
  750. currentStream.add("0 g\n");
  751. currentStream.add(endx + " " + starty + " m\n");
  752. currentStream.add(endx + " " + (starty + 2 * half) + " l\n");
  753. currentStream.add(startx + " " + (starty + 2 * half) + " l\n");
  754. currentStream.add(startx + " " + (starty + half) + " l\n");
  755. currentStream.add((endx - half) + " " + (starty + half) + " l\n");
  756. }
  757. currentStream.add("h\n");
  758. currentStream.add("f\n");
  759. }
  760. }
  761. currentStream.add("Q\n");
  762. currentStream.add("BT\n");
  763. super.renderLeader(area);
  764. }
  765. }