diff options
author | Jeremias Maerki <jeremias@apache.org> | 2008-11-19 20:13:48 +0000 |
---|---|---|
committer | Jeremias Maerki <jeremias@apache.org> | 2008-11-19 20:13:48 +0000 |
commit | be4dc692db093ec6b8039d7e61804e28f1709db4 (patch) | |
tree | aa484221fc31f715fcc9e386afb978e31de8b3a0 /src/java/org/apache/fop | |
parent | dfeb96c655c3e361b5fdf6f0cecbf46adab01a72 (diff) | |
download | xmlgraphics-fop-be4dc692db093ec6b8039d7e61804e28f1709db4.tar.gz xmlgraphics-fop-be4dc692db093ec6b8039d7e61804e28f1709db4.zip |
Added page master name to IFDocumentHandler.startPage() method.
Wired together the support for out-of-order rendering (only applicable to PDF) when the intermediate format is not used (in-memory rendering).
Fixed a logical bug in IFRenderer that caused some unneeded code. Glyph adjustments (kerning, letter/word space...) were not done right. All painters fixed/adjusted accordingly.
Started implementation of the PostScript painter: Supports only text and filled rectangles so far. Work in progress...
git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_AreaTreeNewDesign@719051 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'src/java/org/apache/fop')
20 files changed, 1492 insertions, 211 deletions
diff --git a/src/java/org/apache/fop/render/bitmap/TIFFDocumentHandler.java b/src/java/org/apache/fop/render/bitmap/TIFFDocumentHandler.java index 0da1c02fe..cdc350c9e 100644 --- a/src/java/org/apache/fop/render/bitmap/TIFFDocumentHandler.java +++ b/src/java/org/apache/fop/render/bitmap/TIFFDocumentHandler.java @@ -166,7 +166,8 @@ public class TIFFDocumentHandler extends AbstractBinaryWritingIFDocumentHandler } /** {@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); } diff --git a/src/java/org/apache/fop/render/intermediate/AbstractIFPainter.java b/src/java/org/apache/fop/render/intermediate/AbstractIFPainter.java index 105a2a426..07d8334d5 100644 --- a/src/java/org/apache/fop/render/intermediate/AbstractIFPainter.java +++ b/src/java/org/apache/fop/render/intermediate/AbstractIFPainter.java @@ -350,4 +350,19 @@ public abstract class AbstractIFPainter implements IFPainter { } } + /** + * 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); + } + + } diff --git a/src/java/org/apache/fop/render/intermediate/IFDocumentHandler.java b/src/java/org/apache/fop/render/intermediate/IFDocumentHandler.java index 61093769b..e3634e9ef 100644 --- a/src/java/org/apache/fop/render/intermediate/IFDocumentHandler.java +++ b/src/java/org/apache/fop/render/intermediate/IFDocumentHandler.java @@ -189,10 +189,11 @@ public interface IFDocumentHandler { * 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 diff --git a/src/java/org/apache/fop/render/intermediate/IFParser.java b/src/java/org/apache/fop/render/intermediate/IFParser.java index 5eaef13ae..e514ab889 100644 --- a/src/java/org/apache/fop/render/intermediate/IFParser.java +++ b/src/java/org/apache/fop/render/intermediate/IFParser.java @@ -332,9 +332,10 @@ public class IFParser implements IFConstants { 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 { diff --git a/src/java/org/apache/fop/render/intermediate/IFRenderer.java b/src/java/org/apache/fop/render/intermediate/IFRenderer.java index eddd5f1bd..fecde8ae4 100644 --- a/src/java/org/apache/fop/render/intermediate/IFRenderer.java +++ b/src/java/org/apache/fop/render/intermediate/IFRenderer.java @@ -195,6 +195,12 @@ public class IFRenderer extends AbstractPathOrientedRenderer { } } + /** {@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 @@ -492,7 +498,8 @@ public class IFRenderer extends AbstractPathOrientedRenderer { 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(); @@ -867,8 +874,7 @@ public class IFRenderer extends AbstractPathOrientedRenderer { 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); @@ -891,7 +897,7 @@ public class IFRenderer extends AbstractPathOrientedRenderer { 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; @@ -900,9 +906,7 @@ public class IFRenderer extends AbstractPathOrientedRenderer { glyphAdjust += letterAdjust[i]; } - float adjust = glyphAdjust / fontSize; - - textUtil.adjust(Math.round(adjust * 10)); + textUtil.adjust(glyphAdjust); } } diff --git a/src/java/org/apache/fop/render/intermediate/IFSerializer.java b/src/java/org/apache/fop/render/intermediate/IFSerializer.java index 95d1f20fe..661a2fddd 100644 --- a/src/java/org/apache/fop/render/intermediate/IFSerializer.java +++ b/src/java/org/apache/fop/render/intermediate/IFSerializer.java @@ -191,11 +191,12 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler implements } /** {@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); diff --git a/src/java/org/apache/fop/render/java2d/Java2DPainter.java b/src/java/org/apache/fop/render/java2d/Java2DPainter.java index 013be3f86..9da39a319 100644 --- a/src/java/org/apache/fop/render/java2d/Java2DPainter.java +++ b/src/java/org/apache/fop/render/java2d/Java2DPainter.java @@ -233,7 +233,7 @@ public class Java2DPainter extends AbstractIFPainter { 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); diff --git a/src/java/org/apache/fop/render/pcl/PCLDocumentHandler.java b/src/java/org/apache/fop/render/pcl/PCLDocumentHandler.java index 20dc3cac2..3e9201da4 100644 --- a/src/java/org/apache/fop/render/pcl/PCLDocumentHandler.java +++ b/src/java/org/apache/fop/render/pcl/PCLDocumentHandler.java @@ -173,7 +173,7 @@ public class PCLDocumentHandler extends AbstractBinaryWritingIFDocumentHandler } /** {@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 diff --git a/src/java/org/apache/fop/render/pcl/PCLPainter.java b/src/java/org/apache/fop/render/pcl/PCLPainter.java index b709c28f3..784aca941 100644 --- a/src/java/org/apache/fop/render/pcl/PCLPainter.java +++ b/src/java/org/apache/fop/render/pcl/PCLPainter.java @@ -414,7 +414,7 @@ public class PCLPainter extends AbstractIFPainter implements PCLConstants { glyphAdjust += dx[i + 1]; } - width += cw - glyphAdjust; + width += cw + glyphAdjust; } int extraWidth = font.getFontSize() / 3; boundingRect.setSize( @@ -447,14 +447,14 @@ public class PCLPainter extends AbstractIFPainter implements PCLConstants { 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, diff --git a/src/java/org/apache/fop/render/pdf/PDFContentGenerator.java b/src/java/org/apache/fop/render/pdf/PDFContentGenerator.java index 35dc0960a..70e0f7eb5 100644 --- a/src/java/org/apache/fop/render/pdf/PDFContentGenerator.java +++ b/src/java/org/apache/fop/render/pdf/PDFContentGenerator.java @@ -186,20 +186,6 @@ public class PDFContentGenerator { } /** - * 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) */ diff --git a/src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java b/src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java index 8937d0d1d..1358b1c5e 100644 --- a/src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java +++ b/src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java @@ -168,7 +168,8 @@ public class PDFDocumentHandler extends AbstractBinaryWritingIFDocumentHandler { } /** {@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( diff --git a/src/java/org/apache/fop/render/pdf/PDFPainter.java b/src/java/org/apache/fop/render/pdf/PDFPainter.java index 5baaedbea..8e29ded4c 100644 --- a/src/java/org/apache/fop/render/pdf/PDFPainter.java +++ b/src/java/org/apache/fop/render/pdf/PDFPainter.java @@ -100,7 +100,7 @@ public class PDFPainter extends AbstractIFPainter { 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); } @@ -114,7 +114,7 @@ public class PDFPainter extends AbstractIFPainter { /** {@inheritDoc} */ public void startGroup(AffineTransform transform) throws IFException { generator.saveGraphicsState(); - generator.concatenate(generator.toPoints(transform)); + generator.concatenate(toPoints(transform)); } /** {@inheritDoc} */ @@ -196,8 +196,8 @@ public class PDFPainter extends AbstractIFPainter { 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); @@ -302,7 +302,7 @@ public class PDFPainter extends AbstractIFPainter { //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); } @@ -314,7 +314,7 @@ public class PDFPainter extends AbstractIFPainter { } if (glyphAdjust != 0) { - textutil.adjustGlyphTJ(-glyphAdjust / 10f); + textutil.adjustGlyphTJ(-glyphAdjust / fontSize); } } diff --git a/src/java/org/apache/fop/render/ps/PSConfigurationConstants.java b/src/java/org/apache/fop/render/ps/PSConfigurationConstants.java new file mode 100644 index 000000000..6b3550f57 --- /dev/null +++ b/src/java/org/apache/fop/render/ps/PSConfigurationConstants.java @@ -0,0 +1,33 @@ +/* + * 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"; +} diff --git a/src/java/org/apache/fop/render/ps/PSDocumentHandler.java b/src/java/org/apache/fop/render/ps/PSDocumentHandler.java new file mode 100644 index 000000000..8223ec29b --- /dev/null +++ b/src/java/org/apache/fop/render/ps/PSDocumentHandler.java @@ -0,0 +1,522 @@ +/* + * 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; + } + +} diff --git a/src/java/org/apache/fop/render/ps/PSDocumentHandlerMaker.java b/src/java/org/apache/fop/render/ps/PSDocumentHandlerMaker.java new file mode 100644 index 000000000..635cd0720 --- /dev/null +++ b/src/java/org/apache/fop/render/ps/PSDocumentHandlerMaker.java @@ -0,0 +1,59 @@ +/* + * 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); + } + +} diff --git a/src/java/org/apache/fop/render/ps/PSPainter.java b/src/java/org/apache/fop/render/ps/PSPainter.java new file mode 100644 index 000000000..e43b0f2c4 --- /dev/null +++ b/src/java/org/apache/fop/render/ps/PSPainter.java @@ -0,0 +1,395 @@ +/* + * 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); + } + + +} diff --git a/src/java/org/apache/fop/render/ps/PSRenderer.java b/src/java/org/apache/fop/render/ps/PSRenderer.java index 4ccb0bd52..83dbd4b62 100644 --- a/src/java/org/apache/fop/render/ps/PSRenderer.java +++ b/src/java/org/apache/fop/render/ps/PSRenderer.java @@ -28,7 +28,6 @@ import java.io.File; 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; @@ -128,7 +127,7 @@ import org.apache.fop.util.ColorUtil; * @version $Id$ */ public class PSRenderer extends AbstractPathOrientedRenderer - implements ImageAdapter, PSSupportedFlavors { + implements ImageAdapter, PSSupportedFlavors, PSConfigurationConstants { /** logging instance */ private static Log log = LogFactory.getLog(PSRenderer.class); @@ -136,17 +135,9 @@ public class PSRenderer extends AbstractPathOrientedRenderer /** 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 */ @@ -154,8 +145,6 @@ public class PSRenderer extends AbstractPathOrientedRenderer /** 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; @@ -171,14 +160,11 @@ public class PSRenderer extends AbstractPathOrientedRenderer /** 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; @@ -192,39 +178,11 @@ public class PSRenderer extends AbstractPathOrientedRenderer /** {@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; } /** @@ -233,12 +191,12 @@ public class PSRenderer extends AbstractPathOrientedRenderer * 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(); } /** @@ -246,11 +204,7 @@ public class PSRenderer extends AbstractPathOrientedRenderer * @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); } /** @@ -258,7 +212,7 @@ public class PSRenderer extends AbstractPathOrientedRenderer * @return the language level */ public int getLanguageLevel() { - return this.languageLevel; + return getPSUtil().getLanguageLevel(); } /** @@ -268,12 +222,12 @@ public class PSRenderer extends AbstractPathOrientedRenderer * @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} */ @@ -316,12 +270,15 @@ public class PSRenderer extends AbstractPathOrientedRenderer * @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); } } @@ -931,7 +888,7 @@ public class PSRenderer extends AbstractPathOrientedRenderer //Initial default page device dictionary settings this.pageDeviceDictionary = new PSPageDeviceDictionary(); - pageDeviceDictionary.setFlushOnRetrieval(!this.dscCompliant); + pageDeviceDictionary.setFlushOnRetrieval(!getPSUtil().isDSCComplianceEnabled()); pageDeviceDictionary.put("/ImagingBBox", "null"); } @@ -969,7 +926,7 @@ public class PSRenderer extends AbstractPathOrientedRenderer //Setup gen.writeDSCComment(DSCConstants.BEGIN_SETUP); - writeSetupCodeList(setupCodeList, "SetupCode"); + PSRenderingUtil.writeSetupCodeList(gen, setupCodeList, "SetupCode"); if (!isOptimizeResources()) { this.fontResources = PSFontUtils.writeFontDict(gen, fontInfo); } else { @@ -980,16 +937,6 @@ public class PSRenderer extends AbstractPathOrientedRenderer /** {@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) { @@ -1102,34 +1049,6 @@ public class PSRenderer extends AbstractPathOrientedRenderer 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 { @@ -1151,7 +1070,7 @@ public class PSRenderer extends AbstractPathOrientedRenderer 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))); @@ -1189,7 +1108,7 @@ public class PSRenderer extends AbstractPathOrientedRenderer try { if (setupCodeList != null) { - writeEnclosedExtensionAttachments(setupCodeList); + PSRenderingUtil.writeEnclosedExtensionAttachments(gen, setupCodeList); setupCodeList.clear(); } } catch (IOException e) { @@ -1214,7 +1133,7 @@ public class PSRenderer extends AbstractPathOrientedRenderer 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"); } @@ -1245,12 +1164,12 @@ public class PSRenderer extends AbstractPathOrientedRenderer // 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) { @@ -1633,55 +1552,6 @@ public class PSRenderer extends AbstractPathOrientedRenderer } /** - * 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 * postscript page device. @@ -1694,7 +1564,7 @@ public class PSRenderer extends AbstractPathOrientedRenderer * device macro call (default is false). */ public void setSafeSetPageDevice(boolean safeSetPageDevice) { - this.safeSetPageDevice = safeSetPageDevice; + getPSUtil().setSafeSetPageDevice(safeSetPageDevice); } /** @@ -1711,7 +1581,7 @@ public class PSRenderer extends AbstractPathOrientedRenderer * @param dscCompliant boolean value (default is true) */ public void setDSCCompliant(boolean dscCompliant) { - this.dscCompliant = dscCompliant; + getPSUtil().setDSCComplianceEnabled(dscCompliant); } } diff --git a/src/java/org/apache/fop/render/ps/PSRendererConfigurator.java b/src/java/org/apache/fop/render/ps/PSRendererConfigurator.java index 867888ea5..7ea4fe547 100644 --- a/src/java/org/apache/fop/render/ps/PSRendererConfigurator.java +++ b/src/java/org/apache/fop/render/ps/PSRendererConfigurator.java @@ -19,17 +19,33 @@ 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 @@ -50,23 +66,58 @@ public class PSRendererConfigurator extends PrintRendererConfigurator { 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); } } diff --git a/src/java/org/apache/fop/render/ps/PSRenderingContext.java b/src/java/org/apache/fop/render/ps/PSRenderingContext.java new file mode 100644 index 000000000..63f00ea1a --- /dev/null +++ b/src/java/org/apache/fop/render/ps/PSRenderingContext.java @@ -0,0 +1,59 @@ +/* + * 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; + } + +} diff --git a/src/java/org/apache/fop/render/ps/PSRenderingUtil.java b/src/java/org/apache/fop/render/ps/PSRenderingUtil.java new file mode 100644 index 000000000..a7fe56948 --- /dev/null +++ b/src/java/org/apache/fop/render/ps/PSRenderingUtil.java @@ -0,0 +1,282 @@ +/* + * 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; + } + + +} |