Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

PSRenderer.java 43KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222
  1. /*
  2. * $Id$
  3. * ============================================================================
  4. * The Apache Software License, Version 1.1
  5. * ============================================================================
  6. *
  7. * Copyright (C) 1999-2003 The Apache Software Foundation. All rights reserved.
  8. *
  9. * Redistribution and use in source and binary forms, with or without modifica-
  10. * tion, are permitted provided that the following conditions are met:
  11. *
  12. * 1. Redistributions of source code must retain the above copyright notice,
  13. * this list of conditions and the following disclaimer.
  14. *
  15. * 2. Redistributions in binary form must reproduce the above copyright notice,
  16. * this list of conditions and the following disclaimer in the documentation
  17. * and/or other materials provided with the distribution.
  18. *
  19. * 3. The end-user documentation included with the redistribution, if any, must
  20. * include the following acknowledgment: "This product includes software
  21. * developed by the Apache Software Foundation (http://www.apache.org/)."
  22. * Alternately, this acknowledgment may appear in the software itself, if
  23. * and wherever such third-party acknowledgments normally appear.
  24. *
  25. * 4. The names "FOP" and "Apache Software Foundation" must not be used to
  26. * endorse or promote products derived from this software without prior
  27. * written permission. For written permission, please contact
  28. * apache@apache.org.
  29. *
  30. * 5. Products derived from this software may not be called "Apache", nor may
  31. * "Apache" appear in their name, without prior written permission of the
  32. * Apache Software Foundation.
  33. *
  34. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
  35. * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
  36. * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  37. * APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
  38. * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU-
  39. * DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
  40. * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
  41. * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  42. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  43. * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  44. * ============================================================================
  45. *
  46. * This software consists of voluntary contributions made by many individuals
  47. * on behalf of the Apache Software Foundation and was originally created by
  48. * James Tauber <jtauber@jtauber.com>. For more information on the Apache
  49. * Software Foundation, please see <http://www.apache.org/>.
  50. */
  51. package org.apache.fop.render.ps;
  52. // FOP
  53. import org.apache.fop.svg.SVGArea;
  54. import org.apache.fop.render.AbstractRenderer;
  55. import org.apache.fop.image.ImageArea;
  56. import org.apache.fop.image.FopImage;
  57. import org.apache.fop.image.FopImageException;
  58. import org.apache.fop.image.JpegImage;
  59. import org.apache.fop.layout.FontInfo;
  60. import org.apache.fop.layout.DisplaySpace;
  61. import org.apache.fop.layout.FontState;
  62. import org.apache.fop.layout.LineArea;
  63. import org.apache.fop.layout.Page;
  64. import org.apache.fop.layout.Area;
  65. import org.apache.fop.layout.Box;
  66. import org.apache.fop.layout.BorderAndPadding;
  67. import org.apache.fop.layout.BlockArea;
  68. import org.apache.fop.layout.inline.ForeignObjectArea;
  69. import org.apache.fop.layout.inline.WordArea;
  70. import org.apache.fop.layout.inline.InlineSpace;
  71. import org.apache.fop.layout.inline.LeaderArea;
  72. import org.apache.fop.datatypes.ColorType;
  73. import org.apache.fop.datatypes.ColorSpace;
  74. import org.apache.fop.fo.properties.LeaderPattern;
  75. import org.apache.fop.fo.properties.RuleStyle;
  76. import org.apache.fop.fonts.Glyphs;
  77. import org.apache.fop.render.pdf.Font;
  78. import org.apache.fop.image.SVGImage;
  79. import org.apache.fop.image.EPSImage;
  80. import org.apache.fop.apps.FOPException;
  81. import org.apache.batik.bridge.GVTBuilder;
  82. import org.apache.batik.bridge.BridgeContext;
  83. import org.apache.batik.bridge.ViewBox;
  84. import org.apache.batik.gvt.GraphicsNode;
  85. // SVG
  86. import org.w3c.dom.Document;
  87. import org.w3c.dom.svg.SVGDocument;
  88. import org.w3c.dom.svg.SVGSVGElement;
  89. // Java
  90. import java.io.IOException;
  91. import java.io.OutputStream;
  92. import java.util.Iterator;
  93. import java.util.List;
  94. import java.util.Map;
  95. import java.awt.geom.AffineTransform;
  96. /*
  97. PostScript renderer
  98. Remarks:
  99. - If anyone modifies this renderer please make sure to also follow the DSC to
  100. make it simpler to programmatically modify the generated Postscript files
  101. (ex. extract pages etc.).
  102. - The filters in use are hardcoded at the moment.
  103. - Modified by Mark Lillywhite mark-fop@inomial.com, to use the new
  104. Renderer interface. This PostScript renderer appears to be the
  105. most efficient at producing output.
  106. TODO-List:
  107. - Character size/spacing
  108. - SVG Transcoder for Batik
  109. - configuration
  110. - move to PrintRenderer
  111. - maybe improve filters (I'm not very proud of them)
  112. - add a RunLengthEncode filter (useful for Level 2 Postscript)
  113. - Improve DocumentProcessColors stuff (probably needs to be configurable, then maybe
  114. add a color to grayscale conversion for bitmaps to make output smaller (See
  115. PCLRenderer)
  116. - enhanced font support and font embedding
  117. - fix character encodings (Helvetica uses WinAnsiEncoding internally but is
  118. encoded as ISOLatin1 in PS)
  119. - try to implement image transparency
  120. - Add PPD support
  121. - fix border painting (see table.fo)
  122. */
  123. /**
  124. * Renderer that renders to PostScript.
  125. * <br>
  126. * This class currently generates PostScript Level 2 code. The only exception
  127. * is the FlateEncode filter which is a Level 3 feature. The PostScript code
  128. * generated follows the Document Structuring Conventions (DSC) version 3.0.
  129. *
  130. * @author Jeremias Märki
  131. */
  132. public class PSRenderer extends AbstractRenderer {
  133. /**
  134. * the application producing the PostScript
  135. */
  136. protected String producer;
  137. int imagecount = 0; // DEBUG
  138. int pagecount = 0;
  139. private boolean enableComments = true;
  140. private boolean autoRotateLandscape = false;
  141. /**
  142. * the stream used to output the PostScript
  143. */
  144. protected PSStream out;
  145. private boolean ioTrouble = false;
  146. private String currentFontName;
  147. private int currentFontSize;
  148. private int pageHeight;
  149. private int pageWidth;
  150. private float currRed;
  151. private float currGreen;
  152. private float currBlue;
  153. private FontInfo fontInfo;
  154. private int psLevel = 3;
  155. protected java.util.Map options;
  156. /**
  157. * set the document's producer
  158. *
  159. * @param producer string indicating application producing the PostScript
  160. */
  161. public void setProducer(String producer) {
  162. this.producer = producer;
  163. }
  164. /**
  165. * set up renderer options
  166. */
  167. public void setOptions(java.util.Map options) {
  168. this.options = options;
  169. }
  170. /**
  171. * Sets the PostScript Level to generate.
  172. *
  173. * @param level You can specify either 2 or 3 for the PostScript Level
  174. */
  175. public void setPSLevel(int level) {
  176. switch (level) {
  177. case 2:
  178. case 3:
  179. this.psLevel = level;
  180. break;
  181. default:
  182. throw new IllegalArgumentException("Only PostScript Levels 2 and 3 are supported");
  183. }
  184. }
  185. public int getPSLevel() {
  186. return this.psLevel;
  187. }
  188. public void setAutoRotateLandscape(boolean value) {
  189. this.autoRotateLandscape = value;
  190. }
  191. public boolean isAutoRotateLandscape() {
  192. return this.autoRotateLandscape;
  193. }
  194. /**
  195. * write out a command
  196. */
  197. protected void write(String cmd) {
  198. try {
  199. out.write(cmd);
  200. } catch (IOException e) {
  201. if (!ioTrouble)
  202. e.printStackTrace();
  203. ioTrouble = true;
  204. }
  205. }
  206. /**
  207. * write out a comment
  208. */
  209. protected void comment(String comment) {
  210. if (this.enableComments)
  211. write(comment);
  212. }
  213. protected void writeProcs() {
  214. write("%%BeginResource: procset FOPprocs");
  215. write("%%Title: Utility procedures");
  216. write("/FOPprocs 20 dict dup begin");
  217. write("/bd{bind def}bind def");
  218. write("/ld{load def}bd");
  219. write("/M/moveto ld");
  220. write("/RM/rmoveto ld");
  221. write("/t/show ld");
  222. write("/A/ashow ld");
  223. write("/cp/closepath ld");
  224. write("/re {4 2 roll M"); //define rectangle
  225. write("1 index 0 rlineto");
  226. write("0 exch rlineto");
  227. write("neg 0 rlineto");
  228. write("cp } bd");
  229. write("/ux 0.0 def");
  230. write("/uy 0.0 def");
  231. // <font> <size> F
  232. write("/F {");
  233. write(" /Tp exch def");
  234. // write(" currentdict exch get");
  235. write(" /Tf exch def");
  236. write(" Tf findfont Tp scalefont setfont");
  237. write(" /cf Tf def /cs Tp def /cw ( ) stringwidth pop def");
  238. write("} bd");
  239. write("/ULS {currentpoint /uy exch def /ux exch def} bd");
  240. write("/ULE {");
  241. write(" /Tcx currentpoint pop def");
  242. write(" gsave");
  243. write(" newpath");
  244. write(" cf findfont cs scalefont dup");
  245. write(" /FontMatrix get 0 get /Ts exch def /FontInfo get dup");
  246. write(" /UnderlinePosition get Ts mul /To exch def");
  247. write(" /UnderlineThickness get Ts mul /Tt exch def");
  248. write(" ux uy To add moveto Tcx uy To add lineto");
  249. write(" Tt setlinewidth stroke");
  250. write(" grestore");
  251. write("} bd");
  252. write("/OLE {");
  253. write(" /Tcx currentpoint pop def");
  254. write(" gsave");
  255. write(" newpath");
  256. write(" cf findfont cs scalefont dup");
  257. write(" /FontMatrix get 0 get /Ts exch def /FontInfo get dup");
  258. write(" /UnderlinePosition get Ts mul /To exch def");
  259. write(" /UnderlineThickness get Ts mul /Tt exch def");
  260. write(" ux uy To add cs add moveto Tcx uy To add cs add lineto");
  261. write(" Tt setlinewidth stroke");
  262. write(" grestore");
  263. write("} bd");
  264. write("/SOE {");
  265. write(" /Tcx currentpoint pop def");
  266. write(" gsave");
  267. write(" newpath");
  268. write(" cf findfont cs scalefont dup");
  269. write(" /FontMatrix get 0 get /Ts exch def /FontInfo get dup");
  270. write(" /UnderlinePosition get Ts mul /To exch def");
  271. write(" /UnderlineThickness get Ts mul /Tt exch def");
  272. write(" ux uy To add cs 10 mul 26 idiv add moveto Tcx uy To add cs 10 mul 26 idiv add lineto");
  273. write(" Tt setlinewidth stroke");
  274. write(" grestore");
  275. write("} bd");
  276. write("end def");
  277. write("%%EndResource");
  278. }
  279. protected void writeFontDict(FontInfo fontInfo) {
  280. write("%%BeginResource: procset FOPFonts");
  281. write("%%Title: Font setup (shortcuts) for this file");
  282. write("/FOPFonts 100 dict dup begin");
  283. // write("/gfF1{/Helvetica findfont} bd");
  284. // write("/gfF3{/Helvetica-Bold findfont} bd");
  285. Map fonts = fontInfo.getFonts();
  286. Iterator iter = fonts.keySet().iterator();
  287. while (iter.hasNext()) {
  288. String key = (String)iter.next();
  289. Font fm = (Font)fonts.get(key);
  290. write("/" + key + " /" + fm.fontName() + " def");
  291. }
  292. write("end def");
  293. write("%%EndResource");
  294. defineWinAnsiEncoding();
  295. //Rewrite font encodings
  296. iter = fonts.keySet().iterator();
  297. while (iter.hasNext()) {
  298. String key = (String)iter.next();
  299. Font fm = (Font)fonts.get(key);
  300. if (null == fm.encoding()) {
  301. //ignore (ZapfDingbats and Symbol run through here
  302. //TODO: ZapfDingbats and Symbol should get getEncoding() fixed!
  303. } else if ("WinAnsiEncoding".equals(fm.encoding())) {
  304. write("/" + fm.fontName() + " findfont");
  305. write("dup length dict begin");
  306. write(" {1 index /FID ne {def} {pop pop} ifelse} forall");
  307. write(" /Encoding " + fm.encoding() + " def");
  308. write(" currentdict");
  309. write("end");
  310. write("/" + fm.fontName() + " exch definefont pop");
  311. } else {
  312. log.warn("Only WinAnsiEncoding is supported. Font '"
  313. + fm.fontName() + "' asks for: " + fm.encoding());
  314. }
  315. }
  316. }
  317. private void defineWinAnsiEncoding() {
  318. write("/WinAnsiEncoding [");
  319. StringBuffer sb = new StringBuffer();
  320. for (int i = 0; i < Glyphs.winAnsiEncoding.length; i++) {
  321. if (i > 0) {
  322. if ((i % 5) == 0) {
  323. write(sb.toString());
  324. sb.setLength(0);
  325. } else {
  326. sb.append(" ");
  327. }
  328. }
  329. final char ch = Glyphs.winAnsiEncoding[i];
  330. final String glyphname = Glyphs.charToGlyphName(ch);
  331. if ("".equals(glyphname)) {
  332. sb.append("/" + Glyphs.notdef);
  333. } else {
  334. sb.append("/");
  335. sb.append(glyphname);
  336. }
  337. }
  338. write(sb.toString());
  339. write("] def");
  340. }
  341. protected void movetoCurrPosition() {
  342. write(this.currentXPosition + " " + this.currentYPosition + " M");
  343. }
  344. /**
  345. * set up the font info
  346. *
  347. * @param fontInfo the font info object to set up
  348. */
  349. public void setupFontInfo(FontInfo fontInfo)
  350. throws FOPException {
  351. /* use PDF's font setup to get PDF metrics */
  352. org.apache.fop.render.pdf.FontSetup.setup(fontInfo);
  353. this.fontInfo = fontInfo;
  354. }
  355. protected void addFilledRect(int x, int y, int w, int h,
  356. ColorType col) {
  357. // XXX: cater for braindead, legacy -ve heights
  358. if (h < 0) {
  359. h = -h;
  360. }
  361. write("newpath");
  362. write(x + " " + y + " " + w + " " + -h + " re");
  363. /*
  364. write(x + " " + y + " M");
  365. write(w + " 0 rlineto");
  366. write("0 " + (-h) + " rlineto");
  367. write((-w) + " 0 rlineto");
  368. write("0 " + h + " rlineto");
  369. write("closepath");
  370. */
  371. useColor(col);
  372. write("fill");
  373. }
  374. /**
  375. * render a display space to PostScript
  376. *
  377. * @param space the space to render
  378. */
  379. public void renderDisplaySpace(DisplaySpace space) {
  380. // write("% --- DisplaySpace size="+space.getSize());
  381. this.currentYPosition -= space.getSize();
  382. }
  383. /**
  384. * render a foreign object area
  385. */
  386. public void renderForeignObjectArea(ForeignObjectArea area) {
  387. // if necessary need to scale and align the content
  388. this.currentXPosition = this.currentXPosition + area.getXOffset();
  389. int plOffset = 0;
  390. Area parent = area.getParent();
  391. if (parent instanceof LineArea) {
  392. plOffset = ((LineArea)parent).getPlacementOffset();
  393. }
  394. this.currentYPosition += plOffset;
  395. area.getObject().render(this);
  396. this.currentXPosition += area.getEffectiveWidth();
  397. this.currentYPosition -= plOffset;
  398. }
  399. /**
  400. * render an SVG area to PostScript
  401. *
  402. * @param area the area to render
  403. */
  404. public void renderSVGArea(SVGArea area) {
  405. int x = this.currentXPosition;
  406. int y = this.currentYPosition;
  407. renderSVGDocument(area.getSVGDocument(), x, y, area.getFontState());
  408. }
  409. /**
  410. * render SVG document to PostScript
  411. *
  412. * @param doc the document to render
  413. * @param x the x offset
  414. * @param y the y offset
  415. * @param fs the fontstate to use
  416. */
  417. protected void renderSVGDocument(Document doc, int x, int y,
  418. FontState fs) {
  419. org.apache.fop.svg.SVGUserAgent userAgent
  420. = new org.apache.fop.svg.SVGUserAgent(new AffineTransform());
  421. userAgent.setLogger(log);
  422. GVTBuilder builder = new GVTBuilder();
  423. BridgeContext ctx = new BridgeContext(userAgent);
  424. GraphicsNode root;
  425. try {
  426. root = builder.build(ctx, doc);
  427. } catch (Exception e) {
  428. log.error("svg graphic could not be built: "
  429. + e.getMessage(), e);
  430. return;
  431. }
  432. // get the 'width' and 'height' attributes of the SVG document
  433. float w = (float)ctx.getDocumentSize().getWidth() * 1000f;
  434. float h = (float)ctx.getDocumentSize().getHeight() * 1000f;
  435. //log.debug("drawing SVG image: "+x+"/"+y+" "+w+"/"+h);
  436. SVGSVGElement svg = ((SVGDocument) doc).getRootElement();
  437. AffineTransform at = ViewBox.getPreserveAspectRatioTransform(svg,
  438. w/1000f , h/1000f );
  439. ctx = null;
  440. builder = null;
  441. float sx = 1, sy = -1;
  442. int xOffset = x, yOffset = y;
  443. comment("% --- SVG Area");
  444. write("gsave");
  445. if (w != 0 && h != 0) {
  446. write("newpath");
  447. write(x + " " + y + " M");
  448. write((x + w) + " " + y + " rlineto");
  449. write((x + w) + " " + (y - h) + " rlineto");
  450. write(x + " " + (y - h) + " rlineto");
  451. write("closepath");
  452. write("clippath");
  453. }
  454. // transform so that the coordinates (0,0) is from the top left
  455. // and positive is down and to the right. (0,0) is where the
  456. // viewBox puts it.
  457. write(xOffset + " " + yOffset + " translate");
  458. write((at.getTranslateX() * 1000) + " "
  459. + (-at.getTranslateY() * 1000) + " translate");
  460. write(sx * at.getScaleX() + " " + sy * at.getScaleY() + " scale");
  461. PSGraphics2D graphics = new PSGraphics2D(false, fs,
  462. this, currentFontName,
  463. currentFontSize,
  464. currentXPosition,
  465. currentYPosition);
  466. graphics.setGraphicContext(new org.apache.batik.ext.awt.g2d.GraphicContext());
  467. try {
  468. root.paint(graphics);
  469. } catch (Exception e) {
  470. log.error("svg graphic could not be rendered: "
  471. + e.getMessage(), e);
  472. }
  473. write("grestore");
  474. comment("% --- SVG Area end");
  475. }
  476. /**
  477. * Renders an image, scaling it to the given width and height.
  478. * If the scaled width and height is the same intrinsic size
  479. * of the image, the image is not scaled.
  480. *
  481. * @param x the x position of left edge in millipoints
  482. * @param y the y position of top edge in millipoints
  483. * @param w the width in millipoints
  484. * @param h the height in millipoints
  485. * @param image the image to be rendered
  486. * @param fs the font state to use when rendering text
  487. * in non-bitmapped images.
  488. */
  489. protected void drawImageScaled(int x, int y, int w, int h,
  490. FopImage image,
  491. FontState fs) {
  492. //log.debug("drawing scaled image: "+x+"/"+y+" "+w+"/"+h);
  493. if (image instanceof SVGImage) {
  494. try {
  495. renderSVGDocument(((SVGImage)image).getSVGDocument(), x, y, fs);
  496. } catch (FopImageException e) {
  497. log.error("Error rendering SVG image", e);
  498. }
  499. } else if (image instanceof EPSImage) {
  500. renderEPS(image, x, y, w, h);
  501. } else {
  502. renderBitmap(image, x, y, w, h);
  503. }
  504. }
  505. /**
  506. * Renders an image, clipping it as specified.
  507. *
  508. * @param x the x position of left edge in millipoints.
  509. * @param y the y position of top edge in millipoints.
  510. * @param clipX the left edge of the clip in millipoints
  511. * @param clipY the top edge of the clip in millipoints
  512. * @param clipW the clip width in millipoints
  513. * @param clipH the clip height in millipoints
  514. * @param image the image to be rendered
  515. * @param fs the font state to use when rendering text
  516. * in non-bitmapped images.
  517. */
  518. protected void drawImageClipped(int x, int y,
  519. int clipX, int clipY,
  520. int clipW, int clipH,
  521. FopImage image,
  522. FontState fs) {
  523. //log.debug("drawing clipped image: "+x+"/"+y+" "+clipX+"/"+clipY+" "+clipW+"/"+clipH);
  524. write("gsave");
  525. write(clipX + " " + clipY + " " + clipW + " " + clipH + " re");
  526. write("clippath");
  527. try {
  528. int w = image.getWidth() * 1000;
  529. int h = image.getHeight() * 1000;
  530. drawImageScaled(x, y, w, h, image, fs);
  531. } catch (FopImageException e) {
  532. log.error("Error getting image extents", e);
  533. }
  534. write("grestore");
  535. }
  536. public void renderEPS(FopImage img, int x, int y, int w, int h) {
  537. try {
  538. EPSImage eimg = (EPSImage)img;
  539. int[] bbox = eimg.getBBox();
  540. int bboxw = bbox[2] - bbox[0];
  541. int bboxh = bbox[3] - bbox[1];
  542. write("%%BeginDocument: " + eimg.getDocName());
  543. write("BeginEPSF");
  544. write(x + " " + (y - h) + " translate");
  545. write("0.0 rotate");
  546. write((long)(w/bboxw) + " " + (long)(h/bboxh) + " scale");
  547. write(-bbox[0] + " " + (-bbox[1]) + " translate");
  548. write(bbox[0] + " " + bbox[1] + " " + bboxw + " " + bboxh + " rectclip");
  549. write("newpath");
  550. out.writeByteArr(img.getBitmaps());
  551. write("%%EndDocument");
  552. write("EndEPSF");
  553. write("");
  554. } catch (Exception e) {
  555. e.printStackTrace();
  556. log.error("PSRenderer.renderImageArea(): Error rendering bitmap ("
  557. + e.getMessage() + ")", e);
  558. }
  559. }
  560. public void renderBitmap(FopImage img, int x, int y, int w, int h) {
  561. try {
  562. boolean iscolor = img.getColorSpace().getColorSpace()
  563. != ColorSpace.DEVICE_GRAY;
  564. byte[] imgmap = img.getBitmaps();
  565. write("gsave");
  566. if (img.getColorSpace().getColorSpace() == ColorSpace.DEVICE_CMYK)
  567. write("/DeviceCMYK setcolorspace");
  568. else if (img.getColorSpace().getColorSpace() == ColorSpace.DEVICE_GRAY)
  569. write("/DeviceGray setcolorspace");
  570. else
  571. write("/DeviceRGB setcolorspace");
  572. write(x + " " + (y - h) + " translate");
  573. write(w + " " + h + " scale");
  574. write("{{");
  575. // Template: (RawData is used for the EOF signal only)
  576. // write("/RawData currentfile <first filter> filter def");
  577. // write("/Data RawData <second filter> <third filter> [...] def");
  578. if (img instanceof JpegImage) {
  579. write("/RawData currentfile /ASCII85Decode filter def");
  580. write("/Data RawData << >> /DCTDecode filter def");
  581. } else {
  582. if (this.psLevel >= 3) {
  583. write("/RawData currentfile /ASCII85Decode filter def");
  584. write("/Data RawData /FlateDecode filter def");
  585. } else {
  586. write("/RawData currentfile /ASCII85Decode filter def");
  587. write("/Data RawData /RunLengthDecode filter def");
  588. }
  589. }
  590. write("<<");
  591. write(" /ImageType 1");
  592. write(" /Width " + img.getWidth());
  593. write(" /Height " + img.getHeight());
  594. write(" /BitsPerComponent 8");
  595. if (img.getColorSpace().getColorSpace() == ColorSpace.DEVICE_CMYK) {
  596. if (img.invertImage())
  597. write(" /Decode [1 0 1 0 1 0 1 0]");
  598. else
  599. write(" /Decode [0 1 0 1 0 1 0 1]");
  600. } else if (iscolor) {
  601. write(" /Decode [0 1 0 1 0 1]");
  602. } else {
  603. write(" /Decode [0 1]");
  604. }
  605. // Setup scanning for left-to-right and top-to-bottom
  606. write(" /ImageMatrix [" + img.getWidth() + " 0 0 -"
  607. + img.getHeight() + " 0 " + img.getHeight() + "]");
  608. write(" /DataSource Data");
  609. write(">>");
  610. write("image");
  611. /* the following two lines could be enabled if something still goes wrong
  612. * write("Data closefile");
  613. * write("RawData flushfile");
  614. */
  615. write("} stopped {handleerror} if");
  616. write(" RawData flushfile");
  617. write("} exec");
  618. /*
  619. * for (int y=0; y<img.getHeight(); y++) {
  620. * int indx = y * img.getWidth();
  621. * if (iscolor) indx*= 3;
  622. * for (int x=0; x<img.getWidth(); x++) {
  623. * if (iscolor) {
  624. * writeASCIIHex(imgmap[indx++] & 0xFF);
  625. * writeASCIIHex(imgmap[indx++] & 0xFF);
  626. * writeASCIIHex(imgmap[indx++] & 0xFF);
  627. * } else {
  628. * writeASCIIHex(imgmap[indx++] & 0xFF);
  629. * }
  630. * }
  631. * }
  632. */
  633. try {
  634. // imgmap[0] = 1;
  635. OutputStream out = this.out;
  636. out = new ASCII85OutputStream(out);
  637. if (img instanceof JpegImage) {
  638. //nop
  639. } else {
  640. if (this.psLevel >= 3) {
  641. out = new FlateEncodeOutputStream(out);
  642. } else {
  643. out = new RunLengthEncodeOutputStream(out);
  644. }
  645. }
  646. out.write(imgmap);
  647. if (out instanceof Finalizable) {
  648. ((Finalizable)out).finalizeStream();
  649. } else {
  650. out.flush();
  651. }
  652. } catch (IOException e) {
  653. if (!ioTrouble)
  654. e.printStackTrace();
  655. ioTrouble = true;
  656. }
  657. write("");
  658. write("grestore");
  659. } catch (FopImageException e) {
  660. log.error("PSRenderer.renderImageArea(): Error rendering bitmap ("
  661. + e.getMessage() + ")", e);
  662. }
  663. }
  664. /**
  665. * Render an image area.
  666. *
  667. * @param area the image area to render
  668. */
  669. public void renderImageArea(ImageArea area) {
  670. // adapted from contribution by BoBoGi
  671. int x = this.currentXPosition + area.getXOffset();
  672. int ploffset = 0;
  673. if (area.getParent() instanceof LineArea) {
  674. ploffset = ((LineArea)area.getParent()).getPlacementOffset();
  675. }
  676. int y = this.currentYPosition + ploffset;
  677. int w = area.getContentWidth();
  678. int h = area.getHeight();
  679. //this.currentYPosition -= h;
  680. this.currentXPosition += w;
  681. FopImage img = area.getImage();
  682. if (img == null) {
  683. log.error("Error while loading image: area.getImage() is null");
  684. } else {
  685. drawImageScaled(x, y, w, h, img, area.getFontState());
  686. }
  687. }
  688. /**
  689. * render an image area to PostScript
  690. *
  691. * @param area the area to render
  692. */
  693. /*
  694. public void renderImageArea(ImageArea area) {
  695. int x = this.currentXPosition + area.getXOffset();
  696. int ploffset = 0;
  697. if (area.getParent() instanceof LineArea) {
  698. ploffset = ((LineArea)area.getParent()).getPlacementOffset();
  699. }
  700. int y = this.currentYPosition + ploffset;
  701. int w = area.getContentWidth();
  702. int h = area.getHeight();
  703. this.currentYPosition -= area.getHeight();
  704. imagecount++;
  705. // if (imagecount!=4) return;
  706. comment("% --- ImageArea");
  707. if (area.getImage() instanceof SVGImage) {
  708. renderSVGDocument(((SVGImage)area.getImage()).getSVGDocument(), x, y, area.getFontState());
  709. } else if (area.getImage() instanceof EPSImage) {
  710. renderEPS(area.getImage(), x, y, w, h);
  711. } else {
  712. renderBitmap(area.getImage(), x, y, w, h);
  713. }
  714. comment("% --- ImageArea end");
  715. }*/
  716. /**
  717. * render an inline area to PostScript
  718. *
  719. * @param area the area to render
  720. */
  721. public void renderWordArea(WordArea area) {
  722. movetoCurrPosition();
  723. FontState fs = area.getFontState();
  724. String fontWeight = fs.getFontWeight();
  725. StringBuffer sb = new StringBuffer();
  726. String s = area.getText();
  727. int l = s.length();
  728. for (int i = 0; i < l; i++) {
  729. char ch = s.charAt(i);
  730. char mch = fs.mapChar(ch);
  731. if (mch > 127) {
  732. sb = sb.append("\\" + Integer.toOctalString(mch));
  733. } else {
  734. final String escape = "\\()[]{}";
  735. if (escape.indexOf(mch) >= 0) {
  736. sb.append("\\");
  737. }
  738. sb = sb.append(mch);
  739. }
  740. }
  741. String psString = null;
  742. if (area.getFontState().getLetterSpacing() > 0) {
  743. //float f = area.getFontState().getLetterSpacing() * 1000 / this.currentFontSize;
  744. float f = area.getFontState().getLetterSpacing();
  745. psString = (new StringBuffer().append(f).append(" 0.0 (")
  746. .append(sb.toString()).append(") A")).toString();
  747. } else {
  748. psString = (new StringBuffer("(").append(sb.toString())
  749. .append(") t")).toString();
  750. }
  751. // System.out.println("["+s+"] --> ["+sb.toString()+"]");
  752. // comment("% --- InlineArea font-weight="+fontWeight+": " + sb.toString());
  753. useFont(fs.getFontName(), fs.getFontSize());
  754. useColor(area.getRed(), area.getGreen(), area.getBlue());
  755. if (area.getUnderlined() || area.getLineThrough()
  756. || area.getOverlined())
  757. write("ULS");
  758. write(psString);
  759. if (area.getUnderlined())
  760. write("ULE");
  761. if (area.getLineThrough())
  762. write("SOE");
  763. if (area.getOverlined())
  764. write("OLE");
  765. this.currentXPosition += area.getContentWidth();
  766. }
  767. public void useFont(String name, int size) {
  768. if ((currentFontName != name) || (currentFontSize != size)) {
  769. write(name + " " + size + " F");
  770. currentFontName = name;
  771. currentFontSize = size;
  772. }
  773. }
  774. /**
  775. * render an inline space to PostScript
  776. *
  777. * @param space the space to render
  778. */
  779. public void renderInlineSpace(InlineSpace space) {
  780. // write("% --- InlineSpace size="+space.getSize());
  781. if (space.getUnderlined() || space.getLineThrough()
  782. || space.getOverlined()) {
  783. //start textdeko
  784. movetoCurrPosition();
  785. write("ULS");
  786. write(space.getSize() + " 0 RM");
  787. //end textdeko
  788. if (space.getUnderlined())
  789. write("ULE");
  790. if (space.getLineThrough())
  791. write("SOE");
  792. if (space.getOverlined())
  793. write("OLE");
  794. }
  795. this.currentXPosition += space.getSize();
  796. }
  797. /**
  798. * render a line area to PostScript
  799. *
  800. * @param area the area to render
  801. */
  802. public void renderLineArea(LineArea area) {
  803. int rx = this.currentAreaContainerXPosition + area.getStartIndent();
  804. int ry = this.currentYPosition;
  805. int w = area.getContentWidth();
  806. int h = area.getHeight();
  807. this.currentYPosition -= area.getPlacementOffset();
  808. this.currentXPosition = rx;
  809. int bl = this.currentYPosition;
  810. // method is identical to super method except next line
  811. movetoCurrPosition();
  812. String fontWeight = area.getFontState().getFontWeight();
  813. //comment("% --- LineArea begin font-weight="+fontWeight);
  814. List children = area.getChildren();
  815. for (int i = 0; i < children.size(); i++) {
  816. Box b = (Box)children.get(i);
  817. this.currentYPosition = ry - area.getPlacementOffset();
  818. b.render(this);
  819. }
  820. //comment("% --- LineArea end");
  821. this.currentYPosition = ry - h;
  822. this.currentXPosition = rx;
  823. }
  824. /**
  825. * render a page to PostScript
  826. *
  827. * @param page the page to render
  828. */
  829. public void renderPage(Page page) {
  830. this.pagecount++;
  831. this.idReferences = page.getIDReferences();
  832. write("%%Page: " + page.getNumber() + " " + this.pagecount);
  833. final long pagewidth = page.getWidth();
  834. final long pageheight = page.getHeight();
  835. final double pspagewidth = pagewidth / 1000f;
  836. final double pspageheight = pageheight / 1000f;
  837. boolean rotate = false;
  838. if (isAutoRotateLandscape() && (pageheight < pagewidth)) {
  839. rotate = true;
  840. write("%%PageBoundingBox: 0 0 " +
  841. Math.round(pspageheight) + " " +
  842. Math.round(pspagewidth));
  843. write("%%PageOrientation: Landscape");
  844. } else {
  845. write("%%PageBoundingBox: 0 0 " +
  846. Math.round(pspagewidth) + " " +
  847. Math.round(pspageheight));
  848. if (isAutoRotateLandscape()) {
  849. write("%%PageOrientation: Portrait");
  850. }
  851. }
  852. write("%%BeginPageSetup");
  853. if (rotate) {
  854. write(Math.round(pspageheight) + " 0 translate");
  855. write("90 rotate");
  856. }
  857. write("0.001 0.001 scale");
  858. write("%%EndPageSetup");
  859. renderRegions(page);
  860. write("showpage");
  861. write("%%PageTrailer");
  862. write("%%EndPage"); //This is non-standard, but used by Adobe.
  863. }
  864. /**
  865. * render a leader area to PostScript
  866. *
  867. * @param area the area to render
  868. */
  869. public void renderLeaderArea(LeaderArea area) {
  870. int rx = this.currentXPosition;
  871. int ry = this.currentYPosition;
  872. int w = area.getContentWidth();
  873. int th = area.getRuleThickness();
  874. int th2 = th / 2;
  875. int th3 = th / 3;
  876. int th4 = th / 4;
  877. switch (area.getLeaderPattern()) {
  878. case LeaderPattern.SPACE:
  879. // NOP
  880. break;
  881. case LeaderPattern.RULE:
  882. if (area.getRuleStyle() == RuleStyle.NONE)
  883. break;
  884. useColor(area.getRed(), area.getGreen(), area.getBlue());
  885. write("gsave");
  886. write("0 setlinecap");
  887. switch (area.getRuleStyle()) {
  888. case RuleStyle.DOTTED:
  889. write("newpath");
  890. write("[1000 3000] 0 setdash");
  891. write(th + " setlinewidth");
  892. write(rx + " " + ry + " M");
  893. write(w + " 0 rlineto");
  894. useColor(area.getRed(), area.getGreen(), area.getBlue());
  895. write("stroke");
  896. break;
  897. case RuleStyle.DASHED:
  898. write("newpath");
  899. write("[3000 3000] 0 setdash");
  900. write(th + " setlinewidth");
  901. write(rx + " " + ry + " M");
  902. write(w + " 0 rlineto");
  903. useColor(area.getRed(), area.getGreen(), area.getBlue());
  904. write("stroke");
  905. break;
  906. case RuleStyle.SOLID:
  907. write("newpath");
  908. write(th + " setlinewidth");
  909. write(rx + " " + ry + " M");
  910. write(w + " 0 rlineto");
  911. useColor(area.getRed(), area.getGreen(), area.getBlue());
  912. write("stroke");
  913. break;
  914. case RuleStyle.DOUBLE:
  915. write("newpath");
  916. write(th3 + " setlinewidth");
  917. write(rx + " " + (ry - th3) + " M");
  918. write(w + " 0 rlineto");
  919. write(rx + " " + (ry + th3) + " M");
  920. write(w + " 0 rlineto");
  921. useColor(area.getRed(), area.getGreen(), area.getBlue());
  922. write("stroke");
  923. break;
  924. case RuleStyle.GROOVE:
  925. write(th2 + " setlinewidth");
  926. write("newpath");
  927. write(rx + " " + (ry - th4) + " M");
  928. write(w + " 0 rlineto");
  929. useColor(area.getRed(), area.getGreen(), area.getBlue());
  930. write("stroke");
  931. write("newpath");
  932. write(rx + " " + (ry + th4) + " M");
  933. write(w + " 0 rlineto");
  934. useColor(1, 1, 1); // white
  935. write("stroke");
  936. break;
  937. case RuleStyle.RIDGE:
  938. write(th2 + " setlinewidth");
  939. write("newpath");
  940. write(rx + " " + (ry - th4) + " M");
  941. write(w + " 0 rlineto");
  942. useColor(1, 1, 1); // white
  943. write("stroke");
  944. write("newpath");
  945. write(rx + " " + (ry + th4) + " M");
  946. write(w + " 0 rlineto");
  947. useColor(area.getRed(), area.getGreen(), area.getBlue());
  948. write("stroke");
  949. break;
  950. }
  951. write("grestore");
  952. break;
  953. case LeaderPattern.DOTS:
  954. comment("% --- Leader dots NYI");
  955. log.error("Leader dots: Not yet implemented");
  956. break;
  957. case LeaderPattern.USECONTENT:
  958. comment("% --- Leader use-content NYI");
  959. log.error("Leader use-content: Not yet implemented");
  960. break;
  961. }
  962. this.currentXPosition += area.getContentWidth();
  963. write(area.getContentWidth() + " 0 RM");
  964. }
  965. protected void doFrame(Area area) {
  966. int w, h;
  967. int rx = this.currentAreaContainerXPosition;
  968. w = area.getContentWidth();
  969. BorderAndPadding bap = area.getBorderAndPadding();
  970. if (area instanceof BlockArea)
  971. rx += ((BlockArea)area).getStartIndent();
  972. h = area.getContentHeight();
  973. int ry = this.currentYPosition;
  974. rx = rx - area.getPaddingLeft();
  975. ry = ry + area.getPaddingTop();
  976. w = w + area.getPaddingLeft() + area.getPaddingRight();
  977. h = h + area.getPaddingTop() + area.getPaddingBottom();
  978. rx = rx - area.getBorderLeftWidth();
  979. ry = ry + area.getBorderTopWidth();
  980. w = w + area.getBorderLeftWidth() + area.getBorderRightWidth();
  981. h = h + area.getBorderTopWidth() + area.getBorderBottomWidth();
  982. doBackground(area, rx, ry, w, h);
  983. if (area.getBorderTopWidth() != 0) {
  984. write("newpath");
  985. write(rx + " " + ry + " M");
  986. write(w + " 0 rlineto");
  987. write(area.getBorderTopWidth() + " setlinewidth");
  988. write("0 setlinecap");
  989. useColor(bap.getBorderColor(BorderAndPadding.TOP));
  990. write("stroke");
  991. }
  992. if (area.getBorderLeftWidth() != 0) {
  993. write("newpath");
  994. write(rx + " " + ry + " M");
  995. write("0 " + (-h) + " rlineto");
  996. write(area.getBorderLeftWidth() + " setlinewidth");
  997. write("0 setlinecap");
  998. useColor(bap.getBorderColor(BorderAndPadding.LEFT));
  999. write("stroke");
  1000. }
  1001. if (area.getBorderRightWidth() != 0) {
  1002. write("newpath");
  1003. write((rx + w) + " " + ry + " M");
  1004. write("0 " + (-h) + " rlineto");
  1005. write(area.getBorderRightWidth() + " setlinewidth");
  1006. write("0 setlinecap");
  1007. useColor(bap.getBorderColor(BorderAndPadding.RIGHT));
  1008. write("stroke");
  1009. }
  1010. if (area.getBorderBottomWidth() != 0) {
  1011. write("newpath");
  1012. write(rx + " " + (ry - h) + " M");
  1013. write(w + " 0 rlineto");
  1014. write(area.getBorderBottomWidth() + " setlinewidth");
  1015. write("0 setlinecap");
  1016. useColor(bap.getBorderColor(BorderAndPadding.BOTTOM));
  1017. write("stroke");
  1018. }
  1019. }
  1020. private void useColor(ColorType col) {
  1021. useColor(col.red(), col.green(), col.blue());
  1022. }
  1023. private void useColor(float red, float green, float blue) {
  1024. if ((red != currRed) || (green != currGreen) || (blue != currBlue)) {
  1025. write(red + " " + green + " " + blue + " setrgbcolor");
  1026. currRed = red;
  1027. currGreen = green;
  1028. currBlue = blue;
  1029. }
  1030. }
  1031. /**
  1032. Default start renderer method. This would
  1033. normally be overridden. (mark-fop@inomial.com).
  1034. */
  1035. public void startRenderer(OutputStream outputStream)
  1036. throws IOException {
  1037. log.debug("rendering areas to PostScript");
  1038. this.pagecount = 0;
  1039. this.out = new PSStream(outputStream);
  1040. write("%!PS-Adobe-3.0");
  1041. if (this.producer == null) {
  1042. this.producer = org.apache.fop.apps.Version.getVersion();
  1043. }
  1044. write("%%Creator: "+this.producer);
  1045. write("%%Pages: (atend)");
  1046. write("%%DocumentProcessColors: Black");
  1047. write("%%DocumentSuppliedResources: procset FOPFonts");
  1048. write("%%EndComments");
  1049. write("%%BeginDefaults");
  1050. write("%%EndDefaults");
  1051. write("%%BeginProlog");
  1052. write("%%EndProlog");
  1053. write("%%BeginSetup");
  1054. writeProcs();
  1055. writeFontDict(fontInfo);
  1056. /* Write proc for including EPS */
  1057. write("%%BeginResource: procset EPSprocs");
  1058. write("%%Title: EPS encapsulation procs");
  1059. write("/BeginEPSF { %def");
  1060. write("/b4_Inc_state save def % Save state for cleanup");
  1061. write("/dict_count countdictstack def % Count objects on dict stack");
  1062. write("/op_count count 1 sub def % Count objects on operand stack");
  1063. write("userdict begin % Push userdict on dict stack");
  1064. write("/showpage { } def % Redefine showpage, { } = null proc");
  1065. write("0 setgray 0 setlinecap % Prepare graphics state");
  1066. write("1 setlinewidth 0 setlinejoin");
  1067. write("10 setmiterlimit [ ] 0 setdash newpath");
  1068. write("/languagelevel where % If level not equal to 1 then");
  1069. write("{pop languagelevel % set strokeadjust and");
  1070. write("1 ne % overprint to their defaults.");
  1071. write("{false setstrokeadjust false setoverprint");
  1072. write("} if");
  1073. write("} if");
  1074. write("} bind def");
  1075. write("/EndEPSF { %def");
  1076. write("count op_count sub {pop} repeat % Clean up stacks");
  1077. write("countdictstack dict_count sub {end} repeat");
  1078. write("b4_Inc_state restore");
  1079. write("} bind def");
  1080. write("%%EndResource");
  1081. write("FOPprocs begin");
  1082. write("FOPFonts begin");
  1083. write("%%EndSetup");
  1084. }
  1085. /**
  1086. Default stop renderer method. This would
  1087. normally be overridden. (mark-fop@inomial.com).
  1088. */
  1089. public void stopRenderer(OutputStream outputStream)
  1090. throws IOException {
  1091. write("%%Trailer");
  1092. write("%%Pages: "+this.pagecount);
  1093. write("%%EOF");
  1094. this.out.flush();
  1095. log.debug("written out PostScript");
  1096. }
  1097. public void render(Page page, OutputStream outputStream) {
  1098. this.renderPage(page);
  1099. }
  1100. }