From: Jeremias Maerki Date: Sat, 3 Apr 2004 13:35:09 +0000 (+0000) Subject: Some improvements on the quality fallback for text (use stroking when in doubt). X-Git-Tag: Root_Temp_KnuthStylePageBreaking~775 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=78e4c46bdf3e66a29a605c924d1969bb2211c5b8;p=xmlgraphics-fop.git Some improvements on the quality fallback for text (use stroking when in doubt). PSGenerator returns the PS level to use. Start bringing back bitmap support. git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@197487 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/src/java/org/apache/fop/render/ps/EPSDocumentGraphics2D.java b/src/java/org/apache/fop/render/ps/EPSDocumentGraphics2D.java index 5d46ad533..340ac85de 100644 --- a/src/java/org/apache/fop/render/ps/EPSDocumentGraphics2D.java +++ b/src/java/org/apache/fop/render/ps/EPSDocumentGraphics2D.java @@ -20,6 +20,8 @@ package org.apache.fop.render.ps; import java.io.IOException; +import org.apache.fop.apps.Version; + /** * This class is a wrapper for the AbstractPSDocumentGraphics2D that * is used to create EPS (Encapsulated PostScript) files instead of PS file. @@ -53,13 +55,14 @@ public class EPSDocumentGraphics2D extends AbstractPSDocumentGraphics2D { //PostScript Header gen.writeln(DSCConstants.PS_ADOBE_30 + " " + DSCConstants.EPSF_30); gen.writeDSCComment(DSCConstants.CREATOR, - new String[] {"FOP EPS Transcoder for SVG"}); + new String[] {"Apache FOP " + Version.getVersion() + + ": EPS Transcoder for SVG"}); gen.writeDSCComment(DSCConstants.CREATION_DATE, new Object[] {new java.util.Date()}); gen.writeDSCComment(DSCConstants.PAGES, new Integer(0)); gen.writeDSCComment(DSCConstants.BBOX, new Object[] {ZERO, ZERO, pagewidth, pageheight}); - gen.writeDSCComment(DSCConstants.LANGUAGE_LEVEL, new Integer(2)); + gen.writeDSCComment(DSCConstants.LANGUAGE_LEVEL, new Integer(gen.getPSLevel())); gen.writeDSCComment(DSCConstants.END_COMMENTS); //Prolog diff --git a/src/java/org/apache/fop/render/ps/PSDocumentGraphics2D.java b/src/java/org/apache/fop/render/ps/PSDocumentGraphics2D.java index 93a66fe8e..ce419f722 100644 --- a/src/java/org/apache/fop/render/ps/PSDocumentGraphics2D.java +++ b/src/java/org/apache/fop/render/ps/PSDocumentGraphics2D.java @@ -25,6 +25,7 @@ import java.io.IOException; //FOP import org.apache.fop.apps.Document; +import org.apache.fop.apps.Version; import org.apache.fop.fonts.FontSetup; /** @@ -91,7 +92,8 @@ public class PSDocumentGraphics2D extends AbstractPSDocumentGraphics2D { //PostScript Header gen.writeln(DSCConstants.PS_ADOBE_30); gen.writeDSCComment(DSCConstants.CREATOR, - new String[] {"FOP PostScript Transcoder for SVG"}); + new String[] {"Apache FOP " + Version.getVersion() + + ": PostScript Transcoder for SVG"}); gen.writeDSCComment(DSCConstants.CREATION_DATE, new Object[] {new java.util.Date()}); gen.writeDSCComment(DSCConstants.PAGES, PSGenerator.ATEND); diff --git a/src/java/org/apache/fop/render/ps/PSGenerator.java b/src/java/org/apache/fop/render/ps/PSGenerator.java index 3f739eac9..4a64f626c 100644 --- a/src/java/org/apache/fop/render/ps/PSGenerator.java +++ b/src/java/org/apache/fop/render/ps/PSGenerator.java @@ -68,6 +68,15 @@ public class PSGenerator { return this.out; } + /** + * Returns the selected PostScript level. + * (Hardcoded to level 3 for the moment.) + * @return the PostScript level + */ + public int getPSLevel() { + return 3; + } + /** * Writes a newline character to the OutputStream. * diff --git a/src/java/org/apache/fop/render/ps/PSGraphics2D.java b/src/java/org/apache/fop/render/ps/PSGraphics2D.java index 97ab34417..e527450d7 100644 --- a/src/java/org/apache/fop/render/ps/PSGraphics2D.java +++ b/src/java/org/apache/fop/render/ps/PSGraphics2D.java @@ -38,6 +38,7 @@ import java.awt.Shape; import java.awt.Stroke; import java.awt.TexturePaint; import java.awt.color.ColorSpace; +import java.awt.color.ICC_Profile; import java.awt.font.FontRenderContext; import java.awt.font.GlyphVector; import java.awt.geom.AffineTransform; @@ -54,9 +55,12 @@ import java.io.IOException; //Batik import org.apache.batik.ext.awt.g2d.AbstractGraphics2D; import org.apache.batik.ext.awt.g2d.GraphicContext; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; //FOP import org.apache.fop.fonts.Font; +import org.apache.fop.image.FopImage; import org.apache.fop.apps.Document; /** @@ -73,6 +77,9 @@ import org.apache.fop.apps.Document; */ public class PSGraphics2D extends AbstractGraphics2D { + /** the logger for this class */ + protected Log log = LogFactory.getLog(PSTextPainter.class); + /** the PostScript generator being created */ protected PSGenerator gen; @@ -196,7 +203,7 @@ public class PSGraphics2D extends AbstractGraphics2D { public boolean drawImage(Image img, int x, int y, ImageObserver observer) { preparePainting(); - System.out.println("drawImage: x, y " + img.getClass().getName()); + log.debug("drawImage: " + x + ", " + y + " " + img.getClass().getName()); final int width = img.getWidth(observer); final int height = img.getHeight(observer); @@ -249,24 +256,21 @@ public class PSGraphics2D extends AbstractGraphics2D { // error break; } -/* + try { - FopImage fopimg = new TempImage(width, height, result, mask); + FopImage fopimg = new TempImage(width, height, result, null); AffineTransform at = getTransform(); - double[] matrix = new double[6]; - at.getMatrix(matrix); gen.saveGraphicsState(); Shape imclip = getClip(); writeClip(imclip); - // psRenderer.write("" + matrix[0] + " " + matrix[1] + - // " " + matrix[2] + " " + matrix[3] + " " + - // matrix[4] + " " + matrix[5] + " cm\n"); - //psRenderer.renderBitmap(fopimg, x, y, width, height); + gen.concatMatrix(at); + PSImageUtils.renderFopImage(fopimg, + 1000 * x, 1000 * y, 1000 * width, 1000 * height, gen); gen.restoreGraphicsState(); } catch (IOException ioe) { handleIOException(ioe); } -*/ + return true; } @@ -280,40 +284,44 @@ public class PSGraphics2D extends AbstractGraphics2D { BufferedImage.TYPE_INT_ARGB); } -/* + class TempImage implements FopImage { - int height; - int width; - int bitsPerPixel; - ColorSpace colorSpace; - int bitmapSiye; - byte[] bitmaps; - byte[] mask; - PDFColor transparent = new PDFColor(255, 255, 255); - - TempImage(int width, int height, byte[] result, + private int height; + private int width; + private int bitsPerPixel; + private ColorSpace colorSpace; + private int bitmapSiye; + private byte[] bitmaps; + private byte[] mask; + private Color transparentColor; + + TempImage(int width, int height, byte[] bitmaps, byte[] mask) { this.height = height; this.width = width; this.bitsPerPixel = 8; - this.colorSpace = ColorSpace.new PDFColorSpace(PDFColorSpace.DEVICE_RGB); - this.bitmaps = result; + this.colorSpace = ColorSpace.getInstance(ColorSpace.CS_sRGB); + this.bitmaps = bitmaps; this.mask = mask; } - public boolean load(int type, FOUserAgent ua) { - return true; - } - public String getMimeType() { - return ""; + return "application/octet-stream"; } - public String getURL() { - return "" + this.bitmaps; + /** + * @see org.apache.fop.image.FopImage#load(int, org.apache.commons.logging.Log) + */ + public boolean load(int type, Log logger) { + switch (type) { + case FopImage.DIMENSIONS: break; + case FopImage.BITMAP: break; + case FopImage.ORIGINAL_DATA: break; + default: throw new RuntimeException("Unknown load type: " + type); + } + return true; } - // image size public int getWidth() { return this.width; } @@ -322,23 +330,25 @@ public class PSGraphics2D extends AbstractGraphics2D { return this.height; } - // DeviceGray, DeviceRGB, or DeviceCMYK public ColorSpace getColorSpace() { return this.colorSpace; } - // bits per pixel + public ICC_Profile getICCProfile() { + return null; + } + public int getBitsPerPixel() { return this.bitsPerPixel; } // For transparent images public boolean isTransparent() { - return this.transparent != null; + return getTransparentColor() != null; } - public PDFColor getTransparentColor() { - return this.transparent; + public Color getTransparentColor() { + return this.transparentColor; } public boolean hasSoftMask() { @@ -349,9 +359,6 @@ public class PSGraphics2D extends AbstractGraphics2D { return this.mask; } - // get the image bytes, and bytes properties - - // get uncompressed image bytes public byte[] getBitmaps() { return this.bitmaps; } @@ -372,22 +379,8 @@ public class PSGraphics2D extends AbstractGraphics2D { return 0; } - // return null if no corresponding PDFFilter - public PDFFilter getPDFFilter() { - return null; - } - - // release memory - public void close() { - //nop - } - - public ICC_Profile getICCProfile() { - return null; - } - } -*/ + /** * Draws as much of the specified image as has already been scaled @@ -426,7 +419,7 @@ public class PSGraphics2D extends AbstractGraphics2D { public boolean drawImage(Image img, int x, int y, int width, int height, ImageObserver observer) { preparePainting(); - System.out.println("drawImage"); + log.warn("NYI: drawImage"); return true; } @@ -458,7 +451,6 @@ public class PSGraphics2D extends AbstractGraphics2D { * @see java.awt.Graphics#create */ public void dispose() { - // System.out.println("dispose"); this.gen = null; this.font = null; this.currentColour = null; @@ -529,7 +521,6 @@ public class PSGraphics2D extends AbstractGraphics2D { public void draw(Shape s) { preparePainting(); try { - // System.out.println("draw(Shape)"); gen.saveGraphicsState(); Shape imclip = getClip(); writeClip(imclip); @@ -578,9 +569,9 @@ public class PSGraphics2D extends AbstractGraphics2D { protected void applyPaint(Paint paint, boolean fill) { preparePainting(); if (paint instanceof GradientPaint) { - //NYI + log.warn("NYI: Gradient paint"); } else if (paint instanceof TexturePaint) { - //NYI + log.warn("NYI: texture paint"); } } @@ -618,6 +609,7 @@ public class PSGraphics2D extends AbstractGraphics2D { case BasicStroke.CAP_SQUARE: gen.writeln("2 setlinecap"); break; + default: log.warn("Unsupported line cap: " + ec); } int lj = bs.getLineJoin(); @@ -631,6 +623,7 @@ public class PSGraphics2D extends AbstractGraphics2D { case BasicStroke.JOIN_BEVEL: gen.writeln("2 setlinejoin"); break; + default: log.warn("Unsupported line join: " + lj); } float lw = bs.getLineWidth(); gen.writeln(gen.formatDouble(1000 * lw) + " setlinewidth"); @@ -665,7 +658,7 @@ public class PSGraphics2D extends AbstractGraphics2D { */ public void drawRenderedImage(RenderedImage img, AffineTransform xform) { preparePainting(); - System.out.println("drawRenderedImage"); + log.warn("NYI: drawRenderedImage"); } /** @@ -701,7 +694,7 @@ public class PSGraphics2D extends AbstractGraphics2D { public void drawRenderableImage(RenderableImage img, AffineTransform xform) { preparePainting(); - System.out.println("drawRenderableImage"); + log.warn("NYI: drawRenderableImage"); } /** @@ -807,7 +800,7 @@ public class PSGraphics2D extends AbstractGraphics2D { */ public void drawStringAsText(String s, float x, float y) { preparePainting(); - //System.out.println("drawString('" + s + "', " + x + ", " + y + ")"); + log.trace("drawString('" + s + "', " + x + ", " + y + ")"); try { if (this.overrideFont == null) { java.awt.Font awtFont = getFont(); @@ -926,7 +919,7 @@ public class PSGraphics2D extends AbstractGraphics2D { public void drawString(AttributedCharacterIterator iterator, float x, float y) { preparePainting(); - System.err.println("drawString(AttributedCharacterIterator) NYI"); + log.warn("NYI: drawString(AttributedCharacterIterator)"); /* try { gen.writeln("BT"); @@ -974,7 +967,6 @@ public class PSGraphics2D extends AbstractGraphics2D { */ public void fill(Shape s) { preparePainting(); - // System.err.println("fill"); try { gen.saveGraphicsState(); Shape imclip = getClip(); @@ -1030,7 +1022,6 @@ public class PSGraphics2D extends AbstractGraphics2D { * @return the device configuration */ public GraphicsConfiguration getDeviceConfiguration() { - // System.out.println("getDeviceConviguration"); return GraphicsEnvironment.getLocalGraphicsEnvironment(). getDefaultScreenDevice().getDefaultConfiguration(); } @@ -1083,7 +1074,7 @@ public class PSGraphics2D extends AbstractGraphics2D { * @param c1 the XOR alternation color */ public void setXORMode(Color c1) { - System.out.println("setXORMode"); + log.warn("NYI: setXORMode"); } @@ -1108,7 +1099,7 @@ public class PSGraphics2D extends AbstractGraphics2D { */ public void copyArea(int x, int y, int width, int height, int dx, int dy) { - System.out.println("copyArea"); + log.warn("NYI: copyArea"); } /* --- for debugging diff --git a/src/java/org/apache/fop/render/ps/PSImageUtils.java b/src/java/org/apache/fop/render/ps/PSImageUtils.java new file mode 100644 index 000000000..7edc340ad --- /dev/null +++ b/src/java/org/apache/fop/render/ps/PSImageUtils.java @@ -0,0 +1,150 @@ +/* + * Copyright 2004 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.render.ps; + +import java.awt.color.ColorSpace; +import java.io.IOException; +import java.io.OutputStream; + +import org.apache.fop.image.FopImage; +import org.apache.fop.image.JpegImage; +import org.apache.fop.util.ASCII85OutputStream; +import org.apache.fop.util.Finalizable; +import org.apache.fop.util.FlateEncodeOutputStream; +import org.apache.fop.util.RunLengthEncodeOutputStream; + +/** + * Utility code for rendering images in PostScript. + */ +public class PSImageUtils { + + /** + * Renders an image to PostScript. + * @param img image to render + * @param x x position + * @param y y position + * @param w width + * @param h height + * @param gen PS generator + * @throws IOException In case of an I/O problem while rendering the image + */ + public static void renderFopImage(FopImage img, int x, int y, int w, int h, PSGenerator gen) throws IOException { + boolean iscolor = img.getColorSpace().getType() + != ColorSpace.CS_GRAY; + byte[] imgmap = img.getBitmaps(); + + gen.saveGraphicsState(); + if (img.getColorSpace().getType() == ColorSpace.TYPE_CMYK) { + gen.writeln("/DeviceCMYK setcolorspace"); + } else if (img.getColorSpace().getType() == ColorSpace.CS_GRAY) { + gen.writeln("/DeviceGray setcolorspace"); + } else { + gen.writeln("/DeviceRGB setcolorspace"); + } + + gen.writeln(x + " " + y + " translate"); + gen.writeln(w + " " + h + " scale"); + + gen.writeln("{{"); + // Template: (RawData is used for the EOF signal only) + // gen.write("/RawData currentfile filter def"); + // gen.write("/Data RawData [...] def"); + if (img instanceof JpegImage) { + gen.writeln("/RawData currentfile /ASCII85Decode filter def"); + gen.writeln("/Data RawData << >> /DCTDecode filter def"); + } else { + if (gen.getPSLevel() >= 3) { + gen.writeln("/RawData currentfile /ASCII85Decode filter def"); + gen.writeln("/Data RawData /FlateDecode filter def"); + } else { + gen.writeln("/RawData currentfile /ASCII85Decode filter def"); + gen.writeln("/Data RawData /RunLengthDecode filter def"); + } + } + gen.writeln("<<"); + gen.writeln(" /ImageType 1"); + gen.writeln(" /Width " + img.getWidth()); + gen.writeln(" /Height " + img.getHeight()); + gen.writeln(" /BitsPerComponent 8"); + if (img.getColorSpace().getType() == ColorSpace.TYPE_CMYK) { + if (false /*TODO img.invertImage()*/) { + gen.writeln(" /Decode [1 0 1 0 1 0 1 0]"); + } else { + gen.writeln(" /Decode [0 1 0 1 0 1 0 1]"); + } + } else if (iscolor) { + gen.writeln(" /Decode [0 1 0 1 0 1]"); + } else { + gen.writeln(" /Decode [0 1]"); + } + // Setup scanning for left-to-right and top-to-bottom + gen.writeln(" /ImageMatrix [" + img.getWidth() + " 0 0 " + + img.getHeight() + " 0 0]"); + + gen.writeln(" /DataSource Data"); + gen.writeln(">>"); + gen.writeln("image"); + /* the following two lines could be enabled if something still goes wrong + * gen.write("Data closefile"); + * gen.write("RawData flushfile"); + */ + gen.writeln("} stopped {handleerror} if"); + gen.writeln(" RawData flushfile"); + gen.writeln("} exec"); + + /* + * for (int y=0; y= 3) { + out = new FlateEncodeOutputStream(out); + } else { + out = new RunLengthEncodeOutputStream(out); + } + } + out.write(imgmap); + if (out instanceof Finalizable) { + ((Finalizable)out).finalizeStream(); + } else { + out.flush(); + } + + gen.writeln(""); + gen.restoreGraphicsState(); + } + + +} diff --git a/src/java/org/apache/fop/render/ps/PSRenderer.java b/src/java/org/apache/fop/render/ps/PSRenderer.java index 614e9922a..ef39dcc5f 100644 --- a/src/java/org/apache/fop/render/ps/PSRenderer.java +++ b/src/java/org/apache/fop/render/ps/PSRenderer.java @@ -324,6 +324,7 @@ public class PSRenderer extends AbstractRenderer { writeln(DSCConstants.PS_ADOBE_30); gen.writeDSCComment(DSCConstants.CREATOR, new String[] {"FOP " + this.producer}); gen.writeDSCComment(DSCConstants.CREATION_DATE, new Object[] {new java.util.Date()}); + gen.writeDSCComment(DSCConstants.LANGUAGE_LEVEL, new Integer(gen.getPSLevel())); gen.writeDSCComment(DSCConstants.PAGES, new Object[] {PSGenerator.ATEND}); gen.writeDSCComment(DSCConstants.END_COMMENTS); diff --git a/src/java/org/apache/fop/render/ps/PSTextPainter.java b/src/java/org/apache/fop/render/ps/PSTextPainter.java index 4112ac295..2c58813cc 100644 --- a/src/java/org/apache/fop/render/ps/PSTextPainter.java +++ b/src/java/org/apache/fop/render/ps/PSTextPainter.java @@ -42,6 +42,8 @@ import org.apache.batik.gvt.text.GVTAttributedCharacterIterator; import org.apache.batik.gvt.text.TextPaintInfo; import org.apache.batik.gvt.font.GVTFontFamily; import org.apache.batik.gvt.renderer.StrokingTextPainter; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.apache.fop.fonts.FontMetrics; import org.apache.fop.fonts.Font; @@ -64,6 +66,9 @@ import org.apache.fop.apps.Document; */ public class PSTextPainter implements TextPainter { + /** the logger for this class */ + protected Log log = LogFactory.getLog(PSTextPainter.class); + private Document document; /** @@ -117,6 +122,13 @@ public class PSTextPainter implements TextPainter { private boolean hasUnsupportedAttributes(AttributedCharacterIterator aci) { boolean hasunsupported = false; + String text = getText(aci); + Font font = makeFont(aci); + if (hasUnsupportedGlyphs(text, font)) { + log.trace("-> Unsupported glyphs found"); + hasunsupported = true; + } + TextPaintInfo tpi = (TextPaintInfo) aci.getAttribute( GVTAttributedCharacterIterator.TextAttribute.PAINT_INFO); if ((tpi != null) @@ -124,6 +136,7 @@ public class PSTextPainter implements TextPainter { || (tpi.strikethroughStroke != null) || (tpi.underlineStroke != null) || (tpi.overlineStroke != null))) { + log.trace("-> under/overlines etc. found"); hasunsupported = true; } @@ -132,6 +145,7 @@ public class PSTextPainter implements TextPainter { if (foreground instanceof Color) { Color col = (Color)foreground; if (col.getAlpha() != 255) { + log.trace("-> transparency found"); hasunsupported = true; } } @@ -139,18 +153,21 @@ public class PSTextPainter implements TextPainter { Object letSpace = aci.getAttribute( GVTAttributedCharacterIterator.TextAttribute.LETTER_SPACING); if (letSpace != null) { + log.trace("-> letter spacing found"); hasunsupported = true; } Object wordSpace = aci.getAttribute( GVTAttributedCharacterIterator.TextAttribute.WORD_SPACING); if (wordSpace != null) { + log.trace("-> word spacing found"); hasunsupported = true; } Object lengthAdjust = aci.getAttribute( GVTAttributedCharacterIterator.TextAttribute.LENGTH_ADJUST); if (lengthAdjust != null) { + log.trace("-> length adjustments found"); hasunsupported = true; } @@ -159,6 +176,7 @@ public class PSTextPainter implements TextPainter { if (writeMod != null && !GVTAttributedCharacterIterator.TextAttribute.WRITING_MODE_LTR.equals( writeMod)) { + log.trace("-> Unsupported writing modes found"); hasunsupported = true; } @@ -166,15 +184,20 @@ public class PSTextPainter implements TextPainter { GVTAttributedCharacterIterator.TextAttribute.VERTICAL_ORIENTATION); if (GVTAttributedCharacterIterator.TextAttribute.ORIENTATION_ANGLE.equals( vertOr)) { + log.trace("-> vertical orientation found"); hasunsupported = true; } Object rcDel = aci.getAttribute( GVTAttributedCharacterIterator.TextAttribute.TEXT_COMPOUND_DELIMITER); if (!(rcDel instanceof SVGOMTextElement)) { + log.trace("-> spans found"); hasunsupported = true; //Filter spans } + if (hasunsupported) { + log.trace("Unsupported attributes found in ACI, using StrokingTextPainter"); + } return hasunsupported; } @@ -343,6 +366,9 @@ public class PSTextPainter implements TextPainter { private Font makeFont(AttributedCharacterIterator aci) { Float fontSize = (Float)aci.getAttribute(TextAttribute.SIZE); + if (fontSize == null) { + fontSize = new Float(10.0f); + } String style = getStyle(aci); int weight = getWeight(aci); @@ -410,6 +436,19 @@ public class PSTextPainter implements TextPainter { return wordWidth / 1000f; } + private boolean hasUnsupportedGlyphs(String str, Font font) { + for (int i = 0; i < str.length(); i++) { + float charWidth; + char c = str.charAt(i); + if (!((c == ' ') || (c == '\n') || (c == '\r') || (c == '\t'))) { + if (!font.hasChar(c)) { + return true; + } + } + } + return false; + } + /** * Get the outline shape of the text characters. * This uses the StrokingTextPainter to get the outline