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-ffa450edef68tags/fop-1_0
@@ -1,3 +1,4 @@ | |||
org.apache.fop.render.pdf.PDFDocumentHandlerMaker | |||
org.apache.fop.render.pcl.PCLDocumentHandlerMaker | |||
org.apache.fop.render.bitmap.TIFFDocumentHandlerMaker | |||
org.apache.fop.render.bitmap.TIFFDocumentHandlerMaker | |||
org.apache.fop.render.ps.PSDocumentHandlerMaker |
@@ -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); | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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 |
@@ -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 { |
@@ -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); | |||
} | |||
} | |||
@@ -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); |
@@ -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); |
@@ -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 |
@@ -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, |
@@ -185,20 +185,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) |
@@ -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( |
@@ -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); | |||
} | |||
} |
@@ -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"; | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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) { | |||
@@ -1632,55 +1551,6 @@ public class PSRenderer extends AbstractPathOrientedRenderer | |||
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 | |||
@@ -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); | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -149,7 +149,8 @@ public class SVGDocumentHandler extends AbstractSVGDocumentHandler { | |||
} | |||
/** {@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) { |
@@ -111,7 +111,8 @@ public class SVGPrintDocumentHandler extends AbstractSVGDocumentHandler { | |||
} | |||
/** {@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(); | |||
/* |