From 09076c443ebdb6673a4252be54dbf1b64614df86 Mon Sep 17 00:00:00 2001 From: arved Date: Sat, 10 Mar 2001 18:58:49 +0000 Subject: [PATCH] New files for PCL rendering git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@194146 13f79535-47bb-0310-9956-ffa450edef68 --- .../apache/fop/render/pcl/PCLRenderer.java | 767 +++++++++ .../apache/fop/render/pcl/PCLSVGRenderer.java | 1394 +++++++++++++++++ src/org/apache/fop/render/pcl/PCLStream.java | 42 + 3 files changed, 2203 insertions(+) create mode 100755 src/org/apache/fop/render/pcl/PCLRenderer.java create mode 100755 src/org/apache/fop/render/pcl/PCLSVGRenderer.java create mode 100755 src/org/apache/fop/render/pcl/PCLStream.java diff --git a/src/org/apache/fop/render/pcl/PCLRenderer.java b/src/org/apache/fop/render/pcl/PCLRenderer.java new file mode 100755 index 000000000..8b5fa657c --- /dev/null +++ b/src/org/apache/fop/render/pcl/PCLRenderer.java @@ -0,0 +1,767 @@ +//package com.eastpoint.chrysalis; +package org.apache.fop.render.pcl; + +// FOP +import org.apache.fop.render.PrintRenderer; +import org.apache.fop.messaging.MessageHandler; +import org.apache.fop.image.ImageArea; +import org.apache.fop.image.FopImage; +import org.apache.fop.apps.FOPException; +import org.apache.fop.fo.properties.*; +import org.apache.fop.datatypes.*; +import org.apache.fop.pdf.PDFPathPaint; +import org.apache.fop.pdf.PDFColor; +import org.apache.fop.layout.*; +import org.apache.fop.layout.inline.*; +import org.apache.fop.image.*; + +import org.apache.fop.dom.svg.SVGArea; +import org.w3c.dom.svg.SVGSVGElement; + + +// Java +import java.io.IOException; +import java.io.OutputStream; +import java.util.Enumeration; + +/** + * Renderer that renders areas to PCL + + Created by Arthur E Welch III while at M&I EastPoint Technology + Donated by EastPoint to the Apache FOP project March 2, 2001. + */ +public class PCLRenderer extends PrintRenderer +{ + /** the current stream to add PCL commands to */ + public PCLStream currentStream; + + private int pageHeight = 7920; + + // These variables control the virtual paggination functionality. + public int curdiv = 0; + private int divisions = -1; + public int paperheight = -1; // Paper height in decipoints? + public int orientation = -1; // -1=default/unknown, 0=portrait, 1=landscape. + public int topmargin = -1; // Top margin in decipoints? + public int leftmargin = -1; // Left margin in decipoints? + private int fullmargin = 0; + private final boolean debug = false; + + private int xoffset = -180; // X Offset to allow for PCL implicit 1/4" left margin. + + /** + * Create the PCL renderer + */ + public PCLRenderer() + { + } + + /** + * set the PCL document's producer + * + * @param producer string indicating application producing PCL + */ + public void setProducer(String producer) + { + } + + /** + * render the areas into PCL + * + * @param areaTree the laid-out area tree + * @param stream the Outputstream to write the PCL to + */ + public void render(AreaTree areaTree, OutputStream stream) throws IOException, FOPException + { + MessageHandler.logln("rendering areas to PCL"); + idReferences=areaTree.getIDReferences(); + //this.pdfResources = this.pdfDoc.getResources(); + //this.pdfDoc.setIDReferences(idReferences); + Enumeration e = areaTree.getPages().elements(); + + currentStream = new PCLStream(stream); + + // Set orientation. + if ( orientation > -1 ) + currentStream.add("\033&l" + orientation + "O"); + else + currentStream.add("\033&l0O"); + if ( orientation == 1 || orientation == 3 ) + xoffset = -144; + else + xoffset = -180; + + // Reset the margins. + currentStream.add("\033" + "9\033&l0E"); + + + while (e.hasMoreElements()) + { + this.renderPage((Page) e.nextElement()); + } + if ( !idReferences.isEveryIdValid() ) + { + //throw new FOPException("The following id's were referenced but not found: "+idReferences.getInvalidIds()+"\n"); + MessageHandler.errorln("Warning: The following id's were referenced but not found: "+idReferences.getInvalidIds() + "\n"); + } + + MessageHandler.logln("writing out PCL"); + stream.flush(); + } + + /** + * add a line to the current stream + * + * @param x1 the start x location in millipoints + * @param y1 the start y location in millipoints + * @param x2 the end x location in millipoints + * @param y2 the end y location in millipoints + * @param th the thickness in millipoints + * @param stroke the line color + */ + protected void addLine(int x1, int y1, int x2, int y2, int th, + PDFPathPaint stroke) + { + if ( x1 == x2 ) + addRect(x1, y1, th, y2 - y1 + 1, stroke, stroke); + else if ( y1 == y2 ) + addRect(x1, y1, x2 - x1 + 1, th, stroke, stroke); + } + + /** + * add a line to the current stream + * + * @param x1 the start x location in millipoints + * @param y1 the start y location in millipoints + * @param x2 the end x location in millipoints + * @param y2 the end y location in millipoints + * @param th the thickness in millipoints + * @param rs the rule style + * @param stroke the line color + */ + protected void addLine(int x1, int y1, int x2, int y2, int th, + int rs, PDFPathPaint stroke) + { + int dashon = 0; + int dashoff = 0; + //if ( rs != null && rs.length() > 5 && rs.charAt(0) == '[' && rs.charAt(1) != ']' && rs.charAt(4) == ']' ) + //{ + // dashon = rs.charAt(1) - '0'; + // dashoff = rs.charAt(3) - '0'; + //} + switch (rs) + { + case org.apache.fop.fo.properties.RuleStyle.DASHED: + dashon = 3; + dashoff = 3; + break; + case org.apache.fop.fo.properties.RuleStyle.DOTTED: + dashon = 1; + dashoff = 3; + break; + } + if ( x1 == x2 ) + { + if ( dashon > 0 && dashoff > 0 ) + { + int start = y1; + int len = th * dashon; + while ( start < y2 ) + { + if ( start + len > y2 ) + len = y2 - start; + addRect(x1, start, th, len, stroke, stroke); + start += (len + dashoff * th); + } + } + else + addRect(x1, y1, th, y2 - y1 + 1, stroke, stroke); + } + else if ( y1 == y2 ) + { + if ( dashon > 0 && dashoff > 0 ) + { + int start = x1; + int len = th * dashon; + while ( start < x2 ) + { + if ( start + len > x2 ) + len = x2 - start; + addRect(start, y1, len, th, stroke, stroke); + start += (len + dashoff * th); + } + } + else + addRect(x1, y1, x2 - x1 + 1, th, stroke, stroke); + } + } + + /** + * add a rectangle to the current stream + * + * @param x the x position of left edge in millipoints + * @param y the y position of top edge in millipoints + * @param w the width in millipoints + * @param h the height in millipoints + * @param stroke the stroke color/gradient + */ + protected void addRect(int x, int y, int w, int h, + PDFPathPaint stroke) + { + if ( h < 0 ) + h *= -1; + + if (h < 720 || w < 720) + { + if ( w < 720 ) + w = 720; + if ( h < 720 ) + h = 720; + addRect(x, y, w, h, stroke, stroke); + } + else + { + addRect(x, y, w, 720, stroke, stroke); + addRect(x, y, 720, h, stroke, stroke); + addRect(x + w - 720, y, 720, h, stroke, stroke); + addRect(x, y - h + 720, w, 720, stroke, stroke); + } + } + + /** + * add a filled rectangle to the current stream + * + * @param x the x position of left edge in millipoints + * @param y the y position of top edge in millipoints + * @param w the width in millipoints + * @param h the height in millipoints + * @param fill the fill color/gradient + * @param stroke the stroke color/gradient + */ + protected void addRect(int x, int y, int w, int h, + PDFPathPaint stroke, PDFPathPaint fill) + { + if ((w == 0) || (h == 0)) + return; + if ( h < 0 ) + h *= -1; + + PDFColor sc = (PDFColor)stroke; + PDFColor fc = (PDFColor)fill; + + sc.setColorSpace(ColorSpace.DEVICE_RGB); + fc.setColorSpace(ColorSpace.DEVICE_RGB); + + int lineshade = (int)(100 - ((0.3f * sc.red() + 0.59f * sc.green() + 0.11f * sc.blue()) * 100f)); + int fillshade = (int)(100 - ((0.3f * fc.red() + 0.59f * fc.green() + 0.11f * fc.blue()) * 100f)); + + int xpos = xoffset + (x / 100); + if ( xpos < 0 ) + { + xpos = 0; + MessageHandler.errorln("PCLRenderer.addRect() WARNING: Horizontal position out of bounds."); + } + + currentStream.add("\033*v1O\033&a" + xpos + "h" + (pageHeight - (y / 100)) + "V" + + "\033*c" + (w / 100) + "h" + (h / 100) + "V" + + "\033*c" + lineshade + "G" + + "\033*c2P"); + if ( fillshade != lineshade && (w >= 720 || h >= 720) ) + { + xpos = xoffset + ((x + 240) / 100); + if ( xpos < 0 ) + { + xpos = 0; + MessageHandler.errorln("PCLRenderer.addRect() WARNING: Horizontal position out of bounds."); + } + currentStream.add("\033&a" + xpos + "h" + (pageHeight - ((y + 240)) / 100) + "V" + + "\033*c" + ((w - 480) / 100) + "h" + ((h - 480) / 100) + "V" + + "\033*c" + fillshade + "G" + + "\033*c2P"); + } + // Reset pattern transparency mode. + currentStream.add("\033*v0O"); + } + + boolean printBMP(FopImage img, int x, int y, int w, int h) throws FopImageException + { + // Print the passed image file in PCL. + byte imgmap[] = img.getBitmaps(); + + int ix = 0; + int iy = 0; + int indx = 0; + int iw = img.getWidth(); + int ih = img.getHeight(); + int bytewidth = (iw / 8); + if ( (iw % 8) != 0 ) + bytewidth++; + byte ib; + char ic[] = new char[bytewidth * 2]; + char icu[] = new char[bytewidth]; + int lastcount = -1; + byte lastbyte = 0; + int icwidth = 0; + int cr = 0; + int cg = 0; + int cb = 0; + int grey = 0; + boolean iscolor = img.getColorSpace().getColorSpace() != ColorSpace.DEVICE_GRAY; + int dcount = 0; + int xres = (iw * 72000) / w; + int yres = (ih * 72000) / h; + int resolution = xres; + if ( yres > xres ) + resolution = yres; + + if ( resolution > 300 ) + resolution = 600; + else if ( resolution > 150 ) + resolution = 300; + else if ( resolution > 100 ) + resolution = 150; + else if ( resolution > 75 ) + resolution = 100; + else + resolution = 75; +if ( debug ) +System.out.println("PCLRenderer.printBMP() iscolor = " + iscolor); + // Setup for graphics + currentStream.add("\033*t" + resolution + "R\033*r0F\033*r1A"); + + // Transfer graphics data + for ( iy = 0 ; iy < ih ; iy++ ) + { + ib = 0; + //int padding = iw % 8; + //if ( padding != 0 ) + // padding = 8 - padding; + //padding = 0; + //indx = (ih - iy - 1 + padding) * iw; + indx = iy * iw; + if ( iscolor ) + indx *= 3; + //currentStream.add("\033*b" + bytewidth + "W"); + for ( ix = 0 ; ix < iw ; ix++ ) + { + if ( iscolor ) + { + cr = imgmap[indx++] & 0xFF; + cg = imgmap[indx++] & 0xFF; + cb = imgmap[indx++] & 0xFF; + grey = (cr * 30 + cg * 59 + cb * 11) / 100; + } + else + grey = imgmap[indx++] & 0xFF; + if ( grey < 128 ) + ib |= (1 << (7 - (ix % 8))); + if ( (ix % 8) == 7 || ((ix + 1) == iw)) + { + if ( icwidth < bytewidth ) + { + if ( lastcount >= 0 ) + { + if ( ib == lastbyte ) + lastcount++; + else + { + ic[icwidth++] = (char)(lastcount & 0xFF); + ic[icwidth++] = (char)lastbyte; + lastbyte = ib; + lastcount = 0; + } + } + else + { + lastbyte = ib; + lastcount = 0; + } + if ( lastcount == 255 || ((ix + 1) == iw) ) + { + ic[icwidth++] = (char)(lastcount & 0xFF); + ic[icwidth++] = (char)lastbyte; + lastbyte = 0; + lastcount = -1; + } + } + icu[ix / 8] = (char)ib; + ib = 0; + } + } + if ( icwidth < bytewidth ) + { + currentStream.add("\033*b1m" + icwidth + "W"); + currentStream.add(new String(ic, 0, icwidth)); + } + else + { + currentStream.add("\033*b0m" + bytewidth + "W"); + currentStream.add(new String(icu)); + } + lastcount = -1; + icwidth = 0; + } + + // End graphics + currentStream.add("\033*rB"); + + + return(true); + } + + /** + * render image area to PCL + * + * @param area the image area to render + */ + public void renderImageArea(ImageArea area) + { + int x = this.currentAreaContainerXPosition + area.getXOffset(); + int y = this.currentYPosition; + int w = area.getContentWidth(); + int h = area.getHeight(); + + this.currentYPosition -= h; + + FopImage img = area.getImage(); + + int xpos = xoffset + (x / 100); + if ( xpos < 0 ) + { + xpos = 0; + MessageHandler.errorln("PCLRenderer.renderImageArea() WARNING: Horizontal position out of bounds."); + } + + currentStream.add("\033&a" + xpos + "h" + (pageHeight - (y / 100)) + "V"); + + try + { + printBMP(img, x, y, w, h); + } + catch ( FopImageException e ) + { + //e.printStackTrace(System.out); + MessageHandler.errorln("PCLRenderer.renderImageArea() Error printing BMP (" + e.toString() + ")"); + } + } + + /** render a foreign object area */ + public void renderForeignObjectArea(ForeignObjectArea area) + { + // if necessary need to scale and align the content + this.currentXPosition = this.currentXPosition + area.getXOffset(); + this.currentYPosition = this.currentYPosition; + switch (area.getAlign()) + { + case TextAlign.START: + break; + case TextAlign.END: + break; + case TextAlign.CENTER: + case TextAlign.JUSTIFY: + break; + } + switch (area.getVerticalAlign()) + { + case VerticalAlign.BASELINE: + break; + case VerticalAlign.MIDDLE: + break; + case VerticalAlign.SUB: + break; + case VerticalAlign.SUPER: + break; + case VerticalAlign.TEXT_TOP: + break; + case VerticalAlign.TEXT_BOTTOM: + break; + case VerticalAlign.TOP: + break; + case VerticalAlign.BOTTOM: + break; + } + // in general the content will not be text + + // align and scale + + switch (area.scalingMethod()) + { + case Scaling.UNIFORM: + break; + case Scaling.NON_UNIFORM: + break; + } + // if the overflow is auto (default), scroll or visible + // then the contents should not be clipped, since this + // is considered a printing medium. + switch (area.getOverflow()) + { + case Overflow.VISIBLE: + case Overflow.SCROLL: + case Overflow.AUTO: + break; + case Overflow.HIDDEN: + break; + } + area.getObject().render(this); + + this.currentXPosition += area.getEffectiveWidth(); + // this.currentYPosition -= area.getEffectiveHeight(); + } + + /** + * render SVG area to PCL + * + * @param area the SVG area to render + */ + public void renderSVGArea(SVGArea area) + { +if ( debug ) +System.out.println("PCLRenderer.renderSVGArea(" + area + ")"); + int x = this.currentXPosition; + int y = this.currentYPosition; + SVGSVGElement svg = area.getSVGDocument().getRootElement(); + int w = (int)(svg.getWidth().getBaseVal().getValue() * 1000); + int h = (int)(svg.getHeight().getBaseVal().getValue() * 1000); + + /* + * Clip to the svg area. + * Note: To have the svg overlay (under) a text area then use + * an fo:block-container + */ + + // TODO - translate and clip to viewbox + + PCLSVGRenderer svgRenderer = + new PCLSVGRenderer(this, area.getFontState(), currentFontName, currentFontSize, currentXPosition, currentYPosition, pageHeight, xoffset); + svgRenderer.renderSVG(svg, x, y); + //currentStream.add(svgRenderer.getString()); + + //currentStream.add("Q\n"); + } + + public void setFont(String name, float size) + { + int fontcode = 0; + if ( name.length() > 1 && name.charAt(0) == 'F' ) + { + try + { + fontcode = Integer.parseInt(name.substring(1)); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + switch (fontcode) + { + case 1: // F1 = Helvetica + //currentStream.add("\033(8U\033(s1p" + (size / 1000) + "v0s0b24580T"); + // Arial is more common among PCL5 printers than Helvetica - so use Arial + currentStream.add("\033(0N\033(s1p" + (size / 1000) + "v0s0b16602T"); + break; + case 2: // F2 = Helvetica Oblique + currentStream.add("\033(0N\033(s1p" + (size / 1000) + "v1s0b16602T"); + break; + case 3: // F3 = Helvetica Bold + currentStream.add("\033(0N\033(s1p" + (size / 1000) + "v0s3b16602T"); + break; + case 4: // F4 = Helvetica Bold Oblique + currentStream.add("\033(0N\033(s1p" + (size / 1000) + "v1s3b16602T"); + break; + case 5: // F5 = Times Roman + //currentStream.add("\033(8U\033(s1p" + (size / 1000) + "v0s0b25093T"); + // Times New is more common among PCL5 printers than Times - so use Times New + currentStream.add("\033(0N\033(s1p" + (size / 1000) + "v0s0b16901T"); + break; + case 6: // F6 = Times Italic + currentStream.add("\033(0N\033(s1p" + (size / 1000) + "v1s0b16901T"); + break; + case 7: // F7 = Times Bold + currentStream.add("\033(0N\033(s1p" + (size / 1000) + "v0s3b16901T"); + break; + case 8: // F8 = Times Bold Italic + currentStream.add("\033(0N\033(s1p" + (size / 1000) + "v1s3b16901T"); + break; + case 9: // F9 = Courier + currentStream.add("\033(0N\033(s0p" + (120.01f / (size / 1000.00f)) + "h0s0b4099T"); + break; + case 10: // F10 = Courier Oblique + currentStream.add("\033(0N\033(s0p" + (120.01f / (size / 1000.00f)) + "h1s0b4099T"); + break; + case 11: // F11 = Courier Bold + currentStream.add("\033(0N\033(s0p" + (120.01f / (size / 1000.00f)) + "h0s3b4099T"); + break; + case 12: // F12 = Courier Bold Oblique + currentStream.add("\033(0N\033(s0p" + (120.01f / (size / 1000.00f)) + "h1s3b4099T"); + break; + case 13: // F13 = Symbol + currentStream.add("\033(19M\033(s1p" + (size / 1000) + "v0s0b16686T"); + //currentStream.add("\033(9U\033(s1p" + (size / 1000) + "v0s0b25093T"); // ECMA Latin 1 Symbol Set in Times Roman??? + break; + case 14: // F14 = Zapf Dingbats + currentStream.add("\033(14L\033(s1p" + (size / 1000) + "v0s0b45101T"); + break; + default: + currentStream.add("\033(0N\033(s" + (size / 1000) + "V"); + break; + } + } + + /** + * render inline area to PCL + * + * @param area inline area to render + */ + public void renderWordArea(WordArea area) + { + String name = area.getFontState().getFontName(); + int size = area.getFontState().getFontSize(); + + float red = area.getRed(); + float green = area.getGreen(); + float blue = area.getBlue(); + PDFColor theAreaColor = new PDFColor((double) area.getRed(), + (double) area.getGreen(), (double) area.getBlue()); + + //currentStream.add("\033*c" + (int)(100 - ((0.3f * red + 0.59f * green + 0.11f * blue) * 100f)) + "G\033*v2T"); + currentStream.add("\033*v1O\033*c" + (int)(100 - ((0.3f * red + 0.59f * green + 0.11f * blue) * 100f)) + "G\033*v2T"); + + if ((!name.equals(this.currentFontName)) || (size != this.currentFontSize)) + { + this.currentFontName = name; + this.currentFontSize = size; + setFont(name, size); + } + + this.currentFill = theAreaColor; + + int rx = this.currentXPosition; + int bl = this.currentYPosition; + + 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(); + } + + addWordLines(area, rx, bl, size, theAreaColor); + + int xpos = xoffset + (rx / 100); + if ( xpos < 0 ) + { + xpos = 0; + MessageHandler.errorln("PCLRenderer.renderWordArea() WARNING: Horizontal position out of bounds."); + } + currentStream.add("\033&a" + xpos + "h" + (pageHeight - (bl / 100)) + "V" + s); + + this.currentXPosition += area.getContentWidth(); + } + + /** + * render page into PCL + * + * @param page page to render + */ + public void renderPage(Page page) + { +if ( debug ) +System.out.println("PCLRenderer.renderPage() page.Height() = " + page.getHeight()); + BodyAreaContainer body; + AreaContainer before, after, start, end; + + if ( paperheight > 0 && divisions == -1 ) + divisions = paperheight / (page.getHeight() / 100); + +if ( debug ) +System.out.println("PCLRenderer.renderPage() paperheight=" + paperheight + " divisions=" + divisions); + + // Set top margin. + //float fullmargin = 0; + if ( divisions > 0 ) + fullmargin = paperheight * curdiv / divisions; + if ( topmargin > 0 ) + fullmargin += topmargin; +if ( debug ) +System.out.println("PCLRenderer.renderPage() curdiv=" + curdiv + " fullmargin=" + fullmargin); + //if ( fullmargin > 0 ) + // currentStream.add("\033&l" + (fullmargin / 15f) + "c1e8C"); + //this.currentYPosition = fullmargin * 100; + + if ( paperheight > 0 ) + pageHeight = (paperheight / divisions) + fullmargin; + else + pageHeight = page.getHeight() / 100; +if ( debug ) +System.out.println("PCLRenderer.renderPage() Set currentYPosition=" + this.currentYPosition); + if ( leftmargin > 0 && curdiv == 0 ) + currentStream.add("\033&k" + (leftmargin / 6f) + "H\033&a1L\033&k12H"); + + body = page.getBody(); + before = page.getBefore(); + after = page.getAfter(); + start = page.getStart(); + end = page.getEnd(); + + this.currentFontName = ""; + this.currentFontSize = 0; + + renderBodyAreaContainer(body); + + if (before != null) + renderAreaContainer(before); + + if (after != null) + renderAreaContainer(after); + + if (start != null) + renderAreaContainer(start); + + if (end != null) + renderAreaContainer(end); + + // End page. + if ( ++curdiv == divisions || divisions == -1 ) + { + curdiv = 0; + currentStream.add("\f"); + } + + // Links, etc not implemented... + /* + currentPage = this.pdfDoc.makePage(this.pdfResources, currentStream, + page.getWidth()/1000, page.getHeight()/1000, page); + + if (page.hasLinks()) { + currentAnnotList = this.pdfDoc.makeAnnotList(); + currentPage.setAnnotList(currentAnnotList); + + Enumeration e = page.getLinkSets().elements(); + while (e.hasMoreElements()) { + LinkSet linkSet = (LinkSet) e.nextElement(); + + linkSet.align(); + String dest = linkSet.getDest(); + int linkType = linkSet.getLinkType(); + Enumeration f = linkSet.getRects().elements(); + while (f.hasMoreElements()) { + LinkedRectangle lrect = (LinkedRectangle) f.nextElement(); + currentAnnotList.addLink( + this.pdfDoc.makeLink(lrect.getRectangle(), dest, linkType)); + } + } + } else { + // just to be on the safe side + currentAnnotList = null; + } + */ + } +} diff --git a/src/org/apache/fop/render/pcl/PCLSVGRenderer.java b/src/org/apache/fop/render/pcl/PCLSVGRenderer.java new file mode 100755 index 000000000..e1e194d86 --- /dev/null +++ b/src/org/apache/fop/render/pcl/PCLSVGRenderer.java @@ -0,0 +1,1394 @@ +//package com.eastpoint.chrysalis; +package org.apache.fop.render.pcl; + +// FOP +import org.apache.fop.messaging.MessageHandler; +import org.apache.fop.fo.properties.*; +import org.apache.fop.svg.PathPoint; +import org.apache.fop.pdf.PDFColor; +import org.apache.fop.layout.*; +import org.apache.fop.image.*; + +import org.w3c.dom.*; +import org.w3c.dom.svg.*; +import org.w3c.dom.css.*; + +import org.apache.fop.dom.svg.*; + +// Java +import java.util.Enumeration; +import java.util.Vector; + +/** + * Renderer that renders SVG to PCL + */ +public class PCLSVGRenderer +{ + FontState fontState; + + /** the current stream to add PCL commands to */ + PCLStream currentStream; + + /** the current (internal) font name */ + protected String currentFontName; + + /** the current font size in millipoints */ + protected int currentFontSize; + + /** the current vertical position in millipoints from bottom */ + protected int currentYPosition = 0; + + /** the current horizontal position in millipoints from left */ + protected int currentXPosition = 0; + + /** the current colour for use in svg */ + private PDFColor currentColour = new PDFColor(0, 0, 0); + + private PCLRenderer renderer; + + final boolean debug = false; + + private int pageHeight; + private int rendxoffset; + + /** + * create the SVG renderer + */ + public PCLSVGRenderer(PCLRenderer rend, FontState fs, String font, int size, int xpos, int ypos, int ph, int xo) + { + renderer = rend; + currentFontName = font; + currentFontSize = size; + currentYPosition = ypos; + currentXPosition = xpos; + fontState = fs; + + currentStream = rend.currentStream; + + pageHeight = ph; + rendxoffset = xo; + } + + /** + * Renders an SVG element in an SVG document. + * This renders each of the child elements. + */ + protected void renderSVG(SVGSVGElement svg, int x, int y) { + NodeList nl = svg.getChildNodes(); + for (int count = 0; count < nl.getLength(); count++) { + Node n = nl.item(count); + if (n instanceof SVGElement) { + renderElement((SVGElement) n, x, y); + } + } + } + + public void renderGArea(SVGGElement area, int posx, int posy) { + NodeList nl = area.getChildNodes(); + for (int count = 0; count < nl.getLength(); count++) { + Node n = nl.item(count); + if (n instanceof SVGElement) { + renderElement((SVGElement) n, posx, posy); + } + } + } + + /** + * Handles the SVG switch element. + * The switch determines which of its child elements should be rendered + * according to the required extensions, required features and system language. + */ + protected void handleSwitchElement(int posx, int posy, + SVGSwitchElement ael) { + SVGStringList relist = ael.getRequiredExtensions(); + SVGStringList rflist = ael.getRequiredFeatures(); + SVGStringList sllist = ael.getSystemLanguage(); + NodeList nl = ael.getChildNodes(); + choices: + for (int count = 0; count < nl.getLength(); count++) { + org.w3c.dom.Node n = nl.item(count); + // only render the first child that has a valid + // test data + if (n instanceof GraphicElement) { + GraphicElement graphic = (GraphicElement) n; + SVGStringList grelist = graphic.getRequiredExtensions(); + // if null it evaluates to true + if (grelist != null) { + if (grelist.getNumberOfItems() == 0) { + if ((relist != null) && + relist.getNumberOfItems() != 0) { + continue choices; + } + } + for (int i = 0; i < grelist.getNumberOfItems(); i++) { + String str = (String) grelist.getItem(i); + if (relist == null) { + // use default extension set + // currently no extensions are supported + // if(!(str.equals("http:// ??"))) { + continue choices; + // } + } else { + } + } + } + SVGStringList grflist = graphic.getRequiredFeatures(); + if (grflist != null) { + if (grflist.getNumberOfItems() == 0) { + if ((rflist != null) && + rflist.getNumberOfItems() != 0) { + continue choices; + } + } + for (int i = 0; i < grflist.getNumberOfItems(); i++) { + String str = (String) grflist.getItem(i); + if (rflist == null) { + // use default feature set + if (!(str.equals("org.w3c.svg.static") || + str.equals("org.w3c.dom.svg.all"))) { + continue choices; + } + } else { + boolean found = false; + for (int j = 0; + j < rflist.getNumberOfItems(); j++) { + if (rflist.getItem(j).equals(str)) { + found = true; + break; + } + } + if (!found) + continue choices; + } + } + } + SVGStringList gsllist = graphic.getSystemLanguage(); + if (gsllist != null) { + if (gsllist.getNumberOfItems() == 0) { + if ((sllist != null) && + sllist.getNumberOfItems() != 0) { + continue choices; + } + } + for (int i = 0; i < gsllist.getNumberOfItems(); i++) { + String str = (String) gsllist.getItem(i); + if (sllist == null) { + // use default feature set + if (!(str.equals("en"))) { + continue choices; + } + } else { + boolean found = false; + for (int j = 0; + j < sllist.getNumberOfItems(); j++) { + if (sllist.getItem(j).equals(str)) { + found = true; + break; + } + } + if (!found) + continue choices; + } + } + } + renderElement((SVGElement) n, posx, posy); + // only render the first valid one + break; + } + } + } + + protected void addLine(float x1, float y1, float x2, float y2, PDFColor sc, float sw) + { +if ( debug ) +System.out.println("PCLSVGRenderer.addLine(" + x1 + ", " + y1 + ", " + x2 + ", " + y2 + ", " + sc + ", " + sw + ")"); + if ( x1 == x2 ) + { + addRect(x1 - sw/2, y1, sw, y2 - y1 + 1, 0, 0, sc, null, 0); + } + else if ( y1 == y2 || (Math.abs(y1 - y2) <= 0.24) ) // 72/300=0.24 + { + addRect(x1, y1 - sw/2, x2 - x1 + 1, sw, 0, 0, sc, null, 0); + } + else if ( sc != null ) + { + // Do something for these? + + // Convert dimensions to pixels. + float cfact = 300f / 72f; // 300 dpi, 1pt=1/72in + int ix1 = (int)(x1 * cfact); + int iy1 = (int)(y1 * cfact); + int ix2 = (int)(x2 * cfact); + int iy2 = (int)(y2 * cfact); + int isw = (int)(sw * cfact); + int origix; + + // Normalize + if ( iy1 > iy2 ) + { + int tmp = ix1; + ix1 = ix2; + ix2 = tmp; + tmp = iy1; + iy1 = iy2; + iy2 = tmp; + } + if ( ix1 > ix2 ) + { + origix = ix2; + ix1 -=ix2; + ix2 = 0; + } + else + { + origix = ix1; + ix2 -= ix1; + ix1 = 0; + } + + // Convert line width to a pixel run length. +//System.out.println("PCLRenderer.addLine(" + ix1 + ", " + iy1 + ", " + ix2 + ", " + iy2 + ", " + isw + ")"); + int runlen = (int)Math.sqrt(Math.pow(isw, 2) * (1 + Math.pow((ix1 - ix2) / (iy1 - iy2), 2))); +//System.out.println("PCLRenderer.addLine: runlen = " + runlen); + + // Set Transparency modes and select shading. + currentStream.add("\033*v0n1O\033*c" + (int)(100 - ((0.3f * sc.red() + 0.59f * sc.green() + 0.11f * sc.blue()) * 100f)) + "G\033*v2T"); + + // Draw the line. + int d, dx, dy; + int Aincr, Bincr; + int xincr = 1; + int x, y; + + + dx = Math.abs(ix2 - ix1); + dy = iy2 - iy1; + + if ( origix < 0 ) + MessageHandler.errorln("PCLSVGRenderer.addLine() WARNING: Horizontal position out of bounds."); + + if ( dx > dy ) + { + xincr = dx / dy; + + // Move to starting position. + currentStream.add("\033*p" + origix + "x" + iy1 + "Y"); + x = ix1 - runlen / 2; + iy2 += (isw / 2); + // Start raster graphics + currentStream.add("\033*t300R\033*r" + dx + "s1A\033*b1M"); + } + else + { + // Move to starting position. + currentStream.add("\033*p" + (origix - runlen / 2) + "x" + iy1 + "Y"); + x = ix1; + // Start raster graphics + currentStream.add("\033*t300R\033*r1A\033*b1M"); + } + + if ( ix1 > ix2 ) + xincr *= -1; + d = 2 * dx - dy; + Aincr = 2 * (dx - dy); + Bincr = 2 * dx; + + y = iy1; + + xferLineBytes(x, runlen, null, -1); + + for ( y = iy1 + 1 ; y <= iy2 ; y++ ) + { + if ( d >= 0 ) + { + x += xincr; + d += Aincr; + } + else + d += Bincr; + xferLineBytes(x, runlen, null, -1); + } + + // End raster graphics + currentStream.add("\033*rB"); + // Return to regular print mode. + currentStream.add("\033*v0t0n0O"); + } + } + + private void xferLineBytes(int startpos, int bitcount, Vector save, int start2) + { +//System.out.println("PCLRenderer.xferLineBytes(" + startpos + ", " + bitcount + ")"); + int curbitpos = 0; + if ( start2 > 0 && start2 <= (startpos + bitcount) ) + { + bitcount += (start2 - startpos); + start2 = 0; + } + + char bytes[] = new char[((start2>startpos?start2:startpos) + bitcount) / 4 + 2]; + int dlen = 0; + byte dbyte = 0; + int bytepos = 0; + + do + { + int bits2set; + if ( startpos < 0 ) + { + bits2set = bitcount + startpos; + startpos = 0; + } + else + bits2set = bitcount; + + byte bittype = 0; + do + { + if ( bytepos > 0 ) + { + int inc = startpos - curbitpos; + if ( (inc) >= (8 - bytepos) ) + { + curbitpos += (8 - bytepos); + bytepos = 0; + bytes[dlen++] = (char)0; + bytes[dlen++] = (char)dbyte; + dbyte = 0; + } + else + { + bytepos += inc; + dbyte = (byte)(dbyte ^ (byte)(Math.pow(2, 8 - bytepos) - 1)); + curbitpos += inc; + } + } + + // Set runs of whole bytes. + int setbytes = (startpos - curbitpos) / 8; + if ( setbytes > 0 ) + { + curbitpos += setbytes * 8; + while ( setbytes > 0 ) + { + if ( setbytes > 256 ) + { + bytes[dlen++] = 0xFF; + setbytes -= 256; + } + else + { + bytes[dlen++] = (char)((setbytes - 1) & 0xFF); + setbytes = 0; + } + bytes[dlen++] = (char)bittype; + } + } + // move to position in the first byte. + if ( curbitpos < startpos ) + { + if ( bytepos == 0 ) + dbyte = bittype; + bytepos += startpos - curbitpos; + dbyte = (byte)(dbyte ^ (byte)(Math.pow(2, 8 - bytepos) - 1)); + curbitpos += bytepos; + startpos += bits2set; + } + else + { + startpos += bits2set; + } + + if ( bittype == 0 ) + bittype = (byte)0xFF; + else + bittype = 7; + } while ( bittype != 7 ); + + if ( start2 > 0 ) + { + startpos = start2; + start2 = -1; + } + else + startpos = -1; + } while ( startpos >= 0 ); + if ( bytepos > 0 ) + { + bytes[dlen++] = (char)0; + bytes[dlen++] = (char)dbyte; + } + if ( save == null ) + { + currentStream.add("\033*b" + dlen + "W"); + currentStream.add(new String(bytes, 0, dlen)); + } + else + { + String line = "\033*b" + dlen + "W" + new String(bytes, 0, dlen); + currentStream.add(line); + save.addElement(line); + } + } + + /** + * add a filled rectangle to the current stream + * + * @param x the x position of left edge in millipoints + * @param y the y position of top edge in millipoints + * @param w the width in millipoints + * @param h the height in millipoints + * @param r the red component of edges + * @param g the green component of edges + * @param b the blue component of edges + * @param fr the red component of the fill + * @param fg the green component of the fill + * @param fb the blue component of the fill + */ + protected void addRect(float x, float y, float w, float h, float rx, float ry, + PDFColor fc, PDFColor sc, float sw) + { +if ( debug ) +System.out.println("PCLSVGRenderer.addRect(" + x + ", " + y + ", " + w + ", " + h + ", " + rx + ", " + ry + ", " + fc + ", " + sc + ", " + sw + ")"); + + if ( x < 0 || y < 0 ) + MessageHandler.errorln("PCLSVGRenderer.addRect() WARNING: Position out of bounds."); + + if ( rx == 0 || ry == 0 ) + { + if ( fc != null ) + { + int fillshade = (int)(100 - ((0.3f * fc.red() + 0.59f * fc.green() + 0.11f * fc.blue()) * 100f)); + currentStream.add("\033*v0n1O\033&a" + (x * 10) + "h" + ((y * 10)) + "V" + + "\033*c" + (w * 10) + "h" + (h * 10) + "v" + fillshade + "g2P\033*v0n0O"); + } + if ( sc != null && sw > 0 ) + { + String lend = "v" + String.valueOf((int)(100 - ((0.3f * sc.red() + 0.59f * sc.green() + 0.11f * sc.blue()) * 100f))) + "g2P"; + currentStream.add("\033*v0n1O"); + currentStream.add("\033&a" + ((x - sw/2) * 10) + "h" + (((y - sw/2)) * 10) + "V" + + "\033*c" + ((w + sw) * 10) + "h" + ((sw) * 10) + lend); + currentStream.add("\033&a" + ((x - sw/2) * 10) + "h" + (((y - sw/2)) * 10) + "V" + + "\033*c" + ((sw) * 10) + "h" + ((h + sw) * 10) + lend); + currentStream.add("\033&a" + ((x + w - sw/2) * 10) + "h" + (((y - sw/2)) * 10) + "V" + + "\033*c" + ((sw) * 10) + "h" + ((h + sw) * 10) + lend); + currentStream.add("\033&a" + ((x - sw/2) * 10) + "h" + (((y + h - sw/2)) * 10) + "V" + + "\033*c" + ((w + sw) * 10) + "h" + ((sw) * 10) + lend); + currentStream.add("\033*v0n0O"); + } + } + else + { + // Convert dimensions to pixels. + float cfact = 300f / 72f; // 300 dpi, 1pt=1/72in + int ix = (int)(x * cfact); + int iy = (int)(y * cfact); + int iw = (int)(w * cfact); + int ih = (int)(h * cfact); + int irx = (int)(rx * cfact); + int iry = (int)(ry * cfact); + int isw = (int)(sw * cfact); + int longwidth = 0; + int pass = 0; + PDFColor thecolor = null; + + do + { + if ( pass == 0 && fc != null ) + { + thecolor = fc; + } + else if ( pass == 1 && sc != null ) + { + int iswdiv2 = isw / 2; + thecolor = sc; + ix -= iswdiv2; + iy -= iswdiv2; + irx += iswdiv2; + iry += iswdiv2; + iw += isw; + ih += isw; + longwidth = (int)(isw * 1.414); + } + else + thecolor = null; + + + if ( thecolor != null ) + { + int tx = 0; + int ty = iry; + long a = irx; + long b = iry; + long Asquared = (long)Math.pow(a, 2); + long TwoAsquared = 2 * Asquared; + long Bsquared = (long)Math.pow(b, 2); + long TwoBsquared = 2 * Bsquared; + long d = Bsquared - Asquared * b + Asquared / 4; + long dx = 0; + long dy = TwoAsquared * b; + int rectlen = iw - 2 * irx; + Vector bottomlines = new Vector(); + + int x0 = tx; + + // Set Transparency modes and select shading. + currentStream.add("\033*v0n1O\033*c" + (int)(100 - ((0.3f * thecolor.red() + 0.59f * thecolor.green() + 0.11f * thecolor.blue()) * 100f)) + "G\033*v2T"); + // Move to starting position. + currentStream.add("\033*p" + ix + "x" + iy + "Y"); + // Start raster graphics + currentStream.add("\033*t300R\033*r" + iw + "s1A\033*b1M"); + + while ( dx < dy ) + { + if ( d > 0 ) + { + if ( pass == 0 || ty > (iry - isw) ) + xferLineBytes(irx - x0, rectlen + 2 * x0, bottomlines, -1); + else + xferLineBytes(irx - x0, longwidth, bottomlines, iw - irx + x0 - longwidth); + x0 = tx + 1; + ty--; + dy -= TwoAsquared; + d -= dy; + } + tx++; + dx += TwoBsquared; + d += Bsquared + dx; + } + + d += (3 * (Asquared - Bsquared) / 2 - (dx + dy)) / 2; + + while ( ty > 0 ) + { + if ( pass == 0 || ty >= (iry - isw) ) + xferLineBytes(irx - tx, rectlen + 2 * tx, bottomlines, -1); + else + xferLineBytes(irx - tx, isw, bottomlines, iw - irx + tx - isw); + + if ( d < 0 ) + { + tx++; + dx += TwoBsquared; + d += dx; + } + ty--; + dy -= TwoAsquared; + d += Asquared - dy; + } + + // Draw the middle part of the rectangle + int midlen = ih - 2 * iry; + if ( midlen > 0 ) + { + if ( pass == 0 ) + xferLineBytes(0, iw, null, -1); + else + xferLineBytes(0, isw, null, iw - isw); + currentStream.add("\033*b3M"); + for ( int countr = midlen - 1 ; countr > 0 ; countr-- ) + currentStream.add("\033*b0W"); + currentStream.add("\033*b1M"); + } + + // Draw the bottom. + for ( int countr = bottomlines.size() - 1 ; countr >= 0 ; countr-- ) + currentStream.add((String)bottomlines.elementAt(countr)); + + // End raster graphics + currentStream.add("\033*rB"); + // Return to regular print mode. + currentStream.add("\033*v0t0n0O"); + } + pass++; + } while ( pass < 2 ); + } + } + + // Add a polyline or polygon. Does not support fills yet!!! + protected void addPolyline(Vector points, int posx, int posy, PDFColor fc, PDFColor sc, float sw, boolean close) + { + PathPoint pc; + float lastx = 0; + float lasty = 0; + float curx = 0; + float cury = 0; + float startx = 0; + float starty = 0; + Enumeration e = points.elements(); + if(e.hasMoreElements()) + { + pc = (PathPoint)e.nextElement(); + lastx = rendxoffset / 10 + pc.x + posx / 1000; + lasty = ((pageHeight / 10) - posy/1000) + pc.y; + startx = lastx; + starty = lasty; + //currentStream.add(lastx + " " + lasty + " m\n"); + } + while(e.hasMoreElements()) + { + pc = (PathPoint)e.nextElement(); + curx = rendxoffset / 10 + pc.x + posx / 1000; + cury = ((pageHeight / 10) - posy/1000) + pc.y; + addLine(lastx, lasty, curx, cury, sc, sw); + lastx = curx; + lasty = cury; + //currentStream.add(lastx + " " + lasty + " l\n"); + } + if(close) + { + addLine(lastx, lasty, startx, starty, sc, sw); + //currentStream.add("h\n"); + } + //doDrawing(di); + } + + public void renderImage(String href, float x, float y, float width, float height) + { + if ( x < 0 || y < 0 ) + MessageHandler.errorln("PCLSVGRenderer.renderImage() WARNING: Position out of bounds."); + + try + { + if ( href.indexOf(":") == -1 ) + href = "file:" + href; + FopImage img = FopImageFactory.Make(href); + if(img != null) + { + if ( img instanceof SVGImage ) + { + SVGSVGElement svg = ((SVGImage)img).getSVGDocument().getRootElement(); + renderSVG(svg, (int)x * 1000, (int)y * 1000); + } + else + { + currentStream.add("\033&a" + (x * 10) + "h" + (y * 10) + "V"); + renderer.printBMP(img, (int)x, (int)y, (int)width, (int)height); + } + + } + } + catch(Exception e) + { + MessageHandler.errorln("could not add image to SVG: " + href); + } + } + + /** + * A symbol has a viewbox and preserve aspect ratio. + */ + protected void renderSymbol(SVGSymbolElement symbol, int x, int y) { + NodeList nl = symbol.getChildNodes(); + for (int count = 0; count < nl.getLength(); count++) { + Node n = nl.item(count); + if (n instanceof SVGElement) { + renderElement((SVGElement) n, x, y); + } + } + } + + /** + * Main rendering selection. + * This applies any transform ans style and then calls the appropriate + * rendering method depending on the type of the element. + */ + public void renderElement(SVGElement area, int posx, int posy) + { +if ( debug ) +System.out.println("PCLRenderer.renderElement(" + fontState + ", " + area + ", " + posx + ", " + posy + ")"); + int x = posx; + int y = posy; + SVGStylable style = null; + if ( area instanceof SVGStylable ) + style = (SVGStylable)area; + PDFColor fillColour = null; + PDFColor strokeColour = null; + float strokeWidth = 1; + + //currentStream.add("q\n"); + //if( area instanceof SVGTransformable ) + //{ + // SVGTransformable tf = (SVGTransformable)area; + // SVGAnimatedTransformList trans = tf.getTransform(); + // SVGRect bbox = tf.getBBox(); + // if(trans != null) { + // applyTransform(trans, bbox); + // } + //} + + if(style != null) + { + CSSValue sp; + sp = style.getPresentationAttribute("fill"); + if (sp != null) + { + if (sp.getValueType() == CSSValue.CSS_PRIMITIVE_VALUE) + { + if (((CSSPrimitiveValue) sp).getPrimitiveType() == CSSPrimitiveValue.CSS_RGBCOLOR) + { + RGBColor col = ((CSSPrimitiveValue) sp).getRGBColorValue(); + CSSPrimitiveValue val; + val = col.getRed(); + float red = val.getFloatValue(CSSPrimitiveValue.CSS_NUMBER); + val = col.getGreen(); + float green = val.getFloatValue(CSSPrimitiveValue.CSS_NUMBER); + val = col.getBlue(); + float blue = val.getFloatValue(CSSPrimitiveValue.CSS_NUMBER); + fillColour = new PDFColor(red, green, blue); + currentColour = fillColour; + } + else if ( ((CSSPrimitiveValue) sp).getPrimitiveType() == CSSPrimitiveValue.CSS_STRING) + { + String str = ((CSSPrimitiveValue) sp).getCssText(); + if ( str.equals("none") ) + { + fillColour = null; + } + else if ( str.equals("currentColor") ) + { + fillColour = currentColour; + } + } + } + } + else + { + fillColour = new PDFColor(0, 0, 0); + } + sp = style.getPresentationAttribute("stroke"); + if ( sp != null ) + { + if ( sp.getValueType() == CSSValue.CSS_PRIMITIVE_VALUE ) + { + if ( ((CSSPrimitiveValue) sp).getPrimitiveType() == CSSPrimitiveValue.CSS_RGBCOLOR ) + { + RGBColor col = ((CSSPrimitiveValue) sp).getRGBColorValue(); + CSSPrimitiveValue val; + val = col.getRed(); + float red = val.getFloatValue(CSSPrimitiveValue.CSS_NUMBER); + val = col.getGreen(); + float green = val.getFloatValue(CSSPrimitiveValue.CSS_NUMBER); + val = col.getBlue(); + float blue = val.getFloatValue(CSSPrimitiveValue.CSS_NUMBER); + strokeColour = new PDFColor(red, green, blue); + } + else if ( ((CSSPrimitiveValue) sp).getPrimitiveType() == CSSPrimitiveValue.CSS_STRING ) + { + String str = ((CSSPrimitiveValue) sp).getCssText(); + if (str.equals("none")) + { + strokeColour = null; + } + } + } + } + else + { + strokeColour = new PDFColor(0, 0, 0); + } + sp = style.getPresentationAttribute("stroke-width"); + if ( sp != null ) + { + if ( sp.getValueType() == CSSValue.CSS_PRIMITIVE_VALUE ) + { + strokeWidth = ((CSSPrimitiveValue) sp).getFloatValue(CSSPrimitiveValue.CSS_PT); + } + } + } + + if (area instanceof SVGRectElement) + { + SVGRectElement rg = (SVGRectElement)area; + float rectx = rendxoffset / 10 + rg.getX().getBaseVal().getValue() + posx / 1000; + float recty = ((pageHeight / 10) - posy/1000) + rg.getY().getBaseVal().getValue(); + float rx = rg.getRx().getBaseVal().getValue(); + float ry = rg.getRy().getBaseVal().getValue(); + float rw = rg.getWidth().getBaseVal().getValue(); + float rh = rg.getHeight().getBaseVal().getValue(); + addRect(rectx, recty, rw, rh, rx, ry, fillColour, strokeColour, strokeWidth); + } + else if (area instanceof SVGLineElement) + { + SVGLineElement lg = (SVGLineElement)area; + float x1 = rendxoffset / 10 + lg.getX1().getBaseVal().getValue() + posx / 1000; + float y1 = ((pageHeight / 10) - posy/1000) + lg.getY1().getBaseVal().getValue(); + float x2 = rendxoffset / 10 + lg.getX2().getBaseVal().getValue() + posx / 1000; + float y2 = ((pageHeight / 10) - posy/1000) + lg.getY2().getBaseVal().getValue(); + addLine(x1,y1,x2,y2, strokeColour, strokeWidth); + } + else if (area instanceof SVGTextElementImpl) + { + //currentStream.add("BT\n"); + //renderText((SVGTextElementImpl)area, rendxoffset + posx / 1000f, ((float)(pageHeight / 10) - posy/1000f)); + //currentStream.add("ET\n"); + SVGTextRenderer str = new SVGTextRenderer(fontState, (SVGTextElementImpl)area, rendxoffset / 10 + posx / 1000f, ((float)(pageHeight / 10) - posy/1000f)); + str.renderText((SVGTextElementImpl)area); + } + else if (area instanceof SVGCircleElement) + { + SVGCircleElement cg = (SVGCircleElement)area; + float cx = rendxoffset / 10 + cg.getCx().getBaseVal().getValue() + posx / 1000; + float cy = ((pageHeight / 10) - posy/1000) + cg.getCy().getBaseVal().getValue(); + float r = cg.getR().getBaseVal().getValue(); + //addCircle(cx,cy,r, di); + addRect(cx - r, cy - r, 2 * r, 2 * r, r, r, fillColour, strokeColour, strokeWidth); + } + else if (area instanceof SVGEllipseElement) + { + SVGEllipseElement cg = (SVGEllipseElement)area; + float cx = rendxoffset / 10 + cg.getCx().getBaseVal().getValue() + posx / 1000; + float cy = ((pageHeight / 10) - posy/1000) + cg.getCy().getBaseVal().getValue(); + float rx = cg.getRx().getBaseVal().getValue(); + float ry = cg.getRy().getBaseVal().getValue(); + //addEllipse(cx,cy,rx,ry, di); + addRect(cx - rx, cy - ry, 2 * rx, 2 * ry, rx, ry, fillColour, strokeColour, strokeWidth); + } + else if (area instanceof SVGPathElementImpl) + { + //addPath(((SVGPathElementImpl)area).pathElements, posx, posy, di); + } + else if (area instanceof SVGPolylineElementImpl) + { + addPolyline(((SVGPolylineElementImpl)area).points, posx, posy, fillColour, strokeColour, strokeWidth, false); + } + else if (area instanceof SVGPolygonElementImpl) + { + addPolyline(((SVGPolylineElementImpl)area).points, posx, posy, fillColour, strokeColour, strokeWidth, true); + } + else if (area instanceof SVGGElementImpl) + { + renderGArea((SVGGElementImpl)area, x, y); + } + else if(area instanceof SVGUseElementImpl) + { + SVGUseElementImpl ug = (SVGUseElementImpl) area; + String ref = ug.link; + // ref = ref.substring(1, ref.length()); + SVGElement graph = null; + graph = locateDef(ref, ug); + if (graph != null) { + // probably not the best way to do this, should be able + // to render without the style being set. + // SVGElement parent = graph.getGraphicParent(); + // graph.setParent(area); + // need to clip (if necessary) to the use area + // the style of the linked element is as if it was + // a direct descendant of the use element. + + // scale to the viewBox + + if (graph instanceof SVGSymbolElement) { + //currentStream.write("q\n"); + SVGSymbolElement symbol = (SVGSymbolElement) graph; + SVGRect view = symbol.getViewBox().getBaseVal(); + float usex = ug.getX().getBaseVal().getValue(); + float usey = ug.getY().getBaseVal().getValue(); + float usewidth = ug.getWidth().getBaseVal().getValue(); + float useheight = + ug.getHeight().getBaseVal().getValue(); + float scaleX; + float scaleY; + scaleX = usewidth / view.getWidth(); + scaleY = useheight / view.getHeight(); + //currentStream.write(usex + " " + usey + " m\n"); + //currentStream.write((usex + usewidth) + " " + + // usey + " l\n"); + //currentStream.write((usex + usewidth) + " " + + // (usey + useheight) + " l\n"); + //currentStream.write(usex + " " + + // (usey + useheight) + " l\n"); + //currentStream.write("h\n"); + //currentStream.write("W\n"); + //currentStream.write("n\n"); + //currentStream.write(scaleX + " 0 0 " + scaleY + + // " " + usex + " " + usey + " cm\n"); + renderSymbol(symbol, posx, posy); + //currentStream.write("Q\n"); + } else { + renderElement(graph, posx, posy); + } + // graph.setParent(parent); + } + else + { + MessageHandler.logln("Use Element: " + ref + " not found"); + } + } + else if (area instanceof SVGImageElementImpl) + { + SVGImageElementImpl ig = (SVGImageElementImpl)area; + renderImage(ig.link, ig.x, ig.y, ig.width, ig.height); + } + else if (area instanceof SVGSVGElement) + { + //currentStream.write("q\n"); + SVGSVGElement svgel = (SVGSVGElement) area; + float svgx = 0; + if (svgel.getX() != null) + svgx = svgel.getX().getBaseVal().getValue(); + float svgy = 0; + if (svgel.getY() != null) + svgy = svgel.getY().getBaseVal().getValue(); + //currentStream.write(1 + " 0 0 " + 1 + " " + svgx + " " + + // svgy + " cm\n"); + renderSVG(svgel, (int)(x + 1000 * svgx), + (int)(y + 1000 * svgy)); + //currentStream.write("Q\n"); + // } else if (area instanceof SVGSymbolElement) { + // 'symbol' element is not rendered (except by 'use') + } + else if (area instanceof SVGAElement) + { + SVGAElement ael = (SVGAElement)area; + org.w3c.dom.NodeList nl = ael.getChildNodes(); + for ( int count = 0 ; count < nl.getLength() ; count++ ) + { + org.w3c.dom.Node n = nl.item(count); + if ( n instanceof SVGElement ) + { + if ( n instanceof GraphicElement ) + { + SVGRect rect = ((GraphicElement)n).getBBox(); + if ( rect != null ) + { +/* currentAnnotList = this.pdfDoc.makeAnnotList(); + currentPage.setAnnotList(currentAnnotList); + String dest = linkSet.getDest(); + int linkType = linkSet.getLinkType(); + currentAnnotList.addLink( + this.pdfDoc.makeLink(lrect.getRectangle(), dest, linkType)); + currentAnnotList = null; +*/ } + } + renderElement((SVGElement)n, posx, posy); + } + } + } + else if ( area instanceof SVGSwitchElement ) + { + handleSwitchElement(posx, posy, (SVGSwitchElement)area); + } + // should be done with some cleanup code, so only + // required values are reset. + //currentStream.add("Q\n"); + } + + /** + * Adds an svg string to the output. + * This handles the escaping of special pdf chars and deals with + * whitespace. + */ + protected float addSVGStr(FontState fs, float currentX, String str, + boolean spacing) { + boolean inbetween = false; + boolean addedspace = false; + StringBuffer pdf = new StringBuffer(); + for (int i = 0; i < str.length(); i++) { + char ch = str.charAt(i); + switch (ch) + { + case '\t': + case ' ': + if (spacing) { + pdf = pdf.append(' '); + currentX += fs.width(' ') / 1000f; + } else { + if (inbetween && !addedspace) { + addedspace = true; + pdf = pdf.append(' '); + currentX += fs.width(' ') / 1000f; + } + } + break; + case '\n': + case '\r': + if (spacing) { + pdf = pdf.append(' '); + currentX += fs.width(' ') / 1000f; + } + break; + default: + addedspace = false; + pdf = pdf.append(ch); + currentX += fs.width(ch) / 1000f; + inbetween = true; + break; + } + } + currentStream.add(pdf.toString()); + return currentX; + } + + /** + * Locates a defined element in an svg document. + * Either gets the element defined by its "id" in the current + * SVGDocument, or if the uri reference is to an external + * document it loads the document and returns the element. + */ + protected SVGElement locateDef(String ref, SVGElement currentElement) { + int pos; + ref = ref.trim(); + pos = ref.indexOf("#"); + if (pos == 0) { + // local doc + Document doc = currentElement.getOwnerDocument(); + Element ele = + doc.getElementById(ref.substring(1, ref.length())); + if (ele instanceof SVGElement) { + return (SVGElement) ele; + } + } else if (pos != -1) { + String href = ref.substring(0, pos); + if (href.indexOf(":") == -1) { + href = "file:" + href; + } + try { + // this is really only to get a cached svg image + FopImage img = FopImageFactory.Make(href); + if (img instanceof SVGImage) { + SVGDocument doc = ((SVGImage) img).getSVGDocument(); + Element ele = doc.getElementById( + ref.substring(pos + 1, ref.length())); + if (ele instanceof SVGElement) { + return (SVGElement) ele; + } + } + } catch (Exception e) { + MessageHandler.errorln(e.toString()); + } + } + return null; + } + + /** + * This class is used to handle the rendering of svg text. + * This is so that it can deal with the recursive rendering + * of text markup, while keeping track of the state and position. + */ + class SVGTextRenderer { + FontState fs; + String transstr; + float currentX; + float currentY; + float baseX; + float baseY; + SVGMatrix matrix; + float x; + float y; + + SVGTextRenderer(FontState fontState, SVGTextElementImpl tg, + float x, float y) { + fs = fontState; + + //PDFNumber pdfNumber = new PDFNumber(); + SVGTransformList trans = tg.getTransform().getBaseVal(); + matrix = trans.consolidate().getMatrix(); + //transstr = (pdfNumber.doubleOut(matrix.getA()) + " " + + // pdfNumber.doubleOut(matrix.getB()) + " " + + // pdfNumber.doubleOut(matrix.getC()) + " " + + // pdfNumber.doubleOut(-matrix.getD()) + " "); + this.x = x; + this.y = y; + } + + void renderText(SVGTextElementImpl te) { + float xoffset = 0; + + if (te.anchor.getEnum() != TextAnchor.START) { + // This is a bit of a hack: The code below will update + // the current position, so all I have to do is to + // prevent that the code will write anything to the + // PCL stream... + currentStream.setDoOutput(false); + + _renderText (te, 0f, true); + + float width = currentX - te.x; + currentStream.setDoOutput(true); + + if (te.anchor.getEnum() == TextAnchor.END) { + xoffset = -width; + } else if (te.anchor.getEnum() == TextAnchor.MIDDLE) { + xoffset = -width/2; + } + } + + _renderText (te, xoffset, false); + } + + void _renderText(SVGTextElementImpl te, float xoffset, boolean getWidthOnly) + { + //DrawingInstruction di = applyStyle(te, te); + //if (di.fill) { + // if (di.stroke) { + // currentStream.write("2 Tr\n"); + // } else { + // currentStream.write("0 Tr\n"); + // } + //} else if (di.stroke) { + // currentStream.write("1 Tr\n"); + //} + updateFont(te, fs); + + float tx = te.x; + float ty = te.y; + currentX = x + tx + xoffset; + currentY = y + ty; + baseX = currentX; + baseY = currentY; + NodeList nodel = te.getChildNodes(); + // Vector list = te.textList; + for (int count = 0; count < nodel.getLength(); count++) { + Object o = nodel.item(count); + //applyStyle(te, te); + if ( o instanceof CharacterData ) + { + String str = ((CharacterData) o).getData(); + //currentStream.write(transstr + + // (currentX + matrix.getE()) + " " + + // (baseY + matrix.getF()) + " Tm " + "("); + boolean spacing = "preserve".equals(te.getXMLspace()); + //currentX = addSVGStr(fs, currentX, str, spacing); + //currentStream.write(") Tj\n"); + currentStream.add("\033&a" + (currentX + matrix.getE())*10 + "h" + (baseY + matrix.getF())*10 + "V"); + currentX = addSVGStr(fs, currentX, str, spacing); + } + else if ( o instanceof SVGTextPathElementImpl ) + { + SVGTextPathElementImpl tpg = (SVGTextPathElementImpl) o; + String ref = tpg.str; + SVGElement graph = null; + graph = locateDef(ref, tpg); + if (graph instanceof SVGPathElementImpl) { + // probably not the best way to do this, should be able + // to render without the style being set. + // GraphicImpl parent = graph.getGraphicParent(); + // graph.setParent(tpg); + // set text path?? + // how should this work + // graph.setParent(parent); + } + } else if (o instanceof SVGTRefElementImpl) { + SVGTRefElementImpl trg = (SVGTRefElementImpl) o; + String ref = trg.ref; + SVGElement element = locateDef(ref, trg); + if (element instanceof SVGTextElementImpl) { + // GraphicImpl parent = graph.getGraphicParent(); + // graph.setParent(trg); + SVGTextElementImpl tele = + (SVGTextElementImpl) element; + // the style should be from tele, but it needs to be placed as a child + // of trg to work + //di = applyStyle(trg, trg); + //if (di.fill) { + // if (di.stroke) { + // currentStream.write("2 Tr\n"); + // } else { + // currentStream.write("0 Tr\n"); + // } + //} else if (di.stroke) { + // currentStream.write("1 Tr\n"); + //} + boolean changed = false; + FontState oldfs = fs; + changed = updateFont(te, fs); + NodeList nl = tele.getChildNodes(); + boolean spacing = + "preserve".equals(trg.getXMLspace()); + renderTextNodes(spacing, nl, + trg.getX().getBaseVal(), + trg.getY().getBaseVal(), + trg.getDx().getBaseVal(), + trg.getDy().getBaseVal()); + + if (changed) { + fs = oldfs; + //currentStream.write("/" + + // fs.getFontName() + " " + + // fs.getFontSize() / 1000f + " Tf\n"); + } + // graph.setParent(parent); + } + } else if (o instanceof SVGTSpanElementImpl) { + SVGTSpanElementImpl tsg = (SVGTSpanElementImpl) o; + //applyStyle(tsg, tsg); + boolean changed = false; + FontState oldfs = fs; + changed = updateFont(tsg, fs); + boolean spacing = "preserve".equals(tsg.getXMLspace()); + renderTextNodes(spacing, tsg.getChildNodes(), + tsg.getX().getBaseVal(), + tsg.getY().getBaseVal(), + tsg.getDx().getBaseVal(), + tsg.getDy().getBaseVal()); + + // currentX += fs.width(' ') / 1000f; + if (changed) { + fs = oldfs; + //currentStream.write("/" + fs.getFontName() + + // " " + fs.getFontSize() / 1000f + " Tf\n"); + } + } else { + MessageHandler.errorln("Error: unknown text element " + o); + } + } + } + + void renderTextNodes(boolean spacing, NodeList nl, + SVGLengthList xlist, SVGLengthList ylist, + SVGLengthList dxlist, SVGLengthList dylist) { + boolean inbetween = false; + boolean addedspace = false; + int charPos = 0; + float xpos = currentX; + float ypos = currentY; + + for (int count = 0; count < nl.getLength(); count++) { + Node n = nl.item(count); + if (n instanceof CharacterData) { + //StringBuffer pdf = new StringBuffer(); + String str = ((CharacterData) n).getData(); + for (int i = 0; i < str.length(); i++) { + char ch = str.charAt(i); + xpos = currentX; + ypos = currentY; + if (ylist.getNumberOfItems() > charPos) { + ypos = baseY + (ylist.getItem(charPos)). + getValue(); + } + if (dylist.getNumberOfItems() > charPos) { + ypos = ypos + (dylist.getItem(charPos)). + getValue(); + } + if (xlist.getNumberOfItems() > charPos) { + xpos = baseX + (xlist.getItem(charPos)). + getValue(); + } + if (dxlist.getNumberOfItems() > charPos) { + xpos = xpos + (dxlist.getItem(charPos)). + getValue(); + } + + switch (ch) { + case '\t': + case ' ': + if (spacing) { + currentX = xpos + fs.width(' ') / + 1000f; + currentY = ypos; + charPos++; + } else { + if (inbetween && !addedspace) { + addedspace = true; + currentX = xpos + fs.width(' ') + / 1000f; + currentY = ypos; + charPos++; + } + } + break; + case '\n': + case '\r': + if (spacing) { + currentX = xpos + fs.width(' ') / + 1000f; + currentY = ypos; + charPos++; + } + break; + default: + addedspace = false; + //pdf = pdf.append(transstr + + // (xpos + matrix.getE()) + + // " " + (ypos + + // matrix.getF()) + " Tm " + + // "(" + ch + ") Tj\n"); + currentStream.add("\033&a" + (xpos + matrix.getE())*10 + "h" + (ypos + matrix.getF())*10 + "V" + ch); + currentX = xpos + fs.width(ch) / 1000f; + currentY = ypos; + charPos++; + inbetween = true; + break; + } + //currentStream.write(pdf.toString()); + } + } + } + } + + protected boolean updateFont(SVGStylable style, FontState fs) { + boolean changed = false; + String fontFamily = fs.getFontFamily(); + CSSValue sp = style.getPresentationAttribute("font-family"); + if (sp != null && + sp.getValueType() == CSSValue.CSS_PRIMITIVE_VALUE) { + if (((CSSPrimitiveValue) sp).getPrimitiveType() == + CSSPrimitiveValue.CSS_STRING) { + fontFamily = sp.getCssText(); + } + } + if (!fontFamily.equals(fs.getFontFamily())) { + changed = true; + } + String fontStyle = fs.getFontStyle(); + sp = style.getPresentationAttribute("font-style"); + if (sp != null && + sp.getValueType() == CSSValue.CSS_PRIMITIVE_VALUE) { + if (((CSSPrimitiveValue) sp).getPrimitiveType() == + CSSPrimitiveValue.CSS_STRING) { + fontStyle = sp.getCssText(); + } + } + if (!fontStyle.equals(fs.getFontStyle())) { + changed = true; + } + String fontWeight = fs.getFontWeight(); + sp = style.getPresentationAttribute("font-weight"); + if (sp != null && + sp.getValueType() == CSSValue.CSS_PRIMITIVE_VALUE) { + if (((CSSPrimitiveValue) sp).getPrimitiveType() == + CSSPrimitiveValue.CSS_STRING) { + fontWeight = sp.getCssText(); + } + } + if (!fontWeight.equals(fs.getFontWeight())) { + changed = true; + } + float newSize = fs.getFontSize() / 1000f; + sp = style.getPresentationAttribute("font-size"); + if (sp != null && + sp.getValueType() == CSSValue.CSS_PRIMITIVE_VALUE) { + // if(((CSSPrimitiveValue)sp).getPrimitiveType() == CSSPrimitiveValue.CSS_NUMBER) { + newSize = ((CSSPrimitiveValue) sp).getFloatValue( + CSSPrimitiveValue.CSS_PT); + // } + } + if (fs.getFontSize() / 1000f != newSize) { + changed = true; + } + if (changed) { + try { + fs = new FontState(fs.getFontInfo(), fontFamily, + fontStyle, fontWeight, (int)(newSize * 1000), + FontVariant.NORMAL); + } catch (Exception fope) { + } + this.fs = fs; + + //currentStream.write("/" + fs.getFontName() + " " + + // newSize + " Tf\n"); + renderer.setFont(fs.getFontName(), newSize * 1000); + } else { + if (!currentFontName.equals(fs.getFontName()) || + currentFontSize != fs.getFontSize()) { + // currentFontName = fs.getFontName(); + // currentFontSize = fs.getFontSize(); + //currentStream.write("/" + fs.getFontName() + " " + + // (fs.getFontSize() / 1000) + " Tf\n"); + renderer.setFont(fs.getFontName(), fs.getFontSize()); + } + } + return changed; + } + } +} diff --git a/src/org/apache/fop/render/pcl/PCLStream.java b/src/org/apache/fop/render/pcl/PCLStream.java new file mode 100755 index 000000000..8b5521bd2 --- /dev/null +++ b/src/org/apache/fop/render/pcl/PCLStream.java @@ -0,0 +1,42 @@ +//package com.eastpoint.chrysalis; +package org.apache.fop.render.pcl; + +import java.io.*; + +public class PCLStream +{ + OutputStream out = null; + boolean doOutput = true; + + public PCLStream(OutputStream os) + { + out = os; + } + + public void add(String str) + { + if ( !doOutput ) + return; + + byte buff[] = new byte[str.length()]; + int countr; + int len = str.length(); + for ( countr = 0 ; countr < len ; countr++ ) + buff[countr] = (byte)str.charAt(countr); + try + { + out.write(buff); + } + catch (IOException e) + { + //e.printStackTrace(); + //e.printStackTrace(System.out); + throw new RuntimeException(e.toString()); + } + } + + public void setDoOutput(boolean doout) + { + doOutput = doout; + } +} -- 2.39.5