diff options
Diffstat (limited to 'src/java')
8 files changed, 399 insertions, 93 deletions
diff --git a/src/java/org/apache/fop/render/ps/EPSDocumentGraphics2D.java b/src/java/org/apache/fop/render/ps/EPSDocumentGraphics2D.java index 7a8eacce9..81a202940 100644 --- a/src/java/org/apache/fop/render/ps/EPSDocumentGraphics2D.java +++ b/src/java/org/apache/fop/render/ps/EPSDocumentGraphics2D.java @@ -1,5 +1,5 @@ /* - * Copyright 2003,2004 The Apache Software Foundation. + * Copyright 2003-2005 The Apache Software Foundation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,7 +26,6 @@ import org.apache.fop.apps.Fop; * This class is a wrapper for the <tt>AbstractPSDocumentGraphics2D</tt> that * is used to create EPS (Encapsulated PostScript) files instead of PS file. * - * @author <a href="mailto:jeremias@apache.org">Jeremias Maerki</a> * @version $Id$ * @see org.apache.fop.render.ps.PSGraphics2D * @see org.apache.fop.render.ps.AbstractPSDocumentGraphics2D @@ -70,7 +69,7 @@ public class EPSDocumentGraphics2D extends AbstractPSDocumentGraphics2D { PSProcSets.writeFOPStdProcSet(gen); PSProcSets.writeFOPEPSProcSet(gen); if (fontInfo != null) { - PSProcSets.writeFontDict(gen, fontInfo); + PSFontUtils.writeFontDict(gen, fontInfo); } gen.writeDSCComment(DSCConstants.END_PROLOG); } diff --git a/src/java/org/apache/fop/render/ps/PSDocumentGraphics2D.java b/src/java/org/apache/fop/render/ps/PSDocumentGraphics2D.java index 4a3a05801..afa4a83e2 100644 --- a/src/java/org/apache/fop/render/ps/PSDocumentGraphics2D.java +++ b/src/java/org/apache/fop/render/ps/PSDocumentGraphics2D.java @@ -1,5 +1,5 @@ /* - * Copyright 1999-2004 The Apache Software Foundation. + * Copyright 1999-2005 The Apache Software Foundation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,7 +34,6 @@ import org.apache.fop.fonts.FontSetup; * <tt>PSGraphics2D</tt>. * * @author <a href="mailto:keiron@aftexsw.com">Keiron Liddle</a> - * @author <a href="mailto:jeremias@apache.org">Jeremias Maerki</a> * @version $Id$ * @see org.apache.fop.render.ps.PSGraphics2D */ @@ -114,7 +113,7 @@ public class PSDocumentGraphics2D extends AbstractPSDocumentGraphics2D { PSProcSets.writeFOPStdProcSet(gen); PSProcSets.writeFOPEPSProcSet(gen); if (fontInfo != null) { - PSProcSets.writeFontDict(gen, fontInfo); + PSFontUtils.writeFontDict(gen, fontInfo); } gen.writeDSCComment(DSCConstants.END_SETUP); } diff --git a/src/java/org/apache/fop/render/ps/PSFontUtils.java b/src/java/org/apache/fop/render/ps/PSFontUtils.java new file mode 100644 index 000000000..8bcd0c413 --- /dev/null +++ b/src/java/org/apache/fop/render/ps/PSFontUtils.java @@ -0,0 +1,241 @@ +/* + * Copyright 2001-2005 The Apache Software Foundation. + * + * Licensed 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.BufferedReader; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.util.Iterator; +import java.util.Map; + +import javax.xml.transform.Source; +import javax.xml.transform.stream.StreamSource; + +import org.apache.commons.io.EndianUtils; +import org.apache.commons.io.IOUtils; +import org.apache.fop.fonts.CustomFont; +import org.apache.fop.fonts.FontInfo; +import org.apache.fop.fonts.FontType; +import org.apache.fop.fonts.Glyphs; +import org.apache.fop.fonts.LazyFont; +import org.apache.fop.fonts.Typeface; +import org.apache.fop.util.ASCIIHexOutputStream; +import org.apache.fop.util.SubInputStream; + +/** + * Utility code for font handling in PostScript. + */ +public class PSFontUtils { + + /** + * Generates the PostScript code for the font dictionary. + * @param gen PostScript generator to use for output + * @param fontInfo available fonts + * @return a Map of PSResource instances representing all defined fonts (key: font key) + * @throws IOException in case of an I/O problem + */ + public static Map writeFontDict(PSGenerator gen, FontInfo fontInfo) + throws IOException { + gen.commentln("%FOPBeginFontDict"); + gen.writeln("/FOPFonts 100 dict dup begin"); + + // write("/gfF1{/Helvetica findfont} bd"); + // write("/gfF3{/Helvetica-Bold findfont} bd"); + Map fonts = fontInfo.getFonts(); + Map fontResources = new java.util.HashMap(); + Iterator iter = fonts.keySet().iterator(); + while (iter.hasNext()) { + String key = (String)iter.next(); + Typeface tf = (Typeface)fonts.get(key); + if (tf instanceof LazyFont) { + tf = ((LazyFont)tf).getRealFont(); + } + PSResource fontRes = new PSResource("font", tf.getFontName()); + fontResources.put(key, fontRes); + boolean embeddedFont = false; + if (FontType.TYPE1 == tf.getFontType()) { + if (tf instanceof CustomFont) { + CustomFont cf = (CustomFont)tf; + InputStream in = getInputStreamOnFont(gen, cf); + if (in != null) { + gen.writeDSCComment(DSCConstants.BEGIN_RESOURCE, + fontRes); + embedType1Font(gen, in); + gen.writeDSCComment(DSCConstants.END_RESOURCE); + gen.notifyResourceUsage(fontRes, false); + embeddedFont = true; + } + } + } + if (!embeddedFont) { + gen.writeDSCComment(DSCConstants.INCLUDE_RESOURCE, fontRes); + //Resource usage shall be handled by renderer + //gen.notifyResourceUsage(fontRes, true); + } + gen.commentln("%FOPBeginFontKey: " + key); + gen.writeln("/" + key + " /" + tf.getFontName() + " def"); + gen.commentln("%FOPEndFontKey"); + } + gen.writeln("end def"); + gen.commentln("%FOPEndFontDict"); + gen.commentln("%FOPBeginFontReencode"); + defineWinAnsiEncoding(gen); + + //Rewrite font encodings + iter = fonts.keySet().iterator(); + while (iter.hasNext()) { + String key = (String)iter.next(); + Typeface fm = (Typeface)fonts.get(key); + if (null == fm.getEncoding()) { + //ignore (ZapfDingbats and Symbol run through here + //TODO: ZapfDingbats and Symbol should get getEncoding() fixed! + } else if ("WinAnsiEncoding".equals(fm.getEncoding())) { + gen.writeln("/" + fm.getFontName() + " findfont"); + gen.writeln("dup length dict begin"); + gen.writeln(" {1 index /FID ne {def} {pop pop} ifelse} forall"); + gen.writeln(" /Encoding " + fm.getEncoding() + " def"); + gen.writeln(" currentdict"); + gen.writeln("end"); + gen.writeln("/" + fm.getFontName() + " exch definefont pop"); + } else { + System.out.println("Only WinAnsiEncoding is supported. Font '" + + fm.getFontName() + "' asks for: " + fm.getEncoding()); + } + } + gen.commentln("%FOPEndFontReencode"); + return fontResources; + } + + /** + * This method reads a Type 1 font from a stream and embeds it into a PostScript stream. + * Note: Only the IBM PC Format as described in section 3.3 of the Adobe Technical Note #5040 + * is supported. + * @param gen The PostScript generator + * @param in the InputStream from which to read the Type 1 font + * @throws IOException in case an I/O problem occurs + */ + private static void embedType1Font(PSGenerator gen, InputStream in) throws IOException { + boolean finished = false; + while (!finished) { + int segIndicator = in.read(); + if (segIndicator < 0) { + throw new IOException("Unexpected end-of-file while reading segment indicator"); + } else if (segIndicator != 128) { + throw new IOException("Expected ASCII 128, found: " + segIndicator); + } + int segType = in.read(); + if (segType < 0) { + throw new IOException("Unexpected end-of-file while reading segment type"); + } + int dataSegLen = 0; + switch (segType) { + case 1: //ASCII + dataSegLen = EndianUtils.readSwappedInteger(in); + + BufferedReader reader = new BufferedReader( + new java.io.InputStreamReader( + new SubInputStream(in, dataSegLen), "US-ASCII")); + String line; + while ((line = reader.readLine()) != null) { + gen.writeln(line); + } + break; + case 2: //binary + dataSegLen = EndianUtils.readSwappedInteger(in); + + SubInputStream sin = new SubInputStream(in, dataSegLen); + ASCIIHexOutputStream hexOut = new ASCIIHexOutputStream(gen.getOutputStream()); + IOUtils.copy(sin, hexOut); + gen.newLine(); + break; + case 3: //EOF + finished = true; + break; + default: throw new IOException("Unsupported segment type: " + segType); + } + } + } + + private static InputStream getInputStreamOnFont(PSGenerator gen, CustomFont font) + throws IOException { + if (font.isEmbeddable()) { + Source source = null; + if (font.getEmbedFileName() != null) { + source = gen.resolveURI(font.getEmbedFileName()); + } + if (source == null && font.getEmbedResourceName() != null) { + source = new StreamSource(PSFontUtils.class + .getResourceAsStream(font.getEmbedResourceName())); + } + if (source == null) { + return null; + } + InputStream in = null; + if (source instanceof StreamSource) { + in = ((StreamSource) source).getInputStream(); + } + if (in == null && source.getSystemId() != null) { + try { + in = new java.net.URL(source.getSystemId()).openStream(); + } catch (MalformedURLException e) { + new FileNotFoundException( + "File not found. URL could not be resolved: " + + e.getMessage()); + } + } + if (in == null) { + return null; + } + //Make sure the InputStream is decorated with a BufferedInputStream + if (!(in instanceof java.io.BufferedInputStream)) { + in = new java.io.BufferedInputStream(in); + } + return in; + } else { + return null; + } + } + + private static void defineWinAnsiEncoding(PSGenerator gen) throws IOException { + gen.writeln("/WinAnsiEncoding ["); + for (int i = 0; i < Glyphs.WINANSI_ENCODING.length; i++) { + if (i > 0) { + if ((i % 5) == 0) { + gen.newLine(); + } else { + gen.write(" "); + } + } + final char ch = Glyphs.WINANSI_ENCODING[i]; + final String glyphname = Glyphs.charToGlyphName(ch); + if ("".equals(glyphname)) { + gen.write("/" + Glyphs.NOTDEF); + } else { + gen.write("/"); + gen.write(glyphname); + } + } + gen.newLine(); + gen.writeln("] def"); + } + + +} diff --git a/src/java/org/apache/fop/render/ps/PSGenerator.java b/src/java/org/apache/fop/render/ps/PSGenerator.java index efd07cab0..14d5b18fa 100644 --- a/src/java/org/apache/fop/render/ps/PSGenerator.java +++ b/src/java/org/apache/fop/render/ps/PSGenerator.java @@ -31,6 +31,8 @@ import java.util.Locale; import java.util.Set; import java.util.Stack; +import javax.xml.transform.Source; + /** * This class is used to output PostScript code to an OutputStream. * @@ -85,6 +87,17 @@ public class PSGenerator { } /** + * Attempts to resolve the given URI. PSGenerator should be subclasses to provide more + * sophisticated URI resolution. + * @param uri URI to access + * @return A {@link javax.xml.transform.Source} object, or null if the URI + * cannot be resolved. + */ + public Source resolveURI(String uri) { + return new javax.xml.transform.stream.StreamSource(uri); + } + + /** * Writes a newline character to the OutputStream. * * @throws IOException In case of an I/O problem @@ -241,15 +254,15 @@ public class PSGenerator { initialSize += initialSize / 2; StringBuffer sb = new StringBuffer(initialSize); if ((Long.getLong(text) != null) - || (text.indexOf(" ") >= 0) + || (text.indexOf(' ') >= 0) || forceParentheses) { - sb.append("("); + sb.append('('); for (int i = 0; i < text.length(); i++) { final char c = text.charAt(i); escapeChar(c, sb); } - sb.append(")"); + sb.append(')'); return sb.toString(); } else { return text; @@ -315,6 +328,8 @@ public class PSGenerator { } else if (params[i] instanceof Date) { DateFormat datef = new java.text.SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); tempBuffer.append(convertStringToDSC(datef.format((Date)params[i]))); + } else if (params[i] instanceof PSResource) { + tempBuffer.append(((PSResource)params[i]).getResourceSpecification()); } else { throw new IllegalArgumentException("Unsupported parameter type: " + params[i].getClass().getName()); @@ -528,6 +543,15 @@ public class PSGenerator { } /** + * Indicates whether a particular resource is supplied, rather than needed. + * @param res the resource + * @return true if the resource is registered as being supplied. + */ + public boolean isResourceSupplied(PSResource res) { + return documentSuppliedResources.contains(res); + } + + /** * Writes a DSC comment for the accumulated used resources, either at page level or * at document level. * @param pageLevel true if the DSC comment for the page level should be generated, @@ -562,9 +586,7 @@ public class PSGenerator { tempBuffer.append("%%+ "); } PSResource res = (PSResource)i.next(); - tempBuffer.append(res.getType()); - tempBuffer.append(" "); - tempBuffer.append(res.getName()); + tempBuffer.append(res.getResourceSpecification()); first = false; } writeln(tempBuffer.toString()); diff --git a/src/java/org/apache/fop/render/ps/PSProcSets.java b/src/java/org/apache/fop/render/ps/PSProcSets.java index c56a26a72..56ee8fd0e 100644 --- a/src/java/org/apache/fop/render/ps/PSProcSets.java +++ b/src/java/org/apache/fop/render/ps/PSProcSets.java @@ -1,5 +1,5 @@ /* - * Copyright 1999-2005 The Apache Software Foundation. + * Copyright 2001-2005 The Apache Software Foundation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,12 +19,6 @@ package org.apache.fop.render.ps; import java.io.IOException; -import java.util.Iterator; -import java.util.Map; - -import org.apache.fop.fonts.Typeface; -import org.apache.fop.fonts.Glyphs; -import org.apache.fop.fonts.FontInfo; /** * This class defines the basic resources (procsets) used by FOP's PostScript @@ -211,76 +205,4 @@ public final class PSProcSets { ((EPSProcSet)EPS_PROCSET).writeTo(gen); } - /** - * Generates the PostScript code for the font dictionary. - * @param gen PostScript generator to use for output - * @param fontInfo available fonts - * @throws IOException in case of an I/O problem - */ - public static void writeFontDict(PSGenerator gen, FontInfo fontInfo) - throws IOException { - gen.commentln("%FOPBeginFontDict"); - gen.writeln("/FOPFonts 100 dict dup begin"); - - // write("/gfF1{/Helvetica findfont} bd"); - // write("/gfF3{/Helvetica-Bold findfont} bd"); - Map fonts = fontInfo.getFonts(); - Iterator iter = fonts.keySet().iterator(); - while (iter.hasNext()) { - String key = (String)iter.next(); - Typeface fm = (Typeface)fonts.get(key); - gen.writeln("/" + key + " /" + fm.getFontName() + " def"); - } - gen.writeln("end def"); - gen.commentln("%FOPEndFontDict"); - gen.commentln("%FOPBeginFontReencode"); - defineWinAnsiEncoding(gen); - - //Rewrite font encodings - iter = fonts.keySet().iterator(); - while (iter.hasNext()) { - String key = (String)iter.next(); - Typeface fm = (Typeface)fonts.get(key); - if (null == fm.getEncoding()) { - //ignore (ZapfDingbats and Symbol run through here - //TODO: ZapfDingbats and Symbol should get getEncoding() fixed! - } else if ("WinAnsiEncoding".equals(fm.getEncoding())) { - gen.writeln("/" + fm.getFontName() + " findfont"); - gen.writeln("dup length dict begin"); - gen.writeln(" {1 index /FID ne {def} {pop pop} ifelse} forall"); - gen.writeln(" /Encoding " + fm.getEncoding() + " def"); - gen.writeln(" currentdict"); - gen.writeln("end"); - gen.writeln("/" + fm.getFontName() + " exch definefont pop"); - } else { - System.out.println("Only WinAnsiEncoding is supported. Font '" - + fm.getFontName() + "' asks for: " + fm.getEncoding()); - } - } - gen.commentln("%FOPEndFontReencode"); - } - - private static void defineWinAnsiEncoding(PSGenerator gen) throws IOException { - gen.writeln("/WinAnsiEncoding ["); - for (int i = 0; i < Glyphs.WINANSI_ENCODING.length; i++) { - if (i > 0) { - if ((i % 5) == 0) { - gen.newLine(); - } else { - gen.write(" "); - } - } - final char ch = Glyphs.WINANSI_ENCODING[i]; - final String glyphname = Glyphs.charToGlyphName(ch); - if ("".equals(glyphname)) { - gen.write("/" + Glyphs.NOTDEF); - } else { - gen.write("/"); - gen.write(glyphname); - } - } - gen.newLine(); - gen.writeln("] def"); - } - } diff --git a/src/java/org/apache/fop/render/ps/PSRenderer.java b/src/java/org/apache/fop/render/ps/PSRenderer.java index f674942bb..7d3c046a1 100644 --- a/src/java/org/apache/fop/render/ps/PSRenderer.java +++ b/src/java/org/apache/fop/render/ps/PSRenderer.java @@ -28,6 +28,8 @@ import java.util.Iterator; import java.util.List; import java.util.Map; +import javax.xml.transform.Source; + // FOP import org.apache.avalon.framework.configuration.Configuration; import org.apache.avalon.framework.configuration.ConfigurationException; @@ -103,6 +105,9 @@ public class PSRenderer extends AbstractPathOrientedRenderer { /** 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; /** * @see org.apache.avalon.framework.configuration.Configurable#configure(Configuration) @@ -561,7 +566,12 @@ public class PSRenderer extends AbstractPathOrientedRenderer { log.debug("rendering areas to PostScript"); //Setup for PostScript generation - this.gen = new PSGenerator(outputStream); + this.gen = new PSGenerator(outputStream) { + /** Need to subclass PSGenerator to have better URI resolution */ + public Source resolveURI(String uri) { + return userAgent.resolveURI(uri); + } + }; this.currentPageNumber = 0; //PostScript Header @@ -585,6 +595,20 @@ public class PSRenderer extends AbstractPathOrientedRenderer { * @see org.apache.fop.render.Renderer#stopRenderer() */ public void stopRenderer() throws IOException { + //Notify resource usage for font which are not supplied + Map fonts = fontInfo.getUsedFonts(); + Iterator e = fonts.keySet().iterator(); + while (e.hasNext()) { + String key = (String)e.next(); + //Typeface font = (Typeface)fonts.get(key); + PSResource res = (PSResource)this.fontResources.get(key); + boolean supplied = gen.isResourceSupplied(res); + if (!supplied) { + gen.notifyResourceUsage(res, true); + } + } + + //Write trailer gen.writeDSCComment(DSCConstants.TRAILER); gen.writeDSCComment(DSCConstants.PAGES, new Integer(this.currentPageNumber)); gen.writeResources(false); @@ -623,7 +647,7 @@ public class PSRenderer extends AbstractPathOrientedRenderer { //Setup gen.writeDSCComment(DSCConstants.BEGIN_SETUP); writeSetupCodeList(setupCodeList, "SetupCode"); - PSProcSets.writeFontDict(gen, fontInfo); + this.fontResources = PSFontUtils.writeFontDict(gen, fontInfo); gen.writeln("FOPFonts begin"); gen.writeDSCComment(DSCConstants.END_SETUP); } catch (IOException ioe) { diff --git a/src/java/org/apache/fop/render/ps/PSResource.java b/src/java/org/apache/fop/render/ps/PSResource.java index d37850a2e..b68bcaf6d 100644 --- a/src/java/org/apache/fop/render/ps/PSResource.java +++ b/src/java/org/apache/fop/render/ps/PSResource.java @@ -53,4 +53,11 @@ public class PSResource { return this.name; } + /** @return the <resource> specification as defined in DSC v3.0 spec. */ + public String getResourceSpecification() { + StringBuffer sb = new StringBuffer(); + sb.append(getType()).append(" ").append(PSGenerator.convertStringToDSC(getName())); + return sb.toString(); + } + } diff --git a/src/java/org/apache/fop/util/SubInputStream.java b/src/java/org/apache/fop/util/SubInputStream.java new file mode 100644 index 000000000..83b146402 --- /dev/null +++ b/src/java/org/apache/fop/util/SubInputStream.java @@ -0,0 +1,92 @@ +/* + * Copyright 2005 The Apache Software Foundation. + * + * Licensed 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.util; + +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; + +/** + * This class is a FilterInputStream descendant that reads from an underlying InputStream + * up to a defined number of bytes or the end of the underlying stream. Closing this InputStream + * will not result in the underlying InputStream to be closed, too. + * <p> + * This InputStream can be used to read chunks from a larger file of which the length is + * known in advance. + */ +public class SubInputStream extends FilterInputStream { + + /** Indicates the number of bytes remaning to be read from the underlying InputStream. */ + private long bytesToRead; + + /** + * Creates a new SubInputStream. + * @param in the InputStream to read from + * @param maxLen the maximum number of bytes to read from the underlying InputStream until + * the end-of-file is signalled. + */ + public SubInputStream(InputStream in, long maxLen) { + super(in); + this.bytesToRead = maxLen; + } + + /** @see java.io.InputStream#read() */ + public int read() throws IOException { + if (bytesToRead > 0) { + int result = super.read(); + if (result <= 0) { + bytesToRead--; + return result; + } else { + return -1; + } + } else { + return -1; + } + } + + /** @see java.io.InputStream#read(byte[], int, int) */ + public int read(byte[] b, int off, int len) throws IOException { + if (bytesToRead == 0) { + return -1; + } + int effRead = (int)Math.min(bytesToRead, len); + //cast to int is safe because len can never be bigger than Integer.MAX_VALUE + + int result = super.read(b, off, effRead); + if (result >= 0) { + bytesToRead -= result; + } + return result; + } + + /** @see java.io.InputStream#skip(long) */ + public long skip(long n) throws IOException { + long effRead = Math.min(bytesToRead, n); + long result = super.skip(effRead); + bytesToRead -= result; + return result; + } + + /** @see java.io.InputStream#close() */ + public void close() throws IOException { + //Don't close the underlying InputStream!!! + this.bytesToRead = 0; + } +} |