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.

PSRenderer.java 37KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012
  1. /*
  2. * Copyright 1999-2005 The Apache Software Foundation.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. /* $Id$ */
  17. package org.apache.fop.render.ps;
  18. // Java
  19. import java.awt.Color;
  20. import java.awt.geom.Rectangle2D;
  21. import java.io.IOException;
  22. import java.io.LineNumberReader;
  23. import java.io.OutputStream;
  24. import java.util.Iterator;
  25. import java.util.List;
  26. import java.util.Map;
  27. import javax.xml.transform.Source;
  28. // FOP
  29. import org.apache.avalon.framework.configuration.Configuration;
  30. import org.apache.avalon.framework.configuration.ConfigurationException;
  31. import org.apache.fop.apps.FOPException;
  32. import org.apache.fop.area.Area;
  33. import org.apache.fop.area.BlockViewport;
  34. import org.apache.fop.area.CTM;
  35. import org.apache.fop.area.LineArea;
  36. import org.apache.fop.area.OffDocumentExtensionAttachment;
  37. import org.apache.fop.area.OffDocumentItem;
  38. import org.apache.fop.area.PageViewport;
  39. import org.apache.fop.area.RegionViewport;
  40. import org.apache.fop.area.Trait;
  41. import org.apache.fop.area.inline.AbstractTextArea;
  42. import org.apache.fop.area.inline.Character;
  43. import org.apache.fop.area.inline.ForeignObject;
  44. import org.apache.fop.area.inline.Image;
  45. import org.apache.fop.area.inline.InlineParent;
  46. import org.apache.fop.area.inline.TextArea;
  47. import org.apache.fop.datatypes.ColorType;
  48. import org.apache.fop.apps.FOUserAgent;
  49. import org.apache.fop.fo.Constants;
  50. import org.apache.fop.fo.extensions.ExtensionAttachment;
  51. import org.apache.fop.fonts.FontSetup;
  52. import org.apache.fop.fonts.Typeface;
  53. import org.apache.fop.image.EPSImage;
  54. import org.apache.fop.image.FopImage;
  55. import org.apache.fop.image.ImageFactory;
  56. import org.apache.fop.image.XMLImage;
  57. import org.apache.fop.render.AbstractPathOrientedRenderer;
  58. import org.apache.fop.render.RendererContext;
  59. import org.apache.fop.render.ps.extensions.PSSetupCode;
  60. import org.apache.fop.util.CharUtilities;
  61. import org.w3c.dom.Document;
  62. /**
  63. * Renderer that renders to PostScript.
  64. * <br>
  65. * This class currently generates PostScript Level 2 code. The only exception
  66. * is the FlateEncode filter which is a Level 3 feature. The filters in use
  67. * are hardcoded at the moment.
  68. * <br>
  69. * This class follows the Document Structuring Conventions (DSC) version 3.0.
  70. * If anyone modifies this renderer please make
  71. * sure to also follow the DSC to make it simpler to programmatically modify
  72. * the generated Postscript files (ex. extract pages etc.).
  73. * <br>
  74. * This renderer inserts FOP-specific comments into the PostScript stream which
  75. * may help certain users to do certain types of post-processing of the output.
  76. * These comments all start with "%FOP".
  77. *
  78. * @author <a href="mailto:fop-dev@xmlgraphics.apache.org">Apache FOP Development Team</a>
  79. * @version $Id$
  80. */
  81. public class PSRenderer extends AbstractPathOrientedRenderer {
  82. /** The MIME type for PostScript */
  83. public static final String MIME_TYPE = "application/postscript";
  84. /** The application producing the PostScript */
  85. private int currentPageNumber = 0;
  86. private boolean enableComments = true;
  87. private boolean autoRotateLandscape = false;
  88. /** The PostScript generator used to output the PostScript */
  89. protected PSGenerator gen;
  90. private boolean ioTrouble = false;
  91. private boolean inTextMode = false;
  92. private boolean firstPageSequenceReceived = false;
  93. /** Used to temporarily store PSSetupCode instance until they can be written. */
  94. private List setupCodeList;
  95. /** This is a map of PSResource instances of all fonts defined (key: font key) */
  96. private Map fontResources;
  97. /**
  98. * @see org.apache.avalon.framework.configuration.Configurable#configure(Configuration)
  99. */
  100. public void configure(Configuration cfg) throws ConfigurationException {
  101. super.configure(cfg);
  102. this.autoRotateLandscape = cfg.getChild("auto-rotate-landscape").getValueAsBoolean(false);
  103. //Font configuration
  104. List cfgFonts = FontSetup.buildFontListFromConfiguration(cfg);
  105. if (this.fontList == null) {
  106. this.fontList = cfgFonts;
  107. } else {
  108. this.fontList.addAll(cfgFonts);
  109. }
  110. }
  111. /**
  112. * @see org.apache.fop.render.Renderer#setUserAgent(FOUserAgent)
  113. */
  114. public void setUserAgent(FOUserAgent agent) {
  115. super.setUserAgent(agent);
  116. PSSVGHandler xmlHandler = new PSSVGHandler();
  117. userAgent.getXMLHandlerRegistry().addXMLHandler(xmlHandler);
  118. }
  119. /**
  120. * Write out a command
  121. * @param cmd PostScript command
  122. */
  123. protected void writeln(String cmd) {
  124. try {
  125. gen.writeln(cmd);
  126. } catch (IOException ioe) {
  127. handleIOTrouble(ioe);
  128. }
  129. }
  130. /**
  131. * Central exception handler for I/O exceptions.
  132. * @param ioe IOException to handle
  133. */
  134. protected void handleIOTrouble(IOException ioe) {
  135. if (!ioTrouble) {
  136. log.error("Error while writing to target file", ioe);
  137. ioTrouble = true;
  138. }
  139. }
  140. /**
  141. * Write out a comment
  142. * @param comment Comment to write
  143. */
  144. protected void comment(String comment) {
  145. if (this.enableComments) {
  146. if (comment.startsWith("%")) {
  147. writeln(comment);
  148. } else {
  149. writeln("%" + comment);
  150. }
  151. }
  152. }
  153. /**
  154. * Make sure the cursor is in the right place.
  155. */
  156. protected void movetoCurrPosition() {
  157. moveTo(this.currentIPPosition, this.currentBPPosition);
  158. }
  159. /** @see org.apache.fop.render.AbstractPathOrientedRenderer#clip() */
  160. protected void clip() {
  161. writeln("clip newpath");
  162. //writeln("newpath");
  163. }
  164. /**
  165. * Clip an area.
  166. * Write a clipping operation given coordinates in the current
  167. * transform.
  168. * @param x the x coordinate
  169. * @param y the y coordinate
  170. * @param width the width of the area
  171. * @param height the height of the area
  172. */
  173. protected void clipRect(float x, float y, float width, float height) {
  174. try {
  175. gen.defineRect(x, y, width, height);
  176. gen.writeln("clip");
  177. //comment("clip here");
  178. } catch (IOException ioe) {
  179. handleIOTrouble(ioe);
  180. }
  181. }
  182. /** @see org.apache.fop.render.AbstractPathOrientedRenderer#moveTo(float, float) */
  183. protected void moveTo(float x, float y) {
  184. writeln(gen.formatDouble(x) + " " + gen.formatDouble(y) + " M");
  185. }
  186. /** @see org.apache.fop.render.AbstractPathOrientedRenderer#lineTo(float, float) */
  187. protected void lineTo(float x, float y) {
  188. writeln(gen.formatDouble(x) + " " + gen.formatDouble(y) + " lineto");
  189. }
  190. /** @see org.apache.fop.render.AbstractPathOrientedRenderer#closePath() */
  191. protected void closePath() {
  192. writeln("cp");
  193. }
  194. /** @see org.apache.fop.render.AbstractPathOrientedRenderer */
  195. protected void fillRect(float x, float y, float width, float height) {
  196. try {
  197. gen.defineRect(x, y, width, height);
  198. gen.writeln("fill");
  199. } catch (IOException ioe) {
  200. handleIOTrouble(ioe);
  201. }
  202. }
  203. /** @see org.apache.fop.render.AbstractPathOrientedRenderer */
  204. protected void updateColor(ColorType col, boolean fill) {
  205. try {
  206. useColor(col);
  207. } catch (IOException ioe) {
  208. handleIOTrouble(ioe);
  209. }
  210. }
  211. /** @see org.apache.fop.render.AbstractPathOrientedRenderer */
  212. protected void drawImage(String url, Rectangle2D pos) {
  213. endTextObject();
  214. url = ImageFactory.getURL(url);
  215. ImageFactory fact = ImageFactory.getInstance();
  216. FopImage fopimage = fact.getImage(url, userAgent);
  217. if (fopimage == null) {
  218. return;
  219. }
  220. if (!fopimage.load(FopImage.DIMENSIONS)) {
  221. return;
  222. }
  223. float x = (float)pos.getX() / 1000f;
  224. x += currentIPPosition / 1000f;
  225. float y = (float)pos.getY() / 1000f;
  226. y += currentBPPosition / 1000f;
  227. float w = (float)pos.getWidth() / 1000f;
  228. float h = (float)pos.getHeight() / 1000f;
  229. try {
  230. String mime = fopimage.getMimeType();
  231. if ("text/xml".equals(mime)) {
  232. if (!fopimage.load(FopImage.ORIGINAL_DATA)) {
  233. return;
  234. }
  235. Document doc = ((XMLImage) fopimage).getDocument();
  236. String ns = ((XMLImage) fopimage).getNameSpace();
  237. renderDocument(doc, ns, pos);
  238. } else if ("image/svg+xml".equals(mime)) {
  239. if (!fopimage.load(FopImage.ORIGINAL_DATA)) {
  240. return;
  241. }
  242. Document doc = ((XMLImage) fopimage).getDocument();
  243. String ns = ((XMLImage) fopimage).getNameSpace();
  244. renderDocument(doc, ns, pos);
  245. } else if (fopimage instanceof EPSImage) {
  246. PSImageUtils.renderEPS((EPSImage)fopimage, x, y, w, h, gen);
  247. } else {
  248. PSImageUtils.renderBitmapImage(fopimage, x, y, w, h, gen);
  249. }
  250. } catch (IOException ioe) {
  251. handleIOTrouble(ioe);
  252. }
  253. }
  254. /**
  255. * Draw a line.
  256. *
  257. * @param startx the start x position
  258. * @param starty the start y position
  259. * @param endx the x end position
  260. * @param endy the y end position
  261. */
  262. private void drawLine(float startx, float starty, float endx, float endy) {
  263. writeln(gen.formatDouble(startx) + " "
  264. + gen.formatDouble(starty) + " M "
  265. + gen.formatDouble(endx) + " "
  266. + gen.formatDouble(endy) + " lineto stroke newpath");
  267. }
  268. /** Saves the graphics state of the rendering engine. */
  269. public void saveGraphicsState() {
  270. endTextObject();
  271. try {
  272. //delegate
  273. gen.saveGraphicsState();
  274. } catch (IOException ioe) {
  275. handleIOTrouble(ioe);
  276. }
  277. }
  278. /** Restores the last graphics state of the rendering engine. */
  279. public void restoreGraphicsState() {
  280. try {
  281. //delegate
  282. gen.restoreGraphicsState();
  283. } catch (IOException ioe) {
  284. handleIOTrouble(ioe);
  285. }
  286. }
  287. /**
  288. * Concats the transformation matrix.
  289. * @param a A part
  290. * @param b B part
  291. * @param c C part
  292. * @param d D part
  293. * @param e E part
  294. * @param f F part
  295. */
  296. protected void concatMatrix(double a, double b,
  297. double c, double d,
  298. double e, double f) {
  299. try {
  300. gen.concatMatrix(a, b, c, d, e, f);
  301. } catch (IOException ioe) {
  302. handleIOTrouble(ioe);
  303. }
  304. }
  305. /**
  306. * Concats the transformations matrix.
  307. * @param matrix Matrix to use
  308. */
  309. protected void concatMatrix(double[] matrix) {
  310. try {
  311. gen.concatMatrix(matrix);
  312. } catch (IOException ioe) {
  313. handleIOTrouble(ioe);
  314. }
  315. }
  316. /**
  317. * Changes the currently used font.
  318. * @param name name of the font
  319. * @param size font size
  320. */
  321. public void useFont(String name, int size) {
  322. try {
  323. gen.useFont(name, size / 1000f);
  324. } catch (IOException ioe) {
  325. handleIOTrouble(ioe);
  326. }
  327. }
  328. private void useColor(ColorType col) throws IOException {
  329. gen.useRGBColor(toColor(col));
  330. }
  331. /** @see org.apache.fop.render.AbstractPathOrientedRenderer#drawBackAndBorders(
  332. * Area, float, float, float, float) */
  333. protected void drawBackAndBorders(Area area, float startx, float starty,
  334. float width, float height) {
  335. if (area.hasTrait(Trait.BACKGROUND)
  336. || area.hasTrait(Trait.BORDER_BEFORE)
  337. || area.hasTrait(Trait.BORDER_AFTER)
  338. || area.hasTrait(Trait.BORDER_START)
  339. || area.hasTrait(Trait.BORDER_END)) {
  340. comment("%FOPBeginBackgroundAndBorder: "
  341. + startx + " " + starty + " " + width + " " + height);
  342. super.drawBackAndBorders(area, startx, starty, width, height);
  343. comment("%FOPEndBackgroundAndBorder");
  344. }
  345. }
  346. /** @see org.apache.fop.render.AbstractPathOrientedRenderer */
  347. protected void drawBorderLine(float x1, float y1, float x2, float y2,
  348. boolean horz, boolean startOrBefore, int style, ColorType col) {
  349. try {
  350. float w = x2 - x1;
  351. float h = y2 - y1;
  352. if ((w < 0) || (h < 0)) {
  353. log.error("Negative extent received. Border won't be painted.");
  354. return;
  355. }
  356. switch (style) {
  357. case Constants.EN_DASHED:
  358. useColor(col);
  359. if (horz) {
  360. float unit = Math.abs(2 * h);
  361. int rep = (int)(w / unit);
  362. if (rep % 2 == 0) {
  363. rep++;
  364. }
  365. unit = w / rep;
  366. gen.useDash("[" + unit + "] 0");
  367. gen.useLineCap(0);
  368. gen.useLineWidth(h);
  369. float ym = y1 + (h / 2);
  370. drawLine(x1, ym, x2, ym);
  371. } else {
  372. float unit = Math.abs(2 * w);
  373. int rep = (int)(h / unit);
  374. if (rep % 2 == 0) {
  375. rep++;
  376. }
  377. unit = h / rep;
  378. gen.useDash("[" + unit + "] 0");
  379. gen.useLineCap(0);
  380. gen.useLineWidth(w);
  381. float xm = x1 + (w / 2);
  382. drawLine(xm, y1, xm, y2);
  383. }
  384. break;
  385. case Constants.EN_DOTTED:
  386. useColor(col);
  387. gen.useLineCap(1); //Rounded!
  388. if (horz) {
  389. float unit = Math.abs(2 * h);
  390. int rep = (int)(w / unit);
  391. if (rep % 2 == 0) {
  392. rep++;
  393. }
  394. unit = w / rep;
  395. gen.useDash("[0 " + unit + "] 0");
  396. gen.useLineWidth(h);
  397. float ym = y1 + (h / 2);
  398. drawLine(x1, ym, x2, ym);
  399. } else {
  400. float unit = Math.abs(2 * w);
  401. int rep = (int)(h / unit);
  402. if (rep % 2 == 0) {
  403. rep++;
  404. }
  405. unit = h / rep;
  406. gen.useDash("[0 " + unit + "] 0");
  407. gen.useLineWidth(w);
  408. float xm = x1 + (w / 2);
  409. drawLine(xm, y1, xm, y2);
  410. }
  411. break;
  412. case Constants.EN_DOUBLE:
  413. useColor(col);
  414. gen.useDash(null);
  415. if (horz) {
  416. float h3 = h / 3;
  417. gen.useLineWidth(h3);
  418. float ym1 = y1 + (h3 / 2);
  419. float ym2 = ym1 + h3 + h3;
  420. drawLine(x1, ym1, x2, ym1);
  421. drawLine(x1, ym2, x2, ym2);
  422. } else {
  423. float w3 = w / 3;
  424. gen.useLineWidth(w3);
  425. float xm1 = x1 + (w3 / 2);
  426. float xm2 = xm1 + w3 + w3;
  427. drawLine(xm1, y1, xm1, y2);
  428. drawLine(xm2, y1, xm2, y2);
  429. }
  430. break;
  431. case Constants.EN_GROOVE:
  432. case Constants.EN_RIDGE:
  433. float colFactor = (style == EN_GROOVE ? 0.4f : -0.4f);
  434. gen.useDash(null);
  435. Color c = toColor(col);
  436. if (horz) {
  437. Color uppercol = lightenColor(c, -colFactor);
  438. Color lowercol = lightenColor(c, colFactor);
  439. float h3 = h / 3;
  440. gen.useLineWidth(h3);
  441. float ym1 = y1 + (h3 / 2);
  442. gen.useRGBColor(uppercol);
  443. drawLine(x1, ym1, x2, ym1);
  444. gen.useRGBColor(c);
  445. drawLine(x1, ym1 + h3, x2, ym1 + h3);
  446. gen.useRGBColor(lowercol);
  447. drawLine(x1, ym1 + h3 + h3, x2, ym1 + h3 + h3);
  448. } else {
  449. Color leftcol = lightenColor(c, -colFactor);
  450. Color rightcol = lightenColor(c, colFactor);
  451. float w3 = w / 3;
  452. gen.useLineWidth(w3);
  453. float xm1 = x1 + (w3 / 2);
  454. gen.useRGBColor(leftcol);
  455. drawLine(xm1, y1, xm1, y2);
  456. gen.useRGBColor(c);
  457. drawLine(xm1 + w3, y1, xm1 + w3, y2);
  458. gen.useRGBColor(rightcol);
  459. drawLine(xm1 + w3 + w3, y1, xm1 + w3 + w3, y2);
  460. }
  461. break;
  462. case Constants.EN_INSET:
  463. case Constants.EN_OUTSET:
  464. colFactor = (style == EN_OUTSET ? 0.4f : -0.4f);
  465. gen.useDash(null);
  466. c = toColor(col);
  467. if (horz) {
  468. c = lightenColor(c, (startOrBefore ? 1 : -1) * colFactor);
  469. gen.useLineWidth(h);
  470. float ym1 = y1 + (h / 2);
  471. gen.useRGBColor(c);
  472. drawLine(x1, ym1, x2, ym1);
  473. } else {
  474. c = lightenColor(c, (startOrBefore ? 1 : -1) * colFactor);
  475. gen.useLineWidth(w);
  476. float xm1 = x1 + (w / 2);
  477. gen.useRGBColor(c);
  478. drawLine(xm1, y1, xm1, y2);
  479. }
  480. break;
  481. case Constants.EN_HIDDEN:
  482. break;
  483. default:
  484. useColor(col);
  485. gen.useDash(null);
  486. gen.useLineCap(0);
  487. if (horz) {
  488. gen.useLineWidth(h);
  489. float ym = y1 + (h / 2);
  490. drawLine(x1, ym, x2, ym);
  491. } else {
  492. gen.useLineWidth(w);
  493. float xm = x1 + (w / 2);
  494. drawLine(xm, y1, xm, y2);
  495. }
  496. }
  497. } catch (IOException ioe) {
  498. handleIOTrouble(ioe);
  499. }
  500. }
  501. /**
  502. * @see org.apache.fop.render.Renderer#startRenderer(OutputStream)
  503. */
  504. public void startRenderer(OutputStream outputStream)
  505. throws IOException {
  506. log.debug("rendering areas to PostScript");
  507. //Setup for PostScript generation
  508. this.gen = new PSGenerator(outputStream) {
  509. /** Need to subclass PSGenerator to have better URI resolution */
  510. public Source resolveURI(String uri) {
  511. return userAgent.resolveURI(uri);
  512. }
  513. };
  514. this.currentPageNumber = 0;
  515. //PostScript Header
  516. writeln(DSCConstants.PS_ADOBE_30);
  517. gen.writeDSCComment(DSCConstants.CREATOR, new String[] {userAgent.getProducer()});
  518. gen.writeDSCComment(DSCConstants.CREATION_DATE, new Object[] {new java.util.Date()});
  519. gen.writeDSCComment(DSCConstants.LANGUAGE_LEVEL, new Integer(gen.getPSLevel()));
  520. gen.writeDSCComment(DSCConstants.PAGES, new Object[] {PSGenerator.ATEND});
  521. gen.writeDSCComment(DSCConstants.DOCUMENT_SUPPLIED_RESOURCES,
  522. new Object[] {PSGenerator.ATEND});
  523. gen.writeDSCComment(DSCConstants.END_COMMENTS);
  524. //Defaults
  525. gen.writeDSCComment(DSCConstants.BEGIN_DEFAULTS);
  526. gen.writeDSCComment(DSCConstants.END_DEFAULTS);
  527. //Prolog and Setup written right before the first page-sequence, see startPageSequence()
  528. }
  529. /**
  530. * @see org.apache.fop.render.Renderer#stopRenderer()
  531. */
  532. public void stopRenderer() throws IOException {
  533. //Notify resource usage for font which are not supplied
  534. Map fonts = fontInfo.getUsedFonts();
  535. Iterator e = fonts.keySet().iterator();
  536. while (e.hasNext()) {
  537. String key = (String)e.next();
  538. //Typeface font = (Typeface)fonts.get(key);
  539. PSResource res = (PSResource)this.fontResources.get(key);
  540. boolean supplied = gen.isResourceSupplied(res);
  541. if (!supplied) {
  542. gen.notifyResourceUsage(res, true);
  543. }
  544. }
  545. //Write trailer
  546. gen.writeDSCComment(DSCConstants.TRAILER);
  547. gen.writeDSCComment(DSCConstants.PAGES, new Integer(this.currentPageNumber));
  548. gen.writeResources(false);
  549. gen.writeDSCComment(DSCConstants.EOF);
  550. gen.flush();
  551. }
  552. /** @see org.apache.fop.render.Renderer */
  553. public void processOffDocumentItem(OffDocumentItem oDI) {
  554. log.debug("Handling OffDocumentItem: " + oDI.getName());
  555. if (oDI instanceof OffDocumentExtensionAttachment) {
  556. ExtensionAttachment attachment = ((OffDocumentExtensionAttachment)oDI).getAttachment();
  557. if (PSSetupCode.CATEGORY.equals(attachment.getCategory())) {
  558. PSSetupCode setupCode = (PSSetupCode)attachment;
  559. if (setupCodeList == null) {
  560. setupCodeList = new java.util.ArrayList();
  561. }
  562. setupCodeList.add(setupCode);
  563. }
  564. }
  565. super.processOffDocumentItem(oDI);
  566. }
  567. /** @see org.apache.fop.render.Renderer#startPageSequence(org.apache.fop.area.LineArea) */
  568. public void startPageSequence(LineArea seqTitle) {
  569. super.startPageSequence(seqTitle);
  570. if (!firstPageSequenceReceived) {
  571. //Do this only once, as soon as we have all the content for the Setup section!
  572. try {
  573. //Prolog
  574. gen.writeDSCComment(DSCConstants.BEGIN_PROLOG);
  575. PSProcSets.writeFOPStdProcSet(gen);
  576. PSProcSets.writeFOPEPSProcSet(gen);
  577. gen.writeDSCComment(DSCConstants.END_PROLOG);
  578. //Setup
  579. gen.writeDSCComment(DSCConstants.BEGIN_SETUP);
  580. writeSetupCodeList(setupCodeList, "SetupCode");
  581. this.fontResources = PSFontUtils.writeFontDict(gen, fontInfo);
  582. gen.writeln("FOPFonts begin");
  583. gen.writeDSCComment(DSCConstants.END_SETUP);
  584. } catch (IOException ioe) {
  585. handleIOTrouble(ioe);
  586. }
  587. firstPageSequenceReceived = true;
  588. }
  589. }
  590. /**
  591. * Formats and writes a List of PSSetupCode instances to the output stream.
  592. * @param setupCodeList a List of PSSetupCode instances
  593. * @param type the type of code section
  594. */
  595. private void writeSetupCodeList(List setupCodeList, String type) throws IOException {
  596. if (setupCodeList != null) {
  597. Iterator i = setupCodeList.iterator();
  598. while (i.hasNext()) {
  599. PSSetupCode setupCode = (PSSetupCode)i.next();
  600. gen.commentln("%FOPBegin" + type + ": ("
  601. + (setupCode.getName() != null ? setupCode.getName() : "")
  602. + ")");
  603. LineNumberReader reader = new LineNumberReader(
  604. new java.io.StringReader(setupCode.getContent()));
  605. String line;
  606. while ((line = reader.readLine()) != null) {
  607. line = line.trim();
  608. if (line.length() > 0) {
  609. gen.writeln(line.trim());
  610. }
  611. }
  612. gen.commentln("%FOPEnd" + type);
  613. i.remove();
  614. }
  615. }
  616. }
  617. /**
  618. * @see org.apache.fop.render.Renderer#renderPage(PageViewport)
  619. */
  620. public void renderPage(PageViewport page)
  621. throws IOException, FOPException {
  622. log.debug("renderPage(): " + page);
  623. this.currentPageNumber++;
  624. gen.notifyStartNewPage();
  625. gen.notifyResourceUsage(PSProcSets.STD_PROCSET, false);
  626. gen.writeDSCComment(DSCConstants.PAGE, new Object[]
  627. {page.getPageNumberString(),
  628. new Integer(this.currentPageNumber)});
  629. final Integer zero = new Integer(0);
  630. final long pagewidth = Math.round(page.getViewArea().getWidth());
  631. final long pageheight = Math.round(page.getViewArea().getHeight());
  632. final double pspagewidth = pagewidth / 1000f;
  633. final double pspageheight = pageheight / 1000f;
  634. boolean rotate = false;
  635. if (this.autoRotateLandscape && (pageheight < pagewidth)) {
  636. rotate = true;
  637. gen.writeDSCComment(DSCConstants.PAGE_BBOX, new Object[]
  638. {zero,
  639. zero,
  640. new Long(Math.round(pspageheight)),
  641. new Long(Math.round(pspagewidth))});
  642. gen.writeDSCComment(DSCConstants.PAGE_HIRES_BBOX, new Object[]
  643. {zero,
  644. zero,
  645. new Double(pspageheight),
  646. new Double(pspagewidth)});
  647. gen.writeDSCComment(DSCConstants.PAGE_ORIENTATION, "Landscape");
  648. } else {
  649. gen.writeDSCComment(DSCConstants.PAGE_BBOX, new Object[]
  650. {zero,
  651. zero,
  652. new Long(Math.round(pspagewidth)),
  653. new Long(Math.round(pspageheight))});
  654. gen.writeDSCComment(DSCConstants.PAGE_HIRES_BBOX, new Object[]
  655. {zero,
  656. zero,
  657. new Double(pspagewidth),
  658. new Double(pspageheight)});
  659. if (this.autoRotateLandscape) {
  660. gen.writeDSCComment(DSCConstants.PAGE_ORIENTATION, "Portrait");
  661. }
  662. }
  663. gen.writeDSCComment(DSCConstants.PAGE_RESOURCES,
  664. new Object[] {PSGenerator.ATEND});
  665. gen.commentln("%FOPSimplePageMaster: " + page.getSPM().getMasterName());
  666. gen.writeDSCComment(DSCConstants.BEGIN_PAGE_SETUP);
  667. //Handle PSSetupCode instances on simple-page-master
  668. if (page.getSPM().getExtensionAttachments().size() > 0) {
  669. List list = new java.util.ArrayList();
  670. //Extract all PSSetupCode instances from the attachment list on the s-p-m
  671. Iterator i = page.getSPM().getExtensionAttachments().iterator();
  672. while (i.hasNext()) {
  673. ExtensionAttachment attachment = (ExtensionAttachment)i.next();
  674. if (PSSetupCode.CATEGORY.equals(attachment.getCategory())) {
  675. list.add(attachment);
  676. }
  677. }
  678. writeSetupCodeList(list, "PageSetupCode");
  679. }
  680. if (rotate) {
  681. gen.writeln(Math.round(pspageheight) + " 0 translate");
  682. gen.writeln("90 rotate");
  683. }
  684. gen.writeln("<<");
  685. gen.writeln("/PageSize ["
  686. + Math.round(pspagewidth) + " "
  687. + Math.round(pspageheight) + "]");
  688. gen.writeln("/ImagingBBox null");
  689. gen.writeln(">> setpagedevice");
  690. concatMatrix(1, 0, 0, -1, 0, pageheight / 1000f);
  691. gen.writeDSCComment(DSCConstants.END_PAGE_SETUP);
  692. //Process page
  693. super.renderPage(page);
  694. writeln("showpage");
  695. gen.writeDSCComment(DSCConstants.PAGE_TRAILER);
  696. gen.writeResources(true);
  697. gen.writeDSCComment(DSCConstants.END_PAGE);
  698. }
  699. /** @see org.apache.fop.render.AbstractRenderer */
  700. protected void renderRegionViewport(RegionViewport port) {
  701. if (port != null) {
  702. comment("%FOPBeginRegionViewport: " + port.getRegionReference().getRegionName());
  703. super.renderRegionViewport(port);
  704. comment("%FOPEndRegionViewport");
  705. }
  706. }
  707. /** Indicates the beginning of a text object. */
  708. protected void beginTextObject() {
  709. if (!inTextMode) {
  710. saveGraphicsState();
  711. writeln("BT");
  712. inTextMode = true;
  713. }
  714. }
  715. /** Indicates the end of a text object. */
  716. protected void endTextObject() {
  717. if (inTextMode) {
  718. writeln("ET");
  719. restoreGraphicsState();
  720. inTextMode = false;
  721. }
  722. }
  723. /**
  724. * @see org.apache.fop.render.AbstractRenderer#renderCharacter(Character)
  725. */
  726. public void renderCharacter(Character ch) {
  727. String text = ch.getChar();
  728. renderText(ch, text);
  729. super.renderCharacter(ch); //Updates IPD
  730. }
  731. /**
  732. * @see org.apache.fop.render.AbstractRenderer#renderText(TextArea)
  733. */
  734. public void renderText(TextArea area) {
  735. String text = area.getTextArea();
  736. renderText(area, text);
  737. super.renderText(area); //Updates IPD
  738. }
  739. private void renderText(AbstractTextArea area, String text) {
  740. renderInlineAreaBackAndBorders(area);
  741. String fontname = (String)area.getTrait(Trait.FONT_NAME);
  742. int fontsize = area.getTraitAsInteger(Trait.FONT_SIZE);
  743. // This assumes that *all* CIDFonts use a /ToUnicode mapping
  744. Typeface tf = (Typeface) fontInfo.getFonts().get(fontname);
  745. //Determine position
  746. int rx = currentIPPosition;
  747. int bl = currentBPPosition + area.getOffset() + area.getBaselineOffset();
  748. useFont(fontname, fontsize);
  749. ColorType ct = (ColorType)area.getTrait(Trait.COLOR);
  750. if (ct != null) {
  751. try {
  752. useColor(ct);
  753. } catch (IOException ioe) {
  754. handleIOTrouble(ioe);
  755. }
  756. }
  757. boolean kerningAvailable = false;
  758. Map kerning = tf.getKerningInfo();
  759. if (kerning != null && !kerning.isEmpty()) {
  760. //kerningAvailable = true;
  761. //TODO Fix me when kerning is supported by the layout engine
  762. log.warn("Kerning info is available, but kerning is not yet implemented for"
  763. + " the PS renderer and not currently supported by the layout engine.");
  764. }
  765. beginTextObject();
  766. writeln("1 0 0 -1 " + gen.formatDouble(rx / 1000f)
  767. + " " + gen.formatDouble(bl / 1000f) + " Tm");
  768. int initialSize = text.length();
  769. initialSize += initialSize / 2;
  770. StringBuffer sb = new StringBuffer(initialSize);
  771. int textLen = text.length();
  772. if (area.getTextLetterSpaceAdjust() == 0 && area.getTextWordSpaceAdjust() == 0) {
  773. sb.append("(");
  774. for (int i = 0; i < textLen; i++) {
  775. final char c = text.charAt(i);
  776. final char mapped = tf.mapChar(c);
  777. PSGenerator.escapeChar(mapped, sb);
  778. }
  779. sb.append(") t");
  780. } else {
  781. sb.append("(");
  782. int[] offsets = new int[textLen];
  783. for (int i = 0; i < textLen; i++) {
  784. final char c = text.charAt(i);
  785. final char mapped = tf.mapChar(c);
  786. int wordSpace;
  787. //TODO Synchronize word space behaviour with TextLayoutManager
  788. //Check the other renderers, too!
  789. if (CharUtilities.isAnySpace(mapped)
  790. && mapped != CharUtilities.ZERO_WIDTH_SPACE
  791. && mapped != CharUtilities.ZERO_WIDTH_NOBREAK_SPACE) {
  792. wordSpace = area.getTextWordSpaceAdjust();
  793. } else {
  794. wordSpace = 0;
  795. }
  796. int cw = tf.getWidth(mapped, fontsize) / 1000;
  797. offsets[i] = cw + area.getTextLetterSpaceAdjust() + wordSpace;
  798. PSGenerator.escapeChar(mapped, sb);
  799. }
  800. sb.append(")" + PSGenerator.LF + "[");
  801. for (int i = 0; i < textLen; i++) {
  802. if (i > 0) {
  803. if (i % 8 == 0) {
  804. sb.append(PSGenerator.LF);
  805. } else {
  806. sb.append(" ");
  807. }
  808. }
  809. sb.append(gen.formatDouble(offsets[i] / 1000f));
  810. }
  811. sb.append("]" + PSGenerator.LF + "xshow");
  812. }
  813. writeln(sb.toString());
  814. renderTextDecoration(tf, fontsize, area, bl, rx);
  815. }
  816. /** @see org.apache.fop.render.AbstractPathOrientedRenderer#breakOutOfStateStack() */
  817. protected List breakOutOfStateStack() {
  818. try {
  819. List breakOutList = new java.util.ArrayList();
  820. PSState state;
  821. while (true) {
  822. if (breakOutList.size() == 0) {
  823. endTextObject();
  824. comment("------ break out!");
  825. }
  826. state = gen.getCurrentState();
  827. if (!gen.restoreGraphicsState()) {
  828. break;
  829. }
  830. breakOutList.add(0, state); //Insert because of stack-popping
  831. }
  832. return breakOutList;
  833. } catch (IOException ioe) {
  834. handleIOTrouble(ioe);
  835. return null;
  836. }
  837. }
  838. /** @see org.apache.fop.render.AbstractPathOrientedRenderer */
  839. protected void restoreStateStackAfterBreakOut(List breakOutList) {
  840. try {
  841. comment("------ restoring context after break-out...");
  842. PSState state;
  843. Iterator i = breakOutList.iterator();
  844. while (i.hasNext()) {
  845. state = (PSState)i.next();
  846. saveGraphicsState();
  847. state.reestablish(gen);
  848. }
  849. comment("------ done.");
  850. } catch (IOException ioe) {
  851. handleIOTrouble(ioe);
  852. }
  853. }
  854. /**
  855. * @see org.apache.fop.render.AbstractRenderer#startVParea(CTM)
  856. */
  857. protected void startVParea(CTM ctm) {
  858. saveGraphicsState();
  859. // multiply with current CTM
  860. final double[] matrix = ctm.toArray();
  861. matrix[4] /= 1000f;
  862. matrix[5] /= 1000f;
  863. concatMatrix(matrix);
  864. // Set clip?
  865. }
  866. /**
  867. * @see org.apache.fop.render.AbstractRenderer#endVParea()
  868. */
  869. protected void endVParea() {
  870. endTextObject();
  871. restoreGraphicsState();
  872. }
  873. /** @see org.apache.fop.render.AbstractRenderer */
  874. protected void renderBlockViewport(BlockViewport bv, List children) {
  875. comment("%FOPBeginBlockViewport: " + bv.toString());
  876. super.renderBlockViewport(bv, children);
  877. comment("%FOPEndBlockViewport");
  878. }
  879. /** @see org.apache.fop.render.AbstractRenderer */
  880. protected void renderInlineParent(InlineParent ip) {
  881. super.renderInlineParent(ip);
  882. }
  883. /**
  884. * @see org.apache.fop.render.AbstractRenderer#renderImage(Image, Rectangle2D)
  885. */
  886. public void renderImage(Image image, Rectangle2D pos) {
  887. drawImage(image.getURL(), pos);
  888. }
  889. /**
  890. * @see org.apache.fop.render.AbstractRenderer#renderForeignObject(ForeignObject, Rectangle2D)
  891. */
  892. public void renderForeignObject(ForeignObject fo, Rectangle2D pos) {
  893. Document doc = fo.getDocument();
  894. String ns = fo.getNameSpace();
  895. renderDocument(doc, ns, pos);
  896. }
  897. /**
  898. * Renders an XML document (SVG for example).
  899. * @param doc DOM Document containing the XML document to be rendered
  900. * @param ns Namespace for the XML document
  901. * @param pos Position for the generated graphic/image
  902. */
  903. public void renderDocument(Document doc, String ns, Rectangle2D pos) {
  904. endTextObject();
  905. RendererContext context;
  906. context = new RendererContext(this, MIME_TYPE);
  907. context.setUserAgent(userAgent);
  908. context.setProperty(PSSVGHandler.PS_GENERATOR, this.gen);
  909. context.setProperty(PSSVGHandler.PS_FONT_INFO, fontInfo);
  910. context.setProperty(PSSVGHandler.PS_WIDTH,
  911. new Integer((int) pos.getWidth()));
  912. context.setProperty(PSSVGHandler.PS_HEIGHT,
  913. new Integer((int) pos.getHeight()));
  914. context.setProperty(PSSVGHandler.PS_XPOS,
  915. new Integer(currentIPPosition + (int) pos.getX()));
  916. context.setProperty(PSSVGHandler.PS_YPOS,
  917. new Integer(currentBPPosition + (int) pos.getY()));
  918. //context.setProperty("strokeSVGText", options.get("strokeSVGText"));
  919. renderXML(context, doc, ns);
  920. }
  921. /** @see org.apache.fop.render.AbstractRenderer */
  922. public String getMimeType() {
  923. return MIME_TYPE;
  924. }
  925. }