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

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