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

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