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

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