12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874 |
- /*
- * Copyright 1999-2006 The Apache Software Foundation.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
- /* $Id$ */
-
- package org.apache.fop.svg;
-
- import org.apache.fop.pdf.PDFConformanceException;
- import org.apache.fop.pdf.PDFResourceContext;
- import org.apache.fop.pdf.PDFResources;
- import org.apache.fop.pdf.PDFGState;
- import org.apache.fop.pdf.PDFColorSpace;
- import org.apache.fop.pdf.PDFColor;
- import org.apache.fop.pdf.PDFState;
- import org.apache.fop.pdf.PDFNumber;
- import org.apache.fop.pdf.PDFText;
- import org.apache.fop.pdf.PDFXObject;
- import org.apache.fop.pdf.PDFPattern;
- import org.apache.fop.pdf.PDFDocument;
- import org.apache.fop.pdf.PDFLink;
- import org.apache.fop.pdf.PDFAnnotList;
- import org.apache.fop.pdf.BitmapImage;
- import org.apache.fop.fonts.FontInfo;
- import org.apache.fop.fonts.Font;
- import org.apache.fop.fonts.FontSetup;
- import org.apache.fop.fonts.FontTriplet;
- import org.apache.fop.fonts.LazyFont;
- import org.apache.fop.image.JpegImage;
- import org.apache.fop.fonts.CIDFont;
- import org.apache.fop.render.pdf.FopPDFImage;
-
- import org.apache.batik.ext.awt.g2d.AbstractGraphics2D;
- import org.apache.batik.ext.awt.g2d.GraphicContext;
- import org.apache.batik.ext.awt.RadialGradientPaint;
- import org.apache.batik.ext.awt.LinearGradientPaint;
- import org.apache.batik.ext.awt.MultipleGradientPaint;
- import org.apache.batik.ext.awt.RenderingHintsKeyExt;
- import org.apache.batik.gvt.PatternPaint;
- import org.apache.batik.gvt.GraphicsNode;
-
- import java.text.AttributedCharacterIterator;
- import java.text.CharacterIterator;
- import java.awt.Graphics;
- import java.awt.Graphics2D;
- import java.awt.Color;
- import java.awt.GraphicsConfiguration;
- /* java.awt.Font is not imported to avoid confusion with
- org.apache.fop.fonts.Font */
- import java.awt.Image;
- import java.awt.Shape;
- import java.awt.Stroke;
- import java.awt.Paint;
- import java.awt.PaintContext;
- import java.awt.Rectangle;
- import java.awt.Dimension;
- import java.awt.BasicStroke;
- import java.awt.AlphaComposite;
- import java.awt.geom.AffineTransform;
- import java.awt.color.ColorSpace;
- import java.awt.image.BufferedImage;
- import java.awt.image.ColorModel;
- import java.awt.image.DirectColorModel;
- import java.awt.image.DataBuffer;
- import java.awt.image.DataBufferInt;
- import java.awt.image.ImageObserver;
- import java.awt.image.RenderedImage;
- import java.awt.image.Raster;
- import java.awt.image.WritableRaster;
- import java.awt.image.renderable.RenderableImage;
- import java.awt.geom.PathIterator;
- import java.awt.geom.Point2D;
- import java.awt.geom.Rectangle2D;
- import java.io.StringWriter;
- import java.io.IOException;
- import java.io.OutputStream;
-
- import java.util.Map;
- import java.util.List;
-
- /**
- * PDF Graphics 2D.
- * Used for drawing into a pdf document as if it is a graphics object.
- * This takes a pdf document and draws into it.
- *
- * @author <a href="mailto:keiron@aftexsw.com">Keiron Liddle</a>
- * @version $Id$
- * @see org.apache.batik.ext.awt.g2d.AbstractGraphics2D
- */
- public class PDFGraphics2D extends AbstractGraphics2D {
-
- private static final AffineTransform IDENTITY_TRANSFORM = new AffineTransform();
-
- /** The number of decimal places. */
- private static final int DEC = 8;
-
- /**
- * the PDF Document being created
- */
- protected PDFDocument pdfDoc;
-
- /**
- * The current resource context for adding fonts, patterns etc.
- */
- protected PDFResourceContext resourceContext;
-
- /**
- * The PDF reference of the current page.
- */
- protected String pageRef;
-
- /**
- * the current state of the pdf graphics
- */
- protected PDFState graphicsState;
-
- /**
- * The PDF graphics state level that this svg is being drawn into.
- */
- protected int baseLevel = 0;
-
- /**
- * The count of JPEG images added to document so they recieve
- * unique keys.
- */
- protected int[] jpegCount = {0};
-
- /**
- * The current font information.
- */
- protected FontInfo fontInfo;
-
- /**
- * The override font state used when drawing text and the font cannot be
- * set using java fonts.
- */
- protected Font ovFontState = null;
-
- /**
- * the current stream to add PDF commands to
- */
- protected StringWriter currentStream = new StringWriter();
-
- /**
- * the current (internal) font name
- */
- protected String currentFontName;
-
- /**
- * the current font size in millipoints
- */
- protected float currentFontSize;
-
- /**
- * The output stream for the pdf document.
- * If this is set then it can progressively output
- * the pdf document objects to reduce memory.
- * Especially with images.
- */
- protected OutputStream outputStream = null;
-
- /**
- * Create a new PDFGraphics2D with the given pdf document info.
- * This is used to create a Graphics object for use inside an already
- * existing document.
- *
- * @param textAsShapes if true then draw text as shapes
- * @param fi the current font information
- * @param doc the pdf document for creating pdf objects
- * @param page the current resource context or page
- * @param pref the PDF reference of the current page
- * @param font the current font name
- * @param size the current font size
- */
- public PDFGraphics2D(boolean textAsShapes, FontInfo fi, PDFDocument doc,
- PDFResourceContext page, String pref, String font, float size) {
- this(textAsShapes);
- pdfDoc = doc;
- resourceContext = page;
- currentFontName = font;
- currentFontSize = size;
- fontInfo = fi;
- pageRef = pref;
- graphicsState = new PDFState();
- }
-
- /**
- * Create a new PDFGraphics2D.
- *
- * @param textAsShapes true if drawing text as shapes
- */
- protected PDFGraphics2D(boolean textAsShapes) {
- super(textAsShapes);
- }
-
- /**
- * This constructor supports the create method.
- * This is not implemented properly.
- *
- * @param g the PDF graphics to make a copy of
- */
- public PDFGraphics2D(PDFGraphics2D g) {
- super(g);
- this.pdfDoc = g.pdfDoc;
- this.resourceContext = g.resourceContext;
- this.currentFontName = g.currentFontName;
- this.currentFontSize = g.currentFontSize;
- this.fontInfo = g.fontInfo;
- this.pageRef = g.pageRef;
- this.graphicsState = g.graphicsState;
- this.currentStream = g.currentStream;
- this.jpegCount = g.jpegCount;
- this.outputStream = g.outputStream;
- this.ovFontState = g.ovFontState;
- }
-
- /**
- * Creates a new <code>Graphics</code> object that is
- * a copy of this <code>Graphics</code> object.
- * @return a new graphics context that is a copy of
- * this graphics context.
- */
- public Graphics create() {
- return new PDFGraphics2D(this);
- }
-
- /**
- * Central handler for IOExceptions for this class.
- * @param ioe IOException to handle
- */
- protected void handleIOException(IOException ioe) {
- //TODO Surely, there's a better way to do this.
- ioe.printStackTrace();
- }
-
- /**
- * This method is used by PDFDocumentGraphics2D to prepare a new page if
- * necessary.
- */
- protected void preparePainting() {
- //nop, used by PDFDocumentGraphics2D
- }
-
- /**
- * Set the PDF state to use when starting to draw
- * into the PDF graphics.
- *
- * @param state the PDF state
- */
- public void setPDFState(PDFState state) {
- graphicsState = state;
- baseLevel = graphicsState.getStackLevel();
- }
-
- /**
- * Set the output stream that this PDF document is
- * being drawn to. This is so that it can progressively
- * use the PDF document to output data such as images.
- * This results in a significant saving on memory.
- *
- * @param os the output stream that is being used for the PDF document
- */
- public void setOutputStream(OutputStream os) {
- outputStream = os;
- }
-
- /**
- * Get the string containing all the commands written into this
- * Grpahics.
- * @return the string containing the PDF markup
- */
- public String getString() {
- return currentStream.toString();
- }
-
- /**
- * Get the string buffer from the currentStream, containing all
- * the commands written into this Grpahics so far.
- * @return the StringBuffer containing the PDF markup
- */
- public StringBuffer getBuffer() {
- return currentStream.getBuffer();
- }
-
- /**
- * Set the Grpahics context.
- * @param c the graphics context to use
- */
- public void setGraphicContext(GraphicContext c) {
- gc = c;
- setPrivateHints();
- }
-
- private void setPrivateHints() {
- setRenderingHint(RenderingHintsKeyExt.KEY_AVOID_TILE_PAINTING,
- RenderingHintsKeyExt.VALUE_AVOID_TILE_PAINTING_ON);
- }
-
- /**
- * Set the override font state for drawing text.
- * This is used by the PDF text painter so that it can temporarily
- * set the font state when a java font cannot be used.
- * The next drawString will use this font state.
- *
- * @param infont the font state to use
- */
- public void setOverrideFontState(Font infont) {
- ovFontState = infont;
- }
-
- /**
- * Restore the PDF graphics state to the starting state level.
- */
- /* seems not to be used
- public void restorePDFState() {
- for (int count = graphicsState.getStackLevel(); count > baseLevel; count--) {
- currentStream.write("Q\n");
- }
- graphicsState.restoreLevel(baseLevel);
- }*/
-
- private void concatMatrix(double[] matrix) {
- currentStream.write(PDFNumber.doubleOut(matrix[0], DEC) + " "
- + PDFNumber.doubleOut(matrix[1], DEC) + " "
- + PDFNumber.doubleOut(matrix[2], DEC) + " "
- + PDFNumber.doubleOut(matrix[3], DEC) + " "
- + PDFNumber.doubleOut(matrix[4], DEC) + " "
- + PDFNumber.doubleOut(matrix[5], DEC) + " cm\n");
- }
-
- /**
- * This is a pdf specific method used to add a link to the
- * pdf document.
- *
- * @param bounds the bounds of the link in user coordinates
- * @param trans the transform of the current drawing position
- * @param dest the PDF destination
- * @param linkType the type of link, internal or external
- */
- public void addLink(Rectangle2D bounds, AffineTransform trans, String dest, int linkType) {
- preparePainting();
- AffineTransform at = getTransform();
- Shape b = at.createTransformedShape(bounds);
- b = trans.createTransformedShape(b);
- if (b != null) {
- Rectangle rect = b.getBounds();
-
- if (linkType != PDFLink.EXTERNAL) {
- String pdfdest = "/FitR " + dest;
- resourceContext.addAnnotation(
- pdfDoc.getFactory().makeLink(rect, pageRef, pdfdest));
- } else {
- resourceContext.addAnnotation(
- pdfDoc.getFactory().makeLink(rect, dest, linkType, 0));
- }
- }
- }
-
- /**
- * Add a JPEG image directly to the PDF document.
- * This is used by the PDFImageElementBridge to draw a JPEG
- * directly into the pdf document rather than converting the image into
- * a bitmap and increasing the size.
- *
- * @param jpeg the jpeg image to draw
- * @param x the x position
- * @param y the y position
- * @param width the width to draw the image
- * @param height the height to draw the image
- */
- public void addJpegImage(JpegImage jpeg, float x, float y,
- float width, float height) {
- preparePainting();
- // Need to include hash code as when invoked from FO you
- // may have several 'independent' PDFGraphics2D so the
- // count is not enough.
- String key = "__AddJPEG_" + hashCode() + "_" + jpegCount[0];
- jpegCount[0]++;
- FopPDFImage fopimage = new FopPDFImage(jpeg, key);
- int xObjectNum = this.pdfDoc.addImage(resourceContext,
- fopimage).getXNumber();
- AffineTransform at = getTransform();
- double[] matrix = new double[6];
- at.getMatrix(matrix);
- currentStream.write("q\n");
- if (!at.isIdentity()) {
- concatMatrix(matrix);
- }
- Shape imclip = getClip();
- writeClip(imclip);
-
- currentStream.write("" + width + " 0 0 "
- + (-height) + " "
- + x + " "
- + (y + height) + " cm\n" + "/Im"
- + xObjectNum + " Do\nQ\n");
-
- if (outputStream != null) {
- try {
- this.pdfDoc.output(outputStream);
- } catch (IOException ioe) {
- // ignore exception, will be thrown again later
- }
- }
- }
-
- /**
- * Draws as much of the specified image as is currently available.
- * The image is drawn with its top-left corner at
- * (<i>x</i>, <i>y</i>) in this graphics context's coordinate
- * space. Transparent pixels in the image do not affect whatever
- * pixels are already there.
- * <p>
- * This method returns immediately in all cases, even if the
- * complete image has not yet been loaded, and it has not been dithered
- * and converted for the current output device.
- * <p>
- * If the image has not yet been completely loaded, then
- * <code>drawImage</code> returns <code>false</code>. As more of
- * the image becomes available, the process that draws the image notifies
- * the specified image observer.
- * @param img the specified image to be drawn.
- * @param x the <i>x</i> coordinate.
- * @param y the <i>y</i> coordinate.
- * @param observer object to be notified as more of
- * the image is converted.
- * @return true if the image was drawn
- * @see java.awt.Image
- * @see java.awt.image.ImageObserver
- * @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int)
- */
- public boolean drawImage(Image img, int x, int y,
- ImageObserver observer) {
- preparePainting();
-
- int width = img.getWidth(observer);
- int height = img.getHeight(observer);
-
- if (width == -1 || height == -1) {
- return false;
- }
-
- return drawImage(img, x, y, width, height, observer);
- }
-
- private BufferedImage buildBufferedImage(Dimension size) {
- return new BufferedImage(size.width, size.height,
- BufferedImage.TYPE_INT_ARGB);
- }
-
- /**
- * Draws as much of the specified image as has already been scaled
- * to fit inside the specified rectangle.
- * <p>
- * The image is drawn inside the specified rectangle of this
- * graphics context's coordinate space, and is scaled if
- * necessary. Transparent pixels do not affect whatever pixels
- * are already there.
- * <p>
- * This method returns immediately in all cases, even if the
- * entire image has not yet been scaled, dithered, and converted
- * for the current output device.
- * If the current output representation is not yet complete, then
- * <code>drawImage</code> returns <code>false</code>. As more of
- * the image becomes available, the process that draws the image notifies
- * the image observer by calling its <code>imageUpdate</code> method.
- * <p>
- * A scaled version of an image will not necessarily be
- * available immediately just because an unscaled version of the
- * image has been constructed for this output device. Each size of
- * the image may be cached separately and generated from the original
- * data in a separate image production sequence.
- * @param img the specified image to be drawn.
- * @param x the <i>x</i> coordinate.
- * @param y the <i>y</i> coordinate.
- * @param width the width of the rectangle.
- * @param height the height of the rectangle.
- * @param observer object to be notified as more of
- * the image is converted.
- * @return true if the image was drawn
- * @see java.awt.Image
- * @see java.awt.image.ImageObserver
- * @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int)
- */
- public boolean drawImage(Image img, int x, int y, int width, int height,
- ImageObserver observer) {
- preparePainting();
- // first we look to see if we've already added this image to
- // the pdf document. If so, we just reuse the reference;
- // otherwise we have to build a FopImage and add it to the pdf
- // document
- PDFXObject imageInfo = pdfDoc.getImage("TempImage:" + img.toString());
- if (imageInfo == null) {
- // OK, have to build and add a PDF image
-
- Dimension size = new Dimension(width, height);
- BufferedImage buf = buildBufferedImage(size);
-
- java.awt.Graphics2D g = buf.createGraphics();
- g.setComposite(AlphaComposite.SrcOver);
- g.setBackground(new Color(1, 1, 1, 0));
- g.setPaint(new Color(1, 1, 1, 0));
- g.fillRect(0, 0, width, height);
- g.clip(new Rectangle(0, 0, buf.getWidth(), buf.getHeight()));
- g.setComposite(gc.getComposite());
-
- if (!g.drawImage(img, 0, 0, buf.getWidth(), buf.getHeight(), observer)) {
- return false;
- }
- g.dispose();
-
- final byte[] result = new byte[buf.getWidth() * buf.getHeight() * 3 /*for RGB*/];
- byte[] mask = new byte[buf.getWidth() * buf.getHeight()];
- boolean hasMask = false;
- //boolean binaryMask = true;
-
- Raster raster = buf.getData();
- DataBuffer bd = raster.getDataBuffer();
-
- int count = 0;
- int maskpos = 0;
- int[] iarray;
- int i, j, val, alpha;
- switch (bd.getDataType()) {
- case DataBuffer.TYPE_INT:
- int[][] idata = ((DataBufferInt)bd).getBankData();
- for (i = 0; i < idata.length; i++) {
- iarray = idata[i];
- for (j = 0; j < iarray.length; j++) {
- val = iarray[j];
- alpha = val >>> 24;
- mask[maskpos++] = (byte)(alpha & 0xFF);
- if (alpha != 255) {
- hasMask = true;
- }
- result[count++] = (byte)((val >> 16) & 0xFF);
- result[count++] = (byte)((val >> 8) & 0xFF);
- result[count++] = (byte)((val) & 0xFF);
- }
- }
- break;
- default:
- // error
- break;
- }
- String ref = null;
- if (hasMask) {
- // if the mask is binary then we could convert it into a bitmask
- BitmapImage fopimg = new BitmapImage("TempImageMask:"
- + img.toString(), buf.getWidth(),
- buf.getHeight(), mask, null);
- fopimg.setColorSpace(new PDFColorSpace(PDFColorSpace.DEVICE_GRAY));
- PDFXObject xobj = pdfDoc.addImage(resourceContext, fopimg);
- ref = xobj.referencePDF();
-
- if (outputStream != null) {
- try {
- this.pdfDoc.output(outputStream);
- } catch (IOException ioe) {
- // ignore exception, will be thrown again later
- }
- }
- } else {
- mask = null;
- }
-
- BitmapImage fopimg = new BitmapImage("TempImage:"
- + img.toString(), buf.getWidth(),
- buf.getHeight(), result, ref);
- imageInfo = pdfDoc.addImage(resourceContext, fopimg);
- //int xObjectNum = imageInfo.getXNumber();
-
- if (outputStream != null) {
- try {
- this.pdfDoc.output(outputStream);
- } catch (IOException ioe) {
- // ignore exception, will be thrown again later
- }
- }
- } else {
- resourceContext.getPDFResources().addXObject(imageInfo);
- }
-
- // now do any transformation required and add the actual image
- // placement instance
- AffineTransform at = getTransform();
- double[] matrix = new double[6];
- at.getMatrix(matrix);
- currentStream.write("q\n");
- if (!at.isIdentity()) {
- concatMatrix(matrix);
- }
- Shape imclip = getClip();
- writeClip(imclip);
- currentStream.write("" + width + " 0 0 " + (-height) + " " + x
- + " " + (y + height) + " cm\n" + "/Im"
- + imageInfo.getXNumber() + " Do\nQ\n");
- return true;
- }
-
- /**
- * Disposes of this graphics context and releases
- * any system resources that it is using.
- * A <code>Graphics</code> object cannot be used after
- * <code>dispose</code>has been called.
- * <p>
- * When a Java program runs, a large number of <code>Graphics</code>
- * objects can be created within a short time frame.
- * Although the finalization process of the garbage collector
- * also disposes of the same system resources, it is preferable
- * to manually free the associated resources by calling this
- * method rather than to rely on a finalization process which
- * may not run to completion for a long period of time.
- * <p>
- * Graphics objects which are provided as arguments to the
- * <code>paint</code> and <code>update</code> methods
- * of components are automatically released by the system when
- * those methods return. For efficiency, programmers should
- * call <code>dispose</code> when finished using
- * a <code>Graphics</code> object only if it was created
- * directly from a component or another <code>Graphics</code> object.
- * @see java.awt.Graphics#finalize
- * @see java.awt.Component#paint
- * @see java.awt.Component#update
- * @see java.awt.Component#getGraphics
- * @see java.awt.Graphics#create
- */
- public void dispose() {
- pdfDoc = null;
- fontInfo = null;
- currentStream = null;
- currentFontName = null;
- }
-
- /**
- * Strokes the outline of a <code>Shape</code> using the settings of the
- * current <code>Graphics2D</code> context. The rendering attributes
- * applied include the <code>Clip</code>, <code>Transform</code>,
- * <code>Paint</code>, <code>Composite</code> and
- * <code>Stroke</code> attributes.
- * @param s the <code>Shape</code> to be rendered
- * @see #setStroke
- * @see #setPaint
- * @see java.awt.Graphics#setColor
- * @see #transform
- * @see #setTransform
- * @see #clip
- * @see #setClip
- * @see #setComposite
- */
- public void draw(Shape s) {
- preparePainting();
-
- //Transparency shortcut
- Color c;
- c = getColor();
- if (c.getAlpha() == 0) {
- return;
- }
-
- AffineTransform trans = getTransform();
- double[] tranvals = new double[6];
- trans.getMatrix(tranvals);
-
- Shape imclip = getClip();
- boolean newClip = graphicsState.checkClip(imclip);
- boolean newTransform = graphicsState.checkTransform(trans)
- && !trans.isIdentity();
-
- if (newClip || newTransform) {
- currentStream.write("q\n");
- graphicsState.push();
- if (newTransform) {
- concatMatrix(tranvals);
- }
- if (newClip) {
- writeClip(imclip);
- }
- }
-
- if (c.getAlpha() != 255) {
- Map vals = new java.util.HashMap();
- vals.put(PDFGState.GSTATE_ALPHA_STROKE,
- new Float(c.getAlpha() / 255f));
- PDFGState gstate = pdfDoc.getFactory().makeGState(
- vals, graphicsState.getGState());
- //gstate.setAlpha(c.getAlpha() / 255f, false);
- resourceContext.addGState(gstate);
- currentStream.write("/" + gstate.getName() + " gs\n");
- }
-
- c = getColor();
- if (graphicsState.setColor(c)) {
- applyColor(c, false);
- }
- c = getBackground();
- if (graphicsState.setBackColor(c)) {
- applyColor(c, true);
- }
-
- Paint paint = getPaint();
- if (graphicsState.setPaint(paint)) {
- if (!applyPaint(paint, false)) {
- // Stroke the shape and use it to 'clip'
- // the paint contents.
- Shape ss = getStroke().createStrokedShape(s);
- applyUnknownPaint(paint, ss);
-
- if (newClip || newTransform) {
- currentStream.write("Q\n");
- graphicsState.pop();
- }
- return;
- }
- }
- applyStroke(getStroke());
-
- PathIterator iter = s.getPathIterator(IDENTITY_TRANSFORM);
- processPathIterator(iter);
- doDrawing(false, true, false);
- if (newClip || newTransform) {
- currentStream.write("Q\n");
- graphicsState.pop();
- }
- }
-
- /*
- // in theory we could set the clip using these methods
- // it doesn't seem to improve the file sizes much
- // and makes everything more complicated
-
- Shape lastClip = null;
-
- public void clip(Shape cl) {
- super.clip(cl);
- Shape newClip = getClip();
- if (newClip == null || lastClip == null
- || !(new Area(newClip).equals(new Area(lastClip)))) {
- graphicsState.setClip(newClip);
- writeClip(newClip);
- }
-
- lastClip = newClip;
- }
-
- public void setClip(Shape cl) {
- super.setClip(cl);
- Shape newClip = getClip();
- if (newClip == null || lastClip == null
- || !(new Area(newClip).equals(new Area(lastClip)))) {
- for (int count = graphicsState.getStackLevel(); count > baseLevel; count--) {
- currentStream.write("Q\n");
- }
- graphicsState.restoreLevel(baseLevel);
- currentStream.write("q\n");
- graphicsState.push();
- if (newClip != null) {
- graphicsState.setClip(newClip);
- }
- writeClip(newClip);
- }
-
- lastClip = newClip;
- }
- */
-
- /**
- * Set the clipping shape for future PDF drawing in the current graphics state.
- * This sets creates and writes a clipping shape that will apply
- * to future drawings in the current graphics state.
- *
- * @param s the clipping shape
- */
- protected void writeClip(Shape s) {
- if (s == null) {
- return;
- }
- preparePainting();
- PathIterator iter = s.getPathIterator(IDENTITY_TRANSFORM);
- processPathIterator(iter);
- // clip area
- currentStream.write("W\n");
- currentStream.write("n\n");
- }
-
- /**
- * Apply the java Color to PDF.
- * This converts the java colour to a PDF colour and
- * sets it for the next drawing.
- *
- * @param col the java colour
- * @param fill true if the colour will be used for filling
- */
- protected void applyColor(Color col, boolean fill) {
- preparePainting();
- Color c = col;
- if (c.getColorSpace().getType()
- == ColorSpace.TYPE_RGB) {
- PDFColor currentColour = new PDFColor(c.getRed(), c.getGreen(),
- c.getBlue());
- currentStream.write(currentColour.getColorSpaceOut(fill));
- } else if (c.getColorSpace().getType()
- == ColorSpace.TYPE_CMYK) {
- if (pdfDoc.getPDFAMode().isPDFA1LevelB()) {
- //See PDF/A-1, ISO 19005:1:2005(E), 6.2.3.3
- //FOP is currently restricted to DeviceRGB if PDF/A-1 is active.
- throw new PDFConformanceException(
- "PDF/A-1 does not allow mixing DeviceRGB and DeviceCMYK.");
- }
- float[] cComps = c.getColorComponents(new float[3]);
- double[] cmyk = new double[3];
- for (int i = 0; i < 3; i++) {
- // convert the float elements to doubles for pdf
- cmyk[i] = cComps[i];
- }
- PDFColor currentColour = new PDFColor(cmyk[0], cmyk[1], cmyk[2], cmyk[3]);
- currentStream.write(currentColour.getColorSpaceOut(fill));
- } else if (c.getColorSpace().getType()
- == ColorSpace.TYPE_2CLR) {
- // used for black/magenta
- float[] cComps = c.getColorComponents(new float[1]);
- double[] blackMagenta = new double[1];
- for (int i = 0; i < 1; i++) {
- blackMagenta[i] = cComps[i];
- }
- //PDFColor currentColour = new PDFColor(blackMagenta[0], blackMagenta[1]);
- //currentStream.write(currentColour.getColorSpaceOut(fill));
- } else {
- throw new UnsupportedOperationException(
- "Color Space not supported by PDFGraphics2D");
- }
- }
-
- /**
- * Apply the java paint to the PDF.
- * This takes the java paint sets up the appropraite PDF commands
- * for the drawing with that paint.
- * Currently this supports the gradients and patterns from batik.
- *
- * @param paint the paint to convert to PDF
- * @param fill true if the paint should be set for filling
- */
- protected boolean applyPaint(Paint paint, boolean fill) {
- preparePainting();
-
- if (paint instanceof Color) {
- return true;
- }
- if (paint instanceof LinearGradientPaint) {
- LinearGradientPaint gp = (LinearGradientPaint)paint;
-
- // This code currently doesn't support 'repeat'.
- // For linear gradients it is possible to construct
- // a 'tile' that is repeated with a PDF pattern, but
- // it would be very tricky as you would have to rotate
- // the coordinate system so the repeat was axially
- // aligned. At this point I'm just going to rasterize it.
- MultipleGradientPaint.CycleMethodEnum cycle = gp.getCycleMethod();
- if (cycle != MultipleGradientPaint.NO_CYCLE) {
- return false;
- }
-
- Color[] cols = gp.getColors();
- float[] fractions = gp.getFractions();
-
- // Build proper transform from gradient space to page space
- // ('Patterns' don't get userspace transform).
- AffineTransform transform;
- transform = new AffineTransform(graphicsState.getTransform());
- transform.concatenate(getTransform());
- transform.concatenate(gp.getTransform());
-
- List theMatrix = new java.util.ArrayList();
- double [] mat = new double[6];
- transform.getMatrix(mat);
- for (int idx = 0; idx < mat.length; idx++) {
- theMatrix.add(new Double(mat[idx]));
- }
-
- Point2D p1 = gp.getStartPoint();
- Point2D p2 = gp.getEndPoint();
- List theCoords = new java.util.ArrayList();
- theCoords.add(new Double(p1.getX()));
- theCoords.add(new Double(p1.getY()));
- theCoords.add(new Double(p2.getX()));
- theCoords.add(new Double(p2.getY()));
-
- List theExtend = new java.util.ArrayList();
- theExtend.add(new Boolean(true));
- theExtend.add(new Boolean(true));
-
- List theDomain = new java.util.ArrayList();
- theDomain.add(new Double(0));
- theDomain.add(new Double(1));
-
- List theEncode = new java.util.ArrayList();
- theEncode.add(new Double(0));
- theEncode.add(new Double(1));
- theEncode.add(new Double(0));
- theEncode.add(new Double(1));
-
- List theBounds = new java.util.ArrayList();
-
- List someColors = new java.util.ArrayList();
-
- for (int count = 0; count < cols.length; count++) {
- Color c1 = cols[count];
- if (c1.getAlpha() != 255) {
- return false; // PDF can't do alpha
- }
-
- PDFColor color1 = new PDFColor(c1.getRed(), c1.getGreen(),
- c1.getBlue());
- someColors.add(color1);
- if (count > 0 && count < cols.length - 1) {
- theBounds.add(new Double(fractions[count]));
- }
- }
-
- PDFColorSpace aColorSpace;
- aColorSpace = new PDFColorSpace(PDFColorSpace.DEVICE_RGB);
- PDFPattern myPat = pdfDoc.getFactory().makeGradient(
- resourceContext, false, aColorSpace,
- someColors, theBounds, theCoords, theMatrix);
- currentStream.write(myPat.getColorSpaceOut(fill));
-
- return true;
- }
- if (paint instanceof RadialGradientPaint) {
- RadialGradientPaint rgp = (RadialGradientPaint)paint;
-
- // There is essentially no way to support repeate
- // in PDF for radial gradients (the one option would
- // be to 'grow' the outer circle until it fully covered
- // the bounds and then grow the stops accordingly, the
- // problem is that this may require an extremely large
- // number of stops for cases where the focus is near
- // the edge of the outer circle). so we rasterize.
- MultipleGradientPaint.CycleMethodEnum cycle = rgp.getCycleMethod();
- if (cycle != MultipleGradientPaint.NO_CYCLE) {
- return false;
- }
-
- AffineTransform transform;
- transform = new AffineTransform(graphicsState.getTransform());
- transform.concatenate(getTransform());
- transform.concatenate(rgp.getTransform());
-
- List theMatrix = new java.util.ArrayList();
- double [] mat = new double[6];
- transform.getMatrix(mat);
- for (int idx = 0; idx < mat.length; idx++) {
- theMatrix.add(new Double(mat[idx]));
- }
-
- double ar = rgp.getRadius();
- Point2D ac = rgp.getCenterPoint();
- Point2D af = rgp.getFocusPoint();
-
- List theCoords = new java.util.ArrayList();
- double dx = af.getX() - ac.getX();
- double dy = af.getY() - ac.getY();
- double d = Math.sqrt(dx * dx + dy * dy);
- if (d > ar) {
- // the center point af must be within the circle with
- // radius ar centered at ac so limit it to that.
- double scale = (ar * .9999) / d;
- dx = dx * scale;
- dy = dy * scale;
- }
-
- theCoords.add(new Double(ac.getX() + dx)); // Fx
- theCoords.add(new Double(ac.getY() + dy)); // Fy
- theCoords.add(new Double(0));
- theCoords.add(new Double(ac.getX()));
- theCoords.add(new Double(ac.getY()));
- theCoords.add(new Double(ar));
-
- Color[] cols = rgp.getColors();
- List someColors = new java.util.ArrayList();
- for (int count = 0; count < cols.length; count++) {
- Color cc = cols[count];
- if (cc.getAlpha() != 255) {
- return false; // PDF can't do alpha
- }
-
- someColors.add(new PDFColor(cc.getRed(), cc.getGreen(),
- cc.getBlue()));
- }
-
- float[] fractions = rgp.getFractions();
- List theBounds = new java.util.ArrayList();
- for (int count = 1; count < fractions.length - 1; count++) {
- float offset = fractions[count];
- theBounds.add(new Double(offset));
- }
- PDFColorSpace colSpace;
- colSpace = new PDFColorSpace(PDFColorSpace.DEVICE_RGB);
-
- PDFPattern myPat = pdfDoc.getFactory().makeGradient
- (resourceContext, true, colSpace,
- someColors, theBounds, theCoords, theMatrix);
-
- currentStream.write(myPat.getColorSpaceOut(fill));
-
- return true;
- }
- if (paint instanceof PatternPaint) {
- PatternPaint pp = (PatternPaint)paint;
- return createPattern(pp, fill);
- }
- return false; // unknown paint
- }
-
- private boolean createPattern(PatternPaint pp, boolean fill) {
- preparePainting();
-
- FontInfo fontInfo = new FontInfo();
- FontSetup.setup(fontInfo, null, null);
-
- PDFResources res = pdfDoc.getFactory().makeResources();
- PDFResourceContext context = new PDFResourceContext(res);
- PDFGraphics2D pattGraphic = new PDFGraphics2D(textAsShapes, fontInfo,
- pdfDoc, context, pageRef,
- "", 0);
- pattGraphic.setGraphicContext(new GraphicContext());
- pattGraphic.gc.validateTransformStack();
- pattGraphic.setRenderingHints(this.getRenderingHints());
- pattGraphic.setOutputStream(outputStream);
-
- GraphicsNode gn = pp.getGraphicsNode();
- Rectangle2D gnBBox = gn.getBounds();
- Rectangle2D rect = pp.getPatternRect();
-
- // if (!pp.getOverflow()) {
- gn.paint(pattGraphic);
- // } else {
- // /* Commented out until SVN version of Batik is included */
- // // For overflow we need to paint the content from
- // // all the tiles who's overflow will intersect one
- // // tile (left->right, top->bottom). Then we can
- // // simply replicate that tile as normal.
- // double gnMinX = gnBBox.getX();
- // double gnMaxX = gnBBox.getX() + gnBBox.getWidth();
- // double gnMinY = gnBBox.getY();
- // double gnMaxY = gnBBox.getY() + gnBBox.getHeight();
- // double patMaxX = rect.getX() + rect.getWidth();
- // double patMaxY = rect.getY() + rect.getHeight();
- // double stepX = rect.getWidth();
- // double stepY = rect.getHeight();
- //
- // int startX = (int)((rect.getX() - gnMaxX)/stepX);
- // int startY = (int)((rect.getY() - gnMaxY)/stepY);
- //
- // int endX = (int)((patMaxX - gnMinX)/stepX);
- // int endY = (int)((patMaxY - gnMinY)/stepY);
- //
- // pattGraphic.translate(startX*stepX, startY*stepY);
- // for (int yIdx=startY; yIdx<=endY; yIdx++) {
- // for (int xIdx=startX; xIdx<=endX; xIdx++) {
- // gn.paint(pattGraphic);
- // pattGraphic.translate(stepX,0);
- // }
- // pattGraphic.translate(-(endX-startX+1)*stepX, stepY);
- // }
- // }
-
- List bbox = new java.util.ArrayList();
- bbox.add(new Double(rect.getX()));
- bbox.add(new Double(rect.getHeight() + rect.getY()));
- bbox.add(new Double(rect.getWidth() + rect.getX()));
- bbox.add(new Double(rect.getY()));
-
- AffineTransform transform;
- transform = new AffineTransform(graphicsState.getTransform());
- transform.concatenate(getTransform());
- transform.concatenate(pp.getPatternTransform());
-
- List theMatrix = new java.util.ArrayList();
- double [] mat = new double[6];
- transform.getMatrix(mat);
- for (int idx = 0; idx < mat.length; idx++) {
- theMatrix.add(new Double(mat[idx]));
- }
-
- /** @todo see if pdfDoc and res can be linked here,
- (currently res <> PDFDocument's resources) so addFonts()
- can be moved to PDFDocument class */
- res.addFonts(pdfDoc, fontInfo);
-
- PDFPattern myPat = pdfDoc.getFactory().makePattern(
- resourceContext, 1, res, 1, 1, bbox,
- rect.getWidth(), rect.getHeight(),
- theMatrix, null,
- pattGraphic.getBuffer());
-
- currentStream.write(myPat.getColorSpaceOut(fill));
-
- PDFAnnotList annots = context.getAnnotations();
- if (annots != null) {
- this.pdfDoc.addObject(annots);
- }
-
- if (outputStream != null) {
- try {
- this.pdfDoc.output(outputStream);
- } catch (IOException ioe) {
- // ignore exception, will be thrown again later
- }
- }
- return true;
- }
-
- protected boolean applyUnknownPaint(Paint paint, Shape shape) {
- preparePainting();
-
- Shape clip = getClip();
- Rectangle2D usrClipBounds, usrBounds;
- usrBounds = shape.getBounds2D();
- usrClipBounds = clip.getBounds2D();
- if (!usrClipBounds.intersects(usrBounds)) {
- return true;
- }
- Rectangle2D.intersect(usrBounds, usrClipBounds, usrBounds);
- double usrX = usrBounds.getX();
- double usrY = usrBounds.getY();
- double usrW = usrBounds.getWidth();
- double usrH = usrBounds.getHeight();
-
- Rectangle devShapeBounds, devClipBounds, devBounds;
- AffineTransform at = getTransform();
- devShapeBounds = at.createTransformedShape(shape).getBounds();
- devClipBounds = at.createTransformedShape(clip).getBounds();
- if (!devClipBounds.intersects(devShapeBounds)) {
- return true;
- }
- devBounds = devShapeBounds.intersection(devClipBounds);
- int devX = devBounds.x;
- int devY = devBounds.y;
- int devW = devBounds.width;
- int devH = devBounds.height;
-
- ColorSpace rgbCS = ColorSpace.getInstance(ColorSpace.CS_sRGB);
- ColorModel rgbCM = new DirectColorModel
- (rgbCS, 32, 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000,
- false, DataBuffer.TYPE_BYTE);
-
- PaintContext pctx = paint.createContext(rgbCM, devBounds, usrBounds,
- at, getRenderingHints());
- PDFXObject imageInfo = pdfDoc.getImage
- ("TempImage:" + pctx.toString());
- if (imageInfo != null) {
- resourceContext.getPDFResources().addXObject(imageInfo);
- } else {
- Raster r = pctx.getRaster(devX, devY, devW, devH);
- WritableRaster wr = (WritableRaster)r;
- wr = wr.createWritableTranslatedChild(0, 0);
-
- ColorModel pcm = pctx.getColorModel();
- BufferedImage bi = new BufferedImage
- (pcm, wr, pcm.isAlphaPremultiplied(), null);
- final byte[] rgb = new byte[devW * devH * 3];
- final int[] line = new int[devW];
- final byte[] mask;
- int x, y, val, rgbIdx = 0;
-
- if (pcm.hasAlpha()) {
- mask = new byte[devW * devH];
- int maskIdx = 0;
- for (y = 0; y < devH; y++) {
- bi.getRGB(0, y, devW, 1, line, 0, devW);
- for (x = 0; x < devW; x++) {
- val = line[x];
- mask[maskIdx++] = (byte)(val >>> 24);
- rgb[rgbIdx++] = (byte)((val >> 16) & 0x0FF);
- rgb[rgbIdx++] = (byte)((val >> 8 ) & 0x0FF);
- rgb[rgbIdx++] = (byte)((val ) & 0x0FF);
- }
- }
- } else {
- mask = null;
- for (y = 0; y < devH; y++) {
- bi.getRGB(0, y, devW, 1, line, 0, devW);
- for (x = 0; x < devW; x++) {
- val = line[x];
- rgb[rgbIdx++] = (byte)((val >> 16) & 0x0FF);
- rgb[rgbIdx++] = (byte)((val >> 8 ) & 0x0FF);
- rgb[rgbIdx++] = (byte)((val ) & 0x0FF);
- }
- }
- }
-
- String maskRef = null;
- if (mask != null) {
- BitmapImage fopimg = new BitmapImage
- ("TempImageMask:" + pctx.toString(), devW, devH, mask, null);
- fopimg.setColorSpace(new PDFColorSpace(PDFColorSpace.DEVICE_GRAY));
- PDFXObject xobj = pdfDoc.addImage(resourceContext, fopimg);
- maskRef = xobj.referencePDF();
-
- if (outputStream != null) {
- try {
- this.pdfDoc.output(outputStream);
- } catch (IOException ioe) {
- // ignore exception, will be thrown again later
- }
- }
- }
- BitmapImage fopimg;
- fopimg = new BitmapImage("TempImage:" + pctx.toString(),
- devW, devH, rgb, maskRef);
- fopimg.setTransparent(new PDFColor(255, 255, 255));
- imageInfo = pdfDoc.addImage(resourceContext, fopimg);
- if (outputStream != null) {
- try {
- this.pdfDoc.output(outputStream);
- } catch (IOException ioe) {
- // ignore exception, will be thrown again later
- }
- }
- }
-
- currentStream.write("q\n");
- writeClip(shape);
- currentStream.write("" + usrW + " 0 0 " + (-usrH) + " " + usrX
- + " " + (usrY + usrH) + " cm\n" + "/Im"
- + imageInfo.getXNumber() + " Do\nQ\n");
- return true;
- }
-
- /**
- * Apply the stroke to the PDF.
- * This takes the java stroke and outputs the appropriate settings
- * to the PDF so that the stroke attributes are handled.
- *
- * @param stroke the java stroke
- */
- protected void applyStroke(Stroke stroke) {
- preparePainting();
- if (stroke instanceof BasicStroke) {
- BasicStroke bs = (BasicStroke)stroke;
-
- float[] da = bs.getDashArray();
- if (da != null) {
- currentStream.write("[");
- for (int count = 0; count < da.length; count++) {
- if (((int)da[count]) == 0) {
- // the dasharray units in pdf are (whole) numbers
- // in user space units, cannot be 0
- currentStream.write("1");
- } else {
- currentStream.write("" + ((int)da[count]));
- }
- if (count < da.length - 1) {
- currentStream.write(" ");
- }
- }
- currentStream.write("] ");
- float offset = bs.getDashPhase();
- currentStream.write(((int)offset) + " d\n");
- }
- int ec = bs.getEndCap();
- switch (ec) {
- case BasicStroke.CAP_BUTT:
- currentStream.write(0 + " J\n");
- break;
- case BasicStroke.CAP_ROUND:
- currentStream.write(1 + " J\n");
- break;
- case BasicStroke.CAP_SQUARE:
- currentStream.write(2 + " J\n");
- break;
- }
-
- int lj = bs.getLineJoin();
- switch (lj) {
- case BasicStroke.JOIN_MITER:
- currentStream.write(0 + " j\n");
- break;
- case BasicStroke.JOIN_ROUND:
- currentStream.write(1 + " j\n");
- break;
- case BasicStroke.JOIN_BEVEL:
- currentStream.write(2 + " j\n");
- break;
- }
- float lw = bs.getLineWidth();
- currentStream.write(PDFNumber.doubleOut(lw) + " w\n");
-
- float ml = bs.getMiterLimit();
- currentStream.write(PDFNumber.doubleOut(ml) + " M\n");
- }
- }
-
- /**
- * Renders a {@link RenderedImage},
- * applying a transform from image
- * space into user space before drawing.
- * The transformation from user space into device space is done with
- * the current <code>Transform</code> in the <code>Graphics2D</code>.
- * The specified transformation is applied to the image before the
- * transform attribute in the <code>Graphics2D</code> context is applied.
- * The rendering attributes applied include the <code>Clip</code>,
- * <code>Transform</code>, and <code>Composite</code> attributes. Note
- * that no rendering is done if the specified transform is
- * noninvertible.
- * @param img the image to be rendered
- * @param xform the transformation from image space into user space
- * @see #transform
- * @see #setTransform
- * @see #setComposite
- * @see #clip
- * @see #setClip
- */
- public void drawRenderedImage(RenderedImage img, AffineTransform xform) {
- //NYI
- }
-
- /**
- * Renders a
- * {@link RenderableImage},
- * applying a transform from image space into user space before drawing.
- * The transformation from user space into device space is done with
- * the current <code>Transform</code> in the <code>Graphics2D</code>.
- * The specified transformation is applied to the image before the
- * transform attribute in the <code>Graphics2D</code> context is applied.
- * The rendering attributes applied include the <code>Clip</code>,
- * <code>Transform</code>, and <code>Composite</code> attributes. Note
- * that no rendering is done if the specified transform is
- * noninvertible.
- * <p>
- * Rendering hints set on the <code>Graphics2D</code> object might
- * be used in rendering the <code>RenderableImage</code>.
- * If explicit control is required over specific hints recognized by a
- * specific <code>RenderableImage</code>, or if knowledge of which hints
- * are used is required, then a <code>RenderedImage</code> should be
- * obtained directly from the <code>RenderableImage</code>
- * and rendered using
- * {@link #drawRenderedImage(RenderedImage, AffineTransform) drawRenderedImage}.
- * @param img the image to be rendered
- * @param xform the transformation from image space into user space
- * @see #transform
- * @see #setTransform
- * @see #setComposite
- * @see #clip
- * @see #setClip
- * @see #drawRenderedImage
- */
- public void drawRenderableImage(RenderableImage img,
- AffineTransform xform) {
- //NYI
- }
-
- /**
- * Renders the text specified by the specified <code>String</code>,
- * using the current <code>Font</code> and <code>Paint</code> attributes
- * in the <code>Graphics2D</code> context.
- * The baseline of the first character is at position
- * (<i>x</i>, <i>y</i>) in the User Space.
- * The rendering attributes applied include the <code>Clip</code>,
- * <code>Transform</code>, <code>Paint</code>, <code>Font</code> and
- * <code>Composite</code> attributes. For characters in script systems
- * such as Hebrew and Arabic, the glyphs can be rendered from right to
- * left, in which case the coordinate supplied is the location of the
- * leftmost character on the baseline.
- * @param s the <code>String</code> to be rendered
- * @param x the coordinate where the <code>String</code>
- * should be rendered
- * @param y the coordinate where the <code>String</code>
- * should be rendered
- * @see #setPaint
- * @see java.awt.Graphics#setColor
- * @see java.awt.Graphics#setFont
- * @see #setTransform
- * @see #setComposite
- * @see #setClip
- */
- public void drawString(String s, float x, float y) {
- preparePainting();
-
- Font fontState;
- AffineTransform fontTransform = null;
- if (ovFontState == null) {
- java.awt.Font gFont = getFont();
- fontTransform = gFont.getTransform();
- String n = gFont.getFamily();
- if (n.equals("sanserif")) {
- n = "sans-serif";
- }
- float siz = gFont.getSize2D();
- String style = gFont.isItalic() ? "italic" : "normal";
- int weight = gFont.isBold() ? Font.BOLD : Font.NORMAL;
- FontTriplet triplet = fontInfo.fontLookup(n, style, weight);
- fontState = fontInfo.getFontInstance(triplet, (int)(siz * 1000 + 0.5));
- } else {
- fontState = fontInfo.getFontInstance(
- ovFontState.getFontTriplet(), ovFontState.getFontSize());
- ovFontState = null;
- }
- String name;
- float size;
- name = fontState.getFontName();
- size = (float)fontState.getFontSize() / 1000f;
-
- if ((!name.equals(this.currentFontName))
- || (size != this.currentFontSize)) {
- this.currentFontName = name;
- this.currentFontSize = size;
- currentStream.write("/" + name + " " + size + " Tf\n");
-
- }
-
- currentStream.write("q\n");
-
- Color c = getColor();
- applyColor(c, true);
- applyPaint(getPaint(), true);
- int salpha = c.getAlpha();
-
- if (salpha != 255) {
- Map vals = new java.util.HashMap();
- vals.put(PDFGState.GSTATE_ALPHA_NONSTROKE, new Float(salpha / 255f));
- PDFGState gstate = pdfDoc.getFactory().makeGState(
- vals, graphicsState.getGState());
- resourceContext.addGState(gstate);
- currentStream.write("/" + gstate.getName() + " gs\n");
- }
-
- Map kerning = null;
- boolean kerningAvailable = false;
-
- kerning = fontState.getKerning();
- if (kerning != null && !kerning.isEmpty()) {
- kerningAvailable = true;
- }
-
- // This assumes that *all* CIDFonts use a /ToUnicode mapping
- boolean useMultiByte = false;
- org.apache.fop.fonts.Typeface f =
- (org.apache.fop.fonts.Typeface)fontInfo.getFonts().get(name);
- if (f instanceof LazyFont) {
- if (((LazyFont) f).getRealFont() instanceof CIDFont) {
- useMultiByte = true;
- }
- } else if (f instanceof CIDFont) {
- useMultiByte = true;
- }
-
- // String startText = useMultiByte ? "<FEFF" : "(";
- String startText = useMultiByte ? "<" : "(";
- String endText = useMultiByte ? "> " : ") ";
-
- AffineTransform trans = getTransform();
- //trans.translate(x, y);
- double[] vals = new double[6];
- trans.getMatrix(vals);
-
- concatMatrix(vals);
- Shape imclip = getClip();
- writeClip(imclip);
-
- currentStream.write("BT\n");
-
- AffineTransform localTransform = new AffineTransform();
- localTransform.translate(x, y);
- if (fontTransform != null) {
- localTransform.concatenate(fontTransform);
- }
- localTransform.scale(1, -1);
- double[] lt = new double[6];
- localTransform.getMatrix(lt);
- currentStream.write(PDFNumber.doubleOut(lt[0]) + " "
- + PDFNumber.doubleOut(lt[1]) + " " + PDFNumber.doubleOut(lt[2]) + " "
- + PDFNumber.doubleOut(lt[3]) + " " + PDFNumber.doubleOut(lt[4]) + " "
- + PDFNumber.doubleOut(lt[5]) + " Tm [" + startText);
-
- int l = s.length();
-
- for (int i = 0; i < l; i++) {
- char ch = fontState.mapChar(s.charAt(i));
-
- if (!useMultiByte) {
- if (ch > 127) {
- currentStream.write("\\");
- currentStream.write(Integer.toOctalString((int)ch));
- } else {
- switch (ch) {
- case '(':
- case ')':
- case '\\':
- currentStream.write("\\");
- break;
- }
- currentStream.write(ch);
- }
- } else {
- currentStream.write(PDFText.toUnicodeHex(ch));
- }
-
- if (kerningAvailable && (i + 1) < l) {
- addKerning(currentStream, (new Integer((int)ch)),
- (new Integer((int)fontState.mapChar(s.charAt(i + 1)))),
- kerning, startText, endText);
- }
-
- }
- currentStream.write(endText);
-
-
- currentStream.write("] TJ\n");
-
- currentStream.write("ET\n");
- currentStream.write("Q\n");
- }
-
- private void addKerning(StringWriter buf, Integer ch1, Integer ch2,
- Map kerning, String startText,
- String endText) {
- preparePainting();
- Map kernPair = (Map)kerning.get(ch1);
-
- if (kernPair != null) {
- Integer width = (Integer)kernPair.get(ch2);
- if (width != null) {
- currentStream.write(endText + (-width.intValue()) + " " + startText);
- }
- }
- }
-
- /**
- * Renders the text of the specified iterator, using the
- * <code>Graphics2D</code> context's current <code>Paint</code>. The
- * iterator must specify a font
- * for each character. The baseline of the
- * first character is at position (<i>x</i>, <i>y</i>) in the
- * User Space.
- * The rendering attributes applied include the <code>Clip</code>,
- * <code>Transform</code>, <code>Paint</code>, and
- * <code>Composite</code> attributes.
- * For characters in script systems such as Hebrew and Arabic,
- * the glyphs can be rendered from right to left, in which case the
- * coordinate supplied is the location of the leftmost character
- * on the baseline.
- * @param iterator the iterator whose text is to be rendered
- * @param x the coordinate where the iterator's text is to be
- * rendered
- * @param y the coordinate where the iterator's text is to be
- * rendered
- * @see #setPaint
- * @see java.awt.Graphics#setColor
- * @see #setTransform
- * @see #setComposite
- * @see #setClip
- */
- public void drawString(AttributedCharacterIterator iterator, float x,
- float y) {
- preparePainting();
-
- Font fontState = null;
-
- Shape imclip = getClip();
- writeClip(imclip);
- Color c = getColor();
- applyColor(c, true);
- applyPaint(getPaint(), true);
-
- boolean fill = true;
- boolean stroke = false;
- if (true) {
- Stroke currentStroke = getStroke();
- stroke = true;
- applyStroke(currentStroke);
- applyColor(c, false);
- applyPaint(getPaint(), false);
- }
-
- currentStream.write("BT\n");
-
- // set text rendering mode:
- // 0 - fill, 1 - stroke, 2 - fill then stroke
- int textr = 0;
- if (fill && stroke) {
- textr = 2;
- } else if (stroke) {
- textr = 1;
- }
- currentStream.write(textr + " Tr\n");
-
- AffineTransform trans = getTransform();
- trans.translate(x, y);
- double[] vals = new double[6];
- trans.getMatrix(vals);
-
- for (char ch = iterator.first(); ch != CharacterIterator.DONE;
- ch = iterator.next()) {
- //Map attr = iterator.getAttributes();
-
- String name = fontState.getFontName();
- int size = fontState.getFontSize();
- if ((!name.equals(this.currentFontName))
- || (size != this.currentFontSize)) {
- this.currentFontName = name;
- this.currentFontSize = size;
- currentStream.write("/" + name + " " + (size / 1000)
- + " Tf\n");
-
- }
-
- currentStream.write(PDFNumber.doubleOut(vals[0], DEC) + " "
- + PDFNumber.doubleOut(vals[1], DEC) + " "
- + PDFNumber.doubleOut(vals[2], DEC) + " "
- + PDFNumber.doubleOut(vals[3], DEC) + " "
- + PDFNumber.doubleOut(vals[4], DEC) + " "
- + PDFNumber.doubleOut(vals[5], DEC) + " Tm (" + ch
- + ") Tj\n");
- }
-
- currentStream.write("ET\n");
- }
-
- /**
- * Fills the interior of a <code>Shape</code> using the settings of the
- * <code>Graphics2D</code> context. The rendering attributes applied
- * include the <code>Clip</code>, <code>Transform</code>,
- * <code>Paint</code>, and <code>Composite</code>.
- * @param s the <code>Shape</code> to be filled
- * @see #setPaint
- * @see java.awt.Graphics#setColor
- * @see #transform
- * @see #setTransform
- * @see #setComposite
- * @see #clip
- * @see #setClip
- */
- public void fill(Shape s) {
- preparePainting();
-
- //Transparency shortcut
- Color c;
- c = getBackground();
- if (c.getAlpha() == 0) {
- c = getColor();
- if (c.getAlpha() == 0) {
- return;
- }
- }
-
- AffineTransform trans = getTransform();
- double[] tranvals = new double[6];
- trans.getMatrix(tranvals);
-
- Shape imclip = getClip();
- boolean newClip = graphicsState.checkClip(imclip);
- boolean newTransform = graphicsState.checkTransform(trans)
- && !trans.isIdentity();
-
- if (newClip || newTransform) {
- currentStream.write("q\n");
- graphicsState.push();
- if (newTransform) {
- concatMatrix(tranvals);
- }
- if (newClip) {
- writeClip(imclip);
- }
- }
-
- if (c.getAlpha() != 255) {
- Map vals = new java.util.HashMap();
- vals.put(PDFGState.GSTATE_ALPHA_NONSTROKE,
- new Float(c.getAlpha() / 255f));
- PDFGState gstate = pdfDoc.getFactory().makeGState(
- vals, graphicsState.getGState());
- resourceContext.addGState(gstate);
- currentStream.write("/" + gstate.getName() + " gs\n");
- }
-
- c = getColor();
- if (graphicsState.setColor(c)) {
- applyColor(c, true);
- }
- c = getBackground();
- if (graphicsState.setBackColor(c)) {
- applyColor(c, false);
- }
-
- Paint paint = getPaint();
- if (graphicsState.setPaint(paint)) {
- if (!applyPaint(paint, true)) {
- // Use the shape to 'clip' the paint contents.
- applyUnknownPaint(paint, s);
-
- if (newClip || newTransform) {
- currentStream.write("Q\n");
- graphicsState.pop();
- }
- return;
- }
- }
-
- //PathIterator iter = s.getPathIterator(getTransform());
- PathIterator iter = s.getPathIterator(IDENTITY_TRANSFORM);
- processPathIterator(iter);
- doDrawing(true, false,
- iter.getWindingRule() == PathIterator.WIND_EVEN_ODD);
- if (newClip || newTransform) {
- currentStream.write("Q\n");
- graphicsState.pop();
- }
- }
-
- /**
- * Processes a path iterator generating the necessary painting operations.
- * @param iter PathIterator to process
- */
- public void processPathIterator(PathIterator iter) {
- while (!iter.isDone()) {
- double[] vals = new double[6];
- int type = iter.currentSegment(vals);
- switch (type) {
- case PathIterator.SEG_CUBICTO:
- currentStream.write(PDFNumber.doubleOut(vals[0], DEC) + " "
- + PDFNumber.doubleOut(vals[1], DEC) + " "
- + PDFNumber.doubleOut(vals[2], DEC) + " "
- + PDFNumber.doubleOut(vals[3], DEC) + " "
- + PDFNumber.doubleOut(vals[4], DEC) + " "
- + PDFNumber.doubleOut(vals[5], DEC) + " c\n");
- break;
- case PathIterator.SEG_LINETO:
- currentStream.write(PDFNumber.doubleOut(vals[0], DEC) + " "
- + PDFNumber.doubleOut(vals[1], DEC) + " l\n");
- break;
- case PathIterator.SEG_MOVETO:
- currentStream.write(PDFNumber.doubleOut(vals[0], DEC) + " "
- + PDFNumber.doubleOut(vals[1], DEC) + " m\n");
- break;
- case PathIterator.SEG_QUADTO:
- currentStream.write(PDFNumber.doubleOut(vals[0], DEC) + " "
- + PDFNumber.doubleOut(vals[1], DEC) + " "
- + PDFNumber.doubleOut(vals[2], DEC) + " "
- + PDFNumber.doubleOut(vals[3], DEC) + " y\n");
- break;
- case PathIterator.SEG_CLOSE:
- currentStream.write("h\n");
- break;
- default:
- break;
- }
- iter.next();
- }
- }
-
- /**
- * Do the PDF drawing command.
- * This does the PDF drawing command according to fill
- * stroke and winding rule.
- *
- * @param fill true if filling the path
- * @param stroke true if stroking the path
- * @param nonzero true if using the non-zero winding rule
- */
- protected void doDrawing(boolean fill, boolean stroke, boolean nonzero) {
- preparePainting();
- if (fill) {
- if (stroke) {
- if (nonzero) {
- currentStream.write("B*\n");
- } else {
- currentStream.write("B\n");
- }
- } else {
- if (nonzero) {
- currentStream.write("f*\n");
- } else {
- currentStream.write("f\n");
- }
- }
- } else {
- // if (stroke)
- currentStream.write("S\n");
- }
- }
-
- /**
- * Returns the device configuration associated with this
- * <code>Graphics2D</code>.
- *
- * @return the PDF graphics configuration
- */
- public GraphicsConfiguration getDeviceConfiguration() {
- return new PDFGraphicsConfiguration();
- }
-
- /**
- * Used to create proper font metrics
- */
- private Graphics2D fmg;
-
- {
- BufferedImage bi = new BufferedImage(1, 1,
- BufferedImage.TYPE_INT_ARGB);
-
- fmg = bi.createGraphics();
- }
-
- /**
- * Gets the font metrics for the specified font.
- * @return the font metrics for the specified font.
- * @param f the specified font
- * @see java.awt.Graphics#getFont
- * @see java.awt.FontMetrics
- * @see java.awt.Graphics#getFontMetrics()
- */
- public java.awt.FontMetrics getFontMetrics(java.awt.Font f) {
- return fmg.getFontMetrics(f);
- }
-
- /**
- * Sets the paint mode of this graphics context to alternate between
- * this graphics context's current color and the new specified color.
- * This specifies that logical pixel operations are performed in the
- * XOR mode, which alternates pixels between the current color and
- * a specified XOR color.
- * <p>
- * When drawing operations are performed, pixels which are the
- * current color are changed to the specified color, and vice versa.
- * <p>
- * Pixels that are of colors other than those two colors are changed
- * in an unpredictable but reversible manner; if the same figure is
- * drawn twice, then all pixels are restored to their original values.
- * @param c1 the XOR alternation color
- */
- public void setXORMode(Color c1) {
- //NYI
- }
-
-
- /**
- * Copies an area of the component by a distance specified by
- * <code>dx</code> and <code>dy</code>. From the point specified
- * by <code>x</code> and <code>y</code>, this method
- * copies downwards and to the right. To copy an area of the
- * component to the left or upwards, specify a negative value for
- * <code>dx</code> or <code>dy</code>.
- * If a portion of the source rectangle lies outside the bounds
- * of the component, or is obscured by another window or component,
- * <code>copyArea</code> will be unable to copy the associated
- * pixels. The area that is omitted can be refreshed by calling
- * the component's <code>paint</code> method.
- * @param x the <i>x</i> coordinate of the source rectangle.
- * @param y the <i>y</i> coordinate of the source rectangle.
- * @param width the width of the source rectangle.
- * @param height the height of the source rectangle.
- * @param dx the horizontal distance to copy the pixels.
- * @param dy the vertical distance to copy the pixels.
- */
- public void copyArea(int x, int y, int width, int height, int dx,
- int dy) {
- //NYI
- }
-
- }
|