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

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