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

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one or more
  3. * contributor license agreements. See the NOTICE file distributed with
  4. * this work for additional information regarding copyright ownership.
  5. * The ASF licenses this file to You under the Apache License, Version 2.0
  6. * (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. /* $Id$ */
  18. package org.apache.fop.render.ps;
  19. // Java
  20. import java.awt.Color;
  21. import java.awt.Rectangle;
  22. import java.awt.geom.AffineTransform;
  23. import java.awt.geom.Rectangle2D;
  24. import java.awt.image.RenderedImage;
  25. import java.io.File;
  26. import java.io.FileNotFoundException;
  27. import java.io.IOException;
  28. import java.io.InputStream;
  29. import java.io.OutputStream;
  30. import java.util.Collection;
  31. import java.util.Iterator;
  32. import java.util.List;
  33. import java.util.Map;
  34. import javax.xml.transform.Source;
  35. import org.apache.commons.io.FileUtils;
  36. import org.apache.commons.io.IOUtils;
  37. import org.apache.commons.logging.Log;
  38. import org.apache.commons.logging.LogFactory;
  39. import org.apache.xmlgraphics.image.loader.ImageException;
  40. import org.apache.xmlgraphics.image.loader.ImageFlavor;
  41. import org.apache.xmlgraphics.image.loader.ImageInfo;
  42. import org.apache.xmlgraphics.image.loader.ImageManager;
  43. import org.apache.xmlgraphics.image.loader.ImageSessionContext;
  44. import org.apache.xmlgraphics.image.loader.util.ImageUtil;
  45. import org.apache.xmlgraphics.ps.DSCConstants;
  46. import org.apache.xmlgraphics.ps.PSDictionary;
  47. import org.apache.xmlgraphics.ps.PSDictionaryFormatException;
  48. import org.apache.xmlgraphics.ps.PSGenerator;
  49. import org.apache.xmlgraphics.ps.PSPageDeviceDictionary;
  50. import org.apache.xmlgraphics.ps.PSProcSets;
  51. import org.apache.xmlgraphics.ps.PSResource;
  52. import org.apache.xmlgraphics.ps.PSState;
  53. import org.apache.xmlgraphics.ps.dsc.DSCException;
  54. import org.apache.xmlgraphics.ps.dsc.ResourceTracker;
  55. import org.apache.xmlgraphics.ps.dsc.events.DSCCommentBoundingBox;
  56. import org.apache.xmlgraphics.ps.dsc.events.DSCCommentHiResBoundingBox;
  57. import org.apache.fop.apps.FOPException;
  58. import org.apache.fop.apps.FOUserAgent;
  59. import org.apache.fop.area.Area;
  60. import org.apache.fop.area.BlockViewport;
  61. import org.apache.fop.area.CTM;
  62. import org.apache.fop.area.OffDocumentExtensionAttachment;
  63. import org.apache.fop.area.OffDocumentItem;
  64. import org.apache.fop.area.PageViewport;
  65. import org.apache.fop.area.RegionViewport;
  66. import org.apache.fop.area.Trait;
  67. import org.apache.fop.area.inline.AbstractTextArea;
  68. import org.apache.fop.area.inline.Image;
  69. import org.apache.fop.area.inline.InlineParent;
  70. import org.apache.fop.area.inline.Leader;
  71. import org.apache.fop.area.inline.SpaceArea;
  72. import org.apache.fop.area.inline.TextArea;
  73. import org.apache.fop.area.inline.WordArea;
  74. import org.apache.fop.datatypes.URISpecification;
  75. import org.apache.fop.events.ResourceEventProducer;
  76. import org.apache.fop.fo.Constants;
  77. import org.apache.fop.fo.extensions.ExtensionAttachment;
  78. import org.apache.fop.fonts.Font;
  79. import org.apache.fop.fonts.LazyFont;
  80. import org.apache.fop.fonts.SingleByteFont;
  81. import org.apache.fop.fonts.Typeface;
  82. import org.apache.fop.render.AbstractPathOrientedRenderer;
  83. import org.apache.fop.render.Graphics2DAdapter;
  84. import org.apache.fop.render.ImageAdapter;
  85. import org.apache.fop.render.ImageHandler;
  86. import org.apache.fop.render.ImageHandlerRegistry;
  87. import org.apache.fop.render.RendererContext;
  88. import org.apache.fop.render.RendererEventProducer;
  89. import org.apache.fop.render.ps.extensions.PSCommentAfter;
  90. import org.apache.fop.render.ps.extensions.PSCommentBefore;
  91. import org.apache.fop.render.ps.extensions.PSExtensionAttachment;
  92. import org.apache.fop.render.ps.extensions.PSSetPageDevice;
  93. import org.apache.fop.render.ps.extensions.PSSetupCode;
  94. import org.apache.fop.util.CharUtilities;
  95. import org.apache.fop.util.ColorUtil;
  96. /**
  97. * Renderer that renders to PostScript.
  98. * <br>
  99. * This class currently generates PostScript Level 2 code. The only exception
  100. * is the FlateEncode filter which is a Level 3 feature. The filters in use
  101. * are hardcoded at the moment.
  102. * <br>
  103. * This class follows the Document Structuring Conventions (DSC) version 3.0.
  104. * If anyone modifies this renderer please make
  105. * sure to also follow the DSC to make it simpler to programmatically modify
  106. * the generated Postscript files (ex. extract pages etc.).
  107. * <br>
  108. * This renderer inserts FOP-specific comments into the PostScript stream which
  109. * may help certain users to do certain types of post-processing of the output.
  110. * These comments all start with "%FOP".
  111. *
  112. * @author <a href="mailto:fop-dev@xmlgraphics.apache.org">Apache FOP Development Team</a>
  113. * @version $Id$
  114. */
  115. public class PSRenderer extends AbstractPathOrientedRenderer
  116. implements ImageAdapter, PSSupportedFlavors, PSConfigurationConstants {
  117. /** logging instance */
  118. private static Log log = LogFactory.getLog(PSRenderer.class);
  119. /** The MIME type for PostScript */
  120. public static final String MIME_TYPE = "application/postscript";
  121. /** The application producing the PostScript */
  122. private int currentPageNumber = 0;
  123. /** the OutputStream the PS file is written to */
  124. private OutputStream outputStream;
  125. /** the temporary file in case of two-pass processing */
  126. private File tempFile;
  127. /** The PostScript generator used to output the PostScript */
  128. protected PSGenerator gen;
  129. private boolean ioTrouble = false;
  130. private boolean inTextMode = false;
  131. /** Used to temporarily store PSSetupCode instance until they can be written. */
  132. private List setupCodeList;
  133. /** This is a map of PSResource instances of all fonts defined (key: font key) */
  134. private Map fontResources;
  135. /** This is a map of PSResource instances of all forms (key: uri) */
  136. private Map formResources;
  137. /** encapsulation of dictionary used in setpagedevice instruction **/
  138. private PSPageDeviceDictionary pageDeviceDictionary;
  139. /**
  140. * Utility class which enables all sorts of features that are not directly connected to the
  141. * normal rendering process.
  142. */
  143. protected PSRenderingUtil psUtil;
  144. /** Is used to determine the document's bounding box */
  145. private Rectangle2D documentBoundingBox;
  146. /** This is a collection holding all document header comments */
  147. private Collection headerComments;
  148. /** This is a collection holding all document footer comments */
  149. private Collection footerComments;
  150. /** {@inheritDoc} */
  151. public void setUserAgent(FOUserAgent agent) {
  152. super.setUserAgent(agent);
  153. this.psUtil = new PSRenderingUtil(getUserAgent());
  154. }
  155. PSRenderingUtil getPSUtil() {
  156. return this.psUtil;
  157. }
  158. /**
  159. * Sets the landscape mode for this renderer.
  160. * @param value false will normally generate a "pseudo-portrait" page, true will rotate
  161. * a "wider-than-long" page by 90 degrees.
  162. */
  163. public void setAutoRotateLandscape(boolean value) {
  164. getPSUtil().setAutoRotateLandscape(value);
  165. }
  166. /** @return true if the renderer is configured to rotate landscape pages */
  167. public boolean isAutoRotateLandscape() {
  168. return getPSUtil().isAutoRotateLandscape();
  169. }
  170. /**
  171. * Sets the PostScript language level that the renderer should produce.
  172. * @param level the language level (currently allowed: 2 or 3)
  173. */
  174. public void setLanguageLevel(int level) {
  175. getPSUtil().setLanguageLevel(level);
  176. }
  177. /**
  178. * Return the PostScript language level that the renderer produces.
  179. * @return the language level
  180. */
  181. public int getLanguageLevel() {
  182. return getPSUtil().getLanguageLevel();
  183. }
  184. /**
  185. * Sets the resource optimization mode. If set to true, the renderer does two passes to
  186. * only embed the necessary resources in the PostScript file. This is slower, but produces
  187. * smaller files.
  188. * @param value true to enable the resource optimization
  189. */
  190. public void setOptimizeResources(boolean value) {
  191. getPSUtil().setOptimizeResources(value);
  192. }
  193. /** @return true if the renderer does two passes to optimize PostScript resources */
  194. public boolean isOptimizeResources() {
  195. return getPSUtil().isOptimizeResources();
  196. }
  197. /** {@inheritDoc} */
  198. public Graphics2DAdapter getGraphics2DAdapter() {
  199. return new PSGraphics2DAdapter(this);
  200. }
  201. /** {@inheritDoc} */
  202. public ImageAdapter getImageAdapter() {
  203. return this;
  204. }
  205. /**
  206. * Write out a command
  207. * @param cmd PostScript command
  208. */
  209. protected void writeln(String cmd) {
  210. try {
  211. gen.writeln(cmd);
  212. } catch (IOException ioe) {
  213. handleIOTrouble(ioe);
  214. }
  215. }
  216. /**
  217. * Central exception handler for I/O exceptions.
  218. * @param ioe IOException to handle
  219. */
  220. protected void handleIOTrouble(IOException ioe) {
  221. if (!ioTrouble) {
  222. RendererEventProducer eventProducer = RendererEventProducer.Provider.get(
  223. getUserAgent().getEventBroadcaster());
  224. eventProducer.ioError(this, ioe);
  225. ioTrouble = true;
  226. }
  227. }
  228. /**
  229. * Write out a comment
  230. * @param comment Comment to write
  231. */
  232. protected void comment(String comment) {
  233. try {
  234. if (comment.startsWith("%")) {
  235. gen.commentln(comment);
  236. writeln(comment);
  237. } else {
  238. gen.commentln("%" + comment);
  239. }
  240. } catch (IOException ioe) {
  241. handleIOTrouble(ioe);
  242. }
  243. }
  244. /**
  245. * Make sure the cursor is in the right place.
  246. */
  247. protected void movetoCurrPosition() {
  248. moveTo(this.currentIPPosition, this.currentBPPosition);
  249. }
  250. /** {@inheritDoc} */
  251. protected void clip() {
  252. writeln("clip newpath");
  253. }
  254. /** {@inheritDoc} */
  255. protected void clipRect(float x, float y, float width, float height) {
  256. try {
  257. gen.defineRect(x, y, width, height);
  258. clip();
  259. } catch (IOException ioe) {
  260. handleIOTrouble(ioe);
  261. }
  262. }
  263. /** {@inheritDoc} */
  264. protected void moveTo(float x, float y) {
  265. writeln(gen.formatDouble(x) + " " + gen.formatDouble(y) + " M");
  266. }
  267. /**
  268. * Moves the current point by (x, y) relative to the current position,
  269. * omitting any connecting line segment.
  270. * @param x x coordinate
  271. * @param y y coordinate
  272. */
  273. protected void rmoveTo(float x, float y) {
  274. writeln(gen.formatDouble(x) + " " + gen.formatDouble(y) + " RM");
  275. }
  276. /** {@inheritDoc} */
  277. protected void lineTo(float x, float y) {
  278. writeln(gen.formatDouble(x) + " " + gen.formatDouble(y) + " lineto");
  279. }
  280. /** {@inheritDoc} */
  281. protected void closePath() {
  282. writeln("cp");
  283. }
  284. /** {@inheritDoc} */
  285. protected void fillRect(float x, float y, float width, float height) {
  286. if (width != 0 && height != 0) {
  287. try {
  288. gen.defineRect(x, y, width, height);
  289. gen.writeln("fill");
  290. } catch (IOException ioe) {
  291. handleIOTrouble(ioe);
  292. }
  293. }
  294. }
  295. /** {@inheritDoc} */
  296. protected void updateColor(Color col, boolean fill) {
  297. try {
  298. useColor(col);
  299. } catch (IOException ioe) {
  300. handleIOTrouble(ioe);
  301. }
  302. }
  303. /** {@inheritDoc} */
  304. protected void drawImage(String uri, Rectangle2D pos, Map foreignAttributes) {
  305. endTextObject();
  306. int x = currentIPPosition + (int)Math.round(pos.getX());
  307. int y = currentBPPosition + (int)Math.round(pos.getY());
  308. uri = URISpecification.getURL(uri);
  309. if (log.isDebugEnabled()) {
  310. log.debug("Handling image: " + uri);
  311. }
  312. int width = (int)pos.getWidth();
  313. int height = (int)pos.getHeight();
  314. Rectangle targetRect = new Rectangle(x, y, width, height);
  315. ImageManager manager = getUserAgent().getFactory().getImageManager();
  316. ImageInfo info = null;
  317. try {
  318. ImageSessionContext sessionContext = getUserAgent().getImageSessionContext();
  319. info = manager.getImageInfo(uri, sessionContext);
  320. PSRenderingContext renderingContext = new PSRenderingContext(
  321. getUserAgent(), gen, getFontInfo());
  322. if (!isOptimizeResources()
  323. || PSImageUtils.isImageInlined(info, renderingContext)) {
  324. if (log.isDebugEnabled()) {
  325. log.debug("Image " + info + " is inlined");
  326. }
  327. //Determine supported flavors
  328. ImageFlavor[] flavors;
  329. ImageHandlerRegistry imageHandlerRegistry
  330. = userAgent.getFactory().getImageHandlerRegistry();
  331. flavors = imageHandlerRegistry.getSupportedFlavors(renderingContext);
  332. //Only now fully load/prepare the image
  333. Map hints = ImageUtil.getDefaultHints(sessionContext);
  334. org.apache.xmlgraphics.image.loader.Image img = manager.getImage(
  335. info, flavors, hints, sessionContext);
  336. //Get handler for image
  337. ImageHandler basicHandler = imageHandlerRegistry.getHandler(renderingContext, img);
  338. //...and embed as inline image
  339. basicHandler.handleImage(renderingContext, img, targetRect);
  340. } else {
  341. if (log.isDebugEnabled()) {
  342. log.debug("Image " + info + " is embedded as a form later");
  343. }
  344. //Don't load image at this time, just put a form placeholder in the stream
  345. PSResource form = getFormForImage(info.getOriginalURI());
  346. PSImageUtils.drawForm(form, info, targetRect, gen);
  347. }
  348. } catch (ImageException ie) {
  349. ResourceEventProducer eventProducer = ResourceEventProducer.Provider.get(
  350. getUserAgent().getEventBroadcaster());
  351. eventProducer.imageError(this, (info != null ? info.toString() : uri), ie, null);
  352. } catch (FileNotFoundException fe) {
  353. ResourceEventProducer eventProducer = ResourceEventProducer.Provider.get(
  354. getUserAgent().getEventBroadcaster());
  355. eventProducer.imageNotFound(this, (info != null ? info.toString() : uri), fe, null);
  356. } catch (IOException ioe) {
  357. ResourceEventProducer eventProducer = ResourceEventProducer.Provider.get(
  358. getUserAgent().getEventBroadcaster());
  359. eventProducer.imageIOError(this, (info != null ? info.toString() : uri), ioe, null);
  360. }
  361. }
  362. /**
  363. * Returns a PSResource instance representing a image as a PostScript form.
  364. * @param uri the image URI
  365. * @return a PSResource instance
  366. */
  367. protected PSResource getFormForImage(String uri) {
  368. if (uri == null || "".equals(uri)) {
  369. throw new IllegalArgumentException("uri must not be empty or null");
  370. }
  371. if (this.formResources == null) {
  372. this.formResources = new java.util.HashMap();
  373. }
  374. PSResource form = (PSResource)this.formResources.get(uri);
  375. if (form == null) {
  376. form = new PSImageFormResource(this.formResources.size() + 1, uri);
  377. this.formResources.put(uri, form);
  378. }
  379. return form;
  380. }
  381. /** {@inheritDoc} */
  382. public void paintImage(RenderedImage image, RendererContext context,
  383. int x, int y, int width, int height) throws IOException {
  384. float fx = x / 1000f;
  385. x += currentIPPosition / 1000f;
  386. float fy = y / 1000f;
  387. y += currentBPPosition / 1000f;
  388. float fw = width / 1000f;
  389. float fh = height / 1000f;
  390. PSImageUtils.renderBitmapImage(image, fx, fy, fw, fh, gen);
  391. }
  392. /**
  393. * Draw a line.
  394. *
  395. * @param startx the start x position
  396. * @param starty the start y position
  397. * @param endx the x end position
  398. * @param endy the y end position
  399. */
  400. private void drawLine(float startx, float starty, float endx, float endy) {
  401. writeln(gen.formatDouble(startx) + " "
  402. + gen.formatDouble(starty) + " M "
  403. + gen.formatDouble(endx) + " "
  404. + gen.formatDouble(endy) + " lineto stroke newpath");
  405. }
  406. /** Saves the graphics state of the rendering engine. */
  407. public void saveGraphicsState() {
  408. endTextObject();
  409. try {
  410. //delegate
  411. gen.saveGraphicsState();
  412. } catch (IOException ioe) {
  413. handleIOTrouble(ioe);
  414. }
  415. }
  416. /** Restores the last graphics state of the rendering engine. */
  417. public void restoreGraphicsState() {
  418. try {
  419. endTextObject();
  420. //delegate
  421. gen.restoreGraphicsState();
  422. } catch (IOException ioe) {
  423. handleIOTrouble(ioe);
  424. }
  425. }
  426. /**
  427. * Concats the transformation matrix.
  428. * @param a A part
  429. * @param b B part
  430. * @param c C part
  431. * @param d D part
  432. * @param e E part
  433. * @param f F part
  434. */
  435. protected void concatMatrix(double a, double b,
  436. double c, double d,
  437. double e, double f) {
  438. try {
  439. gen.concatMatrix(a, b, c, d, e, f);
  440. } catch (IOException ioe) {
  441. handleIOTrouble(ioe);
  442. }
  443. }
  444. /**
  445. * Concats the transformations matrix.
  446. * @param matrix Matrix to use
  447. */
  448. protected void concatMatrix(double[] matrix) {
  449. try {
  450. gen.concatMatrix(matrix);
  451. } catch (IOException ioe) {
  452. handleIOTrouble(ioe);
  453. }
  454. }
  455. /** {@inheritDoc} */
  456. protected void concatenateTransformationMatrix(AffineTransform at) {
  457. try {
  458. gen.concatMatrix(at);
  459. } catch (IOException ioe) {
  460. handleIOTrouble(ioe);
  461. }
  462. }
  463. private String getPostScriptNameForFontKey(String key) {
  464. int pos = key.indexOf('_');
  465. String postFix = null;
  466. if (pos > 0) {
  467. postFix = key.substring(pos);
  468. key = key.substring(0, pos);
  469. }
  470. Map fonts = fontInfo.getFonts();
  471. Typeface tf = (Typeface)fonts.get(key);
  472. if (tf instanceof LazyFont) {
  473. tf = ((LazyFont)tf).getRealFont();
  474. }
  475. if (tf == null) {
  476. throw new IllegalStateException("Font not available: " + key);
  477. }
  478. if (postFix == null) {
  479. return tf.getFontName();
  480. } else {
  481. return tf.getFontName() + postFix;
  482. }
  483. }
  484. /**
  485. * Returns the PSResource for the given font key.
  486. * @param key the font key ("F*")
  487. * @return the matching PSResource
  488. */
  489. protected PSResource getPSResourceForFontKey(String key) {
  490. PSResource res = null;
  491. if (this.fontResources != null) {
  492. res = (PSResource)this.fontResources.get(key);
  493. } else {
  494. this.fontResources = new java.util.HashMap();
  495. }
  496. if (res == null) {
  497. res = new PSResource(PSResource.TYPE_FONT, getPostScriptNameForFontKey(key));
  498. this.fontResources.put(key, res);
  499. }
  500. return res;
  501. }
  502. /**
  503. * Changes the currently used font.
  504. * @param key key of the font ("F*")
  505. * @param size font size
  506. */
  507. protected void useFont(String key, int size) {
  508. try {
  509. PSResource res = getPSResourceForFontKey(key);
  510. gen.useFont("/" + res.getName(), size / 1000f);
  511. gen.getResourceTracker().notifyResourceUsageOnPage(res);
  512. } catch (IOException ioe) {
  513. handleIOTrouble(ioe);
  514. }
  515. }
  516. private void useColor(Color col) throws IOException {
  517. gen.useColor(col);
  518. }
  519. /** {@inheritDoc} */
  520. protected void drawBackAndBorders(Area area, float startx, float starty,
  521. float width, float height) {
  522. if (area.hasTrait(Trait.BACKGROUND)
  523. || area.hasTrait(Trait.BORDER_BEFORE)
  524. || area.hasTrait(Trait.BORDER_AFTER)
  525. || area.hasTrait(Trait.BORDER_START)
  526. || area.hasTrait(Trait.BORDER_END)) {
  527. comment("%FOPBeginBackgroundAndBorder: "
  528. + startx + " " + starty + " " + width + " " + height);
  529. super.drawBackAndBorders(area, startx, starty, width, height);
  530. comment("%FOPEndBackgroundAndBorder");
  531. }
  532. }
  533. /** {@inheritDoc} */
  534. protected void drawBorderLine(float x1, float y1, float x2, float y2,
  535. boolean horz, boolean startOrBefore, int style, Color col) {
  536. try {
  537. float w = x2 - x1;
  538. float h = y2 - y1;
  539. if ((w < 0) || (h < 0)) {
  540. log.error("Negative extent received. Border won't be painted.");
  541. return;
  542. }
  543. switch (style) {
  544. case Constants.EN_DASHED:
  545. useColor(col);
  546. if (horz) {
  547. float unit = Math.abs(2 * h);
  548. int rep = (int)(w / unit);
  549. if (rep % 2 == 0) {
  550. rep++;
  551. }
  552. unit = w / rep;
  553. gen.useDash("[" + unit + "] 0");
  554. gen.useLineCap(0);
  555. gen.useLineWidth(h);
  556. float ym = y1 + (h / 2);
  557. drawLine(x1, ym, x2, ym);
  558. } else {
  559. float unit = Math.abs(2 * w);
  560. int rep = (int)(h / unit);
  561. if (rep % 2 == 0) {
  562. rep++;
  563. }
  564. unit = h / rep;
  565. gen.useDash("[" + unit + "] 0");
  566. gen.useLineCap(0);
  567. gen.useLineWidth(w);
  568. float xm = x1 + (w / 2);
  569. drawLine(xm, y1, xm, y2);
  570. }
  571. break;
  572. case Constants.EN_DOTTED:
  573. useColor(col);
  574. gen.useLineCap(1); //Rounded!
  575. if (horz) {
  576. float unit = Math.abs(2 * h);
  577. int rep = (int)(w / unit);
  578. if (rep % 2 == 0) {
  579. rep++;
  580. }
  581. unit = w / rep;
  582. gen.useDash("[0 " + unit + "] 0");
  583. gen.useLineWidth(h);
  584. float ym = y1 + (h / 2);
  585. drawLine(x1, ym, x2, ym);
  586. } else {
  587. float unit = Math.abs(2 * w);
  588. int rep = (int)(h / unit);
  589. if (rep % 2 == 0) {
  590. rep++;
  591. }
  592. unit = h / rep;
  593. gen.useDash("[0 " + unit + "] 0");
  594. gen.useLineWidth(w);
  595. float xm = x1 + (w / 2);
  596. drawLine(xm, y1, xm, y2);
  597. }
  598. break;
  599. case Constants.EN_DOUBLE:
  600. useColor(col);
  601. gen.useDash(null);
  602. if (horz) {
  603. float h3 = h / 3;
  604. gen.useLineWidth(h3);
  605. float ym1 = y1 + (h3 / 2);
  606. float ym2 = ym1 + h3 + h3;
  607. drawLine(x1, ym1, x2, ym1);
  608. drawLine(x1, ym2, x2, ym2);
  609. } else {
  610. float w3 = w / 3;
  611. gen.useLineWidth(w3);
  612. float xm1 = x1 + (w3 / 2);
  613. float xm2 = xm1 + w3 + w3;
  614. drawLine(xm1, y1, xm1, y2);
  615. drawLine(xm2, y1, xm2, y2);
  616. }
  617. break;
  618. case Constants.EN_GROOVE:
  619. case Constants.EN_RIDGE:
  620. float colFactor = (style == EN_GROOVE ? 0.4f : -0.4f);
  621. gen.useDash(null);
  622. if (horz) {
  623. Color uppercol = ColorUtil.lightenColor(col, -colFactor);
  624. Color lowercol = ColorUtil.lightenColor(col, colFactor);
  625. float h3 = h / 3;
  626. gen.useLineWidth(h3);
  627. float ym1 = y1 + (h3 / 2);
  628. gen.useColor(uppercol);
  629. drawLine(x1, ym1, x2, ym1);
  630. gen.useColor(col);
  631. drawLine(x1, ym1 + h3, x2, ym1 + h3);
  632. gen.useColor(lowercol);
  633. drawLine(x1, ym1 + h3 + h3, x2, ym1 + h3 + h3);
  634. } else {
  635. Color leftcol = ColorUtil.lightenColor(col, -colFactor);
  636. Color rightcol = ColorUtil.lightenColor(col, colFactor);
  637. float w3 = w / 3;
  638. gen.useLineWidth(w3);
  639. float xm1 = x1 + (w3 / 2);
  640. gen.useColor(leftcol);
  641. drawLine(xm1, y1, xm1, y2);
  642. gen.useColor(col);
  643. drawLine(xm1 + w3, y1, xm1 + w3, y2);
  644. gen.useColor(rightcol);
  645. drawLine(xm1 + w3 + w3, y1, xm1 + w3 + w3, y2);
  646. }
  647. break;
  648. case Constants.EN_INSET:
  649. case Constants.EN_OUTSET:
  650. colFactor = (style == EN_OUTSET ? 0.4f : -0.4f);
  651. gen.useDash(null);
  652. if (horz) {
  653. Color c = ColorUtil.lightenColor(col, (startOrBefore ? 1 : -1) * colFactor);
  654. gen.useLineWidth(h);
  655. float ym1 = y1 + (h / 2);
  656. gen.useColor(c);
  657. drawLine(x1, ym1, x2, ym1);
  658. } else {
  659. Color c = ColorUtil.lightenColor(col, (startOrBefore ? 1 : -1) * colFactor);
  660. gen.useLineWidth(w);
  661. float xm1 = x1 + (w / 2);
  662. gen.useColor(c);
  663. drawLine(xm1, y1, xm1, y2);
  664. }
  665. break;
  666. case Constants.EN_HIDDEN:
  667. break;
  668. default:
  669. useColor(col);
  670. gen.useDash(null);
  671. gen.useLineCap(0);
  672. if (horz) {
  673. gen.useLineWidth(h);
  674. float ym = y1 + (h / 2);
  675. drawLine(x1, ym, x2, ym);
  676. } else {
  677. gen.useLineWidth(w);
  678. float xm = x1 + (w / 2);
  679. drawLine(xm, y1, xm, y2);
  680. }
  681. }
  682. } catch (IOException ioe) {
  683. handleIOTrouble(ioe);
  684. }
  685. }
  686. /** {@inheritDoc} */
  687. public void startRenderer(OutputStream outputStream)
  688. throws IOException {
  689. log.debug("Rendering areas to PostScript...");
  690. this.outputStream = outputStream;
  691. OutputStream out;
  692. if (isOptimizeResources()) {
  693. this.tempFile = File.createTempFile("fop", null);
  694. out = new java.io.FileOutputStream(this.tempFile);
  695. out = new java.io.BufferedOutputStream(out);
  696. } else {
  697. out = this.outputStream;
  698. }
  699. //Setup for PostScript generation
  700. this.gen = new PSGenerator(out) {
  701. /** Need to subclass PSGenerator to have better URI resolution */
  702. public Source resolveURI(String uri) {
  703. return userAgent.resolveURI(uri);
  704. }
  705. };
  706. this.gen.setPSLevel(getLanguageLevel());
  707. this.currentPageNumber = 0;
  708. //Initial default page device dictionary settings
  709. this.pageDeviceDictionary = new PSPageDeviceDictionary();
  710. pageDeviceDictionary.setFlushOnRetrieval(!getPSUtil().isDSCComplianceEnabled());
  711. pageDeviceDictionary.put("/ImagingBBox", "null");
  712. }
  713. private void writeHeader() throws IOException {
  714. //PostScript Header
  715. writeln(DSCConstants.PS_ADOBE_30);
  716. gen.writeDSCComment(DSCConstants.CREATOR, new String[] {userAgent.getProducer()});
  717. gen.writeDSCComment(DSCConstants.CREATION_DATE, new Object[] {new java.util.Date()});
  718. gen.writeDSCComment(DSCConstants.LANGUAGE_LEVEL, new Integer(gen.getPSLevel()));
  719. gen.writeDSCComment(DSCConstants.PAGES, new Object[] {DSCConstants.ATEND});
  720. gen.writeDSCComment(DSCConstants.BBOX, DSCConstants.ATEND);
  721. gen.writeDSCComment(DSCConstants.HIRES_BBOX, DSCConstants.ATEND);
  722. this.documentBoundingBox = new Rectangle2D.Double();
  723. gen.writeDSCComment(DSCConstants.DOCUMENT_SUPPLIED_RESOURCES,
  724. new Object[] {DSCConstants.ATEND});
  725. if (headerComments != null) {
  726. for (Iterator iter = headerComments.iterator(); iter.hasNext();) {
  727. PSExtensionAttachment comment = (PSExtensionAttachment)iter.next();
  728. gen.writeln("%" + comment.getContent());
  729. }
  730. }
  731. gen.writeDSCComment(DSCConstants.END_COMMENTS);
  732. //Defaults
  733. gen.writeDSCComment(DSCConstants.BEGIN_DEFAULTS);
  734. gen.writeDSCComment(DSCConstants.END_DEFAULTS);
  735. //Prolog and Setup written right before the first page-sequence, see startPageSequence()
  736. //Do this only once, as soon as we have all the content for the Setup section!
  737. //Prolog
  738. gen.writeDSCComment(DSCConstants.BEGIN_PROLOG);
  739. PSProcSets.writeStdProcSet(gen);
  740. PSProcSets.writeEPSProcSet(gen);
  741. gen.writeDSCComment(DSCConstants.END_PROLOG);
  742. //Setup
  743. gen.writeDSCComment(DSCConstants.BEGIN_SETUP);
  744. PSRenderingUtil.writeSetupCodeList(gen, setupCodeList, "SetupCode");
  745. if (!isOptimizeResources()) {
  746. this.fontResources = PSFontUtils.writeFontDict(gen, fontInfo);
  747. } else {
  748. gen.commentln("%FOPFontSetup"); //Place-holder, will be replaced in the second pass
  749. }
  750. gen.writeDSCComment(DSCConstants.END_SETUP);
  751. }
  752. /** {@inheritDoc} */
  753. public void stopRenderer() throws IOException {
  754. //Write trailer
  755. gen.writeDSCComment(DSCConstants.TRAILER);
  756. if (footerComments != null) {
  757. for (Iterator iter = footerComments.iterator(); iter.hasNext();) {
  758. PSExtensionAttachment comment = (PSExtensionAttachment)iter.next();
  759. gen.commentln("%" + comment.getContent());
  760. }
  761. footerComments.clear();
  762. }
  763. gen.writeDSCComment(DSCConstants.PAGES, new Integer(this.currentPageNumber));
  764. new DSCCommentBoundingBox(this.documentBoundingBox).generate(gen);
  765. new DSCCommentHiResBoundingBox(this.documentBoundingBox).generate(gen);
  766. gen.getResourceTracker().writeResources(false, gen);
  767. gen.writeDSCComment(DSCConstants.EOF);
  768. gen.flush();
  769. log.debug("Rendering to PostScript complete.");
  770. if (isOptimizeResources()) {
  771. IOUtils.closeQuietly(gen.getOutputStream());
  772. rewritePostScriptFile();
  773. }
  774. if (footerComments != null) {
  775. headerComments.clear();
  776. }
  777. if (pageDeviceDictionary != null) {
  778. pageDeviceDictionary.clear();
  779. }
  780. }
  781. /**
  782. * Used for two-pass production. This will rewrite the PostScript file from the temporary
  783. * file while adding all needed resources.
  784. * @throws IOException In case of an I/O error.
  785. */
  786. private void rewritePostScriptFile() throws IOException {
  787. log.debug("Processing PostScript resources...");
  788. long startTime = System.currentTimeMillis();
  789. ResourceTracker resTracker = gen.getResourceTracker();
  790. InputStream in = new java.io.FileInputStream(this.tempFile);
  791. in = new java.io.BufferedInputStream(in);
  792. try {
  793. try {
  794. ResourceHandler handler = new ResourceHandler(this.userAgent, this.fontInfo,
  795. resTracker, this.formResources);
  796. handler.process(in, this.outputStream,
  797. this.currentPageNumber, this.documentBoundingBox);
  798. this.outputStream.flush();
  799. } catch (DSCException e) {
  800. throw new RuntimeException(e.getMessage());
  801. }
  802. } finally {
  803. IOUtils.closeQuietly(in);
  804. if (!this.tempFile.delete()) {
  805. this.tempFile.deleteOnExit();
  806. log.warn("Could not delete temporary file: " + this.tempFile);
  807. }
  808. }
  809. if (log.isDebugEnabled()) {
  810. long duration = System.currentTimeMillis() - startTime;
  811. log.debug("Resource Processing complete in " + duration + " ms.");
  812. }
  813. }
  814. /** {@inheritDoc} */
  815. public void processOffDocumentItem(OffDocumentItem oDI) {
  816. if (log.isDebugEnabled()) {
  817. log.debug("Handling OffDocumentItem: " + oDI.getName());
  818. }
  819. if (oDI instanceof OffDocumentExtensionAttachment) {
  820. ExtensionAttachment attachment = ((OffDocumentExtensionAttachment)oDI).getAttachment();
  821. if (attachment != null) {
  822. if (PSExtensionAttachment.CATEGORY.equals(attachment.getCategory())) {
  823. if (attachment instanceof PSSetupCode) {
  824. if (setupCodeList == null) {
  825. setupCodeList = new java.util.ArrayList();
  826. }
  827. if (!setupCodeList.contains(attachment)) {
  828. setupCodeList.add(attachment);
  829. }
  830. } else if (attachment instanceof PSSetPageDevice) {
  831. /**
  832. * Extract all PSSetPageDevice instances from the
  833. * attachment list on the s-p-m and add all dictionary
  834. * entries to our internal representation of the the
  835. * page device dictionary.
  836. */
  837. PSSetPageDevice setPageDevice = (PSSetPageDevice)attachment;
  838. String content = setPageDevice.getContent();
  839. if (content != null) {
  840. try {
  841. this.pageDeviceDictionary.putAll(PSDictionary.valueOf(content));
  842. } catch (PSDictionaryFormatException e) {
  843. PSEventProducer eventProducer = PSEventProducer.Provider.get(
  844. getUserAgent().getEventBroadcaster());
  845. eventProducer.postscriptDictionaryParseError(this, content, e);
  846. }
  847. }
  848. } else if (attachment instanceof PSCommentBefore) {
  849. if (headerComments == null) {
  850. headerComments = new java.util.ArrayList();
  851. }
  852. headerComments.add(attachment);
  853. } else if (attachment instanceof PSCommentAfter) {
  854. if (footerComments == null) {
  855. footerComments = new java.util.ArrayList();
  856. }
  857. footerComments.add(attachment);
  858. }
  859. }
  860. }
  861. }
  862. super.processOffDocumentItem(oDI);
  863. }
  864. /** {@inheritDoc} */
  865. public void renderPage(PageViewport page)
  866. throws IOException, FOPException {
  867. log.debug("renderPage(): " + page);
  868. if (this.currentPageNumber == 0) {
  869. writeHeader();
  870. }
  871. this.currentPageNumber++;
  872. gen.getResourceTracker().notifyStartNewPage();
  873. gen.getResourceTracker().notifyResourceUsageOnPage(PSProcSets.STD_PROCSET);
  874. gen.writeDSCComment(DSCConstants.PAGE, new Object[]
  875. {page.getPageNumberString(),
  876. new Integer(this.currentPageNumber)});
  877. double pageWidth = Math.round(page.getViewArea().getWidth()) / 1000f;
  878. double pageHeight = Math.round(page.getViewArea().getHeight()) / 1000f;
  879. boolean rotate = false;
  880. List pageSizes = new java.util.ArrayList();
  881. if (getPSUtil().isAutoRotateLandscape() && (pageHeight < pageWidth)) {
  882. rotate = true;
  883. pageSizes.add(new Long(Math.round(pageHeight)));
  884. pageSizes.add(new Long(Math.round(pageWidth)));
  885. } else {
  886. pageSizes.add(new Long(Math.round(pageWidth)));
  887. pageSizes.add(new Long(Math.round(pageHeight)));
  888. }
  889. pageDeviceDictionary.put("/PageSize", pageSizes);
  890. if (page.hasExtensionAttachments()) {
  891. for (Iterator iter = page.getExtensionAttachments().iterator();
  892. iter.hasNext();) {
  893. ExtensionAttachment attachment = (ExtensionAttachment) iter.next();
  894. if (attachment instanceof PSSetPageDevice) {
  895. /**
  896. * Extract all PSSetPageDevice instances from the
  897. * attachment list on the s-p-m and add all
  898. * dictionary entries to our internal representation
  899. * of the the page device dictionary.
  900. */
  901. PSSetPageDevice setPageDevice = (PSSetPageDevice)attachment;
  902. String content = setPageDevice.getContent();
  903. if (content != null) {
  904. try {
  905. pageDeviceDictionary.putAll(PSDictionary.valueOf(content));
  906. } catch (PSDictionaryFormatException e) {
  907. PSEventProducer eventProducer = PSEventProducer.Provider.get(
  908. getUserAgent().getEventBroadcaster());
  909. eventProducer.postscriptDictionaryParseError(this, content, e);
  910. }
  911. }
  912. }
  913. }
  914. }
  915. try {
  916. if (setupCodeList != null) {
  917. PSRenderingUtil.writeEnclosedExtensionAttachments(gen, setupCodeList);
  918. setupCodeList.clear();
  919. }
  920. } catch (IOException e) {
  921. log.error(e.getMessage());
  922. }
  923. final Integer zero = new Integer(0);
  924. Rectangle2D pageBoundingBox = new Rectangle2D.Double();
  925. if (rotate) {
  926. pageBoundingBox.setRect(0, 0, pageHeight, pageWidth);
  927. gen.writeDSCComment(DSCConstants.PAGE_BBOX, new Object[] {
  928. zero, zero, new Long(Math.round(pageHeight)),
  929. new Long(Math.round(pageWidth)) });
  930. gen.writeDSCComment(DSCConstants.PAGE_HIRES_BBOX, new Object[] {
  931. zero, zero, new Double(pageHeight),
  932. new Double(pageWidth) });
  933. gen.writeDSCComment(DSCConstants.PAGE_ORIENTATION, "Landscape");
  934. } else {
  935. pageBoundingBox.setRect(0, 0, pageWidth, pageHeight);
  936. gen.writeDSCComment(DSCConstants.PAGE_BBOX, new Object[] {
  937. zero, zero, new Long(Math.round(pageWidth)),
  938. new Long(Math.round(pageHeight)) });
  939. gen.writeDSCComment(DSCConstants.PAGE_HIRES_BBOX, new Object[] {
  940. zero, zero, new Double(pageWidth),
  941. new Double(pageHeight) });
  942. if (getPSUtil().isAutoRotateLandscape()) {
  943. gen.writeDSCComment(DSCConstants.PAGE_ORIENTATION,
  944. "Portrait");
  945. }
  946. }
  947. this.documentBoundingBox.add(pageBoundingBox);
  948. gen.writeDSCComment(DSCConstants.PAGE_RESOURCES,
  949. new Object[] {DSCConstants.ATEND});
  950. gen.commentln("%FOPSimplePageMaster: " + page.getSimplePageMasterName());
  951. gen.writeDSCComment(DSCConstants.BEGIN_PAGE_SETUP);
  952. if (page.hasExtensionAttachments()) {
  953. List extensionAttachments = page.getExtensionAttachments();
  954. for (int i = 0; i < extensionAttachments.size(); i++) {
  955. Object attObj = extensionAttachments.get(i);
  956. if (attObj instanceof PSExtensionAttachment) {
  957. PSExtensionAttachment attachment = (PSExtensionAttachment)attObj;
  958. if (attachment instanceof PSCommentBefore) {
  959. gen.commentln("%" + attachment.getContent());
  960. } else if (attachment instanceof PSSetupCode) {
  961. gen.writeln(attachment.getContent());
  962. }
  963. }
  964. }
  965. }
  966. // Write any unwritten changes to page device dictionary
  967. if (!pageDeviceDictionary.isEmpty()) {
  968. String content = pageDeviceDictionary.getContent();
  969. if (getPSUtil().isSafeSetPageDevice()) {
  970. content += " SSPD";
  971. } else {
  972. content += " setpagedevice";
  973. }
  974. PSRenderingUtil.writeEnclosedExtensionAttachment(gen, new PSSetPageDevice(content));
  975. }
  976. if (rotate) {
  977. gen.writeln(Math.round(pageHeight) + " 0 translate");
  978. gen.writeln("90 rotate");
  979. }
  980. concatMatrix(1, 0, 0, -1, 0, pageHeight);
  981. gen.writeDSCComment(DSCConstants.END_PAGE_SETUP);
  982. //Process page
  983. super.renderPage(page);
  984. //Show page
  985. writeln("showpage");
  986. gen.writeDSCComment(DSCConstants.PAGE_TRAILER);
  987. if (page.hasExtensionAttachments()) {
  988. List extensionAttachments = page.getExtensionAttachments();
  989. for (int i = 0; i < extensionAttachments.size(); i++) {
  990. Object attObj = extensionAttachments.get(i);
  991. if (attObj instanceof PSExtensionAttachment) {
  992. PSExtensionAttachment attachment = (PSExtensionAttachment)attObj;
  993. if (attachment instanceof PSCommentAfter) {
  994. gen.commentln("%" + attachment.getContent());
  995. }
  996. }
  997. }
  998. }
  999. gen.getResourceTracker().writeResources(true, gen);
  1000. }
  1001. /** {@inheritDoc} */
  1002. protected void renderRegionViewport(RegionViewport port) {
  1003. if (port != null) {
  1004. comment("%FOPBeginRegionViewport: " + port.getRegionReference().getRegionName());
  1005. super.renderRegionViewport(port);
  1006. comment("%FOPEndRegionViewport");
  1007. }
  1008. }
  1009. /** Indicates the beginning of a text object. */
  1010. protected void beginTextObject() {
  1011. if (!inTextMode) {
  1012. saveGraphicsState();
  1013. writeln("BT");
  1014. inTextMode = true;
  1015. }
  1016. }
  1017. /** Indicates the end of a text object. */
  1018. protected void endTextObject() {
  1019. if (inTextMode) {
  1020. inTextMode = false; //set before restoreGraphicsState() to avoid recursion
  1021. writeln("ET");
  1022. restoreGraphicsState();
  1023. }
  1024. }
  1025. /** {@inheritDoc} */
  1026. public void renderText(TextArea area) {
  1027. renderInlineAreaBackAndBorders(area);
  1028. String fontkey = getInternalFontNameForArea(area);
  1029. int fontsize = area.getTraitAsInteger(Trait.FONT_SIZE);
  1030. // This assumes that *all* CIDFonts use a /ToUnicode mapping
  1031. Typeface tf = (Typeface) fontInfo.getFonts().get(fontkey);
  1032. //Determine position
  1033. int rx = currentIPPosition + area.getBorderAndPaddingWidthStart();
  1034. int bl = currentBPPosition + area.getOffset() + area.getBaselineOffset();
  1035. Color ct = (Color)area.getTrait(Trait.COLOR);
  1036. if (ct != null) {
  1037. try {
  1038. useColor(ct);
  1039. } catch (IOException ioe) {
  1040. handleIOTrouble(ioe);
  1041. }
  1042. }
  1043. beginTextObject();
  1044. writeln("1 0 0 -1 " + gen.formatDouble(rx / 1000f)
  1045. + " " + gen.formatDouble(bl / 1000f) + " Tm");
  1046. super.renderText(area); //Updates IPD
  1047. renderTextDecoration(tf, fontsize, area, bl, rx);
  1048. }
  1049. /** {@inheritDoc} */
  1050. protected void renderWord(WordArea word) {
  1051. renderText((TextArea)word.getParentArea(), word.getWord(), word.getLetterAdjustArray());
  1052. super.renderWord(word);
  1053. }
  1054. /** {@inheritDoc} */
  1055. protected void renderSpace(SpaceArea space) {
  1056. AbstractTextArea textArea = (AbstractTextArea)space.getParentArea();
  1057. String s = space.getSpace();
  1058. char sp = s.charAt(0);
  1059. Font font = getFontFromArea(textArea);
  1060. int tws = (space.isAdjustable()
  1061. ? ((TextArea) space.getParentArea()).getTextWordSpaceAdjust()
  1062. + 2 * textArea.getTextLetterSpaceAdjust()
  1063. : 0);
  1064. rmoveTo((font.getCharWidth(sp) + tws) / 1000f, 0);
  1065. super.renderSpace(space);
  1066. }
  1067. private Typeface getTypeface(String fontName) {
  1068. Typeface tf = (Typeface)fontInfo.getFonts().get(fontName);
  1069. if (tf instanceof LazyFont) {
  1070. tf = ((LazyFont)tf).getRealFont();
  1071. }
  1072. return tf;
  1073. }
  1074. private void renderText(AbstractTextArea area, String text, int[] letterAdjust) {
  1075. String fontkey = getInternalFontNameForArea(area);
  1076. int fontSize = area.getTraitAsInteger(Trait.FONT_SIZE);
  1077. Font font = getFontFromArea(area);
  1078. Typeface tf = getTypeface(font.getFontName());
  1079. SingleByteFont singleByteFont = null;
  1080. if (tf instanceof SingleByteFont) {
  1081. singleByteFont = (SingleByteFont)tf;
  1082. }
  1083. int textLen = text.length();
  1084. if (singleByteFont != null && singleByteFont.hasAdditionalEncodings()) {
  1085. int start = 0;
  1086. int currentEncoding = -1;
  1087. for (int i = 0; i < textLen; i++) {
  1088. char c = text.charAt(i);
  1089. char mapped = tf.mapChar(c);
  1090. int encoding = mapped / 256;
  1091. if (currentEncoding != encoding) {
  1092. if (i > 0) {
  1093. writeText(area, text, start, i - start, letterAdjust, fontSize, tf);
  1094. }
  1095. if (encoding == 0) {
  1096. useFont(fontkey, fontSize);
  1097. } else {
  1098. useFont(fontkey + "_" + Integer.toString(encoding), fontSize);
  1099. }
  1100. currentEncoding = encoding;
  1101. start = i;
  1102. }
  1103. }
  1104. writeText(area, text, start, textLen - start, letterAdjust, fontSize, tf);
  1105. } else {
  1106. useFont(fontkey, fontSize);
  1107. writeText(area, text, 0, textLen, letterAdjust, fontSize, tf);
  1108. }
  1109. }
  1110. private void writeText(AbstractTextArea area, String text, int start, int len,
  1111. int[] letterAdjust, int fontsize, Typeface tf) {
  1112. int end = start + len;
  1113. int initialSize = text.length();
  1114. initialSize += initialSize / 2;
  1115. StringBuffer sb = new StringBuffer(initialSize);
  1116. if (letterAdjust == null
  1117. && area.getTextLetterSpaceAdjust() == 0
  1118. && area.getTextWordSpaceAdjust() == 0) {
  1119. sb.append("(");
  1120. for (int i = start; i < end; i++) {
  1121. final char c = text.charAt(i);
  1122. final char mapped = (char)(tf.mapChar(c) % 256);
  1123. PSGenerator.escapeChar(mapped, sb);
  1124. }
  1125. sb.append(") t");
  1126. } else {
  1127. sb.append("(");
  1128. int[] offsets = new int[len];
  1129. for (int i = start; i < end; i++) {
  1130. final char c = text.charAt(i);
  1131. final char mapped = tf.mapChar(c);
  1132. char codepoint = (char)(mapped % 256);
  1133. int wordSpace;
  1134. if (CharUtilities.isAdjustableSpace(mapped)) {
  1135. wordSpace = area.getTextWordSpaceAdjust();
  1136. } else {
  1137. wordSpace = 0;
  1138. }
  1139. int cw = tf.getWidth(mapped, fontsize) / 1000;
  1140. int ladj = (letterAdjust != null && i < end - 1 ? letterAdjust[i + 1] : 0);
  1141. int tls = (i < end - 1 ? area.getTextLetterSpaceAdjust() : 0);
  1142. offsets[i - start] = cw + ladj + tls + wordSpace;
  1143. PSGenerator.escapeChar(codepoint, sb);
  1144. }
  1145. sb.append(")" + PSGenerator.LF + "[");
  1146. for (int i = 0; i < len; i++) {
  1147. if (i > 0) {
  1148. if (i % 8 == 0) {
  1149. sb.append(PSGenerator.LF);
  1150. } else {
  1151. sb.append(" ");
  1152. }
  1153. }
  1154. sb.append(gen.formatDouble(offsets[i] / 1000f));
  1155. }
  1156. sb.append("]" + PSGenerator.LF + "xshow");
  1157. }
  1158. writeln(sb.toString());
  1159. }
  1160. /** {@inheritDoc} */
  1161. protected List breakOutOfStateStack() {
  1162. try {
  1163. List breakOutList = new java.util.ArrayList();
  1164. PSState state;
  1165. while (true) {
  1166. if (breakOutList.size() == 0) {
  1167. endTextObject();
  1168. comment("------ break out!");
  1169. }
  1170. state = gen.getCurrentState();
  1171. if (!gen.restoreGraphicsState()) {
  1172. break;
  1173. }
  1174. breakOutList.add(0, state); //Insert because of stack-popping
  1175. }
  1176. return breakOutList;
  1177. } catch (IOException ioe) {
  1178. handleIOTrouble(ioe);
  1179. return null;
  1180. }
  1181. }
  1182. /** {@inheritDoc} */
  1183. protected void restoreStateStackAfterBreakOut(List breakOutList) {
  1184. try {
  1185. comment("------ restoring context after break-out...");
  1186. PSState state;
  1187. Iterator i = breakOutList.iterator();
  1188. while (i.hasNext()) {
  1189. state = (PSState)i.next();
  1190. saveGraphicsState();
  1191. state.reestablish(gen);
  1192. }
  1193. comment("------ done.");
  1194. } catch (IOException ioe) {
  1195. handleIOTrouble(ioe);
  1196. }
  1197. }
  1198. /**
  1199. * {@inheritDoc}
  1200. */
  1201. protected void startVParea(CTM ctm, Rectangle2D clippingRect) {
  1202. saveGraphicsState();
  1203. if (clippingRect != null) {
  1204. clipRect((float)clippingRect.getX() / 1000f,
  1205. (float)clippingRect.getY() / 1000f,
  1206. (float)clippingRect.getWidth() / 1000f,
  1207. (float)clippingRect.getHeight() / 1000f);
  1208. }
  1209. // multiply with current CTM
  1210. final double[] matrix = ctm.toArray();
  1211. matrix[4] /= 1000f;
  1212. matrix[5] /= 1000f;
  1213. concatMatrix(matrix);
  1214. }
  1215. /**
  1216. * {@inheritDoc}
  1217. */
  1218. protected void endVParea() {
  1219. restoreGraphicsState();
  1220. }
  1221. /** {@inheritDoc} */
  1222. protected void renderBlockViewport(BlockViewport bv, List children) {
  1223. comment("%FOPBeginBlockViewport: " + bv.toString());
  1224. super.renderBlockViewport(bv, children);
  1225. comment("%FOPEndBlockViewport");
  1226. }
  1227. /** {@inheritDoc} */
  1228. protected void renderInlineParent(InlineParent ip) {
  1229. super.renderInlineParent(ip);
  1230. }
  1231. /**
  1232. * {@inheritDoc}
  1233. */
  1234. public void renderLeader(Leader area) {
  1235. renderInlineAreaBackAndBorders(area);
  1236. endTextObject();
  1237. saveGraphicsState();
  1238. int style = area.getRuleStyle();
  1239. float startx = (currentIPPosition + area.getBorderAndPaddingWidthStart()) / 1000f;
  1240. float starty = (currentBPPosition + area.getOffset()) / 1000f;
  1241. float endx = (currentIPPosition + area.getBorderAndPaddingWidthStart()
  1242. + area.getIPD()) / 1000f;
  1243. float ruleThickness = area.getRuleThickness() / 1000f;
  1244. Color col = (Color)area.getTrait(Trait.COLOR);
  1245. try {
  1246. switch (style) {
  1247. case EN_SOLID:
  1248. case EN_DASHED:
  1249. case EN_DOUBLE:
  1250. drawBorderLine(startx, starty, endx, starty + ruleThickness,
  1251. true, true, style, col);
  1252. break;
  1253. case EN_DOTTED:
  1254. clipRect(startx, starty, endx - startx, ruleThickness);
  1255. //This displaces the dots to the right by half a dot's width
  1256. //TODO There's room for improvement here
  1257. gen.concatMatrix(1, 0, 0, 1, ruleThickness / 2, 0);
  1258. drawBorderLine(startx, starty, endx, starty + ruleThickness,
  1259. true, true, style, col);
  1260. break;
  1261. case EN_GROOVE:
  1262. case EN_RIDGE:
  1263. float half = area.getRuleThickness() / 2000f;
  1264. gen.useColor(ColorUtil.lightenColor(col, 0.6f));
  1265. moveTo(startx, starty);
  1266. lineTo(endx, starty);
  1267. lineTo(endx, starty + 2 * half);
  1268. lineTo(startx, starty + 2 * half);
  1269. closePath();
  1270. gen.writeln(" fill newpath");
  1271. gen.useColor(col);
  1272. if (style == EN_GROOVE) {
  1273. moveTo(startx, starty);
  1274. lineTo(endx, starty);
  1275. lineTo(endx, starty + half);
  1276. lineTo(startx + half, starty + half);
  1277. lineTo(startx, starty + 2 * half);
  1278. } else {
  1279. moveTo(endx, starty);
  1280. lineTo(endx, starty + 2 * half);
  1281. lineTo(startx, starty + 2 * half);
  1282. lineTo(startx, starty + half);
  1283. lineTo(endx - half, starty + half);
  1284. }
  1285. closePath();
  1286. gen.writeln(" fill newpath");
  1287. break;
  1288. default:
  1289. throw new UnsupportedOperationException("rule style not supported");
  1290. }
  1291. } catch (IOException ioe) {
  1292. handleIOTrouble(ioe);
  1293. }
  1294. restoreGraphicsState();
  1295. super.renderLeader(area);
  1296. }
  1297. /**
  1298. * {@inheritDoc}
  1299. */
  1300. public void renderImage(Image image, Rectangle2D pos) {
  1301. drawImage(image.getURL(), pos, image.getForeignAttributes());
  1302. }
  1303. /**
  1304. * {@inheritDoc}
  1305. */
  1306. protected RendererContext createRendererContext(int x, int y, int width, int height,
  1307. Map foreignAttributes) {
  1308. RendererContext context = super.createRendererContext(
  1309. x, y, width, height, foreignAttributes);
  1310. context.setProperty(PSRendererContextConstants.PS_GENERATOR, this.gen);
  1311. context.setProperty(PSRendererContextConstants.PS_FONT_INFO, fontInfo);
  1312. return context;
  1313. }
  1314. /** {@inheritDoc} */
  1315. public String getMimeType() {
  1316. return MIME_TYPE;
  1317. }
  1318. /**
  1319. * Sets whether or not the safe set page device macro should be used
  1320. * (as opposed to directly invoking setpagedevice) when setting the
  1321. * postscript page device.
  1322. *
  1323. * This option is a useful option when you want to guard against the possibility
  1324. * of invalid/unsupported postscript key/values being placed in the page device.
  1325. *
  1326. * @param safeSetPageDevice setting to false and the renderer will make a
  1327. * standard "setpagedevice" call, setting to true will make a safe set page
  1328. * device macro call (default is false).
  1329. */
  1330. public void setSafeSetPageDevice(boolean safeSetPageDevice) {
  1331. getPSUtil().setSafeSetPageDevice(safeSetPageDevice);
  1332. }
  1333. /**
  1334. * Sets whether or not PostScript Document Structuring Conventions (dsc) compliance are
  1335. * enforced.
  1336. * <p>
  1337. * It can cause problems (unwanted PostScript subsystem initgraphics/erasepage calls)
  1338. * on some printers when the pagedevice is set. If this causes problems on a
  1339. * particular implementation then use this setting with a 'false' value to try and
  1340. * minimize the number of setpagedevice calls in the postscript document output.
  1341. * <p>
  1342. * Set this value to false if you experience unwanted blank pages in your
  1343. * postscript output.
  1344. * @param dscCompliant boolean value (default is true)
  1345. */
  1346. public void setDSCCompliant(boolean dscCompliant) {
  1347. getPSUtil().setDSCComplianceEnabled(dscCompliant);
  1348. }
  1349. }