Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

PDFGraphics2D.java 71KB

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