Attached is a unified diff with current CVS. This patch will render the PostScript file in same appearance (position and size of border, table and font) with PDF's. Although we noted that the size of border and table in PDF file is smaller than it should be. I have tested the code with Fop-0.20.3 but not current CVS.
Regards. Huikang -----Original Message----- From: Jeremias Maerki [mailto:[EMAIL PROTECTED]] Sent: Thursday, 23 May, 2002 5:48 PM To: [EMAIL PROTECTED] Subject: Re: PS Renderer patch Sure, that's great. All you have to do is make your changes against the current CVS, create a unified diff with CVS and send it to this list or create a new bugzilla entry with the diff as attachment. We will then see to include it as fast as possible. See here for more information: http://xml.apache.org/source.html > We are using FOP to generate PostScript and PDF file by XSL. It works fine. > > But we also found a little minor difference compare ps to pdf, such as, the > position and size of border, table and font, the vertical position of image. > You can test that with docs/examples/fo/border.fo by printing them out. We > like postscript very much since it can be handled by Java JPS directly and > dont want two version of XSL files. I have done some work with PSRenderer > and it outputs almost same with PDF. > > I wonder if I can submit it as a patch. Cheers, Jeremias Märki mailto:[EMAIL PROTECTED] OUTLINE AG Postfach 3954 - Rhynauerstr. 15 - CH-6002 Luzern Tel. +41 41 317 2020 - Fax +41 41 317 2029 Internet http://www.outline.ch --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, email: [EMAIL PROTECTED]
Index: xml-fop/src/org/apache/fop/render/ps/PSRenderer.java =================================================================== RCS file: /home/cvspublic/xml-fop/src/org/apache/fop/render/ps/PSRenderer.java,v retrieving revision 1.21 diff -u -r1.21 PSRenderer.java --- xml-fop/src/org/apache/fop/render/ps/PSRenderer.java 11 Apr 2002 09:33:31 -0000 1.21 +++ xml-fop/src/org/apache/fop/render/ps/PSRenderer.java 27 May 2002 01:09:46 +-0000 @@ -1,5 +1,5 @@ /* - * $Id: PSRenderer.java,v 1.21 2002/04/11 09:33:31 keiron Exp $ + * $Id: PSRenderer.java,v 1.15.2.1 2001/12/02 22:17:30 tore Exp $ * Copyright (C) 2001 The Apache Software Foundation. All rights reserved. * For details on use and redistribution please refer to the * LICENSE file included with these sources. @@ -8,9 +8,13 @@ package org.apache.fop.render.ps; // FOP +import org.apache.fop.svg.SVGArea; import org.apache.fop.render.AbstractRenderer; import org.apache.fop.render.Renderer; +import org.apache.fop.image.ImageArea; import org.apache.fop.image.FopImage; +import org.apache.fop.image.JpegImage; +import org.apache.fop.image.FopImageException; import org.apache.fop.layout.*; import org.apache.fop.layout.inline.*; import org.apache.fop.datatypes.*; @@ -26,6 +30,8 @@ import org.apache.batik.gvt.filter.*; import org.apache.batik.gvt.event.*; +import org.apache.log.Logger; + // SVG import org.w3c.dom.svg.SVGSVGElement; import org.w3c.dom.svg.SVGDocument; @@ -37,8 +43,9 @@ import java.util.*; import java.io.IOException; import java.io.OutputStream; -import java.util.Iterator; -import java.util.HashMap; +import java.util.Enumeration; +import java.util.Vector; +import java.util.Hashtable; import java.awt.geom.AffineTransform; import java.awt.geom.Dimension2D; import java.awt.Point; @@ -100,6 +107,9 @@ protected IDReferences idReferences; + protected Hashtable options; + + /** * set the document's producer * @@ -109,6 +119,14 @@ this.producer = producer; } + + /** + * set up renderer options + */ + public void setOptions(Hashtable options) { + this.options = options; + } + /** * write out a command */ @@ -139,6 +157,7 @@ write("/M/moveto ld"); write("/RM/rmoveto ld"); write("/t/show ld"); + write("/A/ashow ld"); write("/ux 0.0 def"); write("/uy 0.0 def"); @@ -198,18 +217,18 @@ // write("/gfF1{/Helvetica findfont} bd"); // write("/gfF3{/Helvetica-Bold findfont} bd"); - HashMap fonts = fontInfo.getFonts(); - Iterator enum = fonts.keySet().iterator(); - while (enum.hasNext()) { - String key = (String)enum.next(); + Hashtable fonts = fontInfo.getFonts(); + Enumeration enum = fonts.keys(); + while (enum.hasMoreElements()) { + String key = (String)enum.nextElement(); Font fm = (Font)fonts.get(key); write("/" + key + " /" + fm.fontName() + " def"); } write("end def"); write("%%EndResource"); - enum = fonts.keySet().iterator(); - while (enum.hasNext()) { - String key = (String)enum.next(); + enum = fonts.keys(); + while (enum.hasMoreElements()) { + String key = (String)enum.nextElement(); Font fm = (Font)fonts.get(key); write("/" + fm.fontName() + " findfont"); write("dup length dict begin"); @@ -222,7 +241,7 @@ } protected void movetoCurrPosition() { - write(this.currentIPPosition + " " + this.currentBPPosition + " M"); + write(this.currentXPosition + " " + this.currentYPosition + " M"); } /** @@ -249,6 +268,238 @@ write("fill"); } + /** + * render a display space to PostScript + * + * @param space the space to render + */ + public void renderDisplaySpace(DisplaySpace space) { + // write("% --- DisplaySpace size="+space.getSize()); + + this.currentYPosition -= space.getSize(); + + if ( this.currentXPosition < 0 || this.currentYPosition < 0 ) return; + movetoCurrPosition(); + } + + /** + * render a foreign object area + */ + public void renderForeignObjectArea(ForeignObjectArea area) { + // if necessary need to scale and align the content + area.getObject().render(this); + } + + /** + * render an SVG area to PostScript + * + * @param area the area to render + */ + public void renderSVGArea(SVGArea area) { + int x = this.currentXPosition; + int y = this.currentYPosition; + Document doc = area.getSVGDocument(); + + org.apache.fop.svg.SVGUserAgent userAgent + = new org.apache.fop.svg.SVGUserAgent(new AffineTransform()); + userAgent.setLogger(log); + + GVTBuilder builder = new GVTBuilder(); + BridgeContext ctx = new BridgeContext(userAgent); + + GraphicsNode root; + try { + root = builder.build(ctx, doc); + } catch (Exception e) { + log.error("svg graphic could not be built: " + + e.getMessage(), e); + return; + } + // get the 'width' and 'height' attributes of the SVG document + float w = (float)ctx.getDocumentSize().getWidth() * 1000f; + float h = (float)ctx.getDocumentSize().getHeight() * 1000f; + ctx = null; + builder = null; + + float sx = 1, sy = -1; + int xOffset = x, yOffset = y; + + comment("% --- SVG Area"); + write("gsave"); + if (w != 0 && h != 0) { + write("newpath"); + write(x + " " + y + " M"); + write((x + w) + " " + y + " rlineto"); + write((x + w) + " " + (y - h) + " rlineto"); + write(x + " " + (y - h) + " rlineto"); + write("closepath"); + write("clippath"); + } + // transform so that the coordinates (0,0) is from the top left + // and positive is down and to the right. (0,0) is where the + // viewBox puts it. + write(xOffset + " " + yOffset + " translate"); + write(sx + " " + sy + " scale"); + + PSGraphics2D graphics = new PSGraphics2D(false, area.getFontState(), + this, currentFontName, + currentFontSize, + currentXPosition, + currentYPosition); + graphics.setGraphicContext(new org.apache.batik.ext.awt.g2d.GraphicContext()); + try { + root.paint(graphics); + } catch (Exception e) { + log.error("svg graphic could not be rendered: " + + e.getMessage(), e); + } + + write("grestore"); + + comment("% --- SVG Area end"); + movetoCurrPosition(); + } + + public void renderEPS(FopImage img, int x, int y, int w, int h) { + try { + EPSImage eimg = (EPSImage)img; + int[] bbox = eimg.getBBox(); + int bboxw = bbox[2] - bbox[0]; + int bboxh = bbox[3] - bbox[1]; + + + write("%%BeginDocument: " + eimg.getDocName()); + write("BeginEPSF"); + + write(x + " " + (y - h) + " translate"); + write("0.0 rotate"); + write((long)(w/bboxw) + " " + (long)(h/bboxh) + " scale"); + write(-bbox[0] + " " + (-bbox[1]) + " translate"); + write(bbox[0] + " " + bbox[1] + " " + bboxw + " " + bboxh + " rectclip"); + write("newpath"); + out.writeByteArr(img.getBitmaps()); + write("%%EndDocument"); + write("EndEPSF"); + write(""); + } catch (Exception e) { + e.printStackTrace(); + log.error("PSRenderer.renderImageArea(): Error rendering bitmap (" + + e.getMessage() + ")", e); + } + } + + public void renderBitmap(FopImage img, int x, int y, int w, int h) { + try { + boolean iscolor = img.getColorSpace().getColorSpace() + != ColorSpace.DEVICE_GRAY; + byte[] imgmap = img.getBitmaps(); + + write("gsave"); + if (img.getColorSpace().getColorSpace() == ColorSpace.DEVICE_CMYK) + write("/DeviceCMYK setcolorspace"); + else + write("/DeviceRGB setcolorspace"); + + write(x + " " + (y - h) + " translate"); + write(w + " " + h + " scale"); + write("<<"); + write(" /ImageType 1"); + write(" /Width " + img.getWidth()); + write(" /Height " + img.getHeight()); + write(" /BitsPerComponent 8"); + if (img.getColorSpace().getColorSpace() == ColorSpace.DEVICE_CMYK) { + if (img.invertImage()) + write(" /Decode [1 0 1 0 1 0 1 0]"); + else + write(" /Decode [0 1 0 1 0 1 0 1]"); + } else if (iscolor) { + write(" /Decode [0 1 0 1 0 1]"); + } else { + write(" /Decode [0 1]"); + } + // Setup scanning for left-to-right and top-to-bottom + write(" /ImageMatrix [" + img.getWidth() + " 0 0 -" + + img.getHeight() + " 0 " + img.getHeight() + "]"); + + if (img instanceof JpegImage) + write(" /DataSource currentfile /ASCII85Decode filter /DCTDecode +filter"); + else + write(" /DataSource currentfile /ASCII85Decode filter /FlateDecode +filter"); + // write(" /DataSource currentfile /ASCIIHexDecode filter /FlateDecode +filter"); + // write(" /DataSource currentfile /ASCII85Decode filter +/RunLengthDecode filter"); + // write(" /DataSource currentfile /ASCIIHexDecode filter +/RunLengthDecode filter"); + // write(" /DataSource currentfile /ASCIIHexDecode filter"); + // write(" /DataSource currentfile /ASCII85Decode filter"); + // write(" /DataSource currentfile /RunLengthDecode filter"); + write(">>"); + write("image"); + + /* + * for (int y=0; y<img.getHeight(); y++) { + * int indx = y * img.getWidth(); + * if (iscolor) indx*= 3; + * for (int x=0; x<img.getWidth(); x++) { + * if (iscolor) { + * writeASCIIHex(imgmap[indx++] & 0xFF); + * writeASCIIHex(imgmap[indx++] & 0xFF); + * writeASCIIHex(imgmap[indx++] & 0xFF); + * } else { + * writeASCIIHex(imgmap[indx++] & 0xFF); + * } + * } + * } + */ + try { + // imgmap[0] = 1; + InputStream bain = new ByteArrayInputStream(imgmap); + InputStream in; + in = bain; + if (!(img instanceof JpegImage)) + in = FlateEncodeFilter.filter(in); + // in = RunLengthEncodeFilter.filter(in); + // in = ASCIIHexEncodeFilter.filter(in); + in = ASCII85EncodeFilter.filter(in); + copyStream(in, this.out); + } catch (IOException e) { + if (!ioTrouble) + e.printStackTrace(); + ioTrouble = true; + } + + write(""); + write("grestore"); + } catch (FopImageException e) { + log.error("PSRenderer.renderImageArea(): Error rendering bitmap (" + + e.getMessage() + ")", e); + } + } + + /** + * render an image area to PostScript + * + * @param area the area to render + */ + public void renderImageArea(ImageArea area) { + + int x = this.currentAreaContainerXPosition + area.getXOffset(); + int y = this.currentYPosition + +((LineArea)area.getParent()).getPlacementOffset();//Fix the image y position bug. + int w = area.getContentWidth(); + int h = area.getHeight(); + + this.currentYPosition -= area.getHeight(); + imagecount++; + // if (imagecount!=4) return; + + comment("% --- ImageArea"); + if (area.getImage() instanceof SVGImage) {} + else if (area.getImage() instanceof EPSImage) { + renderEPS(area.getImage(), x, y, w, h); + } else { + renderBitmap(area.getImage(), x, y, w, h); + } + comment("% --- ImageArea end"); + } + private long copyStream(InputStream in, OutputStream out, int bufferSize) throws IOException { long bytes_total = 0; @@ -267,7 +518,71 @@ return copyStream(in, out, 4096); } + /** + * render an inline area to PostScript + * + * @param area the area to render + */ + public void renderWordArea(WordArea area) { + FontState fs = area.getFontState(); + String fontWeight = fs.getFontWeight(); + StringBuffer sb = new StringBuffer(); + String s; + if (area.getPageNumberID() + != null) { // this text is a page number, so resolve it + s = idReferences.getPageNumber(area.getPageNumberID()); + if (s == null) { + s = ""; + } + } else { + s = area.getText(); + } + int l = s.length(); + + for (int i = 0; i < l; i++) { + char ch = s.charAt(i); + char mch = fs.mapChar(ch); + if (mch > 127) { + sb = sb.append("\\" + Integer.toOctalString(mch)); + } else { + String escape = "\\()[]{}"; + if (escape.indexOf(mch) >= 0) { + sb.append("\\"); + } + sb = sb.append(mch); + } + } + + String psString = null; + if (area.getFontState().getLetterSpacing() > 0) { + float f = area.getFontState().getLetterSpacing() * 1000 / +this.currentFontSize; + psString = (new StringBuffer().append(f).append(" 0.0 (").append(sb). + append(") A")).toString(); + } else { + psString = (new StringBuffer("(").append(sb).append(") t")).toString(); + } + + + // System.out.println("["+s+"] --> ["+sb.toString()+"]"); + + // comment("% --- InlineArea font-weight="+fontWeight+": " + sb.toString()); + useFont(fs.getFontName(), fs.getFontSize()); + useColor(area.getRed(), area.getGreen(), area.getBlue()); + if (area.getUnderlined() || area.getLineThrough() + || area.getOverlined()) + write("ULS"); + write(psString); + if (area.getUnderlined()) + write("ULE"); + if (area.getLineThrough()) + write("SOE"); + if (area.getOverlined()) + write("OLE"); + this.currentXPosition += area.getContentWidth(); + } + public void useFont(String name, int size) { + if ((currentFontName != name) || (currentFontSize != size)) { write(name + " " + size + " F"); currentFontName = name; @@ -275,6 +590,272 @@ } } + /** + * render an inline space to PostScript + * + * @param space the space to render + */ + public void renderInlineSpace(InlineSpace space) { + // write("% --- InlineSpace size="+space.getSize()); + this.currentXPosition += space.getSize(); + if (space.getUnderlined() || space.getLineThrough() + || space.getOverlined()) + write("ULS"); + write(space.getSize() + " 0 RM"); + if (space.getUnderlined()) + write("ULE"); + if (space.getLineThrough()) + write("SOE"); + if (space.getOverlined()) + write("OLE"); + } + + /** + * render a line area to PostScript + * + * @param area the area to render + */ + public void renderLineArea(LineArea area) { + + int rx = this.currentAreaContainerXPosition + area.getStartIndent(); + int ry = this.currentYPosition; + int w = area.getContentWidth(); + int h = area.getHeight(); + + this.currentYPosition -= area.getPlacementOffset(); + this.currentXPosition = rx; + + int bl = this.currentYPosition; + // method is identical to super method except next line + movetoCurrPosition(); + + String fontWeight = area.getFontState().getFontWeight(); + // comment("% --- LineArea begin font-weight="+fontWeight); + Enumeration e = area.getChildren().elements(); + while (e.hasMoreElements()) { + Box b = (Box)e.nextElement(); + this.currentYPosition = ry - area.getPlacementOffset(); + b.render(this); + } + // comment("% --- LineArea end"); + + this.currentYPosition = ry - h; + this.currentXPosition = rx; + } + + /** + * render a page to PostScript + * + * @param page the page to render + */ + public void renderPage(Page page) { + this.idReferences = page.getIDReferences(); + + BodyAreaContainer body; + AreaContainer before, after; + write("%%Page: " + page.getNumber() + " " + page.getNumber()); + write("%%BeginPageSetup"); + write("FOPFonts begin"); + write("12 18 translate"); + write("0.00096 0.00096 scale"); + write("%%EndPageSetup"); + body = page.getBody(); + before = page.getBefore(); + after = page.getAfter(); + if (before != null) { + renderAreaContainer(before); + } + renderBodyAreaContainer(body); + if (after != null) { + renderAreaContainer(after); + } + write("showpage"); + write("%%PageTrailer"); + write("%%EndPage"); + } + + /** + * render a leader area to PostScript + * + * @param area the area to render + */ + public void renderLeaderArea(LeaderArea area) { + + int rx = this.currentXPosition; + int ry = this.currentYPosition; + int w = area.getContentWidth(); + int th = area.getRuleThickness(); + int th2 = th / 2; + int th3 = th / 3; + int th4 = th / 4; + switch (area.getLeaderPattern()) { + case LeaderPattern.SPACE: + // NOP + + break; + case LeaderPattern.RULE: + if (area.getRuleStyle() == RuleStyle.NONE) + break; + useColor(area.getRed(), area.getGreen(), area.getBlue()); + write("gsave"); + write("0 setlinecap"); + switch (area.getRuleStyle()) { + case RuleStyle.DOTTED: + write("newpath"); + write("[1000 3000] 0 setdash"); + write(th + " setlinewidth"); + write(rx + " " + ry + " M"); + write(w + " 0 rlineto"); + useColor(area.getRed(), area.getGreen(), area.getBlue()); + write("stroke"); + break; + case RuleStyle.DASHED: + write("newpath"); + write("[3000 3000] 0 setdash"); + write(th + " setlinewidth"); + write(rx + " " + ry + " M"); + write(w + " 0 rlineto"); + useColor(area.getRed(), area.getGreen(), area.getBlue()); + write("stroke"); + break; + case RuleStyle.SOLID: + write("newpath"); + write(th + " setlinewidth"); + write(rx + " " + ry + " M"); + write(w + " 0 rlineto"); + useColor(area.getRed(), area.getGreen(), area.getBlue()); + write("stroke"); + break; + case RuleStyle.DOUBLE: + write("newpath"); + write(th3 + " setlinewidth"); + write(rx + " " + (ry - th3) + " M"); + write(w + " 0 rlineto"); + write(rx + " " + (ry + th3) + " M"); + write(w + " 0 rlineto"); + useColor(area.getRed(), area.getGreen(), area.getBlue()); + write("stroke"); + break; + case RuleStyle.GROOVE: + write(th2 + " setlinewidth"); + write("newpath"); + write(rx + " " + (ry - th4) + " M"); + write(w + " 0 rlineto"); + useColor(area.getRed(), area.getGreen(), area.getBlue()); + write("stroke"); + write("newpath"); + write(rx + " " + (ry + th4) + " M"); + write(w + " 0 rlineto"); + useColor(1, 1, 1); // white + write("stroke"); + break; + case RuleStyle.RIDGE: + write(th2 + " setlinewidth"); + write("newpath"); + write(rx + " " + (ry - th4) + " M"); + write(w + " 0 rlineto"); + useColor(1, 1, 1); // white + write("stroke"); + write("newpath"); + write(rx + " " + (ry + th4) + " M"); + write(w + " 0 rlineto"); + useColor(area.getRed(), area.getGreen(), area.getBlue()); + write("stroke"); + break; + } + write("grestore"); + break; + case LeaderPattern.DOTS: + comment("% --- Leader dots NYI"); + log.error("Leader dots: Not yet implemented"); + break; + case LeaderPattern.USECONTENT: + comment("% --- Leader use-content NYI"); + log.error("Leader use-content: Not yet implemented"); + break; + } + this.currentXPosition += area.getContentWidth(); + write(area.getContentWidth() + " 0 RM"); + } + + protected void doFrame(Area area) { + int w, h; + int rx = this.currentAreaContainerXPosition; + w = area.getContentWidth(); + BorderAndPadding bap = area.getBorderAndPadding(); + + if (area instanceof BlockArea) + rx += ((BlockArea)area).getStartIndent(); + + h = area.getContentHeight(); + int ry = this.currentYPosition; + + rx = rx - area.getPaddingLeft(); + ry = ry + area.getPaddingTop(); + w = w + area.getPaddingLeft() + area.getPaddingRight(); + h = h + area.getPaddingTop() + area.getPaddingBottom(); + + rx = rx - area.getBorderLeftWidth(); + ry = ry + area.getBorderTopWidth(); + w = w + area.getBorderLeftWidth() + area.getBorderRightWidth(); + h = h + area.getBorderTopWidth() + area.getBorderBottomWidth(); + + if ( rx < 0 || ry < 0 ) return; + // Create a textrect with these dimensions. + // The y co-ordinate is measured +ve downwards so subtract page-height + + ColorType bg = area.getBackgroundColor(); + if ((bg != null) && (bg.alpha() == 0)) { + write("newpath"); + write(rx + " " + ry + " M"); + write(w + " 0 rlineto"); + write("0 " + (-h) + " rlineto"); + write((-w) + " 0 rlineto"); + write("0 " + h + " rlineto"); + write("closepath"); + useColor(bg); + write("fill"); + } + + + if (area.getBorderTopWidth() != 0) { + write("newpath"); + write(rx + " " + ry + " M"); + write(w + " 0 rlineto"); + write(area.getBorderTopWidth() + " setlinewidth"); + write("0 setlinecap"); + useColor(bap.getBorderColor(BorderAndPadding.TOP)); + write("stroke"); + } + if (area.getBorderLeftWidth() != 0) { + write("newpath"); + write(rx + " " + ry + " M"); + write("0 " + (-h) + " rlineto"); + write(area.getBorderLeftWidth() + " setlinewidth"); + write("0 setlinecap"); + useColor(bap.getBorderColor(BorderAndPadding.LEFT)); + write("stroke"); + } + if (area.getBorderRightWidth() != 0) { + write("newpath"); + write((rx + w) + " " + ry + " M"); + write("0 " + (-h) + " rlineto"); + write(area.getBorderRightWidth() + " setlinewidth"); + write("0 setlinecap"); + useColor(bap.getBorderColor(BorderAndPadding.RIGHT)); + write("stroke"); + } + if (area.getBorderBottomWidth() != 0) { + write("newpath"); + write(rx + " " + (ry - h) + " M"); + write(w + " 0 rlineto"); + write(area.getBorderBottomWidth() + " setlinewidth"); + write("0 setlinecap"); + useColor(bap.getBorderColor(BorderAndPadding.BOTTOM)); + write("stroke"); + } + } + private void useColor(ColorType col) { useColor(col.red(), col.green(), col.blue()); } @@ -289,6 +870,8 @@ } /** + Default start renderer method. This would + normally be overridden. ([EMAIL PROTECTED]). */ public void startRenderer(OutputStream outputStream) throws IOException { @@ -310,7 +893,7 @@ /* Write proc for including EPS */ write("%%BeginResource: procset EPSprocs"); write("%%Title: EPS encapsulation procs"); - + write("/BeginEPSF { %def"); write("/b4_Inc_state save def % Save state for cleanup"); write("/dict_count countdictstack def % Count objects on dict stack"); @@ -327,25 +910,32 @@ write("} if"); write("} if"); write("} bind def"); - + write("/EndEPSF { %def"); write("count op_count sub {pop} repeat % Clean up stacks"); write("countdictstack dict_count sub {end} repeat"); write("b4_Inc_state restore"); write("} bind def"); write("%%EndResource"); - + write("%%EndSetup"); write("FOPFonts begin"); } /** + Default stop renderer method. This would + normally be overridden. ([EMAIL PROTECTED]). */ - public void stopRenderer() + public void stopRenderer(OutputStream outputStream) throws IOException { write("%%Trailer"); write("%%EOF"); this.out.flush(); + log.debug("written out PostScript"); } + public void render(Page page, OutputStream outputStream) { + this.renderPage(page); + } } +
--------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, email: [EMAIL PROTECTED]