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

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