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.

PDFGraphics2D.java 65KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833
  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.svg;
  19. import java.awt.AlphaComposite;
  20. import java.awt.BasicStroke;
  21. import java.awt.Color;
  22. import java.awt.Dimension;
  23. import java.awt.GradientPaint;
  24. import java.awt.Graphics;
  25. import java.awt.Graphics2D;
  26. import java.awt.GraphicsConfiguration;
  27. import java.awt.Image;
  28. import java.awt.Paint;
  29. import java.awt.PaintContext;
  30. import java.awt.Rectangle;
  31. import java.awt.Shape;
  32. import java.awt.Stroke;
  33. import java.awt.color.ColorSpace;
  34. import java.awt.geom.AffineTransform;
  35. import java.awt.geom.PathIterator;
  36. import java.awt.geom.Point2D;
  37. import java.awt.geom.Rectangle2D;
  38. import java.awt.image.BufferedImage;
  39. import java.awt.image.ColorModel;
  40. import java.awt.image.DataBuffer;
  41. import java.awt.image.DirectColorModel;
  42. import java.awt.image.ImageObserver;
  43. import java.awt.image.Raster;
  44. import java.awt.image.RenderedImage;
  45. import java.awt.image.WritableRaster;
  46. import java.awt.image.renderable.RenderableImage;
  47. import java.io.IOException;
  48. import java.io.OutputStream;
  49. import java.io.StringWriter;
  50. import java.util.List;
  51. import java.util.Map;
  52. import org.apache.batik.ext.awt.LinearGradientPaint;
  53. import org.apache.batik.ext.awt.MultipleGradientPaint;
  54. import org.apache.batik.ext.awt.RadialGradientPaint;
  55. import org.apache.batik.ext.awt.RenderingHintsKeyExt;
  56. import org.apache.batik.gvt.GraphicsNode;
  57. import org.apache.batik.gvt.PatternPaint;
  58. import org.apache.xmlgraphics.image.GraphicsConstants;
  59. import org.apache.xmlgraphics.image.loader.ImageInfo;
  60. import org.apache.xmlgraphics.image.loader.ImageSize;
  61. import org.apache.xmlgraphics.image.loader.impl.ImageRawCCITTFax;
  62. import org.apache.xmlgraphics.image.loader.impl.ImageRawJPEG;
  63. import org.apache.xmlgraphics.image.loader.impl.ImageRendered;
  64. import org.apache.xmlgraphics.java2d.AbstractGraphics2D;
  65. import org.apache.xmlgraphics.java2d.GraphicContext;
  66. import org.apache.fop.fonts.Font;
  67. import org.apache.fop.fonts.FontInfo;
  68. import org.apache.fop.fonts.FontSetup;
  69. import org.apache.fop.pdf.BitmapImage;
  70. import org.apache.fop.pdf.PDFAnnotList;
  71. import org.apache.fop.pdf.PDFColor;
  72. import org.apache.fop.pdf.PDFColorHandler;
  73. import org.apache.fop.pdf.PDFConformanceException;
  74. import org.apache.fop.pdf.PDFDeviceColorSpace;
  75. import org.apache.fop.pdf.PDFDocument;
  76. import org.apache.fop.pdf.PDFGState;
  77. import org.apache.fop.pdf.PDFImage;
  78. import org.apache.fop.pdf.PDFImageXObject;
  79. import org.apache.fop.pdf.PDFLink;
  80. import org.apache.fop.pdf.PDFNumber;
  81. import org.apache.fop.pdf.PDFPaintingState;
  82. import org.apache.fop.pdf.PDFPattern;
  83. import org.apache.fop.pdf.PDFResourceContext;
  84. import org.apache.fop.pdf.PDFResources;
  85. import org.apache.fop.pdf.PDFText;
  86. import org.apache.fop.pdf.PDFXObject;
  87. import org.apache.fop.render.pdf.ImageRawCCITTFaxAdapter;
  88. import org.apache.fop.render.pdf.ImageRawJPEGAdapter;
  89. import org.apache.fop.render.pdf.ImageRenderedAdapter;
  90. /**
  91. * PDF Graphics 2D.
  92. * Used for drawing into a pdf document as if it is a graphics object.
  93. * This takes a pdf document and draws into it.
  94. *
  95. * @author <a href="mailto:keiron@aftexsw.com">Keiron Liddle</a>
  96. * @version $Id$
  97. * @see org.apache.batik.ext.awt.g2d.AbstractGraphics2D
  98. */
  99. public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHandler {
  100. private static final AffineTransform IDENTITY_TRANSFORM = new AffineTransform();
  101. /** The number of decimal places. */
  102. private static final int DEC = 8;
  103. /** Convenience constant for full opacity */
  104. static final int OPAQUE = 255;
  105. /**
  106. * the PDF Document being created
  107. */
  108. protected PDFDocument pdfDoc;
  109. /**
  110. * The current resource context for adding fonts, patterns etc.
  111. */
  112. protected PDFResourceContext resourceContext;
  113. /**
  114. * The PDF reference of the current page.
  115. */
  116. protected String pageRef;
  117. /**
  118. * The PDF painting state
  119. */
  120. protected PDFPaintingState paintingState;
  121. /** the PDF color handler */
  122. protected PDFColorHandler colorHandler;
  123. /**
  124. * The PDF graphics state level that this svg is being drawn into.
  125. */
  126. protected int baseLevel = 0;
  127. /**
  128. * The count of natively handled images added to document so they receive
  129. * unique keys.
  130. */
  131. protected int nativeCount = 0;
  132. /**
  133. * The current font information.
  134. */
  135. protected FontInfo fontInfo;
  136. /**
  137. * The override font state used when drawing text and the font cannot be
  138. * set using java fonts.
  139. */
  140. protected Font ovFontState = null;
  141. /**
  142. * the current stream to add PDF commands to
  143. */
  144. protected StringWriter currentStream = new StringWriter();
  145. /**
  146. * the current (internal) font name
  147. */
  148. protected String currentFontName;
  149. /**
  150. * the current font size in millipoints
  151. */
  152. protected float currentFontSize;
  153. /**
  154. * The output stream for the pdf document.
  155. * If this is set then it can progressively output
  156. * the pdf document objects to reduce memory.
  157. * Especially with images.
  158. */
  159. protected OutputStream outputStream = null;
  160. /**
  161. * Create a new PDFGraphics2D with the given pdf document info.
  162. * This is used to create a Graphics object for use inside an already
  163. * existing document.
  164. *
  165. * @param textAsShapes if true then draw text as shapes
  166. * @param fi the current font information
  167. * @param doc the pdf document for creating pdf objects
  168. * @param page the current resource context or page
  169. * @param pref the PDF reference of the current page
  170. * @param font the current font name
  171. * @param size the current font size
  172. */
  173. public PDFGraphics2D(boolean textAsShapes, FontInfo fi, PDFDocument doc,
  174. PDFResourceContext page, String pref, String font, float size) {
  175. this(textAsShapes);
  176. pdfDoc = doc;
  177. this.colorHandler = new PDFColorHandler(doc.getResources());
  178. resourceContext = page;
  179. currentFontName = font;
  180. currentFontSize = size;
  181. fontInfo = fi;
  182. pageRef = pref;
  183. paintingState = new PDFPaintingState();
  184. }
  185. /**
  186. * Create a new PDFGraphics2D.
  187. *
  188. * @param textAsShapes true if drawing text as shapes
  189. */
  190. protected PDFGraphics2D(boolean textAsShapes) {
  191. super(textAsShapes);
  192. }
  193. /**
  194. * This constructor supports the create method.
  195. * This is not implemented properly.
  196. *
  197. * @param g the PDF graphics to make a copy of
  198. */
  199. public PDFGraphics2D(PDFGraphics2D g) {
  200. super(g);
  201. this.pdfDoc = g.pdfDoc;
  202. this.colorHandler = g.colorHandler;
  203. this.resourceContext = g.resourceContext;
  204. this.currentFontName = g.currentFontName;
  205. this.currentFontSize = g.currentFontSize;
  206. this.fontInfo = g.fontInfo;
  207. this.pageRef = g.pageRef;
  208. this.paintingState = g.paintingState;
  209. this.currentStream = g.currentStream;
  210. this.nativeCount = g.nativeCount;
  211. this.outputStream = g.outputStream;
  212. this.ovFontState = g.ovFontState;
  213. }
  214. /**
  215. * Creates a new <code>Graphics</code> object that is
  216. * a copy of this <code>Graphics</code> object.
  217. * @return a new graphics context that is a copy of
  218. * this graphics context.
  219. */
  220. @Override
  221. public Graphics create() {
  222. return new PDFGraphics2D(this);
  223. }
  224. /**
  225. * Central handler for IOExceptions for this class.
  226. * @param ioe IOException to handle
  227. */
  228. protected void handleIOException(IOException ioe) {
  229. //TODO Surely, there's a better way to do this.
  230. ioe.printStackTrace();
  231. }
  232. /**
  233. * This method is used by PDFDocumentGraphics2D to prepare a new page if
  234. * necessary.
  235. */
  236. protected void preparePainting() {
  237. //nop, used by PDFDocumentGraphics2D
  238. }
  239. /**
  240. * Set the PDF state to use when starting to draw
  241. * into the PDF graphics.
  242. *
  243. * @param state the PDF state
  244. */
  245. public void setPaintingState(PDFPaintingState state) {
  246. paintingState = state;
  247. baseLevel = paintingState.getStackLevel();
  248. }
  249. /**
  250. * Set the output stream that this PDF document is
  251. * being drawn to. This is so that it can progressively
  252. * use the PDF document to output data such as images.
  253. * This results in a significant saving on memory.
  254. *
  255. * @param os the output stream that is being used for the PDF document
  256. */
  257. public void setOutputStream(OutputStream os) {
  258. outputStream = os;
  259. }
  260. /**
  261. * Get the string containing all the commands written into this
  262. * Graphics.
  263. * @return the string containing the PDF markup
  264. */
  265. public String getString() {
  266. return currentStream.toString();
  267. }
  268. /**
  269. * Get the string buffer from the currentStream, containing all
  270. * the commands written into this Graphics so far.
  271. * @return the StringBuffer containing the PDF markup
  272. */
  273. public StringBuffer getBuffer() {
  274. return currentStream.getBuffer();
  275. }
  276. /**
  277. * Gets the PDF reference of the current page.
  278. * @return the PDF reference of the current page
  279. */
  280. public String getPageReference() {
  281. return this.pageRef;
  282. }
  283. /**
  284. * Set the Graphics context.
  285. * @param c the graphics context to use
  286. */
  287. public void setGraphicContext(GraphicContext c) {
  288. gc = c;
  289. setPrivateHints();
  290. }
  291. private void setPrivateHints() {
  292. setRenderingHint(RenderingHintsKeyExt.KEY_AVOID_TILE_PAINTING,
  293. RenderingHintsKeyExt.VALUE_AVOID_TILE_PAINTING_ON);
  294. }
  295. /**
  296. * Set the override font state for drawing text.
  297. * This is used by the PDF text painter so that it can temporarily
  298. * set the font state when a java font cannot be used.
  299. * The next drawString will use this font state.
  300. *
  301. * @param infont the font state to use
  302. */
  303. public void setOverrideFontState(Font infont) {
  304. ovFontState = infont;
  305. }
  306. /**
  307. * Restore the PDF graphics state to the starting state level.
  308. */
  309. /* seems not to be used
  310. public void restorePDFState() {
  311. for (int count = graphicsState.getStackLevel(); count > baseLevel; count--) {
  312. currentStream.write("Q\n");
  313. }
  314. graphicsState.restoreLevel(baseLevel);
  315. }*/
  316. private void concatMatrix(double[] matrix) {
  317. currentStream.write(PDFNumber.doubleOut(matrix[0], DEC) + " "
  318. + PDFNumber.doubleOut(matrix[1], DEC) + " "
  319. + PDFNumber.doubleOut(matrix[2], DEC) + " "
  320. + PDFNumber.doubleOut(matrix[3], DEC) + " "
  321. + PDFNumber.doubleOut(matrix[4], DEC) + " "
  322. + PDFNumber.doubleOut(matrix[5], DEC) + " cm\n");
  323. }
  324. private void concatMatrix(AffineTransform transform) {
  325. if (!transform.isIdentity()) {
  326. double[] matrix = new double[6];
  327. transform.getMatrix(matrix);
  328. concatMatrix(matrix);
  329. }
  330. }
  331. /**
  332. * This is mainly used for shading patterns which use the document-global coordinate system
  333. * instead of the local one.
  334. * @return the transformation matrix that established the basic user space for this document
  335. */
  336. protected AffineTransform getBaseTransform() {
  337. AffineTransform at = new AffineTransform(paintingState.getTransform());
  338. return at;
  339. }
  340. /**
  341. * This is a pdf specific method used to add a link to the
  342. * pdf document.
  343. *
  344. * @param bounds the bounds of the link in user coordinates
  345. * @param trans the transform of the current drawing position
  346. * @param dest the PDF destination
  347. * @param linkType the type of link, internal or external
  348. */
  349. public void addLink(Rectangle2D bounds, AffineTransform trans, String dest, int linkType) {
  350. if (!pdfDoc.getProfile().isAnnotationAllowed()) {
  351. return;
  352. }
  353. preparePainting();
  354. AffineTransform at = getTransform();
  355. Shape b = at.createTransformedShape(bounds);
  356. b = trans.createTransformedShape(b);
  357. if (b != null) {
  358. Rectangle rect = b.getBounds();
  359. if (linkType != PDFLink.EXTERNAL) {
  360. String pdfdest = "/FitR " + dest;
  361. resourceContext.addAnnotation(
  362. pdfDoc.getFactory().makeLink(rect, getPageReference(), pdfdest));
  363. } else {
  364. resourceContext.addAnnotation(
  365. pdfDoc.getFactory().makeLink(rect, dest, linkType, 0));
  366. }
  367. }
  368. }
  369. /**
  370. * Add a natively handled image directly to the PDF document.
  371. * This is used by the PDFImageElementBridge to draw a natively handled image
  372. * (like JPEG or CCITT images)
  373. * directly into the PDF document rather than converting the image into
  374. * a bitmap and increasing the size.
  375. *
  376. * @param image the image to draw
  377. * @param x the x position
  378. * @param y the y position
  379. * @param width the width to draw the image
  380. * @param height the height to draw the image
  381. */
  382. public void addNativeImage(org.apache.xmlgraphics.image.loader.Image image, float x, float y,
  383. float width, float height) {
  384. preparePainting();
  385. String key = image.getInfo().getOriginalURI();
  386. if (key == null) {
  387. // Need to include hash code as when invoked from FO you
  388. // may have several 'independent' PDFGraphics2D so the
  389. // count is not enough.
  390. key = "__AddNative_" + hashCode() + "_" + nativeCount;
  391. nativeCount++;
  392. }
  393. PDFImage pdfImage;
  394. if (image instanceof ImageRawJPEG) {
  395. pdfImage = new ImageRawJPEGAdapter((ImageRawJPEG)image, key);
  396. } else if (image instanceof ImageRawCCITTFax) {
  397. pdfImage = new ImageRawCCITTFaxAdapter((ImageRawCCITTFax)image, key);
  398. } else {
  399. throw new IllegalArgumentException(
  400. "Unsupported Image subclass: " + image.getClass().getName());
  401. }
  402. PDFXObject xObject = this.pdfDoc.addImage(resourceContext, pdfImage);
  403. flushPDFDocument();
  404. AffineTransform at = new AffineTransform();
  405. at.translate(x, y);
  406. useXObject(xObject, at, width, height);
  407. }
  408. private void flushPDFDocument() {
  409. if (outputStream != null) {
  410. try {
  411. this.pdfDoc.output(outputStream);
  412. } catch (IOException ioe) {
  413. // ignore exception, will be thrown again later
  414. }
  415. }
  416. }
  417. /**
  418. * Draws as much of the specified image as is currently available.
  419. * The image is drawn with its top-left corner at
  420. * (<i>x</i>,&nbsp;<i>y</i>) in this graphics context's coordinate
  421. * space. Transparent pixels in the image do not affect whatever
  422. * pixels are already there.
  423. * <p>
  424. * This method returns immediately in all cases, even if the
  425. * complete image has not yet been loaded, and it has not been dithered
  426. * and converted for the current output device.
  427. * <p>
  428. * If the image has not yet been completely loaded, then
  429. * <code>drawImage</code> returns <code>false</code>. As more of
  430. * the image becomes available, the process that draws the image notifies
  431. * the specified image observer.
  432. * @param img the specified image to be drawn.
  433. * @param x the <i>x</i> coordinate.
  434. * @param y the <i>y</i> coordinate.
  435. * @param observer object to be notified as more of
  436. * the image is converted.
  437. * @return true if the image was drawn
  438. * @see java.awt.Image
  439. * @see java.awt.image.ImageObserver
  440. * @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int)
  441. */
  442. @Override
  443. public boolean drawImage(Image img, int x, int y,
  444. ImageObserver observer) {
  445. preparePainting();
  446. int width = img.getWidth(observer);
  447. int height = img.getHeight(observer);
  448. if (width == -1 || height == -1) {
  449. return false;
  450. }
  451. return drawImage(img, x, y, width, height, observer);
  452. }
  453. private BufferedImage buildBufferedImage(Dimension size) {
  454. return new BufferedImage(size.width, size.height,
  455. BufferedImage.TYPE_INT_ARGB);
  456. }
  457. /** {@inheritDoc} */
  458. @Override
  459. public boolean drawImage(Image img, int x, int y, int width, int height,
  460. ImageObserver observer) {
  461. preparePainting();
  462. // first we look to see if we've already added this image to
  463. // the pdf document. If so, we just reuse the reference;
  464. // otherwise we have to build a FopImage and add it to the pdf
  465. // document
  466. String key = "TempImage:" + img.toString();
  467. PDFXObject xObject = pdfDoc.getXObject(key);
  468. if (xObject == null) {
  469. // OK, have to build and add a PDF image
  470. Dimension size = new Dimension(width, height);
  471. BufferedImage buf = buildBufferedImage(size);
  472. java.awt.Graphics2D g = buf.createGraphics();
  473. g.setComposite(AlphaComposite.SrcOver);
  474. g.setBackground(new Color(1, 1, 1, 0));
  475. g.setPaint(new Color(1, 1, 1, 0));
  476. g.fillRect(0, 0, width, height);
  477. int imageWidth = buf.getWidth();
  478. int imageHeight = buf.getHeight();
  479. g.clip(new Rectangle(0, 0, imageWidth, imageHeight));
  480. g.setComposite(gc.getComposite());
  481. boolean drawn = g.drawImage(img, 0, 0, imageWidth, imageHeight, observer);
  482. if (!drawn) {
  483. return false;
  484. }
  485. g.dispose();
  486. xObject = addRenderedImage(key, buf);
  487. } else {
  488. resourceContext.getPDFResources().addXObject(xObject);
  489. }
  490. AffineTransform at = new AffineTransform();
  491. at.translate(x, y);
  492. useXObject(xObject, at, width, height);
  493. return true;
  494. }
  495. /**
  496. * Disposes of this graphics context and releases
  497. * any system resources that it is using.
  498. * A <code>Graphics</code> object cannot be used after
  499. * <code>dispose</code>has been called.
  500. * <p>
  501. * When a Java program runs, a large number of <code>Graphics</code>
  502. * objects can be created within a short time frame.
  503. * Although the finalization process of the garbage collector
  504. * also disposes of the same system resources, it is preferable
  505. * to manually free the associated resources by calling this
  506. * method rather than to rely on a finalization process which
  507. * may not run to completion for a long period of time.
  508. * <p>
  509. * Graphics objects which are provided as arguments to the
  510. * <code>paint</code> and <code>update</code> methods
  511. * of components are automatically released by the system when
  512. * those methods return. For efficiency, programmers should
  513. * call <code>dispose</code> when finished using
  514. * a <code>Graphics</code> object only if it was created
  515. * directly from a component or another <code>Graphics</code> object.
  516. * @see java.awt.Graphics#finalize
  517. * @see java.awt.Component#paint
  518. * @see java.awt.Component#update
  519. * @see java.awt.Component#getGraphics
  520. * @see java.awt.Graphics#create
  521. */
  522. @Override
  523. public void dispose() {
  524. pdfDoc = null;
  525. fontInfo = null;
  526. currentStream = null;
  527. currentFontName = null;
  528. }
  529. /**
  530. * Strokes the outline of a <code>Shape</code> using the settings of the
  531. * current <code>Graphics2D</code> context. The rendering attributes
  532. * applied include the <code>Clip</code>, <code>Transform</code>,
  533. * <code>Paint</code>, <code>Composite</code> and
  534. * <code>Stroke</code> attributes.
  535. * @param s the <code>Shape</code> to be rendered
  536. * @see #setStroke
  537. * @see #setPaint
  538. * @see java.awt.Graphics#setColor
  539. * @see #transform
  540. * @see #setTransform
  541. * @see #clip
  542. * @see #setClip
  543. * @see #setComposite
  544. */
  545. @Override
  546. public void draw(Shape s) {
  547. preparePainting();
  548. //Transparency shortcut
  549. Color c;
  550. c = getColor();
  551. if (c.getAlpha() == 0) {
  552. return;
  553. }
  554. AffineTransform trans = getTransform();
  555. double[] tranvals = new double[6];
  556. trans.getMatrix(tranvals);
  557. Shape imclip = getClip();
  558. boolean newClip = paintingState.checkClip(imclip);
  559. boolean newTransform = paintingState.checkTransform(trans)
  560. && !trans.isIdentity();
  561. if (newClip || newTransform) {
  562. saveGraphicsState();
  563. if (newTransform) {
  564. concatMatrix(tranvals);
  565. }
  566. if (newClip) {
  567. writeClip(imclip);
  568. }
  569. }
  570. applyAlpha(OPAQUE, c.getAlpha());
  571. c = getColor();
  572. applyColor(c, false);
  573. c = getBackground();
  574. applyColor(c, true);
  575. Paint paint = getPaint();
  576. if (paintingState.setPaint(paint)) {
  577. if (!applyPaint(paint, false)) {
  578. // Stroke the shape and use it to 'clip'
  579. // the paint contents.
  580. Shape ss = getStroke().createStrokedShape(s);
  581. applyUnknownPaint(paint, ss);
  582. if (newClip || newTransform) {
  583. restoreGraphicsState();
  584. }
  585. return;
  586. }
  587. }
  588. applyStroke(getStroke());
  589. PathIterator iter = s.getPathIterator(IDENTITY_TRANSFORM);
  590. processPathIterator(iter);
  591. doDrawing(false, true, false);
  592. if (newClip || newTransform) {
  593. restoreGraphicsState();
  594. }
  595. }
  596. /*
  597. // in theory we could set the clip using these methods
  598. // it doesn't seem to improve the file sizes much
  599. // and makes everything more complicated
  600. Shape lastClip = null;
  601. public void clip(Shape cl) {
  602. super.clip(cl);
  603. Shape newClip = getClip();
  604. if (newClip == null || lastClip == null
  605. || !(new Area(newClip).equals(new Area(lastClip)))) {
  606. graphicsState.setClip(newClip);
  607. writeClip(newClip);
  608. }
  609. lastClip = newClip;
  610. }
  611. public void setClip(Shape cl) {
  612. super.setClip(cl);
  613. Shape newClip = getClip();
  614. if (newClip == null || lastClip == null
  615. || !(new Area(newClip).equals(new Area(lastClip)))) {
  616. for (int count = graphicsState.getStackLevel(); count > baseLevel; count--) {
  617. currentStream.write("Q\n");
  618. }
  619. graphicsState.restoreLevel(baseLevel);
  620. currentStream.write("q\n");
  621. graphicsState.push();
  622. if (newClip != null) {
  623. graphicsState.setClip(newClip);
  624. }
  625. writeClip(newClip);
  626. }
  627. lastClip = newClip;
  628. }
  629. */
  630. /**
  631. * Set the clipping shape for future PDF drawing in the current graphics state.
  632. * This sets creates and writes a clipping shape that will apply
  633. * to future drawings in the current graphics state.
  634. *
  635. * @param s the clipping shape
  636. */
  637. protected void writeClip(Shape s) {
  638. if (s == null) {
  639. return;
  640. }
  641. PathIterator iter = s.getPathIterator(IDENTITY_TRANSFORM);
  642. if (iter.isDone()) {
  643. // no segments available. Not worth doing anything
  644. return;
  645. }
  646. preparePainting();
  647. processPathIterator(iter);
  648. // clip area
  649. currentStream.write("W\n");
  650. currentStream.write("n\n");
  651. }
  652. /**
  653. * Apply the java Color to PDF.
  654. * This converts the java colour to a PDF colour and
  655. * sets it for the next drawing.
  656. *
  657. * @param col the java colour
  658. * @param fill true if the colour will be used for filling
  659. */
  660. protected void applyColor(Color col, boolean fill) {
  661. preparePainting();
  662. //TODO Handle this in PDFColorHandler by automatically converting the color.
  663. //This won't work properly anyway after the redesign of ColorExt
  664. if (col.getColorSpace().getType() == ColorSpace.TYPE_CMYK) {
  665. if (pdfDoc.getProfile().getPDFAMode().isPDFA1LevelB()) {
  666. //See PDF/A-1, ISO 19005:1:2005(E), 6.2.3.3
  667. //FOP is currently restricted to DeviceRGB if PDF/A-1 is active.
  668. throw new PDFConformanceException(
  669. "PDF/A-1 does not allow mixing DeviceRGB and DeviceCMYK.");
  670. }
  671. }
  672. boolean doWrite = false;
  673. if (fill) {
  674. if (paintingState.setBackColor(col)) {
  675. doWrite = true;
  676. }
  677. } else {
  678. if (paintingState.setColor(col)) {
  679. doWrite = true;
  680. }
  681. }
  682. if (doWrite) {
  683. StringBuffer sb = new StringBuffer();
  684. colorHandler.establishColor(sb, col, fill);
  685. currentStream.write(sb.toString());
  686. }
  687. }
  688. /**
  689. * Apply the java paint to the PDF.
  690. * This takes the java paint sets up the appropraite PDF commands
  691. * for the drawing with that paint.
  692. * Currently this supports the gradients and patterns from batik.
  693. *
  694. * @param paint the paint to convert to PDF
  695. * @param fill true if the paint should be set for filling
  696. * @return true if the paint is handled natively, false if the paint should be rasterized
  697. */
  698. protected boolean applyPaint(Paint paint, boolean fill) { // CSOK: MethodLength
  699. preparePainting();
  700. if (paint instanceof Color) {
  701. return true;
  702. }
  703. // convert java.awt.GradientPaint to LinearGradientPaint to avoid rasterization
  704. if (paint instanceof GradientPaint) {
  705. GradientPaint gpaint = (GradientPaint) paint;
  706. paint = new LinearGradientPaint(
  707. (float) gpaint.getPoint1().getX(),
  708. (float) gpaint.getPoint1().getY(),
  709. (float) gpaint.getPoint2().getX(),
  710. (float) gpaint.getPoint2().getY(),
  711. new float[] {0, 1},
  712. new Color[] {gpaint.getColor1(), gpaint.getColor2()},
  713. gpaint.isCyclic() ? LinearGradientPaint.REPEAT : LinearGradientPaint.NO_CYCLE);
  714. }
  715. if (paint instanceof LinearGradientPaint) {
  716. LinearGradientPaint gp = (LinearGradientPaint)paint;
  717. // This code currently doesn't support 'repeat'.
  718. // For linear gradients it is possible to construct
  719. // a 'tile' that is repeated with a PDF pattern, but
  720. // it would be very tricky as you would have to rotate
  721. // the coordinate system so the repeat was axially
  722. // aligned. At this point I'm just going to rasterize it.
  723. MultipleGradientPaint.CycleMethodEnum cycle = gp.getCycleMethod();
  724. if (cycle != MultipleGradientPaint.NO_CYCLE) {
  725. return false;
  726. }
  727. Color[] cols = gp.getColors();
  728. float[] fractions = gp.getFractions();
  729. // Build proper transform from gradient space to page space
  730. // ('Patterns' don't get userspace transform).
  731. AffineTransform transform;
  732. transform = new AffineTransform(getBaseTransform());
  733. transform.concatenate(getTransform());
  734. transform.concatenate(gp.getTransform());
  735. List<Double> theMatrix = new java.util.ArrayList<Double>();
  736. double [] mat = new double[6];
  737. transform.getMatrix(mat);
  738. for (int idx = 0; idx < mat.length; idx++) {
  739. theMatrix.add(new Double(mat[idx]));
  740. }
  741. Point2D p1 = gp.getStartPoint();
  742. Point2D p2 = gp.getEndPoint();
  743. List<Double> theCoords = new java.util.ArrayList<Double>();
  744. theCoords.add(new Double(p1.getX()));
  745. theCoords.add(new Double(p1.getY()));
  746. theCoords.add(new Double(p2.getX()));
  747. theCoords.add(new Double(p2.getY()));
  748. List<Boolean> theExtend = new java.util.ArrayList<Boolean>();
  749. theExtend.add(Boolean.TRUE);
  750. theExtend.add(Boolean.TRUE);
  751. List<Double> theDomain = new java.util.ArrayList<Double>();
  752. theDomain.add(new Double(0));
  753. theDomain.add(new Double(1));
  754. List<Double> theEncode = new java.util.ArrayList<Double>();
  755. theEncode.add(new Double(0));
  756. theEncode.add(new Double(1));
  757. theEncode.add(new Double(0));
  758. theEncode.add(new Double(1));
  759. List<Double> theBounds = new java.util.ArrayList<Double>();
  760. List<Color> someColors = new java.util.ArrayList<Color>();
  761. for (int count = 0; count < cols.length; count++) {
  762. Color c1 = cols[count];
  763. if (c1.getAlpha() != 255) {
  764. return false; // PDF can't do alpha
  765. }
  766. //PDFColor color1 = new PDFColor(c1.getRed(), c1.getGreen(),
  767. // c1.getBlue());
  768. someColors.add(c1);
  769. if (count > 0 && count < cols.length - 1) {
  770. theBounds.add(new Double(fractions[count]));
  771. }
  772. }
  773. //Gradients are currently restricted to sRGB
  774. PDFDeviceColorSpace aColorSpace;
  775. aColorSpace = new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_RGB);
  776. PDFPattern myPat = pdfDoc.getFactory().makeGradient(
  777. resourceContext, false, aColorSpace,
  778. someColors, theBounds, theCoords, theMatrix);
  779. currentStream.write(myPat.getColorSpaceOut(fill));
  780. return true;
  781. }
  782. if (paint instanceof RadialGradientPaint) {
  783. RadialGradientPaint rgp = (RadialGradientPaint)paint;
  784. // There is essentially no way to support repeats
  785. // in PDF for radial gradients (the one option would
  786. // be to 'grow' the outer circle until it fully covered
  787. // the bounds and then grow the stops accordingly, the
  788. // problem is that this may require an extremely large
  789. // number of stops for cases where the focus is near
  790. // the edge of the outer circle). so we rasterize.
  791. MultipleGradientPaint.CycleMethodEnum cycle = rgp.getCycleMethod();
  792. if (cycle != MultipleGradientPaint.NO_CYCLE) {
  793. return false;
  794. }
  795. AffineTransform transform;
  796. transform = new AffineTransform(getBaseTransform());
  797. transform.concatenate(getTransform());
  798. transform.concatenate(rgp.getTransform());
  799. List<Double> theMatrix = new java.util.ArrayList<Double>();
  800. double [] mat = new double[6];
  801. transform.getMatrix(mat);
  802. for (int idx = 0; idx < mat.length; idx++) {
  803. theMatrix.add(new Double(mat[idx]));
  804. }
  805. double ar = rgp.getRadius();
  806. Point2D ac = rgp.getCenterPoint();
  807. Point2D af = rgp.getFocusPoint();
  808. List<Double> theCoords = new java.util.ArrayList<Double>();
  809. double dx = af.getX() - ac.getX();
  810. double dy = af.getY() - ac.getY();
  811. double d = Math.sqrt(dx * dx + dy * dy);
  812. if (d > ar) {
  813. // the center point af must be within the circle with
  814. // radius ar centered at ac so limit it to that.
  815. double scale = (ar * .9999) / d;
  816. dx = dx * scale;
  817. dy = dy * scale;
  818. }
  819. theCoords.add(new Double(ac.getX() + dx)); // Fx
  820. theCoords.add(new Double(ac.getY() + dy)); // Fy
  821. theCoords.add(new Double(0));
  822. theCoords.add(new Double(ac.getX()));
  823. theCoords.add(new Double(ac.getY()));
  824. theCoords.add(new Double(ar));
  825. Color[] cols = rgp.getColors();
  826. List<Color> someColors = new java.util.ArrayList<Color>();
  827. for (int count = 0; count < cols.length; count++) {
  828. Color cc = cols[count];
  829. if (cc.getAlpha() != 255) {
  830. return false; // PDF can't do alpha
  831. }
  832. someColors.add(cc);
  833. }
  834. float[] fractions = rgp.getFractions();
  835. List<Double> theBounds = new java.util.ArrayList<Double>();
  836. for (int count = 1; count < fractions.length - 1; count++) {
  837. float offset = fractions[count];
  838. theBounds.add(new Double(offset));
  839. }
  840. PDFDeviceColorSpace colSpace;
  841. colSpace = new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_RGB);
  842. PDFPattern myPat = pdfDoc.getFactory().makeGradient
  843. (resourceContext, true, colSpace,
  844. someColors, theBounds, theCoords, theMatrix);
  845. currentStream.write(myPat.getColorSpaceOut(fill));
  846. return true;
  847. }
  848. if (paint instanceof PatternPaint) {
  849. PatternPaint pp = (PatternPaint)paint;
  850. return createPattern(pp, fill);
  851. }
  852. return false; // unknown paint
  853. }
  854. private boolean createPattern(PatternPaint pp, boolean fill) {
  855. preparePainting();
  856. FontInfo specialFontInfo = new FontInfo();
  857. FontSetup.setup(specialFontInfo);
  858. PDFResources res = pdfDoc.getFactory().makeResources();
  859. PDFResourceContext context = new PDFResourceContext(res);
  860. PDFGraphics2D pattGraphic = new PDFGraphics2D(textAsShapes, specialFontInfo,
  861. pdfDoc, context, getPageReference(),
  862. "", 0);
  863. pattGraphic.setGraphicContext(new GraphicContext());
  864. pattGraphic.gc.validateTransformStack();
  865. pattGraphic.setRenderingHints(this.getRenderingHints());
  866. pattGraphic.setOutputStream(outputStream);
  867. GraphicsNode gn = pp.getGraphicsNode();
  868. //Rectangle2D gnBBox = gn.getBounds();
  869. Rectangle2D rect = pp.getPatternRect();
  870. // if (!pp.getOverflow()) {
  871. gn.paint(pattGraphic);
  872. // } else {
  873. // /* Commented out until SVN version of Batik is included */
  874. // // For overflow we need to paint the content from
  875. // // all the tiles who's overflow will intersect one
  876. // // tile (left->right, top->bottom). Then we can
  877. // // simply replicate that tile as normal.
  878. // double gnMinX = gnBBox.getX();
  879. // double gnMaxX = gnBBox.getX() + gnBBox.getWidth();
  880. // double gnMinY = gnBBox.getY();
  881. // double gnMaxY = gnBBox.getY() + gnBBox.getHeight();
  882. // double patMaxX = rect.getX() + rect.getWidth();
  883. // double patMaxY = rect.getY() + rect.getHeight();
  884. // double stepX = rect.getWidth();
  885. // double stepY = rect.getHeight();
  886. //
  887. // int startX = (int)((rect.getX() - gnMaxX)/stepX);
  888. // int startY = (int)((rect.getY() - gnMaxY)/stepY);
  889. //
  890. // int endX = (int)((patMaxX - gnMinX)/stepX);
  891. // int endY = (int)((patMaxY - gnMinY)/stepY);
  892. //
  893. // pattGraphic.translate(startX*stepX, startY*stepY);
  894. // for (int yIdx=startY; yIdx<=endY; yIdx++) {
  895. // for (int xIdx=startX; xIdx<=endX; xIdx++) {
  896. // gn.paint(pattGraphic);
  897. // pattGraphic.translate(stepX,0);
  898. // }
  899. // pattGraphic.translate(-(endX-startX+1)*stepX, stepY);
  900. // }
  901. // }
  902. List<Double> bbox = new java.util.ArrayList<Double>();
  903. bbox.add(new Double(rect.getX()));
  904. bbox.add(new Double(rect.getHeight() + rect.getY()));
  905. bbox.add(new Double(rect.getWidth() + rect.getX()));
  906. bbox.add(new Double(rect.getY()));
  907. AffineTransform transform;
  908. transform = new AffineTransform(getBaseTransform());
  909. transform.concatenate(getTransform());
  910. transform.concatenate(pp.getPatternTransform());
  911. List<Double> theMatrix = new java.util.ArrayList<Double>();
  912. double [] mat = new double[6];
  913. transform.getMatrix(mat);
  914. for (int idx = 0; idx < mat.length; idx++) {
  915. theMatrix.add(new Double(mat[idx]));
  916. }
  917. /** @todo see if pdfDoc and res can be linked here,
  918. (currently res <> PDFDocument's resources) so addFonts()
  919. can be moved to PDFDocument class */
  920. res.addFonts(pdfDoc, specialFontInfo);
  921. PDFPattern myPat = pdfDoc.getFactory().makePattern(
  922. resourceContext, 1, res, 1, 1, bbox,
  923. rect.getWidth(), rect.getHeight(),
  924. theMatrix, null,
  925. pattGraphic.getBuffer());
  926. currentStream.write(myPat.getColorSpaceOut(fill));
  927. PDFAnnotList annots = context.getAnnotations();
  928. if (annots != null) {
  929. this.pdfDoc.addObject(annots);
  930. }
  931. flushPDFDocument();
  932. return true;
  933. }
  934. /**
  935. * @param paint some paint
  936. * @param shape a shape
  937. * @return true (always)
  938. */
  939. protected boolean applyUnknownPaint(Paint paint, Shape shape) {
  940. preparePainting();
  941. Shape clip = getClip();
  942. Rectangle2D usrClipBounds, usrBounds;
  943. usrBounds = shape.getBounds2D();
  944. if (clip != null) {
  945. usrClipBounds = clip.getBounds2D();
  946. if (!usrClipBounds.intersects(usrBounds)) {
  947. return true;
  948. }
  949. Rectangle2D.intersect(usrBounds, usrClipBounds, usrBounds);
  950. }
  951. double usrX = usrBounds.getX();
  952. double usrY = usrBounds.getY();
  953. double usrW = usrBounds.getWidth();
  954. double usrH = usrBounds.getHeight();
  955. Rectangle devShapeBounds, devClipBounds, devBounds;
  956. AffineTransform at = getTransform();
  957. devShapeBounds = at.createTransformedShape(shape).getBounds();
  958. if (clip != null) {
  959. devClipBounds = at.createTransformedShape(clip).getBounds();
  960. if (!devClipBounds.intersects(devShapeBounds)) {
  961. return true;
  962. }
  963. devBounds = devShapeBounds.intersection(devClipBounds);
  964. } else {
  965. devBounds = devShapeBounds;
  966. }
  967. int devX = devBounds.x;
  968. int devY = devBounds.y;
  969. int devW = devBounds.width;
  970. int devH = devBounds.height;
  971. ColorSpace rgbCS = ColorSpace.getInstance(ColorSpace.CS_sRGB);
  972. ColorModel rgbCM = new DirectColorModel
  973. (rgbCS, 32, 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000,
  974. false, DataBuffer.TYPE_BYTE);
  975. PaintContext pctx = paint.createContext(rgbCM, devBounds, usrBounds,
  976. at, getRenderingHints());
  977. PDFXObject imageInfo = pdfDoc.getXObject
  978. ("TempImage:" + pctx.toString());
  979. if (imageInfo != null) {
  980. resourceContext.getPDFResources().addXObject(imageInfo);
  981. } else {
  982. Raster r = pctx.getRaster(devX, devY, devW, devH);
  983. WritableRaster wr = (WritableRaster)r;
  984. wr = wr.createWritableTranslatedChild(0, 0);
  985. ColorModel pcm = pctx.getColorModel();
  986. BufferedImage bi = new BufferedImage
  987. (pcm, wr, pcm.isAlphaPremultiplied(), null);
  988. final byte[] rgb = new byte[devW * devH * 3];
  989. final int[] line = new int[devW];
  990. final byte[] mask;
  991. int x, y, val, rgbIdx = 0;
  992. if (pcm.hasAlpha()) {
  993. mask = new byte[devW * devH];
  994. int maskIdx = 0;
  995. for (y = 0; y < devH; y++) {
  996. bi.getRGB(0, y, devW, 1, line, 0, devW);
  997. for (x = 0; x < devW; x++) {
  998. val = line[x];
  999. mask[maskIdx++] = (byte)(val >>> 24);
  1000. rgb[rgbIdx++] = (byte)((val >> 16) & 0x0FF);
  1001. rgb[rgbIdx++] = (byte)((val >> 8 ) & 0x0FF);
  1002. rgb[rgbIdx++] = (byte)((val ) & 0x0FF);
  1003. }
  1004. }
  1005. } else {
  1006. mask = null;
  1007. for (y = 0; y < devH; y++) {
  1008. bi.getRGB(0, y, devW, 1, line, 0, devW);
  1009. for (x = 0; x < devW; x++) {
  1010. val = line[x];
  1011. rgb[rgbIdx++] = (byte)((val >> 16) & 0x0FF);
  1012. rgb[rgbIdx++] = (byte)((val >> 8 ) & 0x0FF);
  1013. rgb[rgbIdx++] = (byte)((val ) & 0x0FF);
  1014. }
  1015. }
  1016. }
  1017. String maskRef = null;
  1018. if (mask != null) {
  1019. BitmapImage fopimg = new BitmapImage
  1020. ("TempImageMask:" + pctx.toString(), devW, devH, mask, null);
  1021. fopimg.setColorSpace(new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_GRAY));
  1022. PDFImageXObject xobj = pdfDoc.addImage(resourceContext, fopimg);
  1023. maskRef = xobj.referencePDF();
  1024. flushPDFDocument();
  1025. }
  1026. BitmapImage fopimg;
  1027. fopimg = new BitmapImage("TempImage:" + pctx.toString(),
  1028. devW, devH, rgb, maskRef);
  1029. fopimg.setTransparent(new PDFColor(255, 255, 255));
  1030. imageInfo = pdfDoc.addImage(resourceContext, fopimg);
  1031. flushPDFDocument();
  1032. }
  1033. currentStream.write("q\n");
  1034. writeClip(shape);
  1035. currentStream.write("" + usrW + " 0 0 " + (-usrH) + " " + usrX
  1036. + " " + (usrY + usrH) + " cm\n"
  1037. + imageInfo.getName() + " Do\nQ\n");
  1038. return true;
  1039. }
  1040. /**
  1041. * Apply the stroke to the PDF.
  1042. * This takes the java stroke and outputs the appropriate settings
  1043. * to the PDF so that the stroke attributes are handled.
  1044. *
  1045. * @param stroke the java stroke
  1046. */
  1047. protected void applyStroke(Stroke stroke) {
  1048. preparePainting();
  1049. if (stroke instanceof BasicStroke) {
  1050. BasicStroke bs = (BasicStroke)stroke;
  1051. float[] da = bs.getDashArray();
  1052. if (da != null) {
  1053. currentStream.write("[");
  1054. for (int count = 0; count < da.length; count++) {
  1055. currentStream.write(PDFNumber.doubleOut(da[count]));
  1056. if (count < da.length - 1) {
  1057. currentStream.write(" ");
  1058. }
  1059. }
  1060. currentStream.write("] ");
  1061. float offset = bs.getDashPhase();
  1062. currentStream.write(PDFNumber.doubleOut(offset) + " d\n");
  1063. }
  1064. int ec = bs.getEndCap();
  1065. switch (ec) {
  1066. case BasicStroke.CAP_BUTT:
  1067. currentStream.write(0 + " J\n");
  1068. break;
  1069. case BasicStroke.CAP_ROUND:
  1070. currentStream.write(1 + " J\n");
  1071. break;
  1072. case BasicStroke.CAP_SQUARE:
  1073. currentStream.write(2 + " J\n");
  1074. break;
  1075. default:
  1076. break;
  1077. }
  1078. int lj = bs.getLineJoin();
  1079. switch (lj) {
  1080. case BasicStroke.JOIN_MITER:
  1081. currentStream.write(0 + " j\n");
  1082. break;
  1083. case BasicStroke.JOIN_ROUND:
  1084. currentStream.write(1 + " j\n");
  1085. break;
  1086. case BasicStroke.JOIN_BEVEL:
  1087. currentStream.write(2 + " j\n");
  1088. break;
  1089. default:
  1090. break;
  1091. }
  1092. float lw = bs.getLineWidth();
  1093. currentStream.write(PDFNumber.doubleOut(lw) + " w\n");
  1094. float ml = bs.getMiterLimit();
  1095. currentStream.write(PDFNumber.doubleOut(ml) + " M\n");
  1096. }
  1097. }
  1098. /** {@inheritDoc} */
  1099. @Override
  1100. public void drawRenderedImage(RenderedImage img, AffineTransform xform) {
  1101. String key = "TempImage:" + img.toString();
  1102. drawInnerRenderedImage(key, img, xform);
  1103. }
  1104. /**
  1105. * @param key a key
  1106. * @param img an image
  1107. * @param xform a transform
  1108. */
  1109. public void drawInnerRenderedImage(String key, RenderedImage img, AffineTransform xform) {
  1110. preparePainting();
  1111. PDFXObject xObject = pdfDoc.getXObject(key);
  1112. if (xObject == null) {
  1113. xObject = addRenderedImage(key, img);
  1114. } else {
  1115. resourceContext.getPDFResources().addXObject(xObject);
  1116. }
  1117. useXObject(xObject, xform, img.getWidth(), img.getHeight());
  1118. }
  1119. private void useXObject(PDFXObject xObject, AffineTransform xform, float width, float height) {
  1120. // now do any transformation required and add the actual image
  1121. // placement instance
  1122. currentStream.write("q\n");
  1123. concatMatrix(getTransform());
  1124. Shape imclip = getClip();
  1125. writeClip(imclip);
  1126. concatMatrix(xform);
  1127. String w = PDFNumber.doubleOut(width, DEC);
  1128. String h = PDFNumber.doubleOut(height, DEC);
  1129. currentStream.write("" + w + " 0 0 -" + h + " 0 " + h + " cm\n"
  1130. + xObject.getName() + " Do\nQ\n");
  1131. }
  1132. private PDFXObject addRenderedImage(String key, RenderedImage img) {
  1133. ImageInfo info = new ImageInfo(null, "image/unknown");
  1134. ImageSize size = new ImageSize(img.getWidth(), img.getHeight(),
  1135. GraphicsConstants.DEFAULT_DPI);
  1136. info.setSize(size);
  1137. ImageRendered imgRend = new ImageRendered(info, img, null);
  1138. ImageRenderedAdapter adapter = new ImageRenderedAdapter(imgRend, key);
  1139. PDFXObject xObject = pdfDoc.addImage(resourceContext, adapter);
  1140. flushPDFDocument();
  1141. return xObject;
  1142. }
  1143. /** {@inheritDoc} */
  1144. @Override
  1145. public void drawRenderableImage(RenderableImage img,
  1146. AffineTransform xform) {
  1147. //TODO Check if this is good enough
  1148. drawRenderedImage(img.createDefaultRendering(), xform);
  1149. }
  1150. /**
  1151. * Renders the text specified by the specified <code>String</code>,
  1152. * using the current <code>Font</code> and <code>Paint</code> attributes
  1153. * in the <code>Graphics2D</code> context.
  1154. * The baseline of the first character is at position
  1155. * (<i>x</i>,&nbsp;<i>y</i>) in the User Space.
  1156. * The rendering attributes applied include the <code>Clip</code>,
  1157. * <code>Transform</code>, <code>Paint</code>, <code>Font</code> and
  1158. * <code>Composite</code> attributes. For characters in script systems
  1159. * such as Hebrew and Arabic, the glyphs can be rendered from right to
  1160. * left, in which case the coordinate supplied is the location of the
  1161. * leftmost character on the baseline.
  1162. * @param s the <code>String</code> to be rendered
  1163. * @param x the coordinate where the <code>String</code>
  1164. * should be rendered
  1165. * @param y the coordinate where the <code>String</code>
  1166. * should be rendered
  1167. * @see #setPaint
  1168. * @see java.awt.Graphics#setColor
  1169. * @see java.awt.Graphics#setFont
  1170. * @see #setTransform
  1171. * @see #setComposite
  1172. * @see #setClip
  1173. */
  1174. @Override
  1175. public void drawString(String s, float x, float y) {
  1176. preparePainting();
  1177. Font fontState;
  1178. AffineTransform fontTransform = null;
  1179. if (ovFontState == null) {
  1180. java.awt.Font gFont = getFont();
  1181. fontTransform = gFont.getTransform();
  1182. fontState = fontInfo.getFontInstanceForAWTFont(gFont);
  1183. } else {
  1184. fontState = fontInfo.getFontInstance(
  1185. ovFontState.getFontTriplet(), ovFontState.getFontSize());
  1186. ovFontState = null;
  1187. }
  1188. updateCurrentFont(fontState);
  1189. saveGraphicsState();
  1190. Color c = getColor();
  1191. applyColor(c, true);
  1192. applyPaint(getPaint(), true);
  1193. applyAlpha(c.getAlpha(), OPAQUE);
  1194. Map<Integer, Map<Integer, Integer>> kerning = fontState.getKerning();
  1195. boolean kerningAvailable = (kerning != null && !kerning.isEmpty());
  1196. boolean useMultiByte = isMultiByteFont(currentFontName);
  1197. // String startText = useMultiByte ? "<FEFF" : "(";
  1198. String startText = useMultiByte ? "<" : "(";
  1199. String endText = useMultiByte ? "> " : ") ";
  1200. AffineTransform trans = getTransform();
  1201. //trans.translate(x, y);
  1202. double[] vals = new double[6];
  1203. trans.getMatrix(vals);
  1204. concatMatrix(vals);
  1205. Shape imclip = getClip();
  1206. writeClip(imclip);
  1207. currentStream.write("BT\n");
  1208. AffineTransform localTransform = new AffineTransform();
  1209. localTransform.translate(x, y);
  1210. if (fontTransform != null) {
  1211. localTransform.concatenate(fontTransform);
  1212. }
  1213. localTransform.scale(1, -1);
  1214. double[] lt = new double[6];
  1215. localTransform.getMatrix(lt);
  1216. currentStream.write(PDFNumber.doubleOut(lt[0]) + " "
  1217. + PDFNumber.doubleOut(lt[1]) + " " + PDFNumber.doubleOut(lt[2]) + " "
  1218. + PDFNumber.doubleOut(lt[3]) + " " + PDFNumber.doubleOut(lt[4]) + " "
  1219. + PDFNumber.doubleOut(lt[5]) + " Tm [" + startText);
  1220. int l = s.length();
  1221. for (int i = 0; i < l; i++) {
  1222. char ch = fontState.mapChar(s.charAt(i));
  1223. if (!useMultiByte) {
  1224. if (ch > 127) {
  1225. currentStream.write("\\");
  1226. currentStream.write(Integer.toOctalString(ch));
  1227. } else {
  1228. switch (ch) {
  1229. case '(':
  1230. case ')':
  1231. case '\\':
  1232. currentStream.write("\\");
  1233. break;
  1234. default:
  1235. }
  1236. currentStream.write(ch);
  1237. }
  1238. } else {
  1239. currentStream.write(PDFText.toUnicodeHex(ch));
  1240. }
  1241. if (kerningAvailable && (i + 1) < l) {
  1242. addKerning(currentStream, (Integer.valueOf(ch)),
  1243. (Integer.valueOf(fontState.mapChar(s.charAt(i + 1)))),
  1244. kerning, startText, endText);
  1245. }
  1246. }
  1247. currentStream.write(endText);
  1248. currentStream.write("] TJ\n");
  1249. currentStream.write("ET\n");
  1250. restoreGraphicsState();
  1251. }
  1252. /**
  1253. * Applies the given alpha values for filling and stroking.
  1254. * @param fillAlpha A value between 0 and 255 (=OPAQUE) for filling
  1255. * @param strokeAlpha A value between 0 and 255 (=OPAQUE) for stroking
  1256. */
  1257. protected void applyAlpha(int fillAlpha, int strokeAlpha) {
  1258. if (fillAlpha != OPAQUE || strokeAlpha != OPAQUE) {
  1259. checkTransparencyAllowed();
  1260. Map<String, Float> vals = new java.util.HashMap<String, Float>();
  1261. if (fillAlpha != OPAQUE) {
  1262. vals.put(PDFGState.GSTATE_ALPHA_NONSTROKE, new Float(fillAlpha / 255f));
  1263. }
  1264. if (strokeAlpha != OPAQUE) {
  1265. vals.put(PDFGState.GSTATE_ALPHA_STROKE, new Float(strokeAlpha / 255f));
  1266. }
  1267. PDFGState gstate = pdfDoc.getFactory().makeGState(
  1268. vals, paintingState.getGState());
  1269. resourceContext.addGState(gstate);
  1270. currentStream.write("/" + gstate.getName() + " gs\n");
  1271. }
  1272. }
  1273. /**
  1274. * Updates the currently selected font.
  1275. * @param font the new font to use
  1276. */
  1277. protected void updateCurrentFont(Font font) {
  1278. String name = font.getFontName();
  1279. float size = font.getFontSize() / 1000f;
  1280. //Only update if necessary
  1281. if ((!name.equals(this.currentFontName))
  1282. || (size != this.currentFontSize)) {
  1283. this.currentFontName = name;
  1284. this.currentFontSize = size;
  1285. currentStream.write("/" + name + " " + size + " Tf\n");
  1286. }
  1287. }
  1288. /**
  1289. * Returns a suitable internal font given an AWT Font instance.
  1290. * @param awtFont the AWT font
  1291. * @return the internal Font
  1292. * @deprecated use FontInfo.getFontInstanceForAWTFont(java.awt.Font awtFont) instead
  1293. */
  1294. @Deprecated
  1295. protected Font getInternalFontForAWTFont(java.awt.Font awtFont) {
  1296. return fontInfo.getFontInstanceForAWTFont(awtFont);
  1297. }
  1298. /**
  1299. * Determines whether the font with the given name is a multi-byte font.
  1300. * @param name the name of the font
  1301. * @return true if it's a multi-byte font
  1302. */
  1303. protected boolean isMultiByteFont(String name) {
  1304. // This assumes that *all* CIDFonts use a /ToUnicode mapping
  1305. org.apache.fop.fonts.Typeface f
  1306. = fontInfo.getFonts().get(name);
  1307. return f.isMultiByte();
  1308. }
  1309. private void addKerning(StringWriter buf, Integer ch1, Integer ch2,
  1310. Map<Integer, Map<Integer, Integer>> kerning, String startText,
  1311. String endText) {
  1312. preparePainting();
  1313. Map<Integer, Integer> kernPair = kerning.get(ch1);
  1314. if (kernPair != null) {
  1315. Integer width = kernPair.get(ch2);
  1316. if (width != null) {
  1317. currentStream.write(endText + (-width.intValue()) + " " + startText);
  1318. }
  1319. }
  1320. }
  1321. /**
  1322. * Renders the text of the specified iterator, using the
  1323. * <code>Graphics2D</code> context's current <code>Paint</code>. The
  1324. * iterator must specify a font
  1325. * for each character. The baseline of the
  1326. * first character is at position (<i>x</i>,&nbsp;<i>y</i>) in the
  1327. * User Space.
  1328. * The rendering attributes applied include the <code>Clip</code>,
  1329. * <code>Transform</code>, <code>Paint</code>, and
  1330. * <code>Composite</code> attributes.
  1331. * For characters in script systems such as Hebrew and Arabic,
  1332. * the glyphs can be rendered from right to left, in which case the
  1333. * coordinate supplied is the location of the leftmost character
  1334. * on the baseline.
  1335. * @param iterator the iterator whose text is to be rendered
  1336. * @param x the coordinate where the iterator's text is to be
  1337. * rendered
  1338. * @param y the coordinate where the iterator's text is to be
  1339. * rendered
  1340. * @see #setPaint
  1341. * @see java.awt.Graphics#setColor
  1342. * @see #setTransform
  1343. * @see #setComposite
  1344. * @see #setClip
  1345. *//* TODO Reimplement for higher efficiency similar to the way it was done in PDFTextPainter
  1346. public void drawString(AttributedCharacterIterator iterator, float x,
  1347. float y) {
  1348. preparePainting();
  1349. Font fontState = null;
  1350. Shape imclip = getClip();
  1351. writeClip(imclip);
  1352. Color c = getColor();
  1353. applyColor(c, true);
  1354. applyPaint(getPaint(), true);
  1355. boolean fill = true;
  1356. boolean stroke = false;
  1357. if (true) {
  1358. Stroke currentStroke = getStroke();
  1359. stroke = true;
  1360. applyStroke(currentStroke);
  1361. applyColor(c, false);
  1362. applyPaint(getPaint(), false);
  1363. }
  1364. currentStream.write("BT\n");
  1365. // set text rendering mode:
  1366. // 0 - fill, 1 - stroke, 2 - fill then stroke
  1367. int textr = 0;
  1368. if (fill && stroke) {
  1369. textr = 2;
  1370. } else if (stroke) {
  1371. textr = 1;
  1372. }
  1373. currentStream.write(textr + " Tr\n");
  1374. AffineTransform trans = getTransform();
  1375. trans.translate(x, y);
  1376. double[] vals = new double[6];
  1377. trans.getMatrix(vals);
  1378. for (char ch = iterator.first(); ch != CharacterIterator.DONE;
  1379. ch = iterator.next()) {
  1380. //Map attr = iterator.getAttributes();
  1381. String name = fontState.getFontName();
  1382. int size = fontState.getFontSize();
  1383. if ((!name.equals(this.currentFontName))
  1384. || (size != this.currentFontSize)) {
  1385. this.currentFontName = name;
  1386. this.currentFontSize = size;
  1387. currentStream.write("/" + name + " " + (size / 1000)
  1388. + " Tf\n");
  1389. }
  1390. currentStream.write(PDFNumber.doubleOut(vals[0], DEC) + " "
  1391. + PDFNumber.doubleOut(vals[1], DEC) + " "
  1392. + PDFNumber.doubleOut(vals[2], DEC) + " "
  1393. + PDFNumber.doubleOut(vals[3], DEC) + " "
  1394. + PDFNumber.doubleOut(vals[4], DEC) + " "
  1395. + PDFNumber.doubleOut(vals[5], DEC) + " Tm (" + ch
  1396. + ") Tj\n");
  1397. }
  1398. currentStream.write("ET\n");
  1399. }*/
  1400. /**
  1401. * Fills the interior of a <code>Shape</code> using the settings of the
  1402. * <code>Graphics2D</code> context. The rendering attributes applied
  1403. * include the <code>Clip</code>, <code>Transform</code>,
  1404. * <code>Paint</code>, and <code>Composite</code>.
  1405. * @param s the <code>Shape</code> to be filled
  1406. * @see #setPaint
  1407. * @see java.awt.Graphics#setColor
  1408. * @see #transform
  1409. * @see #setTransform
  1410. * @see #setComposite
  1411. * @see #clip
  1412. * @see #setClip
  1413. */
  1414. @Override
  1415. public void fill(Shape s) {
  1416. preparePainting();
  1417. //Transparency shortcut
  1418. Color c;
  1419. c = getBackground();
  1420. if (c.getAlpha() == 0) {
  1421. c = getColor();
  1422. if (c.getAlpha() == 0) {
  1423. return;
  1424. }
  1425. }
  1426. AffineTransform trans = getTransform();
  1427. double[] tranvals = new double[6];
  1428. trans.getMatrix(tranvals);
  1429. Shape imclip = getClip();
  1430. boolean newClip = paintingState.checkClip(imclip);
  1431. boolean newTransform = paintingState.checkTransform(trans)
  1432. && !trans.isIdentity();
  1433. if (newClip || newTransform) {
  1434. saveGraphicsState();
  1435. if (newTransform) {
  1436. concatMatrix(tranvals);
  1437. }
  1438. if (newClip) {
  1439. writeClip(imclip);
  1440. }
  1441. }
  1442. applyAlpha(c.getAlpha(), OPAQUE);
  1443. c = getColor();
  1444. applyColor(c, true);
  1445. c = getBackground();
  1446. applyColor(c, false);
  1447. Paint paint = getPaint();
  1448. if (paintingState.setPaint(paint)) {
  1449. if (!applyPaint(paint, true)) {
  1450. // Use the shape to 'clip' the paint contents.
  1451. applyUnknownPaint(paint, s);
  1452. if (newClip || newTransform) {
  1453. restoreGraphicsState();
  1454. }
  1455. return;
  1456. }
  1457. }
  1458. if (s instanceof Rectangle2D) {
  1459. Rectangle2D rect = (Rectangle2D)s;
  1460. currentStream.write(PDFNumber.doubleOut(rect.getMinX(), DEC) + " "
  1461. + PDFNumber.doubleOut(rect.getMinY(), DEC) + " ");
  1462. currentStream.write(PDFNumber.doubleOut(rect.getWidth(), DEC) + " "
  1463. + PDFNumber.doubleOut(rect.getHeight(), DEC) + " re ");
  1464. doDrawing(true, false, false);
  1465. } else {
  1466. PathIterator iter = s.getPathIterator(IDENTITY_TRANSFORM);
  1467. processPathIterator(iter);
  1468. doDrawing(true, false,
  1469. iter.getWindingRule() == PathIterator.WIND_EVEN_ODD);
  1470. }
  1471. if (newClip || newTransform) {
  1472. restoreGraphicsState();
  1473. }
  1474. }
  1475. void saveGraphicsState() {
  1476. currentStream.write("q\n");
  1477. paintingState.save();
  1478. }
  1479. void restoreGraphicsState() {
  1480. currentStream.write("Q\n");
  1481. paintingState.restore();
  1482. }
  1483. /** Checks whether the use of transparency is allowed. */
  1484. protected void checkTransparencyAllowed() {
  1485. pdfDoc.getProfile().verifyTransparencyAllowed("Java2D graphics");
  1486. }
  1487. /**
  1488. * Processes a path iterator generating the necessary painting operations.
  1489. * @param iter PathIterator to process
  1490. */
  1491. public void processPathIterator(PathIterator iter) {
  1492. while (!iter.isDone()) {
  1493. double[] vals = new double[6];
  1494. int type = iter.currentSegment(vals);
  1495. switch (type) {
  1496. case PathIterator.SEG_CUBICTO:
  1497. currentStream.write(PDFNumber.doubleOut(vals[0], DEC) + " "
  1498. + PDFNumber.doubleOut(vals[1], DEC) + " "
  1499. + PDFNumber.doubleOut(vals[2], DEC) + " "
  1500. + PDFNumber.doubleOut(vals[3], DEC) + " "
  1501. + PDFNumber.doubleOut(vals[4], DEC) + " "
  1502. + PDFNumber.doubleOut(vals[5], DEC) + " c\n");
  1503. break;
  1504. case PathIterator.SEG_LINETO:
  1505. currentStream.write(PDFNumber.doubleOut(vals[0], DEC) + " "
  1506. + PDFNumber.doubleOut(vals[1], DEC) + " l\n");
  1507. break;
  1508. case PathIterator.SEG_MOVETO:
  1509. currentStream.write(PDFNumber.doubleOut(vals[0], DEC) + " "
  1510. + PDFNumber.doubleOut(vals[1], DEC) + " m\n");
  1511. break;
  1512. case PathIterator.SEG_QUADTO:
  1513. currentStream.write(PDFNumber.doubleOut(vals[0], DEC) + " "
  1514. + PDFNumber.doubleOut(vals[1], DEC) + " "
  1515. + PDFNumber.doubleOut(vals[2], DEC) + " "
  1516. + PDFNumber.doubleOut(vals[3], DEC) + " y\n");
  1517. break;
  1518. case PathIterator.SEG_CLOSE:
  1519. currentStream.write("h\n");
  1520. break;
  1521. default:
  1522. break;
  1523. }
  1524. iter.next();
  1525. }
  1526. }
  1527. /**
  1528. * Do the PDF drawing command.
  1529. * This does the PDF drawing command according to fill
  1530. * stroke and winding rule.
  1531. *
  1532. * @param fill true if filling the path
  1533. * @param stroke true if stroking the path
  1534. * @param nonzero true if using the non-zero winding rule
  1535. */
  1536. protected void doDrawing(boolean fill, boolean stroke, boolean nonzero) {
  1537. preparePainting();
  1538. if (fill) {
  1539. if (stroke) {
  1540. if (nonzero) {
  1541. currentStream.write("B*\n");
  1542. } else {
  1543. currentStream.write("B\n");
  1544. }
  1545. } else {
  1546. if (nonzero) {
  1547. currentStream.write("f*\n");
  1548. } else {
  1549. currentStream.write("f\n");
  1550. }
  1551. }
  1552. } else {
  1553. // if (stroke)
  1554. currentStream.write("S\n");
  1555. }
  1556. }
  1557. /**
  1558. * Returns the device configuration associated with this
  1559. * <code>Graphics2D</code>.
  1560. *
  1561. * @return the PDF graphics configuration
  1562. */
  1563. @Override
  1564. public GraphicsConfiguration getDeviceConfiguration() {
  1565. return new PDFGraphicsConfiguration();
  1566. }
  1567. /**
  1568. * Used to create proper font metrics
  1569. */
  1570. private Graphics2D fmg;
  1571. {
  1572. BufferedImage bi = new BufferedImage(1, 1,
  1573. BufferedImage.TYPE_INT_ARGB);
  1574. fmg = bi.createGraphics();
  1575. }
  1576. /**
  1577. * Gets the font metrics for the specified font.
  1578. * @return the font metrics for the specified font.
  1579. * @param f the specified font
  1580. * @see java.awt.Graphics#getFont
  1581. * @see java.awt.FontMetrics
  1582. * @see java.awt.Graphics#getFontMetrics()
  1583. */
  1584. @Override
  1585. public java.awt.FontMetrics getFontMetrics(java.awt.Font f) {
  1586. return fmg.getFontMetrics(f);
  1587. }
  1588. /**
  1589. * Sets the paint mode of this graphics context to alternate between
  1590. * this graphics context's current color and the new specified color.
  1591. * This specifies that logical pixel operations are performed in the
  1592. * XOR mode, which alternates pixels between the current color and
  1593. * a specified XOR color.
  1594. * <p>
  1595. * When drawing operations are performed, pixels which are the
  1596. * current color are changed to the specified color, and vice versa.
  1597. * <p>
  1598. * Pixels that are of colors other than those two colors are changed
  1599. * in an unpredictable but reversible manner; if the same figure is
  1600. * drawn twice, then all pixels are restored to their original values.
  1601. * @param c1 the XOR alternation color
  1602. */
  1603. @Override
  1604. public void setXORMode(Color c1) {
  1605. //NYI
  1606. }
  1607. /**
  1608. * Copies an area of the component by a distance specified by
  1609. * <code>dx</code> and <code>dy</code>. From the point specified
  1610. * by <code>x</code> and <code>y</code>, this method
  1611. * copies downwards and to the right. To copy an area of the
  1612. * component to the left or upwards, specify a negative value for
  1613. * <code>dx</code> or <code>dy</code>.
  1614. * If a portion of the source rectangle lies outside the bounds
  1615. * of the component, or is obscured by another window or component,
  1616. * <code>copyArea</code> will be unable to copy the associated
  1617. * pixels. The area that is omitted can be refreshed by calling
  1618. * the component's <code>paint</code> method.
  1619. * @param x the <i>x</i> coordinate of the source rectangle.
  1620. * @param y the <i>y</i> coordinate of the source rectangle.
  1621. * @param width the width of the source rectangle.
  1622. * @param height the height of the source rectangle.
  1623. * @param dx the horizontal distance to copy the pixels.
  1624. * @param dy the vertical distance to copy the pixels.
  1625. */
  1626. @Override
  1627. public void copyArea(int x, int y, int width, int height, int dx,
  1628. int dy) {
  1629. //NYI
  1630. }
  1631. }