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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074
  1. /*
  2. * $Id$
  3. * Copyright (C) 2001 The Apache Software Foundation. All rights reserved.
  4. * For details on use and redistribution please refer to the
  5. * LICENSE file included with these sources.
  6. */
  7. package org.apache.fop.render.ps;
  8. // FOP
  9. import org.apache.fop.svg.SVGArea;
  10. import org.apache.fop.render.AbstractRenderer;
  11. import org.apache.fop.render.Renderer;
  12. import org.apache.fop.image.ImageArea;
  13. import org.apache.fop.image.FopImage;
  14. import org.apache.fop.image.FopImageException;
  15. import org.apache.fop.layout.*;
  16. import org.apache.fop.layout.inline.*;
  17. import org.apache.fop.datatypes.*;
  18. import org.apache.fop.fo.properties.*;
  19. import org.apache.fop.render.pdf.Font;
  20. import org.apache.fop.image.*;
  21. import org.apache.batik.bridge.*;
  22. import org.apache.batik.swing.svg.*;
  23. import org.apache.batik.swing.gvt.*;
  24. import org.apache.batik.gvt.*;
  25. import org.apache.batik.gvt.renderer.*;
  26. import org.apache.batik.gvt.filter.*;
  27. import org.apache.batik.gvt.event.*;
  28. import org.apache.log.Logger;
  29. // SVG
  30. import org.w3c.dom.svg.SVGSVGElement;
  31. import org.w3c.dom.svg.SVGDocument;
  32. import org.w3c.dom.*;
  33. import org.w3c.dom.svg.*;
  34. // Java
  35. import java.io.*;
  36. import java.util.*;
  37. import java.io.IOException;
  38. import java.io.OutputStream;
  39. import java.util.Enumeration;
  40. import java.util.Vector;
  41. import java.util.Hashtable;
  42. import java.awt.geom.AffineTransform;
  43. import java.awt.geom.Dimension2D;
  44. import java.awt.Point;
  45. import java.awt.RenderingHints;
  46. import java.awt.font.FontRenderContext;
  47. import java.awt.Dimension;
  48. /**
  49. * Renderer that renders to PostScript.
  50. * <br>
  51. * This class currently generates PostScript Level 2 code. The only exception
  52. * is the FlateEncode filter which is a Level 3 feature. The filters in use
  53. * are hardcoded at the moment.
  54. * <br>
  55. * This class follows the Document Structuring Conventions (DSC) version 3.0
  56. * (If I did everything right). If anyone modifies this renderer please make
  57. * sure to also follow the DSC to make it simpler to programmatically modify
  58. * the generated Postscript files (ex. extract pages etc.).
  59. * <br>
  60. * TODO: Character size/spacing, SVG Transcoder for Batik, configuration, move
  61. * to PrintRenderer, maybe improve filters (I'm not very proud of them), add a
  62. * RunLengthEncode filter (useful for Level 2 Postscript), Improve
  63. * DocumentProcessColors stuff (probably needs to be configurable, then maybe
  64. * add a color to grayscale conversion for bitmaps to make output smaller (See
  65. * PCLRenderer), font embedding, support different character encodings, try to
  66. * implement image transparency, positioning of images is wrong etc. <P>
  67. *
  68. * Modified by Mark Lillywhite mark-fop@inomial.com, to use the new
  69. * Renderer interface. This PostScript renderer appears to be the
  70. * most efficient at producing output.
  71. *
  72. * @author Jeremias Märki
  73. */
  74. public class PSRenderer extends AbstractRenderer {
  75. /**
  76. * the application producing the PostScript
  77. */
  78. protected String producer;
  79. int imagecount = 0; // DEBUG
  80. private boolean enableComments = true;
  81. /**
  82. * the stream used to output the PostScript
  83. */
  84. protected PSStream out;
  85. private boolean ioTrouble = false;
  86. private String currentFontName;
  87. private int currentFontSize;
  88. private int pageHeight;
  89. private int pageWidth;
  90. private int currentXPosition = 0;
  91. private int currentYPosition = 0;
  92. private int currentAreaContainerXPosition = 0;
  93. private float currRed;
  94. private float currGreen;
  95. private float currBlue;
  96. private FontInfo fontInfo;
  97. protected Hashtable options;
  98. /**
  99. * set the document's producer
  100. *
  101. * @param producer string indicating application producing the PostScript
  102. */
  103. public void setProducer(String producer) {
  104. this.producer = producer;
  105. }
  106. /**
  107. * set up renderer options
  108. */
  109. public void setOptions(Hashtable options) {
  110. this.options = options;
  111. }
  112. /**
  113. * write out a command
  114. */
  115. protected void write(String cmd) {
  116. try {
  117. out.write(cmd);
  118. } catch (IOException e) {
  119. if (!ioTrouble)
  120. e.printStackTrace();
  121. ioTrouble = true;
  122. }
  123. }
  124. /**
  125. * write out a comment
  126. */
  127. protected void comment(String comment) {
  128. if (this.enableComments)
  129. write(comment);
  130. }
  131. protected void writeFontDict(FontInfo fontInfo) {
  132. write("%%BeginResource: procset FOPFonts");
  133. write("%%Title: Font setup (shortcuts) for this file");
  134. write("/FOPFonts 100 dict dup begin");
  135. write("/bd{bind def}bind def");
  136. write("/ld{load def}bd");
  137. write("/M/moveto ld");
  138. write("/RM/rmoveto ld");
  139. write("/t/show ld");
  140. write("/ux 0.0 def");
  141. write("/uy 0.0 def");
  142. // write("/cf /Helvetica def");
  143. // write("/cs 12000 def");
  144. // <font> <size> F
  145. write("/F {");
  146. write(" /Tp exch def");
  147. // write(" currentdict exch get");
  148. write(" /Tf exch def");
  149. write(" Tf findfont Tp scalefont setfont");
  150. write(" /cf Tf def /cs Tp def /cw ( ) stringwidth pop def");
  151. write("} bd");
  152. write("/ULS {currentpoint /uy exch def /ux exch def} bd");
  153. write("/ULE {");
  154. write(" /Tcx currentpoint pop def");
  155. write(" gsave");
  156. write(" newpath");
  157. write(" cf findfont cs scalefont dup");
  158. write(" /FontMatrix get 0 get /Ts exch def /FontInfo get dup");
  159. write(" /UnderlinePosition get Ts mul /To exch def");
  160. write(" /UnderlineThickness get Ts mul /Tt exch def");
  161. write(" ux uy To add moveto Tcx uy To add lineto");
  162. write(" Tt setlinewidth stroke");
  163. write(" grestore");
  164. write("} bd");
  165. write("/OLE {");
  166. write(" /Tcx currentpoint pop def");
  167. write(" gsave");
  168. write(" newpath");
  169. write(" cf findfont cs scalefont dup");
  170. write(" /FontMatrix get 0 get /Ts exch def /FontInfo get dup");
  171. write(" /UnderlinePosition get Ts mul /To exch def");
  172. write(" /UnderlineThickness get Ts mul /Tt exch def");
  173. write(" ux uy To add cs add moveto Tcx uy To add cs add lineto");
  174. write(" Tt setlinewidth stroke");
  175. write(" grestore");
  176. write("} bd");
  177. write("/SOE {");
  178. write(" /Tcx currentpoint pop def");
  179. write(" gsave");
  180. write(" newpath");
  181. write(" cf findfont cs scalefont dup");
  182. write(" /FontMatrix get 0 get /Ts exch def /FontInfo get dup");
  183. write(" /UnderlinePosition get Ts mul /To exch def");
  184. write(" /UnderlineThickness get Ts mul /Tt exch def");
  185. write(" ux uy To add cs 10 mul 26 idiv add moveto Tcx uy To add cs 10 mul 26 idiv add lineto");
  186. write(" Tt setlinewidth stroke");
  187. write(" grestore");
  188. write("} bd");
  189. // write("/gfF1{/Helvetica findfont} bd");
  190. // write("/gfF3{/Helvetica-Bold findfont} bd");
  191. Hashtable fonts = fontInfo.getFonts();
  192. Enumeration enum = fonts.keys();
  193. while (enum.hasMoreElements()) {
  194. String key = (String)enum.nextElement();
  195. Font fm = (Font)fonts.get(key);
  196. write("/" + key + " /" + fm.fontName() + " def");
  197. }
  198. write("end def");
  199. write("%%EndResource");
  200. enum = fonts.keys();
  201. while (enum.hasMoreElements()) {
  202. String key = (String)enum.nextElement();
  203. Font fm = (Font)fonts.get(key);
  204. write("/" + fm.fontName() + " findfont");
  205. write("dup length dict begin");
  206. write(" {1 index /FID ne {def} {pop pop} ifelse} forall");
  207. write(" /Encoding ISOLatin1Encoding def");
  208. write(" currentdict");
  209. write("end");
  210. write("/" + fm.fontName() + " exch definefont pop");
  211. }
  212. }
  213. protected void movetoCurrPosition() {
  214. write(this.currentXPosition + " " + this.currentYPosition + " M");
  215. }
  216. /**
  217. * set up the font info
  218. *
  219. * @param fontInfo the font info object to set up
  220. */
  221. public void setupFontInfo(FontInfo fontInfo) {
  222. /* use PDF's font setup to get PDF metrics */
  223. org.apache.fop.render.pdf.FontSetup.setup(fontInfo);
  224. this.fontInfo = fontInfo;
  225. }
  226. /**
  227. * render an area container to PostScript
  228. *
  229. * @param area the area container to render
  230. */
  231. public void renderAreaContainer(AreaContainer area) {
  232. int saveY = this.currentYPosition;
  233. int saveX = this.currentAreaContainerXPosition;
  234. if (area.getPosition() == Position.ABSOLUTE) {
  235. // Y position is computed assuming positive Y axis, adjust for negative postscript one
  236. this.currentYPosition = area.getYPosition()
  237. - 2 * area.getPaddingTop()
  238. - 2 * area.getBorderTopWidth();
  239. this.currentAreaContainerXPosition = area.getXPosition();
  240. } else if (area.getPosition() == Position.RELATIVE) {
  241. this.currentYPosition -= area.getYPosition();
  242. this.currentAreaContainerXPosition += area.getXPosition();
  243. } else if (area.getPosition() == Position.STATIC) {
  244. this.currentYPosition -= area.getPaddingTop()
  245. + area.getBorderTopWidth();
  246. this.currentAreaContainerXPosition += area.getPaddingLeft()
  247. + area.getBorderLeftWidth();
  248. }
  249. this.currentXPosition = this.currentAreaContainerXPosition;
  250. // comment("% --- AreaContainer begin");
  251. doFrame(area);
  252. Enumeration e = area.getChildren().elements();
  253. while (e.hasMoreElements()) {
  254. Box b = (Box)e.nextElement();
  255. b.render(this);
  256. }
  257. // comment("% --- AreaContainer end");
  258. if (area.getPosition() != Position.STATIC) {
  259. this.currentYPosition = saveY;
  260. this.currentAreaContainerXPosition = saveX;
  261. } else {
  262. this.currentYPosition -= area.getHeight();
  263. }
  264. }
  265. /**
  266. * render a body area container to PostScript
  267. *
  268. * @param area the body area container to render
  269. */
  270. public void renderBodyAreaContainer(BodyAreaContainer area) {
  271. int saveY = this.currentYPosition;
  272. int saveX = this.currentAreaContainerXPosition;
  273. if (area.getPosition() == Position.ABSOLUTE) {
  274. // Y position is computed assuming positive Y axis, adjust for negative postscript one
  275. this.currentYPosition = area.getYPosition();
  276. this.currentAreaContainerXPosition = area.getXPosition();
  277. } else if (area.getPosition() == Position.RELATIVE) {
  278. this.currentYPosition -= area.getYPosition();
  279. this.currentAreaContainerXPosition += area.getXPosition();
  280. }
  281. this.currentXPosition = this.currentAreaContainerXPosition;
  282. int w, h;
  283. int rx = this.currentAreaContainerXPosition;
  284. w = area.getContentWidth();
  285. h = area.getContentHeight();
  286. int ry = this.currentYPosition;
  287. // comment("% --- BodyAreaContainer begin");
  288. doFrame(area);
  289. // movetoCurrPosition();
  290. Enumeration e = area.getChildren().elements();
  291. while (e.hasMoreElements()) {
  292. Box b = (Box)e.nextElement();
  293. b.render(this);
  294. }
  295. // comment("% --- BodyAreaContainer end");
  296. if (area.getPosition() != Position.STATIC) {
  297. this.currentYPosition = saveY;
  298. this.currentAreaContainerXPosition = saveX;
  299. } else {
  300. this.currentYPosition -= area.getHeight();
  301. }
  302. }
  303. /**
  304. * render a span area to PostScript
  305. *
  306. * @param area the span area to render
  307. */
  308. public void renderSpanArea(SpanArea area) {
  309. // comment("% --- SpanArea begin");
  310. Enumeration e = area.getChildren().elements();
  311. while (e.hasMoreElements()) {
  312. Box b = (Box)e.nextElement();
  313. b.render(this);
  314. }
  315. // comment("% --- SpanArea end");
  316. }
  317. /**
  318. * render a block area to PostScript
  319. *
  320. * @param area the block area to render
  321. */
  322. public void renderBlockArea(BlockArea area) {
  323. // comment("% --- BlockArea begin");
  324. doFrame(area);
  325. Enumeration e = area.getChildren().elements();
  326. while (e.hasMoreElements()) {
  327. Box b = (Box)e.nextElement();
  328. b.render(this);
  329. }
  330. // comment("% --- BlockArea end");
  331. }
  332. /**
  333. * render a display space to PostScript
  334. *
  335. * @param space the space to render
  336. */
  337. public void renderDisplaySpace(DisplaySpace space) {
  338. // write("% --- DisplaySpace size="+space.getSize());
  339. this.currentYPosition -= space.getSize();
  340. movetoCurrPosition();
  341. }
  342. /**
  343. * render a foreign object area
  344. */
  345. public void renderForeignObjectArea(ForeignObjectArea area) {
  346. // if necessary need to scale and align the content
  347. area.getObject().render(this);
  348. }
  349. /**
  350. * render an SVG area to PostScript
  351. *
  352. * @param area the area to render
  353. */
  354. public void renderSVGArea(SVGArea area) {
  355. int x = this.currentXPosition;
  356. int y = this.currentYPosition;
  357. Document doc = area.getSVGDocument();
  358. SVGSVGElement svg = ((SVGDocument)doc).getRootElement();
  359. int w = (int)(svg.getWidth().getBaseVal().getValue() * 1000);
  360. int h = (int)(svg.getHeight().getBaseVal().getValue() * 1000);
  361. float sx = 1, sy = -1;
  362. int xOffset = x, yOffset = y;
  363. /*
  364. * Clip to the svg area.
  365. * Note: To have the svg overlay (under) a text area then use
  366. * an fo:block-container
  367. */
  368. comment("% --- SVG Area");
  369. write("gsave");
  370. if (w != 0 && h != 0) {
  371. write("newpath");
  372. write(x / 1000f + " " + y / 1000f + " M");
  373. write((x + w) / 1000f + " " + y / 1000f + " rlineto");
  374. write((x + w) / 1000f + " " + (y - h) / 1000f + " rlineto");
  375. write(x / 1000f + " " + (y - h) / 1000f + " rlineto");
  376. write("closepath");
  377. write("clippath");
  378. }
  379. // transform so that the coordinates (0,0) is from the top left
  380. // and positive is down and to the right. (0,0) is where the
  381. // viewBox puts it.
  382. write(xOffset + " " + yOffset + " translate");
  383. write(sx + " " + sy + " " + " scale");
  384. UserAgent userAgent = new MUserAgent(new AffineTransform(), log);
  385. GVTBuilder builder = new GVTBuilder();
  386. GraphicsNodeRenderContext rc = getRenderContext();
  387. BridgeContext ctx = new BridgeContext(userAgent, rc);
  388. GraphicsNode root;
  389. PSGraphics2D graphics = new PSGraphics2D(false, area.getFontState(),
  390. this, currentFontName,
  391. currentFontSize,
  392. currentXPosition,
  393. currentYPosition);
  394. graphics.setGraphicContext(new org.apache.batik.ext.awt.g2d.GraphicContext());
  395. graphics.setRenderingHints(rc.getRenderingHints());
  396. try {
  397. root = builder.build(ctx, doc);
  398. root.paint(graphics, rc);
  399. } catch (Exception e) {
  400. log.error("svg graphic could not be rendered: "
  401. + e.getMessage(), e);
  402. }
  403. write("grestore");
  404. comment("% --- SVG Area end");
  405. movetoCurrPosition();
  406. }
  407. public GraphicsNodeRenderContext getRenderContext() {
  408. GraphicsNodeRenderContext nodeRenderContext = null;
  409. if (nodeRenderContext == null) {
  410. RenderingHints hints = new RenderingHints(null);
  411. hints.put(RenderingHints.KEY_ANTIALIASING,
  412. RenderingHints.VALUE_ANTIALIAS_ON);
  413. hints.put(RenderingHints.KEY_INTERPOLATION,
  414. RenderingHints.VALUE_INTERPOLATION_BILINEAR);
  415. FontRenderContext fontRenderContext =
  416. new FontRenderContext(new AffineTransform(), true, true);
  417. TextPainter textPainter = new StrokingTextPainter();
  418. // TextPainter textPainter = new PDFTextPainter();
  419. GraphicsNodeRableFactory gnrFactory =
  420. new ConcreteGraphicsNodeRableFactory();
  421. nodeRenderContext =
  422. new GraphicsNodeRenderContext(new AffineTransform(), null,
  423. hints, fontRenderContext,
  424. textPainter, gnrFactory);
  425. nodeRenderContext.setTextPainter(textPainter);
  426. }
  427. return nodeRenderContext;
  428. }
  429. public void renderBitmap(FopImage img, int x, int y, int w, int h) {
  430. try {
  431. boolean iscolor = img.getColorSpace().getColorSpace()
  432. != ColorSpace.DEVICE_GRAY;
  433. byte[] imgmap = img.getBitmaps();
  434. write("gsave");
  435. write("/DeviceRGB setcolorspace");
  436. write(x + " " + (y - h) + " translate");
  437. write(w + " " + h + " scale");
  438. write("<<");
  439. write(" /ImageType 1");
  440. write(" /Width " + img.getWidth());
  441. write(" /Height " + img.getHeight());
  442. write(" /BitsPerComponent 8");
  443. if (iscolor) {
  444. write(" /Decode [0 1 0 1 0 1]");
  445. } else {
  446. write(" /Decode [0 1]");
  447. }
  448. // Setup scanning for left-to-right and top-to-bottom
  449. write(" /ImageMatrix [" + img.getWidth() + " 0 0 -"
  450. + img.getHeight() + " 0 " + img.getHeight() + "]");
  451. write(" /DataSource currentfile /ASCII85Decode filter /FlateDecode filter");
  452. // write(" /DataSource currentfile /ASCIIHexDecode filter /FlateDecode filter");
  453. // write(" /DataSource currentfile /ASCII85Decode filter /RunLengthDecode filter");
  454. // write(" /DataSource currentfile /ASCIIHexDecode filter /RunLengthDecode filter");
  455. // write(" /DataSource currentfile /ASCIIHexDecode filter");
  456. // write(" /DataSource currentfile /ASCII85Decode filter");
  457. // write(" /DataSource currentfile /RunLengthDecode filter");
  458. write(">>");
  459. write("image");
  460. /*
  461. * for (int y=0; y<img.getHeight(); y++) {
  462. * int indx = y * img.getWidth();
  463. * if (iscolor) indx*= 3;
  464. * for (int x=0; x<img.getWidth(); x++) {
  465. * if (iscolor) {
  466. * writeASCIIHex(imgmap[indx++] & 0xFF);
  467. * writeASCIIHex(imgmap[indx++] & 0xFF);
  468. * writeASCIIHex(imgmap[indx++] & 0xFF);
  469. * } else {
  470. * writeASCIIHex(imgmap[indx++] & 0xFF);
  471. * }
  472. * }
  473. * }
  474. */
  475. try {
  476. // imgmap[0] = 1;
  477. InputStream bain = new ByteArrayInputStream(imgmap);
  478. InputStream in;
  479. in = bain;
  480. in = FlateEncodeFilter.filter(in);
  481. // in = RunLengthEncodeFilter.filter(in);
  482. // in = ASCIIHexEncodeFilter.filter(in);
  483. in = ASCII85EncodeFilter.filter(in);
  484. copyStream(in, this.out);
  485. } catch (IOException e) {
  486. if (!ioTrouble)
  487. e.printStackTrace();
  488. ioTrouble = true;
  489. }
  490. write("");
  491. write("grestore");
  492. } catch (FopImageException e) {
  493. log.error("PSRenderer.renderImageArea(): Error rendering bitmap ("
  494. + e.getMessage() + ")", e);
  495. }
  496. }
  497. /**
  498. * render an image area to PostScript
  499. *
  500. * @param area the area to render
  501. */
  502. public void renderImageArea(ImageArea area) {
  503. int x = this.currentAreaContainerXPosition + area.getXOffset();
  504. int y = this.currentYPosition;
  505. int w = area.getContentWidth();
  506. int h = area.getHeight();
  507. this.currentYPosition -= area.getHeight();
  508. imagecount++;
  509. // if (imagecount!=4) return;
  510. comment("% --- ImageArea");
  511. if (area.getImage() instanceof SVGImage) {}
  512. else {
  513. renderBitmap(area.getImage(), x, y, w, h);
  514. }
  515. comment("% --- ImageArea end");
  516. }
  517. private long copyStream(InputStream in, OutputStream out,
  518. int bufferSize) throws IOException {
  519. long bytes_total = 0;
  520. byte[] buf = new byte[bufferSize];
  521. int bytes_read;
  522. while ((bytes_read = in.read(buf)) != -1) {
  523. bytes_total += bytes_read;
  524. out.write(buf, 0, bytes_read);
  525. }
  526. return bytes_total;
  527. }
  528. private long copyStream(InputStream in,
  529. OutputStream out) throws IOException {
  530. return copyStream(in, out, 4096);
  531. }
  532. /**
  533. * render an inline area to PostScript
  534. *
  535. * @param area the area to render
  536. */
  537. public void renderWordArea(WordArea area) {
  538. FontState fs = area.getFontState();
  539. String fontWeight = fs.getFontWeight();
  540. StringBuffer sb = new StringBuffer();
  541. String s = area.getText();
  542. int l = s.length();
  543. for (int i = 0; i < l; i++) {
  544. char ch = s.charAt(i);
  545. char mch = fs.mapChar(ch);
  546. if (mch > 127) {
  547. sb = sb.append("\\" + Integer.toOctalString(mch));
  548. } else {
  549. String escape = "\\()[]{}";
  550. if (escape.indexOf(mch) >= 0) {
  551. sb.append("\\");
  552. }
  553. sb = sb.append(mch);
  554. }
  555. }
  556. // System.out.println("["+s+"] --> ["+sb.toString()+"]");
  557. // comment("% --- InlineArea font-weight="+fontWeight+": " + sb.toString());
  558. useFont(fs.getFontName(), fs.getFontSize());
  559. useColor(area.getRed(), area.getGreen(), area.getBlue());
  560. if (area.getUnderlined() || area.getLineThrough()
  561. || area.getOverlined())
  562. write("ULS");
  563. write("(" + sb.toString() + ") t");
  564. if (area.getUnderlined())
  565. write("ULE");
  566. if (area.getLineThrough())
  567. write("SOE");
  568. if (area.getOverlined())
  569. write("OLE");
  570. this.currentXPosition += area.getContentWidth();
  571. }
  572. public void useFont(String name, int size) {
  573. if ((currentFontName != name) || (currentFontSize != size)) {
  574. write(name + " " + size + " F");
  575. currentFontName = name;
  576. currentFontSize = size;
  577. }
  578. }
  579. /**
  580. * render an inline space to PostScript
  581. *
  582. * @param space the space to render
  583. */
  584. public void renderInlineSpace(InlineSpace space) {
  585. // write("% --- InlineSpace size="+space.getSize());
  586. this.currentXPosition += space.getSize();
  587. if (space.getUnderlined() || space.getLineThrough()
  588. || space.getOverlined())
  589. write("ULS");
  590. write(space.getSize() + " 0 RM");
  591. if (space.getUnderlined())
  592. write("ULE");
  593. if (space.getLineThrough())
  594. write("SOE");
  595. if (space.getOverlined())
  596. write("OLE");
  597. }
  598. /**
  599. * render a line area to PostScript
  600. *
  601. * @param area the area to render
  602. */
  603. public void renderLineArea(LineArea area) {
  604. int rx = this.currentAreaContainerXPosition + area.getStartIndent();
  605. int ry = this.currentYPosition;
  606. int w = area.getContentWidth();
  607. int h = area.getHeight();
  608. this.currentYPosition -= area.getPlacementOffset();
  609. this.currentXPosition = rx;
  610. int bl = this.currentYPosition;
  611. movetoCurrPosition();
  612. String fontWeight = area.getFontState().getFontWeight();
  613. // comment("% --- LineArea begin font-weight="+fontWeight);
  614. Enumeration e = area.getChildren().elements();
  615. while (e.hasMoreElements()) {
  616. Box b = (Box)e.nextElement();
  617. this.currentYPosition = ry - area.getPlacementOffset();
  618. b.render(this);
  619. }
  620. // comment("% --- LineArea end");
  621. this.currentYPosition = ry - h;
  622. this.currentXPosition = rx;
  623. }
  624. /**
  625. * render a page to PostScript
  626. *
  627. * @param page the page to render
  628. */
  629. public void renderPage(Page page) {
  630. BodyAreaContainer body;
  631. AreaContainer before, after;
  632. write("%%Page: " + page.getNumber() + " " + page.getNumber());
  633. write("%%BeginPageSetup");
  634. write("FOPFonts begin");
  635. write("0.001 0.001 scale");
  636. write("%%EndPageSetup");
  637. body = page.getBody();
  638. before = page.getBefore();
  639. after = page.getAfter();
  640. if (before != null) {
  641. renderAreaContainer(before);
  642. }
  643. renderBodyAreaContainer(body);
  644. if (after != null) {
  645. renderAreaContainer(after);
  646. }
  647. write("showpage");
  648. write("%%PageTrailer");
  649. write("%%EndPage");
  650. }
  651. /**
  652. * render a leader area to PostScript
  653. *
  654. * @param area the area to render
  655. */
  656. public void renderLeaderArea(LeaderArea area) {
  657. int rx = this.currentXPosition;
  658. int ry = this.currentYPosition;
  659. int w = area.getContentWidth();
  660. int th = area.getRuleThickness();
  661. int th2 = th / 2;
  662. int th3 = th / 3;
  663. int th4 = th / 4;
  664. switch (area.getLeaderPattern()) {
  665. case LeaderPattern.SPACE:
  666. // NOP
  667. break;
  668. case LeaderPattern.RULE:
  669. if (area.getRuleStyle() == RuleStyle.NONE)
  670. break;
  671. useColor(area.getRed(), area.getGreen(), area.getBlue());
  672. write("gsave");
  673. write("0 setlinecap");
  674. switch (area.getRuleStyle()) {
  675. case RuleStyle.DOTTED:
  676. write("newpath");
  677. write("[1000 3000] 0 setdash");
  678. write(th + " setlinewidth");
  679. write(rx + " " + ry + " M");
  680. write(w + " 0 rlineto");
  681. useColor(area.getRed(), area.getGreen(), area.getBlue());
  682. write("stroke");
  683. break;
  684. case RuleStyle.DASHED:
  685. write("newpath");
  686. write("[3000 3000] 0 setdash");
  687. write(th + " setlinewidth");
  688. write(rx + " " + ry + " M");
  689. write(w + " 0 rlineto");
  690. useColor(area.getRed(), area.getGreen(), area.getBlue());
  691. write("stroke");
  692. break;
  693. case RuleStyle.SOLID:
  694. write("newpath");
  695. write(th + " setlinewidth");
  696. write(rx + " " + ry + " M");
  697. write(w + " 0 rlineto");
  698. useColor(area.getRed(), area.getGreen(), area.getBlue());
  699. write("stroke");
  700. break;
  701. case RuleStyle.DOUBLE:
  702. write("newpath");
  703. write(th3 + " setlinewidth");
  704. write(rx + " " + (ry - th3) + " M");
  705. write(w + " 0 rlineto");
  706. write(rx + " " + (ry + th3) + " M");
  707. write(w + " 0 rlineto");
  708. useColor(area.getRed(), area.getGreen(), area.getBlue());
  709. write("stroke");
  710. break;
  711. case RuleStyle.GROOVE:
  712. write(th2 + " setlinewidth");
  713. write("newpath");
  714. write(rx + " " + (ry - th4) + " M");
  715. write(w + " 0 rlineto");
  716. useColor(area.getRed(), area.getGreen(), area.getBlue());
  717. write("stroke");
  718. write("newpath");
  719. write(rx + " " + (ry + th4) + " M");
  720. write(w + " 0 rlineto");
  721. useColor(1, 1, 1); // white
  722. write("stroke");
  723. break;
  724. case RuleStyle.RIDGE:
  725. write(th2 + " setlinewidth");
  726. write("newpath");
  727. write(rx + " " + (ry - th4) + " M");
  728. write(w + " 0 rlineto");
  729. useColor(1, 1, 1); // white
  730. write("stroke");
  731. write("newpath");
  732. write(rx + " " + (ry + th4) + " M");
  733. write(w + " 0 rlineto");
  734. useColor(area.getRed(), area.getGreen(), area.getBlue());
  735. write("stroke");
  736. break;
  737. }
  738. write("grestore");
  739. break;
  740. case LeaderPattern.DOTS:
  741. comment("% --- Leader dots NYI");
  742. log.error("Leader dots: Not yet implemented");
  743. break;
  744. case LeaderPattern.USECONTENT:
  745. comment("% --- Leader use-content NYI");
  746. log.error("Leader use-content: Not yet implemented");
  747. break;
  748. }
  749. this.currentXPosition += area.getContentWidth();
  750. write(area.getContentWidth() + " 0 RM");
  751. }
  752. private void doFrame(Area area) {
  753. int w, h;
  754. int rx = this.currentAreaContainerXPosition;
  755. w = area.getContentWidth();
  756. BorderAndPadding bap = area.getBorderAndPadding();
  757. if (area instanceof BlockArea)
  758. rx += ((BlockArea)area).getStartIndent();
  759. h = area.getContentHeight();
  760. int ry = this.currentYPosition;
  761. rx = rx - area.getPaddingLeft();
  762. ry = ry + area.getPaddingTop();
  763. w = w + area.getPaddingLeft() + area.getPaddingRight();
  764. h = h + area.getPaddingTop() + area.getPaddingBottom();
  765. rx = rx - area.getBorderLeftWidth();
  766. ry = ry + area.getBorderTopWidth();
  767. w = w + area.getBorderLeftWidth() + area.getBorderRightWidth();
  768. h = h + area.getBorderTopWidth() + area.getBorderBottomWidth();
  769. // Create a textrect with these dimensions.
  770. // The y co-ordinate is measured +ve downwards so subtract page-height
  771. ColorType bg = area.getBackgroundColor();
  772. if ((bg != null) && (bg.alpha() == 0)) {
  773. write("newpath");
  774. write(rx + " " + ry + " M");
  775. write(w + " 0 rlineto");
  776. write("0 " + (-h) + " rlineto");
  777. write((-w) + " 0 rlineto");
  778. write("0 " + h + " rlineto");
  779. write("closepath");
  780. useColor(bg);
  781. write("fill");
  782. }
  783. if (area.getBorderTopWidth() != 0) {
  784. write("newpath");
  785. write(rx + " " + ry + " M");
  786. write(w + " 0 rlineto");
  787. write(area.getBorderTopWidth() + " setlinewidth");
  788. write("0 setlinecap");
  789. useColor(bap.getBorderColor(BorderAndPadding.TOP));
  790. write("stroke");
  791. }
  792. if (area.getBorderLeftWidth() != 0) {
  793. write("newpath");
  794. write(rx + " " + ry + " M");
  795. write("0 " + (-h) + " rlineto");
  796. write(area.getBorderLeftWidth() + " setlinewidth");
  797. write("0 setlinecap");
  798. useColor(bap.getBorderColor(BorderAndPadding.LEFT));
  799. write("stroke");
  800. }
  801. if (area.getBorderRightWidth() != 0) {
  802. write("newpath");
  803. write((rx + w) + " " + ry + " M");
  804. write("0 " + (-h) + " rlineto");
  805. write(area.getBorderRightWidth() + " setlinewidth");
  806. write("0 setlinecap");
  807. useColor(bap.getBorderColor(BorderAndPadding.RIGHT));
  808. write("stroke");
  809. }
  810. if (area.getBorderBottomWidth() != 0) {
  811. write("newpath");
  812. write(rx + " " + (ry - h) + " M");
  813. write(w + " 0 rlineto");
  814. write(area.getBorderBottomWidth() + " setlinewidth");
  815. write("0 setlinecap");
  816. useColor(bap.getBorderColor(BorderAndPadding.BOTTOM));
  817. write("stroke");
  818. }
  819. }
  820. private void useColor(ColorType col) {
  821. useColor(col.red(), col.green(), col.blue());
  822. }
  823. private void useColor(float red, float green, float blue) {
  824. if ((red != currRed) || (green != currGreen) || (blue != currBlue)) {
  825. write(red + " " + green + " " + blue + " setrgbcolor");
  826. currRed = red;
  827. currGreen = green;
  828. currBlue = blue;
  829. }
  830. }
  831. protected class MUserAgent implements UserAgent {
  832. AffineTransform currentTransform = null;
  833. Logger log;
  834. /**
  835. * Creates a new SVGUserAgent.
  836. */
  837. protected MUserAgent(AffineTransform at, Logger logger) {
  838. currentTransform = at;
  839. log = logger;
  840. }
  841. /**
  842. * Displays an error message.
  843. */
  844. public void displayError(String message) {
  845. log.error(message);
  846. }
  847. /**
  848. * Displays an error resulting from the specified Exception.
  849. */
  850. public void displayError(Exception ex) {
  851. log.error("SVG Error" + ex.getMessage(), ex);
  852. }
  853. /**
  854. * Displays a message in the User Agent interface.
  855. * The given message is typically displayed in a status bar.
  856. */
  857. public void displayMessage(String message) {
  858. log.info(message);
  859. }
  860. /**
  861. * Returns a customized the pixel to mm factor.
  862. */
  863. public float getPixelToMM() {
  864. // this is set to 72dpi as the values in fo are 72dpi
  865. return 0.3527777777777777778f; // 72 dpi
  866. // return 0.26458333333333333333333333333333f; // 96dpi
  867. }
  868. /**
  869. * Returns the language settings.
  870. */
  871. public String getLanguages() {
  872. return "en"; // userLanguages;
  873. }
  874. /**
  875. * Returns the user stylesheet uri.
  876. * @return null if no user style sheet was specified.
  877. */
  878. public String getUserStyleSheetURI() {
  879. return null; // userStyleSheetURI;
  880. }
  881. /**
  882. * Returns the class name of the XML parser.
  883. */
  884. public String getXMLParserClassName() {
  885. return org.apache.fop.apps.Driver.getParserClassName();
  886. }
  887. /**
  888. * Opens a link in a new component.
  889. * @param doc The current document.
  890. * @param uri The document URI.
  891. */
  892. public void openLink(SVGAElement elt) {
  893. // application.openLink(uri);
  894. }
  895. public Point getClientAreaLocationOnScreen() {
  896. return new Point(0, 0);
  897. }
  898. public void setSVGCursor(java.awt.Cursor cursor) {}
  899. public AffineTransform getTransform() {
  900. return currentTransform;
  901. }
  902. public Dimension2D getViewportSize() {
  903. return new Dimension(100, 100);
  904. }
  905. public EventDispatcher getEventDispatcher() {
  906. return null;
  907. }
  908. public boolean supportExtension(String str) {
  909. return false;
  910. }
  911. public boolean hasFeature(String str) {
  912. return false;
  913. }
  914. public void registerExtension(BridgeExtension be) {}
  915. public void handleElement(Element elt, Object data) {}
  916. }
  917. /**
  918. Default start renderer method. This would
  919. normally be overridden. (mark-fop@inomial.com).
  920. */
  921. public void startRenderer(OutputStream outputStream)
  922. throws IOException {
  923. log.debug("rendering areas to PostScript");
  924. this.out = new PSStream(outputStream);
  925. write("%!PS-Adobe-3.0");
  926. write("%%Creator: "+this.producer);
  927. write("%%DocumentProcessColors: Black");
  928. write("%%DocumentSuppliedResources: procset FOPFonts");
  929. write("%%EndComments");
  930. write("%%BeginDefaults");
  931. write("%%EndDefaults");
  932. write("%%BeginProlog");
  933. write("%%EndProlog");
  934. write("%%BeginSetup");
  935. writeFontDict(fontInfo);
  936. write("%%EndSetup");
  937. write("FOPFonts begin");
  938. }
  939. /**
  940. Default stop renderer method. This would
  941. normally be overridden. (mark-fop@inomial.com).
  942. */
  943. public void stopRenderer(OutputStream outputStream)
  944. throws IOException {
  945. write("%%Trailer");
  946. write("%%EOF");
  947. this.out.flush();
  948. log.debug("written out PostScript");
  949. }
  950. public void render(Page page, OutputStream outputStream) {
  951. this.renderPage(page);
  952. }
  953. }