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

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