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

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829
  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 && paintingState.setBackColor(col)) {
  674. doWrite = true;
  675. } else if (paintingState.setColor(col)) {
  676. doWrite = true;
  677. }
  678. if (doWrite) {
  679. StringBuffer sb = new StringBuffer();
  680. colorHandler.establishColor(sb, col, fill);
  681. currentStream.write(sb.toString());
  682. }
  683. }
  684. /**
  685. * Apply the java paint to the PDF.
  686. * This takes the java paint sets up the appropraite PDF commands
  687. * for the drawing with that paint.
  688. * Currently this supports the gradients and patterns from batik.
  689. *
  690. * @param paint the paint to convert to PDF
  691. * @param fill true if the paint should be set for filling
  692. * @return true if the paint is handled natively, false if the paint should be rasterized
  693. */
  694. protected boolean applyPaint(Paint paint, boolean fill) { // CSOK: MethodLength
  695. preparePainting();
  696. if (paint instanceof Color) {
  697. return true;
  698. }
  699. // convert java.awt.GradientPaint to LinearGradientPaint to avoid rasterization
  700. if (paint instanceof GradientPaint) {
  701. GradientPaint gpaint = (GradientPaint) paint;
  702. paint = new LinearGradientPaint(
  703. (float) gpaint.getPoint1().getX(),
  704. (float) gpaint.getPoint1().getY(),
  705. (float) gpaint.getPoint2().getX(),
  706. (float) gpaint.getPoint2().getY(),
  707. new float[] {0, 1},
  708. new Color[] {gpaint.getColor1(), gpaint.getColor2()},
  709. gpaint.isCyclic() ? LinearGradientPaint.REPEAT : LinearGradientPaint.NO_CYCLE);
  710. }
  711. if (paint instanceof LinearGradientPaint) {
  712. LinearGradientPaint gp = (LinearGradientPaint)paint;
  713. // This code currently doesn't support 'repeat'.
  714. // For linear gradients it is possible to construct
  715. // a 'tile' that is repeated with a PDF pattern, but
  716. // it would be very tricky as you would have to rotate
  717. // the coordinate system so the repeat was axially
  718. // aligned. At this point I'm just going to rasterize it.
  719. MultipleGradientPaint.CycleMethodEnum cycle = gp.getCycleMethod();
  720. if (cycle != MultipleGradientPaint.NO_CYCLE) {
  721. return false;
  722. }
  723. Color[] cols = gp.getColors();
  724. float[] fractions = gp.getFractions();
  725. // Build proper transform from gradient space to page space
  726. // ('Patterns' don't get userspace transform).
  727. AffineTransform transform;
  728. transform = new AffineTransform(getBaseTransform());
  729. transform.concatenate(getTransform());
  730. transform.concatenate(gp.getTransform());
  731. List<Double> theMatrix = new java.util.ArrayList<Double>();
  732. double [] mat = new double[6];
  733. transform.getMatrix(mat);
  734. for (int idx = 0; idx < mat.length; idx++) {
  735. theMatrix.add(new Double(mat[idx]));
  736. }
  737. Point2D p1 = gp.getStartPoint();
  738. Point2D p2 = gp.getEndPoint();
  739. List<Double> theCoords = new java.util.ArrayList<Double>();
  740. theCoords.add(new Double(p1.getX()));
  741. theCoords.add(new Double(p1.getY()));
  742. theCoords.add(new Double(p2.getX()));
  743. theCoords.add(new Double(p2.getY()));
  744. List<Boolean> theExtend = new java.util.ArrayList<Boolean>();
  745. theExtend.add(Boolean.TRUE);
  746. theExtend.add(Boolean.TRUE);
  747. List<Double> theDomain = new java.util.ArrayList<Double>();
  748. theDomain.add(new Double(0));
  749. theDomain.add(new Double(1));
  750. List<Double> theEncode = new java.util.ArrayList<Double>();
  751. theEncode.add(new Double(0));
  752. theEncode.add(new Double(1));
  753. theEncode.add(new Double(0));
  754. theEncode.add(new Double(1));
  755. List<Double> theBounds = new java.util.ArrayList<Double>();
  756. List<Color> someColors = new java.util.ArrayList<Color>();
  757. for (int count = 0; count < cols.length; count++) {
  758. Color c1 = cols[count];
  759. if (c1.getAlpha() != 255) {
  760. return false; // PDF can't do alpha
  761. }
  762. //PDFColor color1 = new PDFColor(c1.getRed(), c1.getGreen(),
  763. // c1.getBlue());
  764. someColors.add(c1);
  765. if (count > 0 && count < cols.length - 1) {
  766. theBounds.add(new Double(fractions[count]));
  767. }
  768. }
  769. //Gradients are currently restricted to sRGB
  770. PDFDeviceColorSpace aColorSpace;
  771. aColorSpace = new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_RGB);
  772. PDFPattern myPat = pdfDoc.getFactory().makeGradient(
  773. resourceContext, false, aColorSpace,
  774. someColors, theBounds, theCoords, theMatrix);
  775. currentStream.write(myPat.getColorSpaceOut(fill));
  776. return true;
  777. }
  778. if (paint instanceof RadialGradientPaint) {
  779. RadialGradientPaint rgp = (RadialGradientPaint)paint;
  780. // There is essentially no way to support repeats
  781. // in PDF for radial gradients (the one option would
  782. // be to 'grow' the outer circle until it fully covered
  783. // the bounds and then grow the stops accordingly, the
  784. // problem is that this may require an extremely large
  785. // number of stops for cases where the focus is near
  786. // the edge of the outer circle). so we rasterize.
  787. MultipleGradientPaint.CycleMethodEnum cycle = rgp.getCycleMethod();
  788. if (cycle != MultipleGradientPaint.NO_CYCLE) {
  789. return false;
  790. }
  791. AffineTransform transform;
  792. transform = new AffineTransform(getBaseTransform());
  793. transform.concatenate(getTransform());
  794. transform.concatenate(rgp.getTransform());
  795. List<Double> theMatrix = new java.util.ArrayList<Double>();
  796. double [] mat = new double[6];
  797. transform.getMatrix(mat);
  798. for (int idx = 0; idx < mat.length; idx++) {
  799. theMatrix.add(new Double(mat[idx]));
  800. }
  801. double ar = rgp.getRadius();
  802. Point2D ac = rgp.getCenterPoint();
  803. Point2D af = rgp.getFocusPoint();
  804. List<Double> theCoords = new java.util.ArrayList<Double>();
  805. double dx = af.getX() - ac.getX();
  806. double dy = af.getY() - ac.getY();
  807. double d = Math.sqrt(dx * dx + dy * dy);
  808. if (d > ar) {
  809. // the center point af must be within the circle with
  810. // radius ar centered at ac so limit it to that.
  811. double scale = (ar * .9999) / d;
  812. dx = dx * scale;
  813. dy = dy * scale;
  814. }
  815. theCoords.add(new Double(ac.getX() + dx)); // Fx
  816. theCoords.add(new Double(ac.getY() + dy)); // Fy
  817. theCoords.add(new Double(0));
  818. theCoords.add(new Double(ac.getX()));
  819. theCoords.add(new Double(ac.getY()));
  820. theCoords.add(new Double(ar));
  821. Color[] cols = rgp.getColors();
  822. List<Color> someColors = new java.util.ArrayList<Color>();
  823. for (int count = 0; count < cols.length; count++) {
  824. Color cc = cols[count];
  825. if (cc.getAlpha() != 255) {
  826. return false; // PDF can't do alpha
  827. }
  828. someColors.add(cc);
  829. }
  830. float[] fractions = rgp.getFractions();
  831. List<Double> theBounds = new java.util.ArrayList<Double>();
  832. for (int count = 1; count < fractions.length - 1; count++) {
  833. float offset = fractions[count];
  834. theBounds.add(new Double(offset));
  835. }
  836. PDFDeviceColorSpace colSpace;
  837. colSpace = new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_RGB);
  838. PDFPattern myPat = pdfDoc.getFactory().makeGradient
  839. (resourceContext, true, colSpace,
  840. someColors, theBounds, theCoords, theMatrix);
  841. currentStream.write(myPat.getColorSpaceOut(fill));
  842. return true;
  843. }
  844. if (paint instanceof PatternPaint) {
  845. PatternPaint pp = (PatternPaint)paint;
  846. return createPattern(pp, fill);
  847. }
  848. return false; // unknown paint
  849. }
  850. private boolean createPattern(PatternPaint pp, boolean fill) {
  851. preparePainting();
  852. FontInfo specialFontInfo = new FontInfo();
  853. FontSetup.setup(specialFontInfo);
  854. PDFResources res = pdfDoc.getFactory().makeResources();
  855. PDFResourceContext context = new PDFResourceContext(res);
  856. PDFGraphics2D pattGraphic = new PDFGraphics2D(textAsShapes, specialFontInfo,
  857. pdfDoc, context, getPageReference(),
  858. "", 0);
  859. pattGraphic.setGraphicContext(new GraphicContext());
  860. pattGraphic.gc.validateTransformStack();
  861. pattGraphic.setRenderingHints(this.getRenderingHints());
  862. pattGraphic.setOutputStream(outputStream);
  863. GraphicsNode gn = pp.getGraphicsNode();
  864. //Rectangle2D gnBBox = gn.getBounds();
  865. Rectangle2D rect = pp.getPatternRect();
  866. // if (!pp.getOverflow()) {
  867. gn.paint(pattGraphic);
  868. // } else {
  869. // /* Commented out until SVN version of Batik is included */
  870. // // For overflow we need to paint the content from
  871. // // all the tiles who's overflow will intersect one
  872. // // tile (left->right, top->bottom). Then we can
  873. // // simply replicate that tile as normal.
  874. // double gnMinX = gnBBox.getX();
  875. // double gnMaxX = gnBBox.getX() + gnBBox.getWidth();
  876. // double gnMinY = gnBBox.getY();
  877. // double gnMaxY = gnBBox.getY() + gnBBox.getHeight();
  878. // double patMaxX = rect.getX() + rect.getWidth();
  879. // double patMaxY = rect.getY() + rect.getHeight();
  880. // double stepX = rect.getWidth();
  881. // double stepY = rect.getHeight();
  882. //
  883. // int startX = (int)((rect.getX() - gnMaxX)/stepX);
  884. // int startY = (int)((rect.getY() - gnMaxY)/stepY);
  885. //
  886. // int endX = (int)((patMaxX - gnMinX)/stepX);
  887. // int endY = (int)((patMaxY - gnMinY)/stepY);
  888. //
  889. // pattGraphic.translate(startX*stepX, startY*stepY);
  890. // for (int yIdx=startY; yIdx<=endY; yIdx++) {
  891. // for (int xIdx=startX; xIdx<=endX; xIdx++) {
  892. // gn.paint(pattGraphic);
  893. // pattGraphic.translate(stepX,0);
  894. // }
  895. // pattGraphic.translate(-(endX-startX+1)*stepX, stepY);
  896. // }
  897. // }
  898. List<Double> bbox = new java.util.ArrayList<Double>();
  899. bbox.add(new Double(rect.getX()));
  900. bbox.add(new Double(rect.getHeight() + rect.getY()));
  901. bbox.add(new Double(rect.getWidth() + rect.getX()));
  902. bbox.add(new Double(rect.getY()));
  903. AffineTransform transform;
  904. transform = new AffineTransform(getBaseTransform());
  905. transform.concatenate(getTransform());
  906. transform.concatenate(pp.getPatternTransform());
  907. List<Double> theMatrix = new java.util.ArrayList<Double>();
  908. double [] mat = new double[6];
  909. transform.getMatrix(mat);
  910. for (int idx = 0; idx < mat.length; idx++) {
  911. theMatrix.add(new Double(mat[idx]));
  912. }
  913. /** @todo see if pdfDoc and res can be linked here,
  914. (currently res <> PDFDocument's resources) so addFonts()
  915. can be moved to PDFDocument class */
  916. res.addFonts(pdfDoc, specialFontInfo);
  917. PDFPattern myPat = pdfDoc.getFactory().makePattern(
  918. resourceContext, 1, res, 1, 1, bbox,
  919. rect.getWidth(), rect.getHeight(),
  920. theMatrix, null,
  921. pattGraphic.getBuffer());
  922. currentStream.write(myPat.getColorSpaceOut(fill));
  923. PDFAnnotList annots = context.getAnnotations();
  924. if (annots != null) {
  925. this.pdfDoc.addObject(annots);
  926. }
  927. flushPDFDocument();
  928. return true;
  929. }
  930. /**
  931. * @param paint some paint
  932. * @param shape a shape
  933. * @return true (always)
  934. */
  935. protected boolean applyUnknownPaint(Paint paint, Shape shape) {
  936. preparePainting();
  937. Shape clip = getClip();
  938. Rectangle2D usrClipBounds, usrBounds;
  939. usrBounds = shape.getBounds2D();
  940. if (clip != null) {
  941. usrClipBounds = clip.getBounds2D();
  942. if (!usrClipBounds.intersects(usrBounds)) {
  943. return true;
  944. }
  945. Rectangle2D.intersect(usrBounds, usrClipBounds, usrBounds);
  946. }
  947. double usrX = usrBounds.getX();
  948. double usrY = usrBounds.getY();
  949. double usrW = usrBounds.getWidth();
  950. double usrH = usrBounds.getHeight();
  951. Rectangle devShapeBounds, devClipBounds, devBounds;
  952. AffineTransform at = getTransform();
  953. devShapeBounds = at.createTransformedShape(shape).getBounds();
  954. if (clip != null) {
  955. devClipBounds = at.createTransformedShape(clip).getBounds();
  956. if (!devClipBounds.intersects(devShapeBounds)) {
  957. return true;
  958. }
  959. devBounds = devShapeBounds.intersection(devClipBounds);
  960. } else {
  961. devBounds = devShapeBounds;
  962. }
  963. int devX = devBounds.x;
  964. int devY = devBounds.y;
  965. int devW = devBounds.width;
  966. int devH = devBounds.height;
  967. ColorSpace rgbCS = ColorSpace.getInstance(ColorSpace.CS_sRGB);
  968. ColorModel rgbCM = new DirectColorModel
  969. (rgbCS, 32, 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000,
  970. false, DataBuffer.TYPE_BYTE);
  971. PaintContext pctx = paint.createContext(rgbCM, devBounds, usrBounds,
  972. at, getRenderingHints());
  973. PDFXObject imageInfo = pdfDoc.getXObject
  974. ("TempImage:" + pctx.toString());
  975. if (imageInfo != null) {
  976. resourceContext.getPDFResources().addXObject(imageInfo);
  977. } else {
  978. Raster r = pctx.getRaster(devX, devY, devW, devH);
  979. WritableRaster wr = (WritableRaster)r;
  980. wr = wr.createWritableTranslatedChild(0, 0);
  981. ColorModel pcm = pctx.getColorModel();
  982. BufferedImage bi = new BufferedImage
  983. (pcm, wr, pcm.isAlphaPremultiplied(), null);
  984. final byte[] rgb = new byte[devW * devH * 3];
  985. final int[] line = new int[devW];
  986. final byte[] mask;
  987. int x, y, val, rgbIdx = 0;
  988. if (pcm.hasAlpha()) {
  989. mask = new byte[devW * devH];
  990. int maskIdx = 0;
  991. for (y = 0; y < devH; y++) {
  992. bi.getRGB(0, y, devW, 1, line, 0, devW);
  993. for (x = 0; x < devW; x++) {
  994. val = line[x];
  995. mask[maskIdx++] = (byte)(val >>> 24);
  996. rgb[rgbIdx++] = (byte)((val >> 16) & 0x0FF);
  997. rgb[rgbIdx++] = (byte)((val >> 8 ) & 0x0FF);
  998. rgb[rgbIdx++] = (byte)((val ) & 0x0FF);
  999. }
  1000. }
  1001. } else {
  1002. mask = null;
  1003. for (y = 0; y < devH; y++) {
  1004. bi.getRGB(0, y, devW, 1, line, 0, devW);
  1005. for (x = 0; x < devW; x++) {
  1006. val = line[x];
  1007. rgb[rgbIdx++] = (byte)((val >> 16) & 0x0FF);
  1008. rgb[rgbIdx++] = (byte)((val >> 8 ) & 0x0FF);
  1009. rgb[rgbIdx++] = (byte)((val ) & 0x0FF);
  1010. }
  1011. }
  1012. }
  1013. String maskRef = null;
  1014. if (mask != null) {
  1015. BitmapImage fopimg = new BitmapImage
  1016. ("TempImageMask:" + pctx.toString(), devW, devH, mask, null);
  1017. fopimg.setColorSpace(new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_GRAY));
  1018. PDFImageXObject xobj = pdfDoc.addImage(resourceContext, fopimg);
  1019. maskRef = xobj.referencePDF();
  1020. flushPDFDocument();
  1021. }
  1022. BitmapImage fopimg;
  1023. fopimg = new BitmapImage("TempImage:" + pctx.toString(),
  1024. devW, devH, rgb, maskRef);
  1025. fopimg.setTransparent(new PDFColor(255, 255, 255));
  1026. imageInfo = pdfDoc.addImage(resourceContext, fopimg);
  1027. flushPDFDocument();
  1028. }
  1029. currentStream.write("q\n");
  1030. writeClip(shape);
  1031. currentStream.write("" + usrW + " 0 0 " + (-usrH) + " " + usrX
  1032. + " " + (usrY + usrH) + " cm\n"
  1033. + imageInfo.getName() + " Do\nQ\n");
  1034. return true;
  1035. }
  1036. /**
  1037. * Apply the stroke to the PDF.
  1038. * This takes the java stroke and outputs the appropriate settings
  1039. * to the PDF so that the stroke attributes are handled.
  1040. *
  1041. * @param stroke the java stroke
  1042. */
  1043. protected void applyStroke(Stroke stroke) {
  1044. preparePainting();
  1045. if (stroke instanceof BasicStroke) {
  1046. BasicStroke bs = (BasicStroke)stroke;
  1047. float[] da = bs.getDashArray();
  1048. if (da != null) {
  1049. currentStream.write("[");
  1050. for (int count = 0; count < da.length; count++) {
  1051. currentStream.write(PDFNumber.doubleOut(da[count]));
  1052. if (count < da.length - 1) {
  1053. currentStream.write(" ");
  1054. }
  1055. }
  1056. currentStream.write("] ");
  1057. float offset = bs.getDashPhase();
  1058. currentStream.write(PDFNumber.doubleOut(offset) + " d\n");
  1059. }
  1060. int ec = bs.getEndCap();
  1061. switch (ec) {
  1062. case BasicStroke.CAP_BUTT:
  1063. currentStream.write(0 + " J\n");
  1064. break;
  1065. case BasicStroke.CAP_ROUND:
  1066. currentStream.write(1 + " J\n");
  1067. break;
  1068. case BasicStroke.CAP_SQUARE:
  1069. currentStream.write(2 + " J\n");
  1070. break;
  1071. default:
  1072. break;
  1073. }
  1074. int lj = bs.getLineJoin();
  1075. switch (lj) {
  1076. case BasicStroke.JOIN_MITER:
  1077. currentStream.write(0 + " j\n");
  1078. break;
  1079. case BasicStroke.JOIN_ROUND:
  1080. currentStream.write(1 + " j\n");
  1081. break;
  1082. case BasicStroke.JOIN_BEVEL:
  1083. currentStream.write(2 + " j\n");
  1084. break;
  1085. default:
  1086. break;
  1087. }
  1088. float lw = bs.getLineWidth();
  1089. currentStream.write(PDFNumber.doubleOut(lw) + " w\n");
  1090. float ml = bs.getMiterLimit();
  1091. currentStream.write(PDFNumber.doubleOut(ml) + " M\n");
  1092. }
  1093. }
  1094. /** {@inheritDoc} */
  1095. @Override
  1096. public void drawRenderedImage(RenderedImage img, AffineTransform xform) {
  1097. String key = "TempImage:" + img.toString();
  1098. drawInnerRenderedImage(key, img, xform);
  1099. }
  1100. /**
  1101. * @param key a key
  1102. * @param img an image
  1103. * @param xform a transform
  1104. */
  1105. public void drawInnerRenderedImage(String key, RenderedImage img, AffineTransform xform) {
  1106. preparePainting();
  1107. PDFXObject xObject = pdfDoc.getXObject(key);
  1108. if (xObject == null) {
  1109. xObject = addRenderedImage(key, img);
  1110. } else {
  1111. resourceContext.getPDFResources().addXObject(xObject);
  1112. }
  1113. useXObject(xObject, xform, img.getWidth(), img.getHeight());
  1114. }
  1115. private void useXObject(PDFXObject xObject, AffineTransform xform, float width, float height) {
  1116. // now do any transformation required and add the actual image
  1117. // placement instance
  1118. currentStream.write("q\n");
  1119. concatMatrix(getTransform());
  1120. Shape imclip = getClip();
  1121. writeClip(imclip);
  1122. concatMatrix(xform);
  1123. String w = PDFNumber.doubleOut(width, DEC);
  1124. String h = PDFNumber.doubleOut(height, DEC);
  1125. currentStream.write("" + w + " 0 0 -" + h + " 0 " + h + " cm\n"
  1126. + xObject.getName() + " Do\nQ\n");
  1127. }
  1128. private PDFXObject addRenderedImage(String key, RenderedImage img) {
  1129. ImageInfo info = new ImageInfo(null, "image/unknown");
  1130. ImageSize size = new ImageSize(img.getWidth(), img.getHeight(),
  1131. GraphicsConstants.DEFAULT_DPI);
  1132. info.setSize(size);
  1133. ImageRendered imgRend = new ImageRendered(info, img, null);
  1134. ImageRenderedAdapter adapter = new ImageRenderedAdapter(imgRend, key);
  1135. PDFXObject xObject = pdfDoc.addImage(resourceContext, adapter);
  1136. flushPDFDocument();
  1137. return xObject;
  1138. }
  1139. /** {@inheritDoc} */
  1140. @Override
  1141. public void drawRenderableImage(RenderableImage img,
  1142. AffineTransform xform) {
  1143. //TODO Check if this is good enough
  1144. drawRenderedImage(img.createDefaultRendering(), xform);
  1145. }
  1146. /**
  1147. * Renders the text specified by the specified <code>String</code>,
  1148. * using the current <code>Font</code> and <code>Paint</code> attributes
  1149. * in the <code>Graphics2D</code> context.
  1150. * The baseline of the first character is at position
  1151. * (<i>x</i>,&nbsp;<i>y</i>) in the User Space.
  1152. * The rendering attributes applied include the <code>Clip</code>,
  1153. * <code>Transform</code>, <code>Paint</code>, <code>Font</code> and
  1154. * <code>Composite</code> attributes. For characters in script systems
  1155. * such as Hebrew and Arabic, the glyphs can be rendered from right to
  1156. * left, in which case the coordinate supplied is the location of the
  1157. * leftmost character on the baseline.
  1158. * @param s the <code>String</code> to be rendered
  1159. * @param x the coordinate where the <code>String</code>
  1160. * should be rendered
  1161. * @param y the coordinate where the <code>String</code>
  1162. * should be rendered
  1163. * @see #setPaint
  1164. * @see java.awt.Graphics#setColor
  1165. * @see java.awt.Graphics#setFont
  1166. * @see #setTransform
  1167. * @see #setComposite
  1168. * @see #setClip
  1169. */
  1170. @Override
  1171. public void drawString(String s, float x, float y) {
  1172. preparePainting();
  1173. Font fontState;
  1174. AffineTransform fontTransform = null;
  1175. if (ovFontState == null) {
  1176. java.awt.Font gFont = getFont();
  1177. fontTransform = gFont.getTransform();
  1178. fontState = fontInfo.getFontInstanceForAWTFont(gFont);
  1179. } else {
  1180. fontState = fontInfo.getFontInstance(
  1181. ovFontState.getFontTriplet(), ovFontState.getFontSize());
  1182. ovFontState = null;
  1183. }
  1184. updateCurrentFont(fontState);
  1185. currentStream.write("q\n");
  1186. Color c = getColor();
  1187. applyColor(c, true);
  1188. applyPaint(getPaint(), true);
  1189. applyAlpha(c.getAlpha(), OPAQUE);
  1190. Map<Integer, Map<Integer, Integer>> kerning = fontState.getKerning();
  1191. boolean kerningAvailable = (kerning != null && !kerning.isEmpty());
  1192. boolean useMultiByte = isMultiByteFont(currentFontName);
  1193. // String startText = useMultiByte ? "<FEFF" : "(";
  1194. String startText = useMultiByte ? "<" : "(";
  1195. String endText = useMultiByte ? "> " : ") ";
  1196. AffineTransform trans = getTransform();
  1197. //trans.translate(x, y);
  1198. double[] vals = new double[6];
  1199. trans.getMatrix(vals);
  1200. concatMatrix(vals);
  1201. Shape imclip = getClip();
  1202. writeClip(imclip);
  1203. currentStream.write("BT\n");
  1204. AffineTransform localTransform = new AffineTransform();
  1205. localTransform.translate(x, y);
  1206. if (fontTransform != null) {
  1207. localTransform.concatenate(fontTransform);
  1208. }
  1209. localTransform.scale(1, -1);
  1210. double[] lt = new double[6];
  1211. localTransform.getMatrix(lt);
  1212. currentStream.write(PDFNumber.doubleOut(lt[0]) + " "
  1213. + PDFNumber.doubleOut(lt[1]) + " " + PDFNumber.doubleOut(lt[2]) + " "
  1214. + PDFNumber.doubleOut(lt[3]) + " " + PDFNumber.doubleOut(lt[4]) + " "
  1215. + PDFNumber.doubleOut(lt[5]) + " Tm [" + startText);
  1216. int l = s.length();
  1217. for (int i = 0; i < l; i++) {
  1218. char ch = fontState.mapChar(s.charAt(i));
  1219. if (!useMultiByte) {
  1220. if (ch > 127) {
  1221. currentStream.write("\\");
  1222. currentStream.write(Integer.toOctalString(ch));
  1223. } else {
  1224. switch (ch) {
  1225. case '(':
  1226. case ')':
  1227. case '\\':
  1228. currentStream.write("\\");
  1229. break;
  1230. default:
  1231. }
  1232. currentStream.write(ch);
  1233. }
  1234. } else {
  1235. currentStream.write(PDFText.toUnicodeHex(ch));
  1236. }
  1237. if (kerningAvailable && (i + 1) < l) {
  1238. addKerning(currentStream, (Integer.valueOf(ch)),
  1239. (Integer.valueOf(fontState.mapChar(s.charAt(i + 1)))),
  1240. kerning, startText, endText);
  1241. }
  1242. }
  1243. currentStream.write(endText);
  1244. currentStream.write("] TJ\n");
  1245. currentStream.write("ET\n");
  1246. currentStream.write("Q\n");
  1247. }
  1248. /**
  1249. * Applies the given alpha values for filling and stroking.
  1250. * @param fillAlpha A value between 0 and 255 (=OPAQUE) for filling
  1251. * @param strokeAlpha A value between 0 and 255 (=OPAQUE) for stroking
  1252. */
  1253. protected void applyAlpha(int fillAlpha, int strokeAlpha) {
  1254. if (fillAlpha != OPAQUE || strokeAlpha != OPAQUE) {
  1255. checkTransparencyAllowed();
  1256. Map<String, Float> vals = new java.util.HashMap<String, Float>();
  1257. if (fillAlpha != OPAQUE) {
  1258. vals.put(PDFGState.GSTATE_ALPHA_NONSTROKE, new Float(fillAlpha / 255f));
  1259. }
  1260. if (strokeAlpha != OPAQUE) {
  1261. vals.put(PDFGState.GSTATE_ALPHA_STROKE, new Float(strokeAlpha / 255f));
  1262. }
  1263. PDFGState gstate = pdfDoc.getFactory().makeGState(
  1264. vals, paintingState.getGState());
  1265. resourceContext.addGState(gstate);
  1266. currentStream.write("/" + gstate.getName() + " gs\n");
  1267. }
  1268. }
  1269. /**
  1270. * Updates the currently selected font.
  1271. * @param font the new font to use
  1272. */
  1273. protected void updateCurrentFont(Font font) {
  1274. String name = font.getFontName();
  1275. float size = font.getFontSize() / 1000f;
  1276. //Only update if necessary
  1277. if ((!name.equals(this.currentFontName))
  1278. || (size != this.currentFontSize)) {
  1279. this.currentFontName = name;
  1280. this.currentFontSize = size;
  1281. currentStream.write("/" + name + " " + size + " Tf\n");
  1282. }
  1283. }
  1284. /**
  1285. * Returns a suitable internal font given an AWT Font instance.
  1286. * @param awtFont the AWT font
  1287. * @return the internal Font
  1288. * @deprecated use FontInfo.getFontInstanceForAWTFont(java.awt.Font awtFont) instead
  1289. */
  1290. @Deprecated
  1291. protected Font getInternalFontForAWTFont(java.awt.Font awtFont) {
  1292. return fontInfo.getFontInstanceForAWTFont(awtFont);
  1293. }
  1294. /**
  1295. * Determines whether the font with the given name is a multi-byte font.
  1296. * @param name the name of the font
  1297. * @return true if it's a multi-byte font
  1298. */
  1299. protected boolean isMultiByteFont(String name) {
  1300. // This assumes that *all* CIDFonts use a /ToUnicode mapping
  1301. org.apache.fop.fonts.Typeface f
  1302. = fontInfo.getFonts().get(name);
  1303. return f.isMultiByte();
  1304. }
  1305. private void addKerning(StringWriter buf, Integer ch1, Integer ch2,
  1306. Map<Integer, Map<Integer, Integer>> kerning, String startText,
  1307. String endText) {
  1308. preparePainting();
  1309. Map<Integer, Integer> kernPair = kerning.get(ch1);
  1310. if (kernPair != null) {
  1311. Integer width = kernPair.get(ch2);
  1312. if (width != null) {
  1313. currentStream.write(endText + (-width.intValue()) + " " + startText);
  1314. }
  1315. }
  1316. }
  1317. /**
  1318. * Renders the text of the specified iterator, using the
  1319. * <code>Graphics2D</code> context's current <code>Paint</code>. The
  1320. * iterator must specify a font
  1321. * for each character. The baseline of the
  1322. * first character is at position (<i>x</i>,&nbsp;<i>y</i>) in the
  1323. * User Space.
  1324. * The rendering attributes applied include the <code>Clip</code>,
  1325. * <code>Transform</code>, <code>Paint</code>, and
  1326. * <code>Composite</code> attributes.
  1327. * For characters in script systems such as Hebrew and Arabic,
  1328. * the glyphs can be rendered from right to left, in which case the
  1329. * coordinate supplied is the location of the leftmost character
  1330. * on the baseline.
  1331. * @param iterator the iterator whose text is to be rendered
  1332. * @param x the coordinate where the iterator's text is to be
  1333. * rendered
  1334. * @param y the coordinate where the iterator's text is to be
  1335. * rendered
  1336. * @see #setPaint
  1337. * @see java.awt.Graphics#setColor
  1338. * @see #setTransform
  1339. * @see #setComposite
  1340. * @see #setClip
  1341. *//* TODO Reimplement for higher efficiency similar to the way it was done in PDFTextPainter
  1342. public void drawString(AttributedCharacterIterator iterator, float x,
  1343. float y) {
  1344. preparePainting();
  1345. Font fontState = null;
  1346. Shape imclip = getClip();
  1347. writeClip(imclip);
  1348. Color c = getColor();
  1349. applyColor(c, true);
  1350. applyPaint(getPaint(), true);
  1351. boolean fill = true;
  1352. boolean stroke = false;
  1353. if (true) {
  1354. Stroke currentStroke = getStroke();
  1355. stroke = true;
  1356. applyStroke(currentStroke);
  1357. applyColor(c, false);
  1358. applyPaint(getPaint(), false);
  1359. }
  1360. currentStream.write("BT\n");
  1361. // set text rendering mode:
  1362. // 0 - fill, 1 - stroke, 2 - fill then stroke
  1363. int textr = 0;
  1364. if (fill && stroke) {
  1365. textr = 2;
  1366. } else if (stroke) {
  1367. textr = 1;
  1368. }
  1369. currentStream.write(textr + " Tr\n");
  1370. AffineTransform trans = getTransform();
  1371. trans.translate(x, y);
  1372. double[] vals = new double[6];
  1373. trans.getMatrix(vals);
  1374. for (char ch = iterator.first(); ch != CharacterIterator.DONE;
  1375. ch = iterator.next()) {
  1376. //Map attr = iterator.getAttributes();
  1377. String name = fontState.getFontName();
  1378. int size = fontState.getFontSize();
  1379. if ((!name.equals(this.currentFontName))
  1380. || (size != this.currentFontSize)) {
  1381. this.currentFontName = name;
  1382. this.currentFontSize = size;
  1383. currentStream.write("/" + name + " " + (size / 1000)
  1384. + " Tf\n");
  1385. }
  1386. currentStream.write(PDFNumber.doubleOut(vals[0], DEC) + " "
  1387. + PDFNumber.doubleOut(vals[1], DEC) + " "
  1388. + PDFNumber.doubleOut(vals[2], DEC) + " "
  1389. + PDFNumber.doubleOut(vals[3], DEC) + " "
  1390. + PDFNumber.doubleOut(vals[4], DEC) + " "
  1391. + PDFNumber.doubleOut(vals[5], DEC) + " Tm (" + ch
  1392. + ") Tj\n");
  1393. }
  1394. currentStream.write("ET\n");
  1395. }*/
  1396. /**
  1397. * Fills the interior of a <code>Shape</code> using the settings of the
  1398. * <code>Graphics2D</code> context. The rendering attributes applied
  1399. * include the <code>Clip</code>, <code>Transform</code>,
  1400. * <code>Paint</code>, and <code>Composite</code>.
  1401. * @param s the <code>Shape</code> to be filled
  1402. * @see #setPaint
  1403. * @see java.awt.Graphics#setColor
  1404. * @see #transform
  1405. * @see #setTransform
  1406. * @see #setComposite
  1407. * @see #clip
  1408. * @see #setClip
  1409. */
  1410. @Override
  1411. public void fill(Shape s) {
  1412. preparePainting();
  1413. //Transparency shortcut
  1414. Color c;
  1415. c = getBackground();
  1416. if (c.getAlpha() == 0) {
  1417. c = getColor();
  1418. if (c.getAlpha() == 0) {
  1419. return;
  1420. }
  1421. }
  1422. AffineTransform trans = getTransform();
  1423. double[] tranvals = new double[6];
  1424. trans.getMatrix(tranvals);
  1425. Shape imclip = getClip();
  1426. boolean newClip = paintingState.checkClip(imclip);
  1427. boolean newTransform = paintingState.checkTransform(trans)
  1428. && !trans.isIdentity();
  1429. if (newClip || newTransform) {
  1430. saveGraphicsState();
  1431. if (newTransform) {
  1432. concatMatrix(tranvals);
  1433. }
  1434. if (newClip) {
  1435. writeClip(imclip);
  1436. }
  1437. }
  1438. applyAlpha(c.getAlpha(), OPAQUE);
  1439. c = getColor();
  1440. applyColor(c, true);
  1441. c = getBackground();
  1442. applyColor(c, false);
  1443. Paint paint = getPaint();
  1444. if (paintingState.setPaint(paint)) {
  1445. if (!applyPaint(paint, true)) {
  1446. // Use the shape to 'clip' the paint contents.
  1447. applyUnknownPaint(paint, s);
  1448. if (newClip || newTransform) {
  1449. restoreGraphicsState();
  1450. }
  1451. return;
  1452. }
  1453. }
  1454. if (s instanceof Rectangle2D) {
  1455. Rectangle2D rect = (Rectangle2D)s;
  1456. currentStream.write(PDFNumber.doubleOut(rect.getMinX(), DEC) + " "
  1457. + PDFNumber.doubleOut(rect.getMinY(), DEC) + " ");
  1458. currentStream.write(PDFNumber.doubleOut(rect.getWidth(), DEC) + " "
  1459. + PDFNumber.doubleOut(rect.getHeight(), DEC) + " re ");
  1460. doDrawing(true, false, false);
  1461. } else {
  1462. PathIterator iter = s.getPathIterator(IDENTITY_TRANSFORM);
  1463. processPathIterator(iter);
  1464. doDrawing(true, false,
  1465. iter.getWindingRule() == PathIterator.WIND_EVEN_ODD);
  1466. }
  1467. if (newClip || newTransform) {
  1468. restoreGraphicsState();
  1469. }
  1470. }
  1471. void saveGraphicsState() {
  1472. currentStream.write("q\n");
  1473. paintingState.save();
  1474. }
  1475. void restoreGraphicsState() {
  1476. currentStream.write("Q\n");
  1477. paintingState.restore();
  1478. }
  1479. /** Checks whether the use of transparency is allowed. */
  1480. protected void checkTransparencyAllowed() {
  1481. pdfDoc.getProfile().verifyTransparencyAllowed("Java2D graphics");
  1482. }
  1483. /**
  1484. * Processes a path iterator generating the necessary painting operations.
  1485. * @param iter PathIterator to process
  1486. */
  1487. public void processPathIterator(PathIterator iter) {
  1488. while (!iter.isDone()) {
  1489. double[] vals = new double[6];
  1490. int type = iter.currentSegment(vals);
  1491. switch (type) {
  1492. case PathIterator.SEG_CUBICTO:
  1493. currentStream.write(PDFNumber.doubleOut(vals[0], DEC) + " "
  1494. + PDFNumber.doubleOut(vals[1], DEC) + " "
  1495. + PDFNumber.doubleOut(vals[2], DEC) + " "
  1496. + PDFNumber.doubleOut(vals[3], DEC) + " "
  1497. + PDFNumber.doubleOut(vals[4], DEC) + " "
  1498. + PDFNumber.doubleOut(vals[5], DEC) + " c\n");
  1499. break;
  1500. case PathIterator.SEG_LINETO:
  1501. currentStream.write(PDFNumber.doubleOut(vals[0], DEC) + " "
  1502. + PDFNumber.doubleOut(vals[1], DEC) + " l\n");
  1503. break;
  1504. case PathIterator.SEG_MOVETO:
  1505. currentStream.write(PDFNumber.doubleOut(vals[0], DEC) + " "
  1506. + PDFNumber.doubleOut(vals[1], DEC) + " m\n");
  1507. break;
  1508. case PathIterator.SEG_QUADTO:
  1509. currentStream.write(PDFNumber.doubleOut(vals[0], DEC) + " "
  1510. + PDFNumber.doubleOut(vals[1], DEC) + " "
  1511. + PDFNumber.doubleOut(vals[2], DEC) + " "
  1512. + PDFNumber.doubleOut(vals[3], DEC) + " y\n");
  1513. break;
  1514. case PathIterator.SEG_CLOSE:
  1515. currentStream.write("h\n");
  1516. break;
  1517. default:
  1518. break;
  1519. }
  1520. iter.next();
  1521. }
  1522. }
  1523. /**
  1524. * Do the PDF drawing command.
  1525. * This does the PDF drawing command according to fill
  1526. * stroke and winding rule.
  1527. *
  1528. * @param fill true if filling the path
  1529. * @param stroke true if stroking the path
  1530. * @param nonzero true if using the non-zero winding rule
  1531. */
  1532. protected void doDrawing(boolean fill, boolean stroke, boolean nonzero) {
  1533. preparePainting();
  1534. if (fill) {
  1535. if (stroke) {
  1536. if (nonzero) {
  1537. currentStream.write("B*\n");
  1538. } else {
  1539. currentStream.write("B\n");
  1540. }
  1541. } else {
  1542. if (nonzero) {
  1543. currentStream.write("f*\n");
  1544. } else {
  1545. currentStream.write("f\n");
  1546. }
  1547. }
  1548. } else {
  1549. // if (stroke)
  1550. currentStream.write("S\n");
  1551. }
  1552. }
  1553. /**
  1554. * Returns the device configuration associated with this
  1555. * <code>Graphics2D</code>.
  1556. *
  1557. * @return the PDF graphics configuration
  1558. */
  1559. @Override
  1560. public GraphicsConfiguration getDeviceConfiguration() {
  1561. return new PDFGraphicsConfiguration();
  1562. }
  1563. /**
  1564. * Used to create proper font metrics
  1565. */
  1566. private Graphics2D fmg;
  1567. {
  1568. BufferedImage bi = new BufferedImage(1, 1,
  1569. BufferedImage.TYPE_INT_ARGB);
  1570. fmg = bi.createGraphics();
  1571. }
  1572. /**
  1573. * Gets the font metrics for the specified font.
  1574. * @return the font metrics for the specified font.
  1575. * @param f the specified font
  1576. * @see java.awt.Graphics#getFont
  1577. * @see java.awt.FontMetrics
  1578. * @see java.awt.Graphics#getFontMetrics()
  1579. */
  1580. @Override
  1581. public java.awt.FontMetrics getFontMetrics(java.awt.Font f) {
  1582. return fmg.getFontMetrics(f);
  1583. }
  1584. /**
  1585. * Sets the paint mode of this graphics context to alternate between
  1586. * this graphics context's current color and the new specified color.
  1587. * This specifies that logical pixel operations are performed in the
  1588. * XOR mode, which alternates pixels between the current color and
  1589. * a specified XOR color.
  1590. * <p>
  1591. * When drawing operations are performed, pixels which are the
  1592. * current color are changed to the specified color, and vice versa.
  1593. * <p>
  1594. * Pixels that are of colors other than those two colors are changed
  1595. * in an unpredictable but reversible manner; if the same figure is
  1596. * drawn twice, then all pixels are restored to their original values.
  1597. * @param c1 the XOR alternation color
  1598. */
  1599. @Override
  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. @Override
  1623. public void copyArea(int x, int y, int width, int height, int dx,
  1624. int dy) {
  1625. //NYI
  1626. }
  1627. }