org.apache.fop.render.pdf.PDFDocumentHandlerMaker\r
org.apache.fop.render.pcl.PCLDocumentHandlerMaker\r
-org.apache.fop.render.bitmap.TIFFDocumentHandlerMaker
\ No newline at end of file
+org.apache.fop.render.bitmap.TIFFDocumentHandlerMaker\r
+org.apache.fop.render.ps.PSDocumentHandlerMaker
\ No newline at end of file
}
/** {@inheritDoc} */
- public void startPage(int index, String name, Dimension size) throws IFException {
+ public void startPage(int index, String name, String pageMasterName, Dimension size)
+ throws IFException {
this.pageCount++;
this.currentPageDimensions = new Dimension(size);
}
}
}
+ /**
+ * Converts a transformation matrix from millipoints to points.
+ * @param transform the transformation matrix (in millipoints)
+ * @return the converted transformation matrix (in points)
+ */
+ public static AffineTransform toPoints(AffineTransform transform) {
+ final double[] matrix = new double[6];
+ transform.getMatrix(matrix);
+ //Convert from millipoints to points
+ matrix[4] /= 1000;
+ matrix[5] /= 1000;
+ return new AffineTransform(matrix);
+ }
+
+
}
* Indicates the start of a new page.
* @param index the index of the page (0-based)
* @param name the page name (usually the formatted page number)
+ * @param pageMasterName the name of the simple-page-master that generated this page
* @param size the size of the page (equivalent to the MediaBox in PDF)
* @throws IFException if an error occurs while handling this event
*/
- void startPage(int index, String name, Dimension size) throws IFException;
+ void startPage(int index, String name, String pageMasterName, Dimension size) throws IFException;
/**
* Indicates the end of a page
public void startElement(Attributes attributes) throws IFException {
int index = Integer.parseInt(attributes.getValue("index"));
String name = attributes.getValue("name");
+ String pageMasterName = attributes.getValue("page-master-name");
int width = Integer.parseInt(attributes.getValue("width"));
int height = Integer.parseInt(attributes.getValue("height"));
- documentHandler.startPage(index, name, new Dimension(width, height));
+ documentHandler.startPage(index, name, pageMasterName, new Dimension(width, height));
}
public void endElement() throws IFException {
}
}
+ /** {@inheritDoc} */
+ public boolean supportsOutOfOrder() {
+ return (this.documentHandler != null
+ ? this.documentHandler.supportsPagesOutOfOrder() : false);
+ }
+
/**
* Creates a default {@code IFDocumentHandler} when none has been set.
* @return the default IFDocumentHandler
Dimension dim = new Dimension(
(int)Math.ceil(viewArea.getWidth()),
(int)Math.ceil(viewArea.getHeight()));
- documentHandler.startPage(page.getPageIndex(), page.getPageNumberString(), dim);
+ documentHandler.startPage(page.getPageIndex(), page.getPageNumberString(),
+ page.getSimplePageMasterName(), dim);
documentHandler.startPageHeader();
//TODO Handle page header
documentHandler.endPageHeader();
int tws = ((TextArea) space.getParentArea()).getTextWordSpaceAdjust()
+ 2 * textArea.getTextLetterSpaceAdjust();
if (tws != 0) {
- float fontSize = font.getFontSize() / 1000f;
- textUtil.adjust(Math.round(tws / fontSize * 10));
+ textUtil.adjust(tws);
}
}
super.renderSpace(space);
for (int i = 0; i < l; i++) {
char ch = s.charAt(i);
textUtil.addChar(ch);
- float glyphAdjust = 0;
+ int glyphAdjust = 0;
if (font.hasChar(ch)) {
int tls = (i < l - 1 ? parentArea.getTextLetterSpaceAdjust() : 0);
glyphAdjust += tls;
glyphAdjust += letterAdjust[i];
}
- float adjust = glyphAdjust / fontSize;
-
- textUtil.adjust(Math.round(adjust * 10));
+ textUtil.adjust(glyphAdjust);
}
}
}
/** {@inheritDoc} */
- public void startPage(int index, String name, Dimension size) throws IFException {
+ public void startPage(int index, String name, String pageMasterName, Dimension size) throws IFException {
try {
AttributesImpl atts = new AttributesImpl();
addAttribute(atts, "index", Integer.toString(index));
addAttribute(atts, "name", name);
+ addAttribute(atts, "page-master-name", name);
addAttribute(atts, "width", Integer.toString(size.width));
addAttribute(atts, "height", Integer.toString(size.height));
handler.startElement(EL_PAGE, atts);
glyphAdjust += dx[i + 1];
}
- cursor.setLocation(cursor.getX() + cw - glyphAdjust, cursor.getY());
+ cursor.setLocation(cursor.getX() + cw + glyphAdjust, cursor.getY());
gv.setGlyphPosition(i + 1, cursor);
}
g2d.drawGlyphVector(gv, x, y);
}
/** {@inheritDoc} */
- public void startPage(int index, String name, Dimension size) throws IFException {
+ public void startPage(int index, String name, String pageMasterName, Dimension size) throws IFException {
try {
//TODO Add support for paper-source and duplex-mode
glyphAdjust += dx[i + 1];
}
- width += cw - glyphAdjust;
+ width += cw + glyphAdjust;
}
int extraWidth = font.getFontSize() / 3;
boundingRect.setSize(
g2d.setBackground(Color.LIGHT_GRAY);
g2d.clearRect(0, 0, (int)area.getWidth(), (int)area.getHeight());
}
- g2d.translate(0, -y + baselineOffset);
+ g2d.translate(-x, -y + baselineOffset);
if (DEBUG) {
Rectangle rect = new Rectangle(x, y - maxAscent, 3000, maxAscent);
g2d.draw(rect);
rect = new Rectangle(x, y - ascent, 2000, ascent);
g2d.draw(rect);
- rect = new Rectangle(x, y, 1000, - descent);
+ rect = new Rectangle(x, y, 1000, -descent);
g2d.draw(rect);
}
Java2DPainter painter = new Java2DPainter(g2d,
}
}
- /**
- * Converts a transformation matrix from millipoints to points.
- * @param transform the transformation matrix (in millipoints)
- * @return the converted transformation matrix (in points)
- */
- public AffineTransform toPoints(AffineTransform transform) {
- final double[] matrix = new double[6];
- transform.getMatrix(matrix);
- //Convert from millipoints to points
- matrix[4] /= 1000;
- matrix[5] /= 1000;
- return new AffineTransform(matrix);
- }
-
/**
* Concatenates the given transformation matrix with the current one.
* @param transform the transformation matrix (in points)
}
/** {@inheritDoc} */
- public void startPage(int index, String name, Dimension size) throws IFException {
+ public void startPage(int index, String name, String pageMasterName, Dimension size)
+ throws IFException {
this.pdfResources = this.pdfDoc.getResources();
this.currentPage = this.pdfDoc.getFactory().makePage(
public void startViewport(AffineTransform transform, Dimension size, Rectangle clipRect)
throws IFException {
generator.saveGraphicsState();
- generator.concatenate(generator.toPoints(transform));
+ generator.concatenate(toPoints(transform));
if (clipRect != null) {
clipRect(clipRect);
}
/** {@inheritDoc} */
public void startGroup(AffineTransform transform) throws IFException {
generator.saveGraphicsState();
- generator.concatenate(generator.toPoints(transform));
+ generator.concatenate(toPoints(transform));
}
/** {@inheritDoc} */
if (fill == null) {
return;
}
- generator.endTextObject();
if (rect.width != 0 && rect.height != 0) {
+ generator.endTextObject();
if (fill != null) {
if (fill instanceof Color) {
generator.updateColor((Color)fill, true, null);
//Fixed width space are rendered as spaces so copy/paste works in a reader
ch = font.mapChar(CharUtilities.SPACE);
int spaceDiff = font.getCharWidth(ch) - font.getCharWidth(orgChar);
- glyphAdjust = -(10 * spaceDiff / fontSize);
+ glyphAdjust = -spaceDiff;
} else {
ch = font.mapChar(orgChar);
}
}
if (glyphAdjust != 0) {
- textutil.adjustGlyphTJ(-glyphAdjust / 10f);
+ textutil.adjustGlyphTJ(-glyphAdjust / fontSize);
}
}
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.ps;
+
+/**
+ * Constants used for configuring PostScript output.
+ */
+public interface PSConfigurationConstants {
+
+ /** Controls the behaviour for landscape pages */
+ String AUTO_ROTATE_LANDSCAPE = "auto-rotate-landscape";
+ /** Controls whether resources are optimized (rather than inlined) */
+ String OPTIMIZE_RESOURCES = "optimize-resources";
+ /** Determines the PostScript language level to be generated */
+ String LANGUAGE_LEVEL = "language-level";
+}
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.ps;
+
+import java.awt.Dimension;
+import java.awt.geom.Rectangle2D;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.transform.Source;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.xmlgraphics.ps.DSCConstants;
+import org.apache.xmlgraphics.ps.PSGenerator;
+import org.apache.xmlgraphics.ps.PSPageDeviceDictionary;
+import org.apache.xmlgraphics.ps.PSProcSets;
+import org.apache.xmlgraphics.ps.PSResource;
+import org.apache.xmlgraphics.ps.dsc.DSCException;
+import org.apache.xmlgraphics.ps.dsc.ResourceTracker;
+import org.apache.xmlgraphics.ps.dsc.events.DSCCommentBoundingBox;
+import org.apache.xmlgraphics.ps.dsc.events.DSCCommentHiResBoundingBox;
+
+import org.apache.fop.apps.FOUserAgent;
+import org.apache.fop.apps.MimeConstants;
+import org.apache.fop.fonts.LazyFont;
+import org.apache.fop.fonts.Typeface;
+import org.apache.fop.render.intermediate.AbstractBinaryWritingIFDocumentHandler;
+import org.apache.fop.render.intermediate.IFDocumentHandlerConfigurator;
+import org.apache.fop.render.intermediate.IFException;
+import org.apache.fop.render.intermediate.IFPainter;
+import org.apache.fop.render.ps.extensions.PSExtensionAttachment;
+import org.apache.fop.render.ps.extensions.PSSetPageDevice;
+
+/**
+ * {@code IFDocumentHandler} implementation that produces PostScript.
+ */
+public class PSDocumentHandler extends AbstractBinaryWritingIFDocumentHandler {
+
+ /** logging instance */
+ private static Log log = LogFactory.getLog(PSDocumentHandler.class);
+
+ /**
+ * Utility class which enables all sorts of features that are not directly connected to the
+ * normal rendering process.
+ */
+ protected PSRenderingUtil psUtil;
+
+ /** The PostScript generator used to output the PostScript */
+ protected PSGenerator gen;
+
+ /** the temporary file in case of two-pass processing */
+ private File tempFile;
+
+ private int currentPageNumber = 0;
+
+ /** Is used to determine the document's bounding box */
+ private Rectangle2D documentBoundingBox;
+
+ /** Used to temporarily store PSSetupCode instance until they can be written. */
+ private List setupCodeList;
+
+ /** This is a map of PSResource instances of all fonts defined (key: font key) */
+ private Map fontResources;
+ /** This is a map of PSResource instances of all forms (key: uri) */
+ private Map formResources;
+
+ /** encapsulation of dictionary used in setpagedevice instruction **/
+ private PSPageDeviceDictionary pageDeviceDictionary;
+
+ /** This is a collection holding all document header comments */
+ private Collection headerComments;
+
+ /** This is a collection holding all document footer comments */
+ private Collection footerComments;
+
+ /**
+ * Default constructor.
+ */
+ public PSDocumentHandler() {
+ }
+
+ /** {@inheritDoc} */
+ public boolean supportsPagesOutOfOrder() {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ public String getMimeType() {
+ return MimeConstants.MIME_POSTSCRIPT;
+ }
+
+ /** {@inheritDoc} */
+ public void setUserAgent(FOUserAgent ua) {
+ super.setUserAgent(ua);
+ this.psUtil = new PSRenderingUtil(ua);
+ }
+
+ /** {@inheritDoc} */
+ public IFDocumentHandlerConfigurator getConfigurator() {
+ return new PSRendererConfigurator(getUserAgent());
+ }
+
+ PSRenderingUtil getPSUtil() {
+ return this.psUtil;
+ }
+
+ /** {@inheritDoc} */
+ public void startDocument() throws IFException {
+ try {
+ if (getUserAgent() == null) {
+ throw new IllegalStateException(
+ "User agent must be set before starting PostScript generation");
+ }
+ if (this.outputStream == null) {
+ throw new IllegalStateException("OutputStream hasn't been set through setResult()");
+ }
+ OutputStream out;
+ if (psUtil.isOptimizeResources()) {
+ this.tempFile = File.createTempFile("fop", null);
+ out = new java.io.FileOutputStream(this.tempFile);
+ out = new java.io.BufferedOutputStream(out);
+ } else {
+ out = this.outputStream;
+ }
+
+ //Setup for PostScript generation
+ this.gen = new PSGenerator(out) {
+ /** Need to subclass PSGenerator to have better URI resolution */
+ public Source resolveURI(String uri) {
+ return getUserAgent().resolveURI(uri);
+ }
+ };
+ this.gen.setPSLevel(psUtil.getLanguageLevel());
+ this.currentPageNumber = 0;
+ this.documentBoundingBox = new Rectangle2D.Double();
+
+ //Initial default page device dictionary settings
+ this.pageDeviceDictionary = new PSPageDeviceDictionary();
+ pageDeviceDictionary.setFlushOnRetrieval(!psUtil.isDSCComplianceEnabled());
+ pageDeviceDictionary.put("/ImagingBBox", "null");
+ } catch (IOException e) {
+ throw new IFException("I/O error in startDocument()", e);
+ }
+ }
+
+ private void writeHeader() throws IOException {
+ //PostScript Header
+ gen.writeln(DSCConstants.PS_ADOBE_30);
+ gen.writeDSCComment(DSCConstants.CREATOR, new String[] {getUserAgent().getProducer()});
+ gen.writeDSCComment(DSCConstants.CREATION_DATE, new Object[] {new java.util.Date()});
+ gen.writeDSCComment(DSCConstants.LANGUAGE_LEVEL, new Integer(gen.getPSLevel()));
+ gen.writeDSCComment(DSCConstants.PAGES, new Object[] {DSCConstants.ATEND});
+ gen.writeDSCComment(DSCConstants.BBOX, DSCConstants.ATEND);
+ gen.writeDSCComment(DSCConstants.HIRES_BBOX, DSCConstants.ATEND);
+ gen.writeDSCComment(DSCConstants.DOCUMENT_SUPPLIED_RESOURCES,
+ new Object[] {DSCConstants.ATEND});
+ if (headerComments != null) {
+ for (Iterator iter = headerComments.iterator(); iter.hasNext();) {
+ PSExtensionAttachment comment = (PSExtensionAttachment)iter.next();
+ gen.writeln("%" + comment.getContent());
+ }
+ }
+ gen.writeDSCComment(DSCConstants.END_COMMENTS);
+
+ //Defaults
+ gen.writeDSCComment(DSCConstants.BEGIN_DEFAULTS);
+ gen.writeDSCComment(DSCConstants.END_DEFAULTS);
+
+ //Prolog and Setup written right before the first page-sequence, see startPageSequence()
+ //Do this only once, as soon as we have all the content for the Setup section!
+ //Prolog
+ gen.writeDSCComment(DSCConstants.BEGIN_PROLOG);
+ PSProcSets.writeStdProcSet(gen);
+ PSProcSets.writeEPSProcSet(gen);
+ gen.writeDSCComment(DSCConstants.END_PROLOG);
+
+ //Setup
+ gen.writeDSCComment(DSCConstants.BEGIN_SETUP);
+ PSRenderingUtil.writeSetupCodeList(gen, setupCodeList, "SetupCode");
+ if (!psUtil.isOptimizeResources()) {
+ this.fontResources = PSFontUtils.writeFontDict(gen, fontInfo);
+ } else {
+ gen.commentln("%FOPFontSetup"); //Place-holder, will be replaced in the second pass
+ }
+ gen.writeDSCComment(DSCConstants.END_SETUP);
+ }
+
+ /** {@inheritDoc} */
+ public void endDocumentHeader() throws IFException {
+ try {
+ writeHeader();
+ } catch (IOException ioe) {
+ throw new IFException("I/O error writing the PostScript header", ioe);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void endDocument() throws IFException {
+ try {
+ //Write trailer
+ gen.writeDSCComment(DSCConstants.TRAILER);
+ if (footerComments != null) {
+ for (Iterator iter = footerComments.iterator(); iter.hasNext();) {
+ PSExtensionAttachment comment = (PSExtensionAttachment)iter.next();
+ gen.commentln("%" + comment.getContent());
+ }
+ footerComments.clear();
+ }
+ gen.writeDSCComment(DSCConstants.PAGES, new Integer(this.currentPageNumber));
+ new DSCCommentBoundingBox(this.documentBoundingBox).generate(gen);
+ new DSCCommentHiResBoundingBox(this.documentBoundingBox).generate(gen);
+ gen.getResourceTracker().writeResources(false, gen);
+ gen.writeDSCComment(DSCConstants.EOF);
+ gen.flush();
+ log.debug("Rendering to PostScript complete.");
+ if (psUtil.isOptimizeResources()) {
+ IOUtils.closeQuietly(gen.getOutputStream());
+ rewritePostScriptFile();
+ }
+ if (footerComments != null) {
+ headerComments.clear();
+ }
+ if (pageDeviceDictionary != null) {
+ pageDeviceDictionary.clear();
+ }
+ } catch (IOException ioe) {
+ throw new IFException("I/O error in endDocument()", ioe);
+ }
+ super.endDocument();
+ }
+
+ /**
+ * Used for two-pass production. This will rewrite the PostScript file from the temporary
+ * file while adding all needed resources.
+ * @throws IOException In case of an I/O error.
+ */
+ private void rewritePostScriptFile() throws IOException {
+ log.debug("Processing PostScript resources...");
+ long startTime = System.currentTimeMillis();
+ ResourceTracker resTracker = gen.getResourceTracker();
+ InputStream in = new java.io.FileInputStream(this.tempFile);
+ in = new java.io.BufferedInputStream(in);
+ try {
+ try {
+ ResourceHandler.process(getUserAgent(), in, this.outputStream,
+ this.fontInfo, resTracker, this.formResources,
+ this.currentPageNumber, this.documentBoundingBox);
+ this.outputStream.flush();
+ } catch (DSCException e) {
+ throw new RuntimeException(e.getMessage());
+ }
+ } finally {
+ IOUtils.closeQuietly(in);
+ if (!this.tempFile.delete()) {
+ this.tempFile.deleteOnExit();
+ log.warn("Could not delete temporary file: " + this.tempFile);
+ }
+ }
+ if (log.isDebugEnabled()) {
+ long duration = System.currentTimeMillis() - startTime;
+ log.debug("Resource Processing complete in " + duration + " ms.");
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void startPageSequence(String id) throws IFException {
+ //nop
+ }
+
+ /** {@inheritDoc} */
+ public void endPageSequence() throws IFException {
+ //nop
+ }
+
+ /** {@inheritDoc} */
+ public void startPage(int index, String name, String pageMasterName, Dimension size)
+ throws IFException {
+ try {
+ if (this.currentPageNumber == 0) {
+ //writeHeader();
+ }
+
+ this.currentPageNumber++;
+
+ gen.getResourceTracker().notifyStartNewPage();
+ gen.getResourceTracker().notifyResourceUsageOnPage(PSProcSets.STD_PROCSET);
+ gen.writeDSCComment(DSCConstants.PAGE, new Object[]
+ {name,
+ new Integer(this.currentPageNumber)});
+
+ double pageWidth = size.width / 1000.0;
+ double pageHeight = size.height / 1000.0;
+ boolean rotate = false;
+ List pageSizes = new java.util.ArrayList();
+ if (this.psUtil.isAutoRotateLandscape() && (pageHeight < pageWidth)) {
+ rotate = true;
+ pageSizes.add(new Long(Math.round(pageHeight)));
+ pageSizes.add(new Long(Math.round(pageWidth)));
+ } else {
+ pageSizes.add(new Long(Math.round(pageWidth)));
+ pageSizes.add(new Long(Math.round(pageHeight)));
+ }
+ pageDeviceDictionary.put("/PageSize", pageSizes);
+
+ //TODO Handle extension attachments for the page!!!!!!!
+ /*
+ if (page.hasExtensionAttachments()) {
+ for (Iterator iter = page.getExtensionAttachments().iterator();
+ iter.hasNext();) {
+ ExtensionAttachment attachment = (ExtensionAttachment) iter.next();
+ if (attachment instanceof PSSetPageDevice) {*/
+ /**
+ * Extract all PSSetPageDevice instances from the
+ * attachment list on the s-p-m and add all
+ * dictionary entries to our internal representation
+ * of the the page device dictionary.
+ *//*
+ PSSetPageDevice setPageDevice = (PSSetPageDevice)attachment;
+ String content = setPageDevice.getContent();
+ if (content != null) {
+ try {
+ pageDeviceDictionary.putAll(PSDictionary.valueOf(content));
+ } catch (PSDictionaryFormatException e) {
+ PSEventProducer eventProducer = PSEventProducer.Provider.get(
+ getUserAgent().getEventBroadcaster());
+ eventProducer.postscriptDictionaryParseError(this, content, e);
+ }
+ }
+ }
+ }
+ }*/
+
+ if (setupCodeList != null) {
+ PSRenderingUtil.writeEnclosedExtensionAttachments(gen, setupCodeList);
+ setupCodeList.clear();
+ }
+ final Integer zero = new Integer(0);
+ Rectangle2D pageBoundingBox = new Rectangle2D.Double();
+ if (rotate) {
+ pageBoundingBox.setRect(0, 0, pageHeight, pageWidth);
+ gen.writeDSCComment(DSCConstants.PAGE_BBOX, new Object[] {
+ zero, zero, new Long(Math.round(pageHeight)),
+ new Long(Math.round(pageWidth)) });
+ gen.writeDSCComment(DSCConstants.PAGE_HIRES_BBOX, new Object[] {
+ zero, zero, new Double(pageHeight),
+ new Double(pageWidth) });
+ gen.writeDSCComment(DSCConstants.PAGE_ORIENTATION, "Landscape");
+ } else {
+ pageBoundingBox.setRect(0, 0, pageWidth, pageHeight);
+ gen.writeDSCComment(DSCConstants.PAGE_BBOX, new Object[] {
+ zero, zero, new Long(Math.round(pageWidth)),
+ new Long(Math.round(pageHeight)) });
+ gen.writeDSCComment(DSCConstants.PAGE_HIRES_BBOX, new Object[] {
+ zero, zero, new Double(pageWidth),
+ new Double(pageHeight) });
+ if (psUtil.isAutoRotateLandscape()) {
+ gen.writeDSCComment(DSCConstants.PAGE_ORIENTATION,
+ "Portrait");
+ }
+ }
+ this.documentBoundingBox.add(pageBoundingBox);
+ gen.writeDSCComment(DSCConstants.PAGE_RESOURCES,
+ new Object[] {DSCConstants.ATEND});
+
+ gen.commentln("%FOPSimplePageMaster: " + pageMasterName);
+
+ gen.writeDSCComment(DSCConstants.BEGIN_PAGE_SETUP);
+
+ //TODO Handle extension attachments for the page!!!!!!!
+ /*
+ if (page.hasExtensionAttachments()) {
+ List extensionAttachments = page.getExtensionAttachments();
+ for (int i = 0; i < extensionAttachments.size(); i++) {
+ Object attObj = extensionAttachments.get(i);
+ if (attObj instanceof PSExtensionAttachment) {
+ PSExtensionAttachment attachment = (PSExtensionAttachment)attObj;
+ if (attachment instanceof PSCommentBefore) {
+ gen.commentln("%" + attachment.getContent());
+ } else if (attachment instanceof PSSetupCode) {
+ gen.writeln(attachment.getContent());
+ }
+ }
+ }
+ }*/
+
+ // Write any unwritten changes to page device dictionary
+ if (!pageDeviceDictionary.isEmpty()) {
+ String content = pageDeviceDictionary.getContent();
+ if (psUtil.isSafeSetPageDevice()) {
+ content += " SSPD";
+ } else {
+ content += " setpagedevice";
+ }
+ PSRenderingUtil.writeEnclosedExtensionAttachment(gen, new PSSetPageDevice(content));
+ }
+
+ if (rotate) {
+ gen.writeln(Math.round(pageHeight) + " 0 translate");
+ gen.writeln("90 rotate");
+ }
+ gen.concatMatrix(1, 0, 0, -1, 0, pageHeight);
+
+ gen.writeDSCComment(DSCConstants.END_PAGE_SETUP);
+ } catch (IOException ioe) {
+ throw new IFException("I/O error in startPage()", ioe);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public IFPainter startPageContent() throws IFException {
+ return new PSPainter(this);
+ }
+
+ /** {@inheritDoc} */
+ public void endPageContent() throws IFException {
+ try {
+ //Show page
+ gen.writeln("showpage");
+ } catch (IOException ioe) {
+ throw new IFException("I/O error in endPageContent()", ioe);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void endPage() throws IFException {
+ try {
+ gen.writeDSCComment(DSCConstants.PAGE_TRAILER);
+
+ //TODO Handle extension attachments for the page!!!!!!!
+ /*
+ if (page.hasExtensionAttachments()) {
+ List extensionAttachments = page.getExtensionAttachments();
+ for (int i = 0; i < extensionAttachments.size(); i++) {
+ Object attObj = extensionAttachments.get(i);
+ if (attObj instanceof PSExtensionAttachment) {
+ PSExtensionAttachment attachment = (PSExtensionAttachment)attObj;
+ if (attachment instanceof PSCommentAfter) {
+ gen.commentln("%" + attachment.getContent());
+ }
+ }
+ }
+ }*/
+ gen.getResourceTracker().writeResources(true, gen);
+ } catch (IOException ioe) {
+ throw new IFException("I/O error in endPage()", ioe);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void handleExtensionObject(Object extension) throws IFException {
+ log.debug("Don't know how to handle extension object. Ignoring: "
+ + extension + " (" + extension.getClass().getName() + ")");
+ }
+
+ private String getPostScriptNameForFontKey(String key) {
+ int pos = key.indexOf('_');
+ String postFix = null;
+ if (pos > 0) {
+ postFix = key.substring(pos);
+ key = key.substring(0, pos);
+ }
+ Map fonts = fontInfo.getFonts();
+ Typeface tf = (Typeface)fonts.get(key);
+ if (tf instanceof LazyFont) {
+ tf = ((LazyFont)tf).getRealFont();
+ }
+ if (tf == null) {
+ throw new IllegalStateException("Font not available: " + key);
+ }
+ if (postFix == null) {
+ return tf.getFontName();
+ } else {
+ return tf.getFontName() + postFix;
+ }
+ }
+
+ /**
+ * Returns the PSResource for the given font key.
+ * @param key the font key ("F*")
+ * @return the matching PSResource
+ */
+ protected PSResource getPSResourceForFontKey(String key) {
+ PSResource res = null;
+ if (this.fontResources != null) {
+ res = (PSResource)this.fontResources.get(key);
+ } else {
+ this.fontResources = new java.util.HashMap();
+ }
+ if (res == null) {
+ res = new PSResource(PSResource.TYPE_FONT, getPostScriptNameForFontKey(key));
+ this.fontResources.put(key, res);
+ }
+ return res;
+ }
+
+}
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.ps;
+
+import org.apache.fop.apps.FOUserAgent;
+import org.apache.fop.apps.MimeConstants;
+import org.apache.fop.render.intermediate.AbstractIFDocumentHandlerMaker;
+import org.apache.fop.render.intermediate.IFDocumentHandler;
+import org.apache.fop.render.intermediate.IFDocumentHandlerConfigurator;
+
+/**
+ * Intermediate format document handler factory for PostScript output.
+ */
+public class PSDocumentHandlerMaker extends AbstractIFDocumentHandlerMaker {
+
+ //TODO Revert to normal MIME after stabilization!
+ private static final String[] MIMES = new String[]
+ {MimeConstants.MIME_POSTSCRIPT + ";mode=painter"};
+
+ /** {@inheritDoc} */
+ public IFDocumentHandler makeIFDocumentHandler(FOUserAgent ua) {
+ PSDocumentHandler handler = new PSDocumentHandler();
+ handler.setUserAgent(ua);
+ return handler;
+ }
+
+ /** {@inheritDoc} */
+ public boolean needsOutputStream() {
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ public String[] getSupportedMimeTypes() {
+ return MIMES;
+ }
+
+ /** {@inheritDoc} */
+ public IFDocumentHandlerConfigurator getConfigurator(FOUserAgent userAgent) {
+ return new PSRendererConfigurator(userAgent);
+ }
+
+}
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.ps;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Paint;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.geom.AffineTransform;
+import java.io.IOException;
+import java.util.Map;
+
+import org.w3c.dom.Document;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.xmlgraphics.ps.PSGenerator;
+import org.apache.xmlgraphics.ps.PSResource;
+
+import org.apache.fop.apps.FOUserAgent;
+import org.apache.fop.fonts.Font;
+import org.apache.fop.fonts.FontInfo;
+import org.apache.fop.fonts.FontTriplet;
+import org.apache.fop.fonts.LazyFont;
+import org.apache.fop.fonts.SingleByteFont;
+import org.apache.fop.fonts.Typeface;
+import org.apache.fop.render.RenderingContext;
+import org.apache.fop.render.intermediate.AbstractIFPainter;
+import org.apache.fop.render.intermediate.IFException;
+import org.apache.fop.render.intermediate.IFState;
+import org.apache.fop.traits.BorderProps;
+import org.apache.fop.traits.RuleStyle;
+import org.apache.fop.util.CharUtilities;
+
+/**
+ * IFPainter implementation that produces PostScript.
+ */
+public class PSPainter extends AbstractIFPainter {
+
+ /** logging instance */
+ private static Log log = LogFactory.getLog(PSPainter.class);
+
+ private PSDocumentHandler documentHandler;
+
+ private boolean inTextMode = false;
+
+ /**
+ * Default constructor.
+ * @param documentHandler the parent document handler
+ */
+ public PSPainter(PSDocumentHandler documentHandler) {
+ super();
+ this.documentHandler = documentHandler;
+ this.state = IFState.create();
+ }
+
+ /** {@inheritDoc} */
+ protected FOUserAgent getUserAgent() {
+ return this.documentHandler.getUserAgent();
+ }
+
+ PSRenderingUtil getPSUtil() {
+ return this.documentHandler.psUtil;
+ }
+
+ FontInfo getFontInfo() {
+ return this.documentHandler.getFontInfo();
+ }
+
+ private PSGenerator getGenerator() {
+ return this.documentHandler.gen;
+ }
+
+ /** {@inheritDoc} */
+ public void startViewport(AffineTransform transform, Dimension size, Rectangle clipRect)
+ throws IFException {
+ try {
+ PSGenerator generator = getGenerator();
+ saveGraphicsState();
+ generator.concatMatrix(toPoints(transform));
+ } catch (IOException ioe) {
+ throw new IFException("I/O error in startViewport()", ioe);
+ }
+ if (clipRect != null) {
+ clipRect(clipRect);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void endViewport() throws IFException {
+ try {
+ restoreGraphicsState();
+ } catch (IOException ioe) {
+ throw new IFException("I/O error in endViewport()", ioe);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void startGroup(AffineTransform transform) throws IFException {
+ try {
+ PSGenerator generator = getGenerator();
+ saveGraphicsState();
+ generator.concatMatrix(toPoints(transform));
+ } catch (IOException ioe) {
+ throw new IFException("I/O error in startGroup()", ioe);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void endGroup() throws IFException {
+ try {
+ restoreGraphicsState();
+ } catch (IOException ioe) {
+ throw new IFException("I/O error in endGroup()", ioe);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void drawImage(String uri, Rectangle rect, Map foreignAttributes) throws IFException {
+ //TODO Implement me
+ }
+
+ /** {@inheritDoc} */
+ protected RenderingContext createRenderingContext() {
+ PSRenderingContext psContext = new PSRenderingContext(
+ getUserAgent(), getFontInfo());
+ return psContext;
+ }
+
+ /** {@inheritDoc} */
+ public void drawImage(Document doc, Rectangle rect, Map foreignAttributes) throws IFException {
+ drawImageUsingDocument(doc, rect);
+ }
+
+ /** {@inheritDoc} */
+ public void clipRect(Rectangle rect) throws IFException {
+ try {
+ PSGenerator generator = getGenerator();
+ endTextObject();
+ generator.defineRect(rect.x / 1000.0, rect.y / 1000.0,
+ rect.width / 1000.0, rect.height / 1000.0);
+ generator.writeln("clip newpath");
+ } catch (IOException ioe) {
+ throw new IFException("I/O error in clipRect()", ioe);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void fillRect(Rectangle rect, Paint fill) throws IFException {
+ if (fill == null) {
+ return;
+ }
+ if (rect.width != 0 && rect.height != 0) {
+ try {
+ endTextObject();
+ PSGenerator generator = getGenerator();
+ if (fill != null) {
+ if (fill instanceof Color) {
+ generator.useColor((Color)fill);
+ } else {
+ throw new UnsupportedOperationException("Non-Color paints NYI");
+ }
+ }
+ generator.defineRect(rect.x / 1000.0, rect.y / 1000.0,
+ rect.width / 1000.0, rect.height / 1000.0);
+ generator.writeln("fill");
+ } catch (IOException ioe) {
+ throw new IFException("I/O error in fillRect()", ioe);
+ }
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void drawBorderRect(Rectangle rect, BorderProps before, BorderProps after,
+ BorderProps start, BorderProps end) throws IFException {
+ if (before != null || after != null || start != null || end != null) {
+ try {
+ //TODO Implement me
+ endTextObject();
+ //this.borderPainter.drawBorders(rect, before, after, start, end);
+ } catch (IOException ioe) {
+ throw new IFException("I/O error in drawBorderRect()", ioe);
+ }
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void drawLine(Point start, Point end, int width, Color color, RuleStyle style)
+ throws IFException {
+ try {
+ //TODO Implement me
+ endTextObject();
+ //this.borderPainter.drawLine(start, end, width, color, style);
+ } catch (IOException ioe) {
+ throw new IFException("I/O error in drawLine()", ioe);
+ }
+ }
+
+ private Typeface getTypeface(String fontName) {
+ if (fontName == null) {
+ throw new NullPointerException("fontName must not be null");
+ }
+ Typeface tf = (Typeface)getFontInfo().getFonts().get(fontName);
+ if (tf instanceof LazyFont) {
+ tf = ((LazyFont)tf).getRealFont();
+ }
+ return tf;
+ }
+
+ /**
+ * Saves the graphics state of the rendering engine.
+ * @throws IOException if an I/O error occurs
+ */
+ protected void saveGraphicsState() throws IOException {
+ endTextObject();
+ getGenerator().saveGraphicsState();
+ }
+
+ /**
+ * Restores the last graphics state of the rendering engine.
+ * @throws IOException if an I/O error occurs
+ */
+ protected void restoreGraphicsState() throws IOException {
+ endTextObject();
+ getGenerator().restoreGraphicsState();
+ }
+
+ /**
+ * Indicates the beginning of a text object.
+ * @throws IOException if an I/O error occurs
+ */
+ protected void beginTextObject() throws IOException {
+ if (!inTextMode) {
+ PSGenerator generator = getGenerator();
+ generator.saveGraphicsState();
+ generator.writeln("BT");
+ inTextMode = true;
+ }
+ }
+
+ /**
+ * Indicates the end of a text object.
+ * @throws IOException if an I/O error occurs
+ */
+ protected void endTextObject() throws IOException {
+ if (inTextMode) {
+ inTextMode = false;
+ PSGenerator generator = getGenerator();
+ generator.writeln("ET");
+ generator.restoreGraphicsState();
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void drawText(int x, int y, int[] dx, int[] dy, String text) throws IFException {
+ try {
+ //Note: dy is currently ignored
+ PSGenerator generator = getGenerator();
+ generator.useColor(state.getTextColor());
+ beginTextObject();
+ FontTriplet triplet = new FontTriplet(
+ state.getFontFamily(), state.getFontStyle(), state.getFontWeight());
+ //TODO Ignored: state.getFontVariant()
+ //TODO Opportunity for font caching if font state is more heavily used
+ String fontKey = getFontInfo().getInternalFontKey(triplet);
+ int sizeMillipoints = state.getFontSize();
+ float fontSize = sizeMillipoints / 1000f;
+
+ // This assumes that *all* CIDFonts use a /ToUnicode mapping
+ Typeface tf = getTypeface(fontKey);
+ SingleByteFont singleByteFont = null;
+ if (tf instanceof SingleByteFont) {
+ singleByteFont = (SingleByteFont)tf;
+ }
+ Font font = getFontInfo().getFontInstance(triplet, sizeMillipoints);
+ //String fontName = font.getFontName();
+
+ PSResource res = this.documentHandler.getPSResourceForFontKey(fontKey);
+ generator.useFont("/" + res.getName(), fontSize);
+ generator.getResourceTracker().notifyResourceUsageOnPage(res);
+ //textutil.updateTf(fontKey, fontSize, tf.isMultiByte());
+
+ generator.writeln("1 0 0 -1 " + generator.formatDouble(x / 1000.0)
+ + " " + generator.formatDouble(y / 1000.0) + " Tm");
+ //textutil.writeTextMatrix(new AffineTransform(1, 0, 0, -1, x / 1000f, y / 1000f));
+
+ int textLen = text.length();
+ if (singleByteFont != null && singleByteFont.hasAdditionalEncodings()) {
+ //Analyze string and split up in order to paint in different sub-fonts/encodings
+ int start = 0;
+ int currentEncoding = -1;
+ for (int i = 0; i < textLen; i++) {
+ char c = text.charAt(i);
+ char mapped = tf.mapChar(c);
+ int encoding = mapped / 256;
+ if (currentEncoding != encoding) {
+ if (i > 0) {
+ writeText(text, start, i - start, dx, dy, font, tf);
+ }
+ if (encoding == 0) {
+ useFont(fontKey, sizeMillipoints);
+ } else {
+ useFont(fontKey + "_" + Integer.toString(encoding), sizeMillipoints);
+ }
+ currentEncoding = encoding;
+ start = i;
+ }
+ }
+ writeText(text, start, textLen - start, dx, dy, font, tf);
+ } else {
+ //Simple single-font painting
+ useFont(fontKey, sizeMillipoints);
+ writeText(text, 0, textLen, dx, dy, font, tf);
+ }
+ } catch (IOException ioe) {
+ throw new IFException("I/O error in drawText()", ioe);
+ }
+ }
+
+ private void writeText(String text, int start, int len, int[] dx, int[] dy,
+ Font font, Typeface tf) throws IOException {
+ PSGenerator generator = getGenerator();
+ int end = start + len;
+ int initialSize = len;
+ initialSize += initialSize / 2;
+ StringBuffer sb = new StringBuffer(initialSize);
+ sb.append("(");
+ int[] offsets = new int[len];
+ int dxl = (dx != null ? dx.length : 0);
+ for (int i = start; i < end; i++) {
+ char orgChar = text.charAt(i);
+ char ch;
+ int cw;
+ if (CharUtilities.isFixedWidthSpace(orgChar)) {
+ //Fixed width space are rendered as spaces so copy/paste works in a reader
+ ch = font.mapChar(CharUtilities.SPACE);
+ //int spaceDiff = font.getCharWidth(ch) - font.getCharWidth(orgChar);
+ //glyphAdjust = -(spaceDiff);
+ } else {
+ ch = font.mapChar(orgChar);
+ //cw = tf.getWidth(ch, font.getFontSize()) / 1000;
+ }
+
+ cw = font.getCharWidth(orgChar);
+ int glyphAdjust = 0;
+ if (dx != null && i < dxl - 1) {
+ glyphAdjust += dx[i + 1];
+ }
+ offsets[i - start] = cw + glyphAdjust;
+ char codepoint = (char)(ch % 256);
+ PSGenerator.escapeChar(codepoint, sb);
+ }
+ sb.append(")" + PSGenerator.LF + "[");
+ for (int i = 0; i < len; i++) {
+ if (i > 0) {
+ if (i % 8 == 0) {
+ sb.append(PSGenerator.LF);
+ } else {
+ sb.append(" ");
+ }
+ }
+ sb.append(generator.formatDouble(offsets[i] / 1000f));
+ }
+ sb.append("]" + PSGenerator.LF + "xshow");
+ generator.writeln(sb.toString());
+ }
+
+ private void useFont(String key, int size) throws IOException {
+ PSResource res = this.documentHandler.getPSResourceForFontKey(key);
+ PSGenerator generator = getGenerator();
+ generator.useFont("/" + res.getName(), size / 1000f);
+ generator.getResourceTracker().notifyResourceUsageOnPage(res);
+ }
+
+
+}
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
-import java.io.LineNumberReader;
import java.io.OutputStream;
import java.util.Collection;
import java.util.Iterator;
* @version $Id$
*/
public class PSRenderer extends AbstractPathOrientedRenderer
- implements ImageAdapter, PSSupportedFlavors {
+ implements ImageAdapter, PSSupportedFlavors, PSConfigurationConstants {
/** logging instance */
private static Log log = LogFactory.getLog(PSRenderer.class);
/** The MIME type for PostScript */
public static final String MIME_TYPE = "application/postscript";
- private static final String AUTO_ROTATE_LANDSCAPE = "auto-rotate-landscape";
- private static final String OPTIMIZE_RESOURCES = "optimize-resources";
- private static final String LANGUAGE_LEVEL = "language-level";
-
/** The application producing the PostScript */
private int currentPageNumber = 0;
- private final boolean enableComments = true;
- private boolean autoRotateLandscape = false;
- private int languageLevel = PSGenerator.DEFAULT_LANGUAGE_LEVEL;
-
/** the OutputStream the PS file is written to */
private OutputStream outputStream;
/** the temporary file in case of two-pass processing */
/** The PostScript generator used to output the PostScript */
protected PSGenerator gen;
- /** Determines whether the PS file is generated in two passes to minimize file size */
- private boolean twoPassGeneration = false;
private boolean ioTrouble = false;
private boolean inTextMode = false;
/** encapsulation of dictionary used in setpagedevice instruction **/
private PSPageDeviceDictionary pageDeviceDictionary;
- /** Whether or not the safe set page device macro will be used or not */
- private boolean safeSetPageDevice = false;
-
/**
- * Whether or not PostScript Document Structuring Conventions (DSC) compliant output are
- * enforced.
+ * Utility class which enables all sorts of features that are not directly connected to the
+ * normal rendering process.
*/
- private boolean dscCompliant = true;
+ protected PSRenderingUtil psUtil;
/** Is used to determine the document's bounding box */
private Rectangle2D documentBoundingBox;
/** {@inheritDoc} */
public void setUserAgent(FOUserAgent agent) {
super.setUserAgent(agent);
- Object obj;
- obj = agent.getRendererOptions().get(AUTO_ROTATE_LANDSCAPE);
- if (obj != null) {
- setAutoRotateLandscape(booleanValueOf(obj));
- }
- obj = agent.getRendererOptions().get(LANGUAGE_LEVEL);
- if (obj != null) {
- setLanguageLevel(intValueOf(obj));
- }
- obj = agent.getRendererOptions().get(OPTIMIZE_RESOURCES);
- if (obj != null) {
- setOptimizeResources(booleanValueOf(obj));
- }
+ this.psUtil = new PSRenderingUtil(getUserAgent());
}
- private boolean booleanValueOf(Object obj) {
- if (obj instanceof Boolean) {
- return ((Boolean)obj).booleanValue();
- } else if (obj instanceof String) {
- return Boolean.valueOf((String)obj).booleanValue();
- } else {
- throw new IllegalArgumentException("Boolean or \"true\" or \"false\" expected.");
- }
- }
-
- private int intValueOf(Object obj) {
- if (obj instanceof Integer) {
- return ((Integer)obj).intValue();
- } else if (obj instanceof String) {
- return Integer.parseInt((String)obj);
- } else {
- throw new IllegalArgumentException("Integer or String with a number expected.");
- }
+ PSRenderingUtil getPSUtil() {
+ return this.psUtil;
}
/**
* a "wider-than-long" page by 90 degrees.
*/
public void setAutoRotateLandscape(boolean value) {
- this.autoRotateLandscape = value;
+ getPSUtil().setAutoRotateLandscape(value);
}
/** @return true if the renderer is configured to rotate landscape pages */
public boolean isAutoRotateLandscape() {
- return this.autoRotateLandscape;
+ return getPSUtil().isAutoRotateLandscape();
}
/**
* @param level the language level (currently allowed: 2 or 3)
*/
public void setLanguageLevel(int level) {
- if (level == 2 || level == 3) {
- this.languageLevel = level;
- } else {
- throw new IllegalArgumentException("Only language levels 2 or 3 are allowed/supported");
- }
+ getPSUtil().setLanguageLevel(level);
}
/**
* @return the language level
*/
public int getLanguageLevel() {
- return this.languageLevel;
+ return getPSUtil().getLanguageLevel();
}
/**
* @param value true to enable the resource optimization
*/
public void setOptimizeResources(boolean value) {
- this.twoPassGeneration = value;
+ getPSUtil().setOptimizeResources(value);
}
/** @return true if the renderer does two passes to optimize PostScript resources */
public boolean isOptimizeResources() {
- return this.twoPassGeneration;
+ return getPSUtil().isOptimizeResources();
}
/** {@inheritDoc} */
* @param comment Comment to write
*/
protected void comment(String comment) {
- if (this.enableComments) {
+ try {
if (comment.startsWith("%")) {
+ gen.commentln(comment);
writeln(comment);
} else {
- writeln("%" + comment);
+ gen.commentln("%" + comment);
}
+ } catch (IOException ioe) {
+ handleIOTrouble(ioe);
}
}
//Initial default page device dictionary settings
this.pageDeviceDictionary = new PSPageDeviceDictionary();
- pageDeviceDictionary.setFlushOnRetrieval(!this.dscCompliant);
+ pageDeviceDictionary.setFlushOnRetrieval(!getPSUtil().isDSCComplianceEnabled());
pageDeviceDictionary.put("/ImagingBBox", "null");
}
//Setup
gen.writeDSCComment(DSCConstants.BEGIN_SETUP);
- writeSetupCodeList(setupCodeList, "SetupCode");
+ PSRenderingUtil.writeSetupCodeList(gen, setupCodeList, "SetupCode");
if (!isOptimizeResources()) {
this.fontResources = PSFontUtils.writeFontDict(gen, fontInfo);
} else {
/** {@inheritDoc} */
public void stopRenderer() throws IOException {
- //Notify resource usage for font which are not supplied
- /* done in useFont now
- Map fonts = fontInfo.getUsedFonts();
- Iterator e = fonts.keySet().iterator();
- while (e.hasNext()) {
- String key = (String)e.next();
- PSResource res = (PSResource)this.fontResources.get(key);
- gen.notifyResourceUsage(res);
- }*/
-
//Write trailer
gen.writeDSCComment(DSCConstants.TRAILER);
if (footerComments != null) {
super.processOffDocumentItem(oDI);
}
- /**
- * Formats and writes a List of PSSetupCode instances to the output stream.
- * @param setupCodeList a List of PSSetupCode instances
- * @param type the type of code section
- */
- private void writeSetupCodeList(List setupCodeList, String type) throws IOException {
- if (setupCodeList != null) {
- Iterator i = setupCodeList.iterator();
- while (i.hasNext()) {
- PSSetupCode setupCode = (PSSetupCode)i.next();
- gen.commentln("%FOPBegin" + type + ": ("
- + (setupCode.getName() != null ? setupCode.getName() : "")
- + ")");
- LineNumberReader reader = new LineNumberReader(
- new java.io.StringReader(setupCode.getContent()));
- String line;
- while ((line = reader.readLine()) != null) {
- line = line.trim();
- if (line.length() > 0) {
- gen.writeln(line.trim());
- }
- }
- gen.commentln("%FOPEnd" + type);
- i.remove();
- }
- }
- }
-
/** {@inheritDoc} */
public void renderPage(PageViewport page)
throws IOException, FOPException {
double pageHeight = Math.round(page.getViewArea().getHeight()) / 1000f;
boolean rotate = false;
List pageSizes = new java.util.ArrayList();
- if (this.autoRotateLandscape && (pageHeight < pageWidth)) {
+ if (getPSUtil().isAutoRotateLandscape() && (pageHeight < pageWidth)) {
rotate = true;
pageSizes.add(new Long(Math.round(pageHeight)));
pageSizes.add(new Long(Math.round(pageWidth)));
try {
if (setupCodeList != null) {
- writeEnclosedExtensionAttachments(setupCodeList);
+ PSRenderingUtil.writeEnclosedExtensionAttachments(gen, setupCodeList);
setupCodeList.clear();
}
} catch (IOException e) {
gen.writeDSCComment(DSCConstants.PAGE_HIRES_BBOX, new Object[] {
zero, zero, new Double(pageWidth),
new Double(pageHeight) });
- if (autoRotateLandscape) {
+ if (getPSUtil().isAutoRotateLandscape()) {
gen.writeDSCComment(DSCConstants.PAGE_ORIENTATION,
"Portrait");
}
// Write any unwritten changes to page device dictionary
if (!pageDeviceDictionary.isEmpty()) {
String content = pageDeviceDictionary.getContent();
- if (safeSetPageDevice) {
+ if (getPSUtil().isSafeSetPageDevice()) {
content += " SSPD";
} else {
content += " setpagedevice";
}
- writeEnclosedExtensionAttachment(new PSSetPageDevice(content));
+ PSRenderingUtil.writeEnclosedExtensionAttachment(gen, new PSSetPageDevice(content));
}
if (rotate) {
return MIME_TYPE;
}
- /**
- * Formats and writes a PSExtensionAttachment to the output stream.
- *
- * @param attachment an PSExtensionAttachment instance
- */
- private void writeEnclosedExtensionAttachment(PSExtensionAttachment attachment)
- throws IOException {
- String info = "";
- if (attachment instanceof PSSetupCode) {
- PSSetupCode setupCodeAttach = (PSSetupCode)attachment;
- String name = setupCodeAttach.getName();
- if (name != null) {
- info += ": (" + name + ")";
- }
- }
- String type = attachment.getType();
- gen.commentln("%FOPBegin" + type + info);
- LineNumberReader reader = new LineNumberReader(
- new java.io.StringReader(attachment.getContent()));
- String line;
- while ((line = reader.readLine()) != null) {
- line = line.trim();
- if (line.length() > 0) {
- gen.writeln(line);
- }
- }
- gen.commentln("%FOPEnd" + type);
- }
-
- /**
- * Formats and writes a Collection of PSExtensionAttachment instances to
- * the output stream.
- *
- * @param attachmentCollection
- * a Collection of PSExtensionAttachment instances
- */
- private void writeEnclosedExtensionAttachments(Collection attachmentCollection)
- throws IOException {
- Iterator iter = attachmentCollection.iterator();
- while (iter.hasNext()) {
- PSExtensionAttachment attachment = (PSExtensionAttachment)iter
- .next();
- if (attachment != null) {
- writeEnclosedExtensionAttachment(attachment);
- }
- iter.remove();
- }
- }
-
/**
* Sets whether or not the safe set page device macro should be used
* (as opposed to directly invoking setpagedevice) when setting the
* device macro call (default is false).
*/
public void setSafeSetPageDevice(boolean safeSetPageDevice) {
- this.safeSetPageDevice = safeSetPageDevice;
+ getPSUtil().setSafeSetPageDevice(safeSetPageDevice);
}
/**
* @param dscCompliant boolean value (default is true)
*/
public void setDSCCompliant(boolean dscCompliant) {
- this.dscCompliant = dscCompliant;
+ getPSUtil().setDSCComplianceEnabled(dscCompliant);
}
}
package org.apache.fop.render.ps;
+import java.util.List;
+
import org.apache.avalon.framework.configuration.Configuration;
+
+import org.apache.xmlgraphics.ps.PSGenerator;
+
import org.apache.fop.apps.FOPException;
import org.apache.fop.apps.FOUserAgent;
+import org.apache.fop.fonts.CustomFontCollection;
+import org.apache.fop.fonts.FontCollection;
+import org.apache.fop.fonts.FontEventAdapter;
+import org.apache.fop.fonts.FontEventListener;
+import org.apache.fop.fonts.FontInfo;
+import org.apache.fop.fonts.FontManager;
+import org.apache.fop.fonts.FontResolver;
+import org.apache.fop.fonts.base14.Base14FontCollection;
+import org.apache.fop.render.DefaultFontResolver;
import org.apache.fop.render.PrintRendererConfigurator;
import org.apache.fop.render.Renderer;
-import org.apache.xmlgraphics.ps.PSGenerator;
+import org.apache.fop.render.intermediate.IFDocumentHandler;
+import org.apache.fop.render.intermediate.IFDocumentHandlerConfigurator;
/**
* Postscript renderer config
*/
-public class PSRendererConfigurator extends PrintRendererConfigurator {
+public class PSRendererConfigurator extends PrintRendererConfigurator
+ implements IFDocumentHandlerConfigurator {
/**
* Default constructor
super.configure(renderer);
PSRenderer psRenderer = (PSRenderer)renderer;
+ configure(psRenderer.getPSUtil(), cfg);
+ }
+ }
- psRenderer.setAutoRotateLandscape(
- cfg.getChild("auto-rotate-landscape").getValueAsBoolean(false));
- Configuration child;
- child = cfg.getChild("language-level");
- if (child != null) {
- psRenderer.setLanguageLevel(child.getValueAsInteger(
- PSGenerator.DEFAULT_LANGUAGE_LEVEL));
- }
- child = cfg.getChild("optimize-resources");
- if (child != null) {
- psRenderer.setOptimizeResources(child.getValueAsBoolean(false));
- }
- psRenderer.setSafeSetPageDevice(
- cfg.getChild("safe-set-page-device").getValueAsBoolean(false));
- psRenderer.setDSCCompliant(
- cfg.getChild("dsc-compliant").getValueAsBoolean(true));
+ private void configure(PSRenderingUtil psUtil, Configuration cfg) {
+ psUtil.setAutoRotateLandscape(
+ cfg.getChild("auto-rotate-landscape").getValueAsBoolean(false));
+ Configuration child;
+ child = cfg.getChild("language-level");
+ if (child != null) {
+ psUtil.setLanguageLevel(child.getValueAsInteger(
+ PSGenerator.DEFAULT_LANGUAGE_LEVEL));
+ }
+ child = cfg.getChild("optimize-resources");
+ if (child != null) {
+ psUtil.setOptimizeResources(child.getValueAsBoolean(false));
}
+ psUtil.setSafeSetPageDevice(
+ cfg.getChild("safe-set-page-device").getValueAsBoolean(false));
+ psUtil.setDSCComplianceEnabled(
+ cfg.getChild("dsc-compliant").getValueAsBoolean(true));
+ }
+
+ /** {@inheritDoc} */
+ public void configure(IFDocumentHandler documentHandler) throws FOPException {
+ Configuration cfg = super.getRendererConfig(documentHandler.getMimeType());
+ if (cfg != null) {
+ PSDocumentHandler psDocumentHandler = (PSDocumentHandler)documentHandler;
+ configure(psDocumentHandler.getPSUtil(), cfg);
+ }
+
+ }
+
+ /** {@inheritDoc} */
+ public void setupFontInfo(IFDocumentHandler documentHandler, FontInfo fontInfo)
+ throws FOPException {
+ FontManager fontManager = userAgent.getFactory().getFontManager();
+ List fontCollections = new java.util.ArrayList();
+ fontCollections.add(new Base14FontCollection(fontManager.isBase14KerningEnabled()));
+
+ Configuration cfg = super.getRendererConfig(documentHandler.getMimeType());
+ if (cfg != null) {
+ FontResolver fontResolver = new DefaultFontResolver(userAgent);
+ FontEventListener listener = new FontEventAdapter(
+ userAgent.getEventBroadcaster());
+ List fontList = buildFontList(cfg, fontResolver, listener);
+ fontCollections.add(new CustomFontCollection(fontResolver, fontList));
+ }
+
+ fontManager.setup(fontInfo,
+ (FontCollection[])fontCollections.toArray(
+ new FontCollection[fontCollections.size()]));
+ documentHandler.setFontInfo(fontInfo);
}
}
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.ps;
+
+import org.apache.xmlgraphics.util.MimeConstants;
+
+import org.apache.fop.apps.FOUserAgent;
+import org.apache.fop.fonts.FontInfo;
+import org.apache.fop.render.AbstractRenderingContext;
+
+/**
+ * Rendering context for PostScript production.
+ */
+public class PSRenderingContext extends AbstractRenderingContext {
+
+ private FontInfo fontInfo;
+
+ /**
+ * Main constructor.
+ * @param userAgent the user agent
+ * @param fontInfo the font list
+ */
+ public PSRenderingContext(FOUserAgent userAgent,
+ FontInfo fontInfo) {
+ super(userAgent);
+ this.fontInfo = fontInfo;
+ }
+
+ /** {@inheritDoc} */
+ public String getMimeType() {
+ return MimeConstants.MIME_POSTSCRIPT;
+ }
+
+ /**
+ * Returns the font list.
+ * @return the font list
+ */
+ public FontInfo getFontInfo() {
+ return this.fontInfo;
+ }
+
+}
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.ps;
+
+import java.io.IOException;
+import java.io.LineNumberReader;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.xmlgraphics.ps.PSGenerator;
+
+import org.apache.fop.apps.FOUserAgent;
+import org.apache.fop.render.ps.extensions.PSExtensionAttachment;
+import org.apache.fop.render.ps.extensions.PSSetupCode;
+
+/**
+ * Utility class which enables all sorts of features that are not directly connected to the
+ * normal rendering process.
+ */
+public class PSRenderingUtil implements PSConfigurationConstants {
+
+ private FOUserAgent userAgent;
+
+ /** Whether or not the safe set page device macro will be used or not */
+ private boolean safeSetPageDevice = false;
+
+ /**
+ * Whether or not PostScript Document Structuring Conventions (DSC) compliant output are
+ * enforced.
+ */
+ private boolean dscCompliant = true;
+
+ private boolean autoRotateLandscape = false;
+ private int languageLevel = PSGenerator.DEFAULT_LANGUAGE_LEVEL;
+
+ /** Determines whether the PS file is generated in two passes to minimize file size */
+ private boolean optimizeResources = false;
+
+ PSRenderingUtil(FOUserAgent userAgent) {
+ this.userAgent = userAgent;
+ initialize();
+ }
+
+ private void initialize() {
+ Object obj;
+ obj = userAgent.getRendererOptions().get(AUTO_ROTATE_LANDSCAPE);
+ if (obj != null) {
+ setAutoRotateLandscape(booleanValueOf(obj));
+ }
+ obj = userAgent.getRendererOptions().get(LANGUAGE_LEVEL);
+ if (obj != null) {
+ setLanguageLevel(intValueOf(obj));
+ }
+ obj = userAgent.getRendererOptions().get(OPTIMIZE_RESOURCES);
+ if (obj != null) {
+ setOptimizeResources(booleanValueOf(obj));
+ }
+ }
+
+ private boolean booleanValueOf(Object obj) {
+ if (obj instanceof Boolean) {
+ return ((Boolean)obj).booleanValue();
+ } else if (obj instanceof String) {
+ return Boolean.valueOf((String)obj).booleanValue();
+ } else {
+ throw new IllegalArgumentException("Boolean or \"true\" or \"false\" expected.");
+ }
+ }
+
+ private int intValueOf(Object obj) {
+ if (obj instanceof Integer) {
+ return ((Integer)obj).intValue();
+ } else if (obj instanceof String) {
+ return Integer.parseInt((String)obj);
+ } else {
+ throw new IllegalArgumentException("Integer or String with a number expected.");
+ }
+ }
+
+ /**
+ * Formats and writes a List of PSSetupCode instances to the output stream.
+ * @param gen the PS generator
+ * @param setupCodeList a List of PSSetupCode instances
+ * @param type the type of code section
+ * @throws IOException if an I/O error occurs.
+ */
+ public static void writeSetupCodeList(PSGenerator gen, List setupCodeList, String type)
+ throws IOException {
+ if (setupCodeList != null) {
+ Iterator i = setupCodeList.iterator();
+ while (i.hasNext()) {
+ PSSetupCode setupCode = (PSSetupCode)i.next();
+ gen.commentln("%FOPBegin" + type + ": ("
+ + (setupCode.getName() != null ? setupCode.getName() : "")
+ + ")");
+ LineNumberReader reader = new LineNumberReader(
+ new java.io.StringReader(setupCode.getContent()));
+ String line;
+ while ((line = reader.readLine()) != null) {
+ line = line.trim();
+ if (line.length() > 0) {
+ gen.writeln(line.trim());
+ }
+ }
+ gen.commentln("%FOPEnd" + type);
+ i.remove();
+ }
+ }
+ }
+
+ /**
+ * Formats and writes a Collection of PSExtensionAttachment instances to
+ * the output stream. The instances are removed from the collection when they
+ * have been written.
+ *
+ * @param gen the PS generator
+ * @param attachmentCollection
+ * a Collection of PSExtensionAttachment instances
+ * @throws IOException if an I/O error occurs.
+ */
+ public static void writeEnclosedExtensionAttachments(PSGenerator gen,
+ Collection attachmentCollection) throws IOException {
+ Iterator iter = attachmentCollection.iterator();
+ while (iter.hasNext()) {
+ PSExtensionAttachment attachment = (PSExtensionAttachment)iter.next();
+ if (attachment != null) {
+ writeEnclosedExtensionAttachment(gen, attachment);
+ }
+ iter.remove();
+ }
+ }
+
+ /**
+ * Formats and writes a PSExtensionAttachment to the output stream.
+ *
+ * @param gen the PS generator
+ * @param attachment an PSExtensionAttachment instance
+ * @throws IOException if an I/O error occurs.
+ */
+ public static void writeEnclosedExtensionAttachment(PSGenerator gen,
+ PSExtensionAttachment attachment) throws IOException {
+ String info = "";
+ if (attachment instanceof PSSetupCode) {
+ PSSetupCode setupCodeAttach = (PSSetupCode)attachment;
+ String name = setupCodeAttach.getName();
+ if (name != null) {
+ info += ": (" + name + ")";
+ }
+ }
+ String type = attachment.getType();
+ gen.commentln("%FOPBegin" + type + info);
+ LineNumberReader reader = new LineNumberReader(
+ new java.io.StringReader(attachment.getContent()));
+ String line;
+ while ((line = reader.readLine()) != null) {
+ line = line.trim();
+ if (line.length() > 0) {
+ gen.writeln(line);
+ }
+ }
+ gen.commentln("%FOPEnd" + type);
+ }
+
+ /**
+ * Sets whether or not PostScript Document Structuring Conventions (DSC) compliance are
+ * enforced.
+ * <p>
+ * It can cause problems (unwanted PostScript subsystem initgraphics/erasepage calls)
+ * on some printers when the pagedevice is set. If this causes problems on a
+ * particular implementation then use this setting with a 'false' value to try and
+ * minimize the number of setpagedevice calls in the PostScript document output.
+ * <p>
+ * Set this value to false if you experience unwanted blank pages in your
+ * PostScript output.
+ * @param value boolean value (default is true)
+ */
+ public void setSafeSetPageDevice(boolean value) {
+ this.safeSetPageDevice = value;
+ }
+
+ /**
+ * Indicates whether the "safe setpagedevice" mode is active.
+ * See {@code #setSafeSetPageDevice(boolean)} for more information.
+ * @return true if active
+ */
+ public boolean isSafeSetPageDevice() {
+ return this.safeSetPageDevice;
+ }
+
+ /**
+ * Sets whether or not the safe set page device macro should be used
+ * (as opposed to directly invoking setpagedevice) when setting the
+ * PostScript page device.
+ * <p>
+ * This option is a useful option when you want to guard against the possibility
+ * of invalid/unsupported PostScript key/values being placed in the page device.
+ * <p>
+ * @param value setting to false and the renderer will make a
+ * standard "setpagedevice" call, setting to true will make a safe set page
+ * device macro call (default is false).
+ */
+ public void setDSCComplianceEnabled(boolean value) {
+ this.dscCompliant = value;
+ }
+
+ public boolean isDSCComplianceEnabled() {
+ return this.dscCompliant;
+ }
+
+ /**
+ * Controls whether landscape pages should be rotated.
+ * @param value true to enable the rotation
+ */
+ public void setAutoRotateLandscape(boolean value) {
+ this.autoRotateLandscape = value;
+ }
+
+ /**
+ * Indicates whether landscape pages are rotated.
+ * @return true if landscape pages are to be rotated
+ */
+ public boolean isAutoRotateLandscape() {
+ return autoRotateLandscape;
+ }
+
+ /**
+ * Sets the PostScript language level.
+ * @param level the PostScript language level (Only 2 and 3 are currently supported)
+ */
+ public void setLanguageLevel(int level) {
+ if (level == 2 || level == 3) {
+ this.languageLevel = level;
+ } else {
+ throw new IllegalArgumentException("Only language levels 2 or 3 are allowed/supported");
+ }
+ }
+
+ /**
+ * Indicates the selected PostScript language level.
+ * @return the PostScript language level
+ */
+ public int getLanguageLevel() {
+ return languageLevel;
+ }
+
+ /**
+ * Controls whether PostScript resources are optimized in a second pass over the document.
+ * Enable this to obtain smaller PostScript files.
+ * @param value true to enable resource optimization
+ */
+ public void setOptimizeResources(boolean value) {
+ this.optimizeResources = value;
+ }
+
+ /**
+ * Indicates whether PostScript resources are optimized in a second pass over the document.
+ * @return true if resource optimization is enabled
+ */
+ public boolean isOptimizeResources() {
+ return optimizeResources;
+ }
+
+
+}
}
/** {@inheritDoc} */
- public void startPage(int index, String name, Dimension size) throws IFException {
+ public void startPage(int index, String name, String pageMasterName, Dimension size)
+ throws IFException {
OutputStream out;
try {
if (index == 0) {
}
/** {@inheritDoc} */
- public void startPage(int index, String name, Dimension size) throws IFException {
+ public void startPage(int index, String name, String pageMasterName, Dimension size)
+ throws IFException {
try {
AttributesImpl atts = new AttributesImpl();
/*