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.

PSGraphics2D.java 41KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181
  1. /*
  2. * Copyright 1999-2006 The Apache Software Foundation.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. /* $Id$ */
  17. package org.apache.fop.render.ps;
  18. //Java
  19. import java.text.AttributedCharacterIterator;
  20. import java.awt.AlphaComposite;
  21. import java.awt.BasicStroke;
  22. import java.awt.Color;
  23. import java.awt.Dimension;
  24. /* java.awt.Font is not imported to avoid confusion with
  25. org.apache.fop.fonts.Font */
  26. import java.awt.GradientPaint;
  27. import java.awt.Graphics;
  28. import java.awt.Graphics2D;
  29. import java.awt.GraphicsConfiguration;
  30. import java.awt.GraphicsEnvironment;
  31. import java.awt.Image;
  32. import java.awt.Paint;
  33. import java.awt.Rectangle;
  34. import java.awt.Shape;
  35. import java.awt.Stroke;
  36. import java.awt.TexturePaint;
  37. import java.awt.color.ColorSpace;
  38. import java.awt.color.ICC_Profile;
  39. import java.awt.font.FontRenderContext;
  40. import java.awt.font.GlyphVector;
  41. import java.awt.geom.AffineTransform;
  42. import java.awt.geom.PathIterator;
  43. import java.awt.image.BufferedImage;
  44. import java.awt.image.DataBuffer;
  45. import java.awt.image.DataBufferInt;
  46. import java.awt.image.ImageObserver;
  47. import java.awt.image.Raster;
  48. import java.awt.image.RenderedImage;
  49. import java.awt.image.renderable.RenderableImage;
  50. import java.io.IOException;
  51. //Batik
  52. import org.apache.batik.ext.awt.RenderingHintsKeyExt;
  53. import org.apache.batik.ext.awt.g2d.AbstractGraphics2D;
  54. import org.apache.batik.ext.awt.g2d.GraphicContext;
  55. import org.apache.commons.logging.Log;
  56. import org.apache.commons.logging.LogFactory;
  57. //FOP
  58. import org.apache.fop.fonts.Font;
  59. import org.apache.fop.fonts.FontInfo;
  60. import org.apache.fop.fonts.FontTriplet;
  61. import org.apache.fop.image.FopImage;
  62. /**
  63. * This concrete implementation of <tt>AbstractGraphics2D</tt> is a
  64. * simple help to programmers to get started with their own
  65. * implementation of <tt>Graphics2D</tt>.
  66. * <tt>DefaultGraphics2D</tt> implements all the abstract methods
  67. * is <tt>AbstractGraphics2D</tt> and makes it easy to start
  68. * implementing a <tt>Graphic2D</tt> piece-meal.
  69. *
  70. * @author <a href="mailto:keiron@aftexsw.com">Keiron Liddle</a>
  71. * @version $Id$
  72. * @see org.apache.batik.ext.awt.g2d.AbstractGraphics2D
  73. */
  74. public class PSGraphics2D extends AbstractGraphics2D {
  75. private static final AffineTransform IDENTITY_TRANSFORM = new AffineTransform();
  76. /** the logger for this class */
  77. protected Log log = LogFactory.getLog(PSTextPainter.class);
  78. /** the PostScript generator being created */
  79. protected PSGenerator gen;
  80. private boolean clippingDisabled = false;
  81. /** Currently valid FontState */
  82. protected Font font;
  83. /** Overriding FontState */
  84. protected Font overrideFont = null;
  85. /** the current (internal) font name */
  86. protected String currentFontName;
  87. /** the current font size in millipoints */
  88. protected int currentFontSize;
  89. /**
  90. * the current colour for use in svg
  91. */
  92. protected Color currentColour = new Color(0, 0, 0);
  93. /** FontInfo containing all available fonts */
  94. protected FontInfo fontInfo;
  95. /**
  96. * Create a new Graphics2D that generates PostScript code.
  97. * @param textAsShapes True if text should be rendered as graphics
  98. * @see org.apache.batik.ext.awt.g2d.AbstractGraphics2D#AbstractGraphics2D(boolean)
  99. */
  100. public PSGraphics2D(boolean textAsShapes) {
  101. super(textAsShapes);
  102. }
  103. /**
  104. * Create a new Graphics2D that generates PostScript code.
  105. * @param textAsShapes True if text should be rendered as graphics
  106. * @param gen PostScript generator to use for output
  107. * @see org.apache.batik.ext.awt.g2d.AbstractGraphics2D#AbstractGraphics2D(boolean)
  108. */
  109. public PSGraphics2D(boolean textAsShapes, PSGenerator gen) {
  110. this(textAsShapes);
  111. setPSGenerator(gen);
  112. }
  113. /**
  114. * Constructor for creating copies
  115. * @param g parent PostScript Graphics2D
  116. */
  117. public PSGraphics2D(PSGraphics2D g) {
  118. super(g);
  119. setPSGenerator(g.gen);
  120. this.clippingDisabled = g.clippingDisabled;
  121. this.font = g.font;
  122. this.overrideFont = g.overrideFont;
  123. this.currentFontName = g.currentFontName;
  124. this.currentFontSize = g.currentFontSize;
  125. this.currentColour = g.currentColour;
  126. this.fontInfo = g.fontInfo;
  127. }
  128. /**
  129. * Sets the PostScript generator
  130. * @param gen the PostScript generator
  131. */
  132. public void setPSGenerator(PSGenerator gen) {
  133. this.gen = gen;
  134. }
  135. /**
  136. * Sets the GraphicContext
  137. * @param c GraphicContext to use
  138. */
  139. public void setGraphicContext(GraphicContext c) {
  140. gc = c;
  141. setPrivateHints();
  142. }
  143. private void setPrivateHints() {
  144. setRenderingHint(RenderingHintsKeyExt.KEY_AVOID_TILE_PAINTING,
  145. RenderingHintsKeyExt.VALUE_AVOID_TILE_PAINTING_ON);
  146. }
  147. /**
  148. * Creates a new <code>Graphics</code> object that is
  149. * a copy of this <code>Graphics</code> object.
  150. * @return a new graphics context that is a copy of
  151. * this graphics context.
  152. */
  153. public Graphics create() {
  154. return new PSGraphics2D(this);
  155. }
  156. /**
  157. * Return the font information associated with this object
  158. * @return the FontInfo object
  159. */
  160. public FontInfo getFontInfo() {
  161. return fontInfo;
  162. }
  163. /**
  164. * Central handler for IOExceptions for this class.
  165. * @param ioe IOException to handle
  166. */
  167. protected void handleIOException(IOException ioe) {
  168. //TODO Surely, there's a better way to do this.
  169. ioe.printStackTrace();
  170. }
  171. /**
  172. * This method is used by AbstractPSDocumentGraphics2D to prepare a new page if
  173. * necessary.
  174. */
  175. protected void preparePainting() {
  176. //nop, used by AbstractPSDocumentGraphics2D
  177. }
  178. /**
  179. * Draws as much of the specified image as is currently available.
  180. * The image is drawn with its top-left corner at
  181. * (<i>x</i>,&nbsp;<i>y</i>) in this graphics context's coordinate
  182. * space. Transparent pixels in the image do not affect whatever
  183. * pixels are already there.
  184. * <p>
  185. * This method returns immediately in all cases, even if the
  186. * complete image has not yet been loaded, and it has not been dithered
  187. * and converted for the current output device.
  188. * <p>
  189. * If the image has not yet been completely loaded, then
  190. * <code>drawImage</code> returns <code>false</code>. As more of
  191. * the image becomes available, the process that draws the image notifies
  192. * the specified image observer.
  193. * @param img the specified image to be drawn.
  194. * @param x the <i>x</i> coordinate.
  195. * @param y the <i>y</i> coordinate.
  196. * @param observer object to be notified as more of
  197. * the image is converted.
  198. * @return True if the image has been fully drawn/loaded
  199. * @see java.awt.Image
  200. * @see java.awt.image.ImageObserver
  201. * @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int)
  202. */
  203. public boolean drawImage(Image img, int x, int y,
  204. ImageObserver observer) {
  205. preparePainting();
  206. log.debug("drawImage: " + x + ", " + y + " " + img.getClass().getName());
  207. final int width = img.getWidth(observer);
  208. final int height = img.getHeight(observer);
  209. if (width == -1 || height == -1) {
  210. return false;
  211. }
  212. Dimension size = new Dimension(width, height);
  213. BufferedImage buf = buildBufferedImage(size);
  214. java.awt.Graphics2D g = buf.createGraphics();
  215. g.setComposite(AlphaComposite.SrcOver);
  216. g.setBackground(new Color(1, 1, 1, 0));
  217. g.setPaint(new Color(1, 1, 1, 0));
  218. g.fillRect(0, 0, width, height);
  219. g.clip(new Rectangle(0, 0, buf.getWidth(), buf.getHeight()));
  220. if (!g.drawImage(img, 0, 0, observer)) {
  221. return false;
  222. }
  223. g.dispose();
  224. final byte[] result = new byte[buf.getWidth() * buf.getHeight() * 3];
  225. //final byte[] mask = new byte[buf.getWidth() * buf.getHeight()];
  226. Raster raster = buf.getData();
  227. DataBuffer bd = raster.getDataBuffer();
  228. int count = 0;
  229. //int maskpos = 0;
  230. switch (bd.getDataType()) {
  231. case DataBuffer.TYPE_INT:
  232. int[][] idata = ((DataBufferInt)bd).getBankData();
  233. for (int i = 0; i < idata.length; i++) {
  234. for (int j = 0; j < idata[i].length; j++) {
  235. // mask[maskpos++] = (byte)((idata[i][j] >> 24) & 0xFF);
  236. if (((idata[i][j] >> 24) & 0xFF) != 255) {
  237. result[count++] = (byte)0xFF;
  238. result[count++] = (byte)0xFF;
  239. result[count++] = (byte)0xFF;
  240. } else {
  241. result[count++] = (byte)((idata[i][j] >> 16) & 0xFF);
  242. result[count++] = (byte)((idata[i][j] >> 8) & 0xFF);
  243. result[count++] = (byte)((idata[i][j]) & 0xFF);
  244. }
  245. }
  246. }
  247. break;
  248. default:
  249. // error
  250. break;
  251. }
  252. try {
  253. FopImage fopimg = new TempImage(width, height, result, null);
  254. AffineTransform at = getTransform();
  255. gen.saveGraphicsState();
  256. gen.concatMatrix(at);
  257. Shape imclip = getClip();
  258. writeClip(imclip);
  259. PSImageUtils.renderBitmapImage(fopimg,
  260. x, y, width, height, gen);
  261. gen.restoreGraphicsState();
  262. } catch (IOException ioe) {
  263. handleIOException(ioe);
  264. }
  265. return true;
  266. }
  267. /**
  268. * Creates a buffered image.
  269. * @param size dimensions of the image to be created
  270. * @return the buffered image
  271. */
  272. public BufferedImage buildBufferedImage(Dimension size) {
  273. return new BufferedImage(size.width, size.height,
  274. BufferedImage.TYPE_INT_ARGB);
  275. }
  276. class TempImage implements FopImage {
  277. private int height;
  278. private int width;
  279. private int bitsPerPixel;
  280. private ColorSpace colorSpace;
  281. private byte[] bitmaps;
  282. private byte[] mask;
  283. private Color transparentColor;
  284. TempImage(int width, int height, byte[] bitmaps,
  285. byte[] mask) {
  286. this.height = height;
  287. this.width = width;
  288. this.bitsPerPixel = 8;
  289. this.colorSpace = ColorSpace.getInstance(ColorSpace.CS_sRGB);
  290. this.bitmaps = bitmaps;
  291. this.mask = mask;
  292. }
  293. public String getMimeType() {
  294. return "application/octet-stream";
  295. }
  296. public String getOriginalURI() {
  297. return "temp-image:" + this.toString();
  298. }
  299. /**
  300. * @see org.apache.fop.image.FopImage#load(int, org.apache.commons.logging.Log)
  301. */
  302. public boolean load(int type) {
  303. switch (type) {
  304. case FopImage.DIMENSIONS: break;
  305. case FopImage.BITMAP: break;
  306. case FopImage.ORIGINAL_DATA: break;
  307. default: throw new RuntimeException("Unknown load type: " + type);
  308. }
  309. return true;
  310. }
  311. public int getWidth() {
  312. return this.width;
  313. }
  314. public int getHeight() {
  315. return this.height;
  316. }
  317. public ColorSpace getColorSpace() {
  318. return this.colorSpace;
  319. }
  320. public ICC_Profile getICCProfile() {
  321. return null;
  322. }
  323. public int getBitsPerPixel() {
  324. return this.bitsPerPixel;
  325. }
  326. // For transparent images
  327. public boolean isTransparent() {
  328. return getTransparentColor() != null;
  329. }
  330. public Color getTransparentColor() {
  331. return this.transparentColor;
  332. }
  333. public boolean hasSoftMask() {
  334. return this.mask != null;
  335. }
  336. public byte[] getSoftMask() {
  337. return this.mask;
  338. }
  339. public boolean isInverted() {
  340. return false;
  341. }
  342. public byte[] getBitmaps() {
  343. return this.bitmaps;
  344. }
  345. // width * (bitsPerPixel / 8) * height, no ?
  346. public int getBitmapsSize() {
  347. return getWidth() * getHeight() * 3; //Assumes RGB!
  348. }
  349. // get compressed image bytes
  350. // I don't know if we really need it, nor if it
  351. // should be changed...
  352. public byte[] getRessourceBytes() {
  353. return null;
  354. }
  355. public int getRessourceBytesSize() {
  356. return 0;
  357. }
  358. /** @see org.apache.fop.image.FopImage#getIntrinsicWidth() */
  359. public int getIntrinsicWidth() {
  360. return (int)(getWidth() * 72 / getHorizontalResolution());
  361. }
  362. /** @see org.apache.fop.image.FopImage#getIntrinsicHeight() */
  363. public int getIntrinsicHeight() {
  364. return (int)(getHeight() * 72 / getVerticalResolution());
  365. }
  366. /** @see org.apache.fop.image.FopImage#getHorizontalResolution() */
  367. public double getHorizontalResolution() {
  368. return 72;
  369. }
  370. /** @see org.apache.fop.image.FopImage#getVerticalResolution() */
  371. public double getVerticalResolution() {
  372. return 72;
  373. }
  374. }
  375. /**
  376. * Draws as much of the specified image as has already been scaled
  377. * to fit inside the specified rectangle.
  378. * <p>
  379. * The image is drawn inside the specified rectangle of this
  380. * graphics context's coordinate space, and is scaled if
  381. * necessary. Transparent pixels do not affect whatever pixels
  382. * are already there.
  383. * <p>
  384. * This method returns immediately in all cases, even if the
  385. * entire image has not yet been scaled, dithered, and converted
  386. * for the current output device.
  387. * If the current output representation is not yet complete, then
  388. * <code>drawImage</code> returns <code>false</code>. As more of
  389. * the image becomes available, the process that draws the image notifies
  390. * the image observer by calling its <code>imageUpdate</code> method.
  391. * <p>
  392. * A scaled version of an image will not necessarily be
  393. * available immediately just because an unscaled version of the
  394. * image has been constructed for this output device. Each size of
  395. * the image may be cached separately and generated from the original
  396. * data in a separate image production sequence.
  397. * @param img the specified image to be drawn.
  398. * @param x the <i>x</i> coordinate.
  399. * @param y the <i>y</i> coordinate.
  400. * @param width the width of the rectangle.
  401. * @param height the height of the rectangle.
  402. * @param observer object to be notified as more of
  403. * the image is converted.
  404. * @return True if the image has been fully loaded/drawn
  405. * @see java.awt.Image
  406. * @see java.awt.image.ImageObserver
  407. * @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int)
  408. */
  409. public boolean drawImage(Image img, int x, int y, int width, int height,
  410. ImageObserver observer) {
  411. preparePainting();
  412. log.warn("NYI: drawImage");
  413. return true;
  414. }
  415. /**
  416. * Disposes of this graphics context and releases
  417. * any system resources that it is using.
  418. * A <code>Graphics</code> object cannot be used after
  419. * <code>dispose</code>has been called.
  420. * <p>
  421. * When a Java program runs, a large number of <code>Graphics</code>
  422. * objects can be created within a short time frame.
  423. * Although the finalization process of the garbage collector
  424. * also disposes of the same system resources, it is preferable
  425. * to manually free the associated resources by calling this
  426. * method rather than to rely on a finalization process which
  427. * may not run to completion for a long period of time.
  428. * <p>
  429. * Graphics objects which are provided as arguments to the
  430. * <code>paint</code> and <code>update</code> methods
  431. * of components are automatically released by the system when
  432. * those methods return. For efficiency, programmers should
  433. * call <code>dispose</code> when finished using
  434. * a <code>Graphics</code> object only if it was created
  435. * directly from a component or another <code>Graphics</code> object.
  436. * @see java.awt.Graphics#finalize
  437. * @see java.awt.Component#paint
  438. * @see java.awt.Component#update
  439. * @see java.awt.Component#getGraphics
  440. * @see java.awt.Graphics#create
  441. */
  442. public void dispose() {
  443. this.gen = null;
  444. this.font = null;
  445. this.currentColour = null;
  446. this.fontInfo = null;
  447. }
  448. /**
  449. * Processes a path iterator generating the nexessary painting operations.
  450. * @param iter PathIterator to process
  451. * @throws IOException In case of an I/O problem.
  452. */
  453. public void processPathIterator(PathIterator iter) throws IOException {
  454. double[] vals = new double[6];
  455. while (!iter.isDone()) {
  456. int type = iter.currentSegment(vals);
  457. switch (type) {
  458. case PathIterator.SEG_CUBICTO:
  459. gen.writeln(gen.formatDouble(vals[0]) + " "
  460. + gen.formatDouble(vals[1]) + " "
  461. + gen.formatDouble(vals[2]) + " "
  462. + gen.formatDouble(vals[3]) + " "
  463. + gen.formatDouble(vals[4]) + " "
  464. + gen.formatDouble(vals[5])
  465. + " curveto");
  466. break;
  467. case PathIterator.SEG_LINETO:
  468. gen.writeln(gen.formatDouble(vals[0]) + " "
  469. + gen.formatDouble(vals[1])
  470. + " lineto");
  471. break;
  472. case PathIterator.SEG_MOVETO:
  473. gen.writeln(gen.formatDouble(vals[0]) + " "
  474. + gen.formatDouble(vals[1])
  475. + " M");
  476. break;
  477. case PathIterator.SEG_QUADTO:
  478. gen.writeln(gen.formatDouble(vals[0]) + " "
  479. + gen.formatDouble(vals[1]) + " "
  480. + gen.formatDouble(vals[2]) + " "
  481. + gen.formatDouble(vals[3]) + " QUADTO ");
  482. break;
  483. case PathIterator.SEG_CLOSE:
  484. gen.writeln("closepath");
  485. break;
  486. default:
  487. break;
  488. }
  489. iter.next();
  490. }
  491. }
  492. /**
  493. * Strokes the outline of a <code>Shape</code> using the settings of the
  494. * current <code>Graphics2D</code> context. The rendering attributes
  495. * applied include the <code>Clip</code>, <code>Transform</code>,
  496. * <code>Paint</code>, <code>Composite</code> and
  497. * <code>Stroke</code> attributes.
  498. * @param s the <code>Shape</code> to be rendered
  499. * @see #setStroke
  500. * @see #setPaint
  501. * @see java.awt.Graphics#setColor
  502. * @see #transform
  503. * @see #setTransform
  504. * @see #clip
  505. * @see #setClip
  506. * @see #setComposite
  507. */
  508. public void draw(Shape s) {
  509. preparePainting();
  510. try {
  511. gen.saveGraphicsState();
  512. AffineTransform trans = getTransform();
  513. boolean newTransform = gen.getCurrentState().checkTransform(trans)
  514. && !trans.isIdentity();
  515. if (newTransform) {
  516. gen.concatMatrix(trans);
  517. }
  518. Shape imclip = getClip();
  519. writeClip(imclip);
  520. establishColor(getColor());
  521. applyPaint(getPaint(), false);
  522. applyStroke(getStroke());
  523. gen.writeln("newpath");
  524. PathIterator iter = s.getPathIterator(IDENTITY_TRANSFORM);
  525. processPathIterator(iter);
  526. doDrawing(false, true, false);
  527. gen.restoreGraphicsState();
  528. } catch (IOException ioe) {
  529. handleIOException(ioe);
  530. }
  531. }
  532. /**
  533. * Establishes a clipping region
  534. * @param s Shape defining the clipping region
  535. */
  536. protected void writeClip(Shape s) {
  537. if (s == null) {
  538. return;
  539. }
  540. if (!this.clippingDisabled) {
  541. preparePainting();
  542. try {
  543. gen.writeln("newpath");
  544. PathIterator iter = s.getPathIterator(IDENTITY_TRANSFORM);
  545. processPathIterator(iter);
  546. // clip area
  547. gen.writeln("clip");
  548. } catch (IOException ioe) {
  549. handleIOException(ioe);
  550. }
  551. }
  552. }
  553. /**
  554. * Applies a new Paint object.
  555. * @param paint Paint object to use
  556. * @param fill True if to be applied for filling
  557. */
  558. protected void applyPaint(Paint paint, boolean fill) {
  559. preparePainting();
  560. if (paint instanceof GradientPaint) {
  561. log.warn("NYI: Gradient paint");
  562. } else if (paint instanceof TexturePaint) {
  563. log.warn("NYI: texture paint");
  564. }
  565. }
  566. /**
  567. * Applies a new Stroke object.
  568. * @param stroke Stroke object to use
  569. */
  570. protected void applyStroke(Stroke stroke) {
  571. preparePainting();
  572. try {
  573. if (stroke instanceof BasicStroke) {
  574. BasicStroke bs = (BasicStroke)stroke;
  575. float[] da = bs.getDashArray();
  576. if (da != null) {
  577. gen.write("[");
  578. for (int count = 0; count < da.length; count++) {
  579. gen.write("" + ((int)da[count]));
  580. if (count < da.length - 1) {
  581. gen.write(" ");
  582. }
  583. }
  584. gen.write("] ");
  585. float offset = bs.getDashPhase();
  586. gen.writeln(((int)offset) + " setdash");
  587. }
  588. int ec = bs.getEndCap();
  589. switch (ec) {
  590. case BasicStroke.CAP_BUTT:
  591. gen.writeln("0 setlinecap");
  592. break;
  593. case BasicStroke.CAP_ROUND:
  594. gen.writeln("1 setlinecap");
  595. break;
  596. case BasicStroke.CAP_SQUARE:
  597. gen.writeln("2 setlinecap");
  598. break;
  599. default: log.warn("Unsupported line cap: " + ec);
  600. }
  601. int lj = bs.getLineJoin();
  602. switch (lj) {
  603. case BasicStroke.JOIN_MITER:
  604. gen.writeln("0 setlinejoin");
  605. break;
  606. case BasicStroke.JOIN_ROUND:
  607. gen.writeln("1 setlinejoin");
  608. break;
  609. case BasicStroke.JOIN_BEVEL:
  610. gen.writeln("2 setlinejoin");
  611. break;
  612. default: log.warn("Unsupported line join: " + lj);
  613. }
  614. float lw = bs.getLineWidth();
  615. gen.writeln(gen.formatDouble(lw) + " setlinewidth");
  616. float ml = bs.getMiterLimit();
  617. gen.writeln(gen.formatDouble(ml) + " setmiterlimit");
  618. }
  619. } catch (IOException ioe) {
  620. handleIOException(ioe);
  621. }
  622. }
  623. /**
  624. * Renders a {@link RenderedImage},
  625. * applying a transform from image
  626. * space into user space before drawing.
  627. * The transformation from user space into device space is done with
  628. * the current <code>Transform</code> in the <code>Graphics2D</code>.
  629. * The specified transformation is applied to the image before the
  630. * transform attribute in the <code>Graphics2D</code> context is applied.
  631. * The rendering attributes applied include the <code>Clip</code>,
  632. * <code>Transform</code>, and <code>Composite</code> attributes. Note
  633. * that no rendering is done if the specified transform is
  634. * noninvertible.
  635. * @param img the image to be rendered
  636. * @param xform the transformation from image space into user space
  637. * @see #transform
  638. * @see #setTransform
  639. * @see #setComposite
  640. * @see #clip
  641. * @see #setClip
  642. */
  643. public void drawRenderedImage(RenderedImage img, AffineTransform xform) {
  644. preparePainting();
  645. log.warn("NYI: drawRenderedImage");
  646. }
  647. /**
  648. * Renders a
  649. * {@link RenderableImage},
  650. * applying a transform from image space into user space before drawing.
  651. * The transformation from user space into device space is done with
  652. * the current <code>Transform</code> in the <code>Graphics2D</code>.
  653. * The specified transformation is applied to the image before the
  654. * transform attribute in the <code>Graphics2D</code> context is applied.
  655. * The rendering attributes applied include the <code>Clip</code>,
  656. * <code>Transform</code>, and <code>Composite</code> attributes. Note
  657. * that no rendering is done if the specified transform is
  658. * noninvertible.
  659. * <p>
  660. * Rendering hints set on the <code>Graphics2D</code> object might
  661. * be used in rendering the <code>RenderableImage</code>.
  662. * If explicit control is required over specific hints recognized by a
  663. * specific <code>RenderableImage</code>, or if knowledge of which hints
  664. * are used is required, then a <code>RenderedImage</code> should be
  665. * obtained directly from the <code>RenderableImage</code>
  666. * and rendered using
  667. * {@link #drawRenderedImage(RenderedImage, AffineTransform) drawRenderedImage}.
  668. * @param img the image to be rendered
  669. * @param xform the transformation from image space into user space
  670. * @see #transform
  671. * @see #setTransform
  672. * @see #setComposite
  673. * @see #clip
  674. * @see #setClip
  675. * @see #drawRenderedImage
  676. */
  677. public void drawRenderableImage(RenderableImage img,
  678. AffineTransform xform) {
  679. preparePainting();
  680. log.warn("NYI: drawRenderableImage");
  681. }
  682. /**
  683. * Establishes the given color in the PostScript interpreter.
  684. * @param c the color to set
  685. * @throws IOException In case of an I/O problem
  686. */
  687. protected void establishColor(Color c) throws IOException {
  688. StringBuffer p = new StringBuffer();
  689. float[] comps = c.getColorComponents(null);
  690. if (c.getColorSpace().getType() == ColorSpace.TYPE_RGB) {
  691. // according to pdfspec 12.1 p.399
  692. // if the colors are the same then just use the g or G operator
  693. boolean same = (comps[0] == comps[1]
  694. && comps[0] == comps[2]);
  695. // output RGB
  696. if (same) {
  697. p.append(gen.formatDouble(comps[0]));
  698. } else {
  699. for (int i = 0; i < c.getColorSpace().getNumComponents(); i++) {
  700. if (i > 0) {
  701. p.append(" ");
  702. }
  703. p.append(gen.formatDouble(comps[i]));
  704. }
  705. }
  706. if (same) {
  707. p.append(" setgray");
  708. } else {
  709. p.append(" setrgbcolor");
  710. }
  711. } else if (c.getColorSpace().getType() == ColorSpace.TYPE_CMYK) {
  712. // colorspace is CMYK
  713. for (int i = 0; i < c.getColorSpace().getNumComponents(); i++) {
  714. if (i > 0) {
  715. p.append(" ");
  716. }
  717. p.append(gen.formatDouble(comps[i]));
  718. }
  719. p.append(" setcmykcolor");
  720. } else {
  721. // means we're in DeviceGray or Unknown.
  722. // assume we're in DeviceGray, because otherwise we're screwed.
  723. p.append(gen.formatDouble(comps[0]));
  724. p.append(" setgray");
  725. }
  726. gen.writeln(p.toString());
  727. }
  728. /**
  729. * Renders the text specified by the specified <code>String</code>,
  730. * using the current <code>Font</code> and <code>Paint</code> attributes
  731. * in the <code>Graphics2D</code> context.
  732. * The baseline of the first character is at position
  733. * (<i>x</i>,&nbsp;<i>y</i>) in the User Space.
  734. * The rendering attributes applied include the <code>Clip</code>,
  735. * <code>Transform</code>, <code>Paint</code>, <code>Font</code> and
  736. * <code>Composite</code> attributes. For characters in script systems
  737. * such as Hebrew and Arabic, the glyphs can be rendered from right to
  738. * left, in which case the coordinate supplied is the location of the
  739. * leftmost character on the baseline.
  740. * @param s the <code>String</code> to be rendered
  741. * @param x the x-coordinate where the <code>String</code>
  742. * should be rendered
  743. * @param y the y-coordinate where the <code>String</code>
  744. * should be rendered
  745. * @see #setPaint
  746. * @see java.awt.Graphics#setColor
  747. * @see java.awt.Graphics#setFont
  748. * @see #setTransform
  749. * @see #setComposite
  750. * @see #setClip
  751. */
  752. public void drawString(String s, float x, float y) {
  753. if (this.textAsShapes) {
  754. drawStringAsShapes(s, x, y);
  755. } else {
  756. drawStringAsText(s, x, y);
  757. }
  758. }
  759. /**
  760. * Draw a string to the PostScript document. The text is painted as shapes.
  761. * @param s the string to draw
  762. * @param x the x position
  763. * @param y the y position
  764. */
  765. public void drawStringAsShapes(String s, float x, float y) {
  766. java.awt.Font awtFont = super.getFont();
  767. FontRenderContext frc = super.getFontRenderContext();
  768. GlyphVector gv = awtFont.createGlyphVector(frc, s);
  769. Shape glyphOutline = gv.getOutline(x, y);
  770. fill(glyphOutline);
  771. }
  772. /**
  773. * Draw a string to the PostScript document. The text is painted using
  774. * text operations.
  775. * @param s the string to draw
  776. * @param x the x position
  777. * @param y the y position
  778. */
  779. public void drawStringAsText(String s, float x, float y) {
  780. preparePainting();
  781. log.trace("drawString('" + s + "', " + x + ", " + y + ")");
  782. try {
  783. if (this.overrideFont == null) {
  784. java.awt.Font awtFont = getFont();
  785. this.font = createFont(awtFont);
  786. } else {
  787. this.font = this.overrideFont;
  788. this.overrideFont = null;
  789. }
  790. //Color and Font state
  791. establishColor(getColor());
  792. establishCurrentFont();
  793. //Clip
  794. Shape imclip = getClip();
  795. writeClip(imclip);
  796. gen.saveGraphicsState();
  797. //Prepare correct transformation
  798. AffineTransform trans = getTransform();
  799. gen.concatMatrix(trans);
  800. gen.writeln(gen.formatDouble(x) + " "
  801. + gen.formatDouble(y) + " moveto ");
  802. gen.writeln("1 -1 scale");
  803. StringBuffer sb = new StringBuffer("(");
  804. escapeText(s, sb);
  805. sb.append(") t ");
  806. gen.writeln(sb.toString());
  807. gen.restoreGraphicsState();
  808. } catch (IOException ioe) {
  809. handleIOException(ioe);
  810. }
  811. }
  812. private void escapeText(final String text, StringBuffer target) {
  813. final int l = text.length();
  814. for (int i = 0; i < l; i++) {
  815. final char ch = text.charAt(i);
  816. final char mch = this.font.mapChar(ch);
  817. PSGenerator.escapeChar(mch, target);
  818. }
  819. }
  820. private Font createFont(java.awt.Font f) {
  821. String fontFamily = f.getFamily();
  822. if (fontFamily.equals("sanserif")) {
  823. fontFamily = "sans-serif";
  824. }
  825. int fontSize = 1000 * f.getSize();
  826. String style = f.isItalic() ? "italic" : "normal";
  827. int weight = f.isBold() ? Font.BOLD : Font.NORMAL;
  828. FontTriplet triplet = fontInfo.findAdjustWeight(fontFamily, style, weight);
  829. if (triplet == null) {
  830. triplet = fontInfo.findAdjustWeight("sans-serif", style, weight);
  831. }
  832. return fontInfo.getFontInstance(triplet, fontSize);
  833. }
  834. private void establishCurrentFont() throws IOException {
  835. if ((currentFontName != this.font.getFontName())
  836. || (currentFontSize != this.font.getFontSize())) {
  837. gen.writeln(this.font.getFontName() + " " + this.font.getFontSize() + " F");
  838. currentFontName = this.font.getFontName();
  839. currentFontSize = this.font.getFontSize();
  840. }
  841. }
  842. /**
  843. * Renders the text of the specified iterator, using the
  844. * <code>Graphics2D</code> context's current <code>Paint</code>. The
  845. * iterator must specify a font
  846. * for each character. The baseline of the
  847. * first character is at position (<i>x</i>,&nbsp;<i>y</i>) in the
  848. * User Space.
  849. * The rendering attributes applied include the <code>Clip</code>,
  850. * <code>Transform</code>, <code>Paint</code>, and
  851. * <code>Composite</code> attributes.
  852. * For characters in script systems such as Hebrew and Arabic,
  853. * the glyphs can be rendered from right to left, in which case the
  854. * coordinate supplied is the location of the leftmost character
  855. * on the baseline.
  856. * @param iterator the iterator whose text is to be rendered
  857. * @param x the x-coordinate where the iterator's text is to be
  858. * rendered
  859. * @param y the y-coordinate where the iterator's text is to be
  860. * rendered
  861. * @see #setPaint
  862. * @see java.awt.Graphics#setColor
  863. * @see #setTransform
  864. * @see #setComposite
  865. * @see #setClip
  866. */
  867. public void drawString(AttributedCharacterIterator iterator, float x,
  868. float y) {
  869. preparePainting();
  870. log.warn("NYI: drawString(AttributedCharacterIterator)");
  871. /*
  872. try {
  873. gen.writeln("BT");
  874. Shape imclip = getClip();
  875. writeClip(imclip);
  876. establishColor(getColor());
  877. AffineTransform trans = getTransform();
  878. trans.translate(x, y);
  879. double[] vals = new double[6];
  880. trans.getMatrix(vals);
  881. for (char ch = iterator.first(); ch != CharacterIterator.DONE;
  882. ch = iterator.next()) {
  883. //Map attr = iterator.getAttributes();
  884. gen.writeln(gen.formatDouble(vals[0]) + " "
  885. + gen.formatDouble(vals[1]) + " "
  886. + gen.formatDouble(vals[2]) + " "
  887. + gen.formatDouble(vals[3]) + " "
  888. + gen.formatDouble(vals[4]) + " "
  889. + gen.formatDouble(vals[5]) + " "
  890. + gen.formatDouble(vals[6]) + " Tm [" + ch
  891. + "]");
  892. }
  893. gen.writeln("ET");
  894. } catch (IOException ioe) {
  895. handleIOException(ioe);
  896. }*/
  897. }
  898. /**
  899. * Fills the interior of a <code>Shape</code> using the settings of the
  900. * <code>Graphics2D</code> context. The rendering attributes applied
  901. * include the <code>Clip</code>, <code>Transform</code>,
  902. * <code>Paint</code>, and <code>Composite</code>.
  903. * @param s the <code>Shape</code> to be filled
  904. * @see #setPaint
  905. * @see java.awt.Graphics#setColor
  906. * @see #transform
  907. * @see #setTransform
  908. * @see #setComposite
  909. * @see #clip
  910. * @see #setClip
  911. */
  912. public void fill(Shape s) {
  913. preparePainting();
  914. try {
  915. gen.saveGraphicsState();
  916. AffineTransform trans = getTransform();
  917. boolean newTransform = gen.getCurrentState().checkTransform(trans)
  918. && !trans.isIdentity();
  919. if (newTransform) {
  920. gen.concatMatrix(trans);
  921. }
  922. Shape imclip = getClip();
  923. writeClip(imclip);
  924. establishColor(getColor());
  925. applyPaint(getPaint(), true);
  926. gen.writeln("newpath");
  927. PathIterator iter = s.getPathIterator(IDENTITY_TRANSFORM);
  928. processPathIterator(iter);
  929. doDrawing(true, false,
  930. iter.getWindingRule() == PathIterator.WIND_EVEN_ODD);
  931. gen.restoreGraphicsState();
  932. } catch (IOException ioe) {
  933. handleIOException(ioe);
  934. }
  935. }
  936. /**
  937. * Commits a painting operation.
  938. * @param fill filling
  939. * @param stroke stroking
  940. * @param nonzero true if the non-zero winding rule should be used when filling
  941. * @exception IOException In case of an I/O problem
  942. */
  943. protected void doDrawing(boolean fill, boolean stroke, boolean nonzero)
  944. throws IOException {
  945. preparePainting();
  946. if (fill) {
  947. if (stroke) {
  948. if (!nonzero) {
  949. gen.writeln("gsave fill grestore stroke");
  950. } else {
  951. gen.writeln("gsave eofill grestore stroke");
  952. }
  953. } else {
  954. if (!nonzero) {
  955. gen.writeln("fill");
  956. } else {
  957. gen.writeln("eofill");
  958. }
  959. }
  960. } else {
  961. // if(stroke)
  962. gen.writeln("stroke");
  963. }
  964. }
  965. /**
  966. * Returns the device configuration associated with this
  967. * <code>Graphics2D</code>.
  968. * @return the device configuration
  969. */
  970. public GraphicsConfiguration getDeviceConfiguration() {
  971. return GraphicsEnvironment.getLocalGraphicsEnvironment().
  972. getDefaultScreenDevice().getDefaultConfiguration();
  973. }
  974. /**
  975. * Used to create proper font metrics
  976. */
  977. private Graphics2D fmg;
  978. {
  979. BufferedImage bi = new BufferedImage(1, 1,
  980. BufferedImage.TYPE_INT_ARGB);
  981. fmg = bi.createGraphics();
  982. }
  983. /**
  984. * Sets the overriding font.
  985. * @param font Font to set
  986. */
  987. public void setOverrideFont(Font font) {
  988. this.overrideFont = font;
  989. }
  990. /**
  991. * Gets the font metrics for the specified font.
  992. * @return the font metrics for the specified font.
  993. * @param f the specified font
  994. * @see java.awt.Graphics#getFont
  995. * @see java.awt.FontMetrics
  996. * @see java.awt.Graphics#getFontMetrics()
  997. */
  998. public java.awt.FontMetrics getFontMetrics(java.awt.Font f) {
  999. return fmg.getFontMetrics(f);
  1000. }
  1001. /**
  1002. * Sets the paint mode of this graphics context to alternate between
  1003. * this graphics context's current color and the new specified color.
  1004. * This specifies that logical pixel operations are performed in the
  1005. * XOR mode, which alternates pixels between the current color and
  1006. * a specified XOR color.
  1007. * <p>
  1008. * When drawing operations are performed, pixels which are the
  1009. * current color are changed to the specified color, and vice versa.
  1010. * <p>
  1011. * Pixels that are of colors other than those two colors are changed
  1012. * in an unpredictable but reversible manner; if the same figure is
  1013. * drawn twice, then all pixels are restored to their original values.
  1014. * @param c1 the XOR alternation color
  1015. */
  1016. public void setXORMode(Color c1) {
  1017. log.warn("NYI: setXORMode");
  1018. }
  1019. /**
  1020. * Copies an area of the component by a distance specified by
  1021. * <code>dx</code> and <code>dy</code>. From the point specified
  1022. * by <code>x</code> and <code>y</code>, this method
  1023. * copies downwards and to the right. To copy an area of the
  1024. * component to the left or upwards, specify a negative value for
  1025. * <code>dx</code> or <code>dy</code>.
  1026. * If a portion of the source rectangle lies outside the bounds
  1027. * of the component, or is obscured by another window or component,
  1028. * <code>copyArea</code> will be unable to copy the associated
  1029. * pixels. The area that is omitted can be refreshed by calling
  1030. * the component's <code>paint</code> method.
  1031. * @param x the <i>x</i> coordinate of the source rectangle.
  1032. * @param y the <i>y</i> coordinate of the source rectangle.
  1033. * @param width the width of the source rectangle.
  1034. * @param height the height of the source rectangle.
  1035. * @param dx the horizontal distance to copy the pixels.
  1036. * @param dy the vertical distance to copy the pixels.
  1037. */
  1038. public void copyArea(int x, int y, int width, int height, int dx,
  1039. int dy) {
  1040. log.warn("NYI: copyArea");
  1041. }
  1042. /* --- for debugging
  1043. public void transform(AffineTransform tx) {
  1044. System.out.println("transform(" + toArray(tx) + ")");
  1045. super.transform(zx);
  1046. }
  1047. public void scale(double sx, double sy) {
  1048. System.out.println("scale(" + sx + ", " + sy + ")");
  1049. super.scale(sx, sy);
  1050. }
  1051. public void translate(double tx, double ty) {
  1052. System.out.println("translate(double " + tx + ", " + ty + ")");
  1053. super.translate(tx, ty);
  1054. }
  1055. public void translate(int tx, int ty) {
  1056. System.out.println("translate(int " + tx + ", " + ty + ")");
  1057. super.translate(tx, ty);
  1058. }
  1059. */
  1060. }