I have a few suggestions how to improve the PDFGraphics2D class to be more conforming to the java.awt.Graphics2D interface. I use the FOP library for direct generation of PDF from a Java application (i.e. I do not use FOP for XML to PDF translation), but I think that the suggestions might be relevant to the FO processing as well. I found the following problems in the class implementation: 1) When rendering text, the class uses Font.getSize instead of Font.getSize2D. This means that the text scales only with integer resolution, which results in big errors when rendering text with non-integer size. 2) Text rendering ignores Font's transformation matrix. The matrix is useful e.g. for generating slanted or narrowed/widened text. Enabling it is very easy (see below). 3) For some reason, drawImage(Image, int, int, ImageObserver) translates the white opaque pixels to transparent. I suggest not to set white color as transparent, but to change fully transparent pixels to some less usual color (I used 255,255,254), and setting this color as transparent. 4) I also changed the text rendering to automatically convert texts containing "unsupported chars" (chars that do not display in PDF correctly using default fonts) to curves. But this issue you probably solve elsewhere. It would be also nice to have the PDFGraphics2D.currentStream field protected, so that I can fix these problems by subclassing the PDFGraphics2D and override the problematic methods instead of rebuilding the original FOP jar file. Below see the commented diff of the PDFGraphics2D.java file (# precedes comments added to the diff) The diffis made against fop-0.20.5 source distribution. (unfortunately the line numbers may be slightly incorrect as I reformated the source code) The diff: ---------------------------------------------------------------- 71a72 > import java.awt.font.GlyphVector; 148c149 < protected int currentFontSize; --- > protected float currentFontSize; # in public boolean drawImage(Image, int, int, ImageObserver) # change fully transparent pixels (alpha = 0) to (255,255,254) 360c361,365 < if (alpha != 255) { --- > if (alpha == 0) { > result[count++] = (byte) (0xFF); > result[count++] = (byte) (0xFF); > result[count++] = (byte) (0xFE); > } else if (alpha != 255) { # in class TempImage #change transparent color to our special color 429c434 < PDFColor transparent = new PDFColor(255, 255, 255); --- > PDFColor transparent = new PDFColor(255, 255, 254); # a method that draws text as curves 923a929,933 > private void drawStringAsGlyphs(String s, float x, float y) { > GlyphVector gv = getFont().createGlyphVector(getFontRenderContext(), s); > fill(gv.getOutline(x, y)); > } > # in public void drawString(String, float, float) # draw unsupported text as string 949c959,962 < // System.out.println("drawString(String)"); --- > if (!isSupportedString(s)) { > drawStringAsGlyphs(s, x, y); > return; > } # save font's transform 950a964 > AffineTransform fontTransform = null; 952a967 > fontTransform = gFont.getTransform(); #use fractional height instead of integer height 957c972 < int siz = gFont.getSize(); --- > float siz = gFont.getSize2D(); 962c977 < weight, siz * 1000, 0); --- > weight, (int) (siz * 1000 + 0.5), 0); 971c986 < int size; --- > float size; 973c988 < size = fontState.getFontSize() / 1000; --- > size = fontState.getFontSize() / 1000f; # apply font transform if neccessary 1019a1035,1037 > if (fontTransform != null && !fontTransform.isIdentity()) > trans.concatenate(fontTransform); > # new helper methods determining whether string is "supported" 1070a1089,1115 > private boolean isSupportedString(String s) { > for (int i = 0; i < s.length(); ++i) > if (!isSupportedChar(s.charAt(i))) > return false; > return true; > } > > private boolean isSupportedChar(char c) { > if (c >= 0x20 && c < 0xFF) > return true; > return ("\u20AC\u201A\u0192\u201E\u2026\u2020\u2021\u02C6\u2030 \u0160" > + "\u2039\u004F\u017D\u2018\u2019\u201C\u201D\u2022\u2013 \u2014" > + "\u02DC\u2122\u0161\u203A\u006F\u017E\u0178\u00A1\u00A2 \u00A3" > + "\u00A4\u00A5\u00A6\u00A7\u00A8\u00A9 \u00AA\u00AB\u00AC\u002D" > + "\u00AE\u00AF\u00B0\u00B1\u00B2\u00B3\u00B4\u03BC\u00B6 \u00B7" > + "\u00B8\u00B9\u00BA\u00BB\u00BC\u00BD\u00BE\u00BF\u00C0 \u00C1" > + "\u00C2\u00C3\u00C4\u00C5\u00C6\u00C7\u00C8\u00C9 \u00CA\u00CB" > + "\u00CC\u00CD\u00CE\u00CF\u00D0\u00D1\u00D2\u00D3\u00D4 \u00D5" > + "\u00D6\u00D7\u00D8\u00D9 \u00DA\u00DB\u00DC\u00DD\u00DE\u00DF" > + "\u00E0\u00E1\u00E2\u00E3\u00E4\u00E5\u00E6\u00E7\u00E8 \u00E9" > + "\u00EA\u00EB\u00EC\u00ED\u00EE\u00EF\u00F0\u00F1\u00F2 \u00F3" > + "\u00F4\u00F5\u00F6\u00F7\u00F8\u00F9 \u00FA\u00FB\u00FC\u00FD" > + "\u00FE\u00FF\u004F\u006F\u0160\u0161\u0178 \u017D\u017E\u0192" > + "\u2013\u2014\u2018\u2019\u201A\u201C\u201D\u201E\u2020 \u2021" > + "\u2022\u2026\u2030\u2039\u203A\u20AC\u2122").indexOf(c) ! = -1; > } > 1367a1413 > 1371a1418,1425 > > // needed for compiling under jdk1.4 > > public java.awt.image.VolatileImage createCompatibleVolatileImage( > int width, int height, int transparency) { > return null; > } >
Created attachment 17756 [details] the new suggested implementation of PDFGraphics2D.java
I'm happy to see that the PDFGraphics2D is actually useful outside of FOP and Batik. And thanks for your suggestions to improve the code. However, I would like to encourage you to download the latest FOP source code (FOP Trunk) from Subversion: http://xmlgraphics.apache.org/fop/download.html Subversion checkout URL: http://svn.apache.org/repos/asf/xmlgraphics/fop/trunk/ There have been major improvements in that area although most of what you suggest hasn't been changed. Please apply the changes to FOP Trunk and attach a patch generated by the "svn diff" command to this Bugzilla issue. We will then review your changes and integrate them. Please note that the FOP 0.20.5 development line has been frozen.
Created attachment 17757 [details] PDFGraphics2D patched w.r.t. fop-0.91 I hope that the patch file is what you want, I'm not familiar with the subversion system, I generated it using TortoiseSVN. Few comments, as things changed quite a bit since my last post: 1) the code for generating string output mysteriously changed since last time, the translation of text was moved from graphic's global transform to text's local transform, so it was more difficult to combine with font's transform, but I did it anyway, so hopefully it works. I tested it on two typical cases: narrowed text and slanted text. Please note that I did not change drawString with AttributedCharacterIterator as I do not use it, but the change is analogous. Suprisingly this second method uses the "old" way of combining graphic and text transforms, i.e. the text translation is combined with graphic transform, and the text uses simple Y inverse transform 2) the transparency handling changed a lot, but the bug is still there. It seems to me that it is sufficient to comment out forgotten BitmapImage.setTransparent line. The transparency seems to be correctly handled by a companion mask stream added to the primary bitmap stream.
Created attachment 17758 [details] a sample file for handling missing glyphs Btw I moved the stuff for text->curve replacement to a subclass of PDFGraphics2D, so I attach it for those who are interested; but I suspect it won't be very useful of the FOP library itself :-|
Patch applied: http://svn.apache.org/viewcvs?rev=386954&view=rev Thank you, Michal, for this useful patch. Concerning your special code about stroking text that contains unsupported characters: This would actually be useful but not in the way you've implemented it. Our font subsystem has methods to tell you which characters are available from a font (Font.hasChar(char)). So, you can do this in a generic way and don't have to hard-code the characters.
batch transition pre-FOP1.0 resolved+fixed bugs to closed+fixed