All formats

Apache XML Graphics Commons

LanguageJava
LicenseApache-2.0
Repositorygithub.com/apache/xmlgraphics-commons
DocumentationPostScript support
Depends oncommons-logging
Read / writeDSC structure only (no interpreter) / yes (Level 2 PostScript, EPS)

XML Graphics Commons is the shared code of Apache FOP and Apache Batik. Its PostScript package has:

It can’t render PostScript. Transparency isn’t supported in PostScript, so semi-transparent Java2D drawing is not reproduced.

Installation#

Maven:

XML
<dependency>
  <groupId>org.apache.xmlgraphics</groupId>
  <artifactId>xmlgraphics-commons</artifactId>
  <version>2.11</version>
</dependency>

Gradle:

implementation("org.apache.xmlgraphics:xmlgraphics-commons:2.11")

Basic usage: draw a two-page PostScript file with Java2D#

Java
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

import org.apache.xmlgraphics.java2d.GraphicContext;
import org.apache.xmlgraphics.java2d.ps.PSDocumentGraphics2D;

public class WritePs {
    public static void main(String[] args) throws IOException {
        try (OutputStream out = new BufferedOutputStream(new FileOutputStream("out.ps"))) {
            // true: draw text as shapes (exact look); false: use PostScript fonts
            PSDocumentGraphics2D g2d = new PSDocumentGraphics2D(true);
            g2d.setGraphicContext(new GraphicContext());
            g2d.setupDocument(out, 595, 842);  // A4 in points

            g2d.setColor(new Color(50, 100, 200));
            g2d.fillRect(72, 72, 300, 150);    // origin is top-left, as in Java2D
            g2d.setColor(Color.BLACK);
            g2d.setFont(new Font("SansSerif", Font.BOLD, 24));
            g2d.drawString("Hello, PostScript", 72, 280);

            g2d.nextPage();
            g2d.setStroke(new BasicStroke(4f));
            g2d.setColor(Color.RED);
            g2d.drawOval(197, 321, 200, 200);

            g2d.finish();  // writes the trailer
        }
    }
}

How to read the page count and bounding box of a PostScript file#

DSCParser walks the DSC comments. Wrap the input in a BufferedInputStream: the parser needs mark() support.

Java
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.InputStream;

import org.apache.xmlgraphics.ps.dsc.DSCParser;
import org.apache.xmlgraphics.ps.dsc.events.DSCComment;
import org.apache.xmlgraphics.ps.dsc.events.DSCCommentBoundingBox;
import org.apache.xmlgraphics.ps.dsc.events.DSCCommentPage;
import org.apache.xmlgraphics.ps.dsc.events.DSCCommentPages;
import org.apache.xmlgraphics.ps.dsc.events.DSCCommentTitle;
import org.apache.xmlgraphics.ps.dsc.events.DSCEvent;

public class PsInfo {
    public static void main(String[] args) throws Exception {
        try (InputStream in = new BufferedInputStream(new FileInputStream(args[0]))) {
            DSCParser parser = new DSCParser(in);
            int pagesSeen = 0;
            while (parser.hasNext()) {
                DSCEvent event = parser.nextEvent();
                if (!event.isDSCComment()) continue;
                DSCComment c = event.asDSCComment();
                if (c instanceof DSCCommentTitle t) {
                    System.out.println("title: " + t.getTitle());
                } else if (c instanceof DSCCommentPages p) {
                    System.out.println("%%Pages: " + p.getPageCount());
                } else if (c instanceof DSCCommentBoundingBox b && b.getBoundingBox() != null) {
                    System.out.println("bounding box: " + b.getBoundingBox());
                } else if (c instanceof DSCCommentPage page) {
                    pagesSeen++;
                    System.out.println("page " + page.getPageName() + " (#" + page.getPagePosition() + ")");
                }
            }
            System.out.println("pages found: " + pagesSeen);
        }
    }
}

%%Pages: (atend) puts the real value in the trailer, so count %%Page: comments to be safe.

How to find a single DSC comment#

nextDSCComment skips to the next comment with a given name and returns null at the end of the file:

Java
import org.apache.xmlgraphics.ps.DSCConstants;

DSCParser parser = new DSCParser(new BufferedInputStream(new FileInputStream("in.ps")));
DSCComment bbox = parser.nextDSCComment(DSCConstants.BBOX);
if (bbox instanceof DSCCommentBoundingBox b) {
    System.out.println(b.getBoundingBox());  // java.awt.geom.Rectangle2D in points
}

Tips#