/*
* $Id$
* 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.
*/
package org.apache.fop.render.ps;
// FOP
import org.apache.fop.render.AbstractRenderer;
import org.apache.fop.render.Renderer;
import org.apache.fop.image.FopImage;
import org.apache.fop.layout.*;
import org.apache.fop.layout.inline.*;
import org.apache.fop.datatypes.*;
import org.apache.fop.fo.properties.*;
import org.apache.fop.render.pdf.Font;
import org.apache.fop.image.*;
import org.apache.batik.bridge.*;
import org.apache.batik.swing.svg.*;
import org.apache.batik.swing.gvt.*;
import org.apache.batik.gvt.*;
import org.apache.batik.gvt.renderer.*;
import org.apache.batik.gvt.filter.*;
import org.apache.batik.gvt.event.*;
// SVG
import org.w3c.dom.svg.SVGSVGElement;
import org.w3c.dom.svg.SVGDocument;
import org.w3c.dom.*;
import org.w3c.dom.svg.*;
// Java
import java.io.*;
import java.util.*;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Iterator;
import java.util.HashMap;
import java.awt.geom.AffineTransform;
import java.awt.geom.Dimension2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.Dimension;
/**
* Renderer that renders to PostScript.
*
* This class currently generates PostScript Level 2 code. The only exception
* is the FlateEncode filter which is a Level 3 feature. The filters in use
* are hardcoded at the moment.
*
* This class follows the Document Structuring Conventions (DSC) version 3.0
* (If I did everything right). If anyone modifies this renderer please make
* sure to also follow the DSC to make it simpler to programmatically modify
* the generated Postscript files (ex. extract pages etc.).
*
* TODO: Character size/spacing, SVG Transcoder for Batik, configuration, move
* to PrintRenderer, maybe improve filters (I'm not very proud of them), add a
* RunLengthEncode filter (useful for Level 2 Postscript), Improve
* DocumentProcessColors stuff (probably needs to be configurable, then maybe
* add a color to grayscale conversion for bitmaps to make output smaller (See
* PCLRenderer), font embedding, support different character encodings, try to
* implement image transparency, positioning of images is wrong etc.
*
* Modified by Mark Lillywhite mark-fop@inomial.com, to use the new
* Renderer interface. This PostScript renderer appears to be the
* most efficient at producing output.
*
* @author Jeremias Märki
*/
public class PSRenderer extends AbstractRenderer {
/**
* the application producing the PostScript
*/
protected String producer;
int imagecount = 0; // DEBUG
private boolean enableComments = true;
/**
* the stream used to output the PostScript
*/
protected PSStream out;
private boolean ioTrouble = false;
private String currentFontName;
private int currentFontSize;
private int pageHeight;
private int pageWidth;
private float currRed;
private float currGreen;
private float currBlue;
private FontInfo fontInfo;
protected IDReferences idReferences;
/**
* set the document's producer
*
* @param producer string indicating application producing the PostScript
*/
public void setProducer(String producer) {
this.producer = producer;
}
/**
* write out a command
*/
protected void write(String cmd) {
try {
out.write(cmd);
} catch (IOException e) {
if (!ioTrouble)
e.printStackTrace();
ioTrouble = true;
}
}
/**
* write out a comment
*/
protected void comment(String comment) {
if (this.enableComments)
write(comment);
}
protected void writeFontDict(FontInfo fontInfo) {
write("%%BeginResource: procset FOPFonts");
write("%%Title: Font setup (shortcuts) for this file");
write("/FOPFonts 100 dict dup begin");
write("/bd{bind def}bind def");
write("/ld{load def}bd");
write("/M/moveto ld");
write("/RM/rmoveto ld");
write("/t/show ld");
write("/ux 0.0 def");
write("/uy 0.0 def");
// write("/cf /Helvetica def");
// write("/cs 12000 def");
// F
write("/F {");
write(" /Tp exch def");
// write(" currentdict exch get");
write(" /Tf exch def");
write(" Tf findfont Tp scalefont setfont");
write(" /cf Tf def /cs Tp def /cw ( ) stringwidth pop def");
write("} bd");
write("/ULS {currentpoint /uy exch def /ux exch def} bd");
write("/ULE {");
write(" /Tcx currentpoint pop def");
write(" gsave");
write(" newpath");
write(" cf findfont cs scalefont dup");
write(" /FontMatrix get 0 get /Ts exch def /FontInfo get dup");
write(" /UnderlinePosition get Ts mul /To exch def");
write(" /UnderlineThickness get Ts mul /Tt exch def");
write(" ux uy To add moveto Tcx uy To add lineto");
write(" Tt setlinewidth stroke");
write(" grestore");
write("} bd");
write("/OLE {");
write(" /Tcx currentpoint pop def");
write(" gsave");
write(" newpath");
write(" cf findfont cs scalefont dup");
write(" /FontMatrix get 0 get /Ts exch def /FontInfo get dup");
write(" /UnderlinePosition get Ts mul /To exch def");
write(" /UnderlineThickness get Ts mul /Tt exch def");
write(" ux uy To add cs add moveto Tcx uy To add cs add lineto");
write(" Tt setlinewidth stroke");
write(" grestore");
write("} bd");
write("/SOE {");
write(" /Tcx currentpoint pop def");
write(" gsave");
write(" newpath");
write(" cf findfont cs scalefont dup");
write(" /FontMatrix get 0 get /Ts exch def /FontInfo get dup");
write(" /UnderlinePosition get Ts mul /To exch def");
write(" /UnderlineThickness get Ts mul /Tt exch def");
write(" ux uy To add cs 10 mul 26 idiv add moveto Tcx uy To add cs 10 mul 26 idiv add lineto");
write(" Tt setlinewidth stroke");
write(" grestore");
write("} bd");
// 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();
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();
Font fm = (Font)fonts.get(key);
write("/" + fm.fontName() + " findfont");
write("dup length dict begin");
write(" {1 index /FID ne {def} {pop pop} ifelse} forall");
write(" /Encoding ISOLatin1Encoding def");
write(" currentdict");
write("end");
write("/" + fm.fontName() + " exch definefont pop");
}
}
protected void movetoCurrPosition() {
write(this.currentIPPosition + " " + this.currentBPPosition + " M");
}
/**
* set up the font info
*
* @param fontInfo the font info object to set up
*/
public void setupFontInfo(FontInfo fontInfo) {
/* use PDF's font setup to get PDF metrics */
org.apache.fop.render.pdf.FontSetup.setup(fontInfo);
this.fontInfo = fontInfo;
}
protected void addFilledRect(int x, int y, int w, int h,
ColorType col) {
write("newpath");
write(x + " " + y + " M");
write(w + " 0 rlineto");
write("0 " + (-h) + " rlineto");
write((-w) + " 0 rlineto");
write("0 " + h + " rlineto");
write("closepath");
useColor(col);
write("fill");
}
private long copyStream(InputStream in, OutputStream out,
int bufferSize) throws IOException {
long bytes_total = 0;
byte[] buf = new byte[bufferSize];
int bytes_read;
while ((bytes_read = in.read(buf)) != -1) {
bytes_total += bytes_read;
out.write(buf, 0, bytes_read);
}
return bytes_total;
}
private long copyStream(InputStream in,
OutputStream out) throws IOException {
return copyStream(in, out, 4096);
}
public void useFont(String name, int size) {
if ((currentFontName != name) || (currentFontSize != size)) {
write(name + " " + size + " F");
currentFontName = name;
currentFontSize = size;
}
}
private void useColor(ColorType col) {
useColor(col.red(), col.green(), col.blue());
}
private void useColor(float red, float green, float blue) {
if ((red != currRed) || (green != currGreen) || (blue != currBlue)) {
write(red + " " + green + " " + blue + " setrgbcolor");
currRed = red;
currGreen = green;
currBlue = blue;
}
}
/**
*/
public void startRenderer(OutputStream outputStream)
throws IOException {
log.debug("rendering areas to PostScript");
this.out = new PSStream(outputStream);
write("%!PS-Adobe-3.0");
write("%%Creator: "+this.producer);
write("%%DocumentProcessColors: Black");
write("%%DocumentSuppliedResources: procset FOPFonts");
write("%%EndComments");
write("%%BeginDefaults");
write("%%EndDefaults");
write("%%BeginProlog");
write("%%EndProlog");
write("%%BeginSetup");
writeFontDict(fontInfo);
/* 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");
write("/op_count count 1 sub def % Count objects on operand stack");
write("userdict begin % Push userdict on dict stack");
write("/showpage { } def % Redefine showpage, { } = null proc");
write("0 setgray 0 setlinecap % Prepare graphics state");
write("1 setlinewidth 0 setlinejoin");
write("10 setmiterlimit [ ] 0 setdash newpath");
write("/languagelevel where % If level not equal to 1 then");
write("{pop languagelevel % set strokeadjust and");
write("1 ne % overprint to their defaults.");
write("{false setstrokeadjust false setoverprint");
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");
}
/**
*/
public void stopRenderer()
throws IOException {
write("%%Trailer");
write("%%EOF");
this.out.flush();
}
}