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

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