diff options
Diffstat (limited to 'src/java/org/apache/fop')
139 files changed, 2920 insertions, 7003 deletions
diff --git a/src/java/org/apache/fop/apps/FopFactory.java b/src/java/org/apache/fop/apps/FopFactory.java index e6ac70d67..ce77d9040 100644 --- a/src/java/org/apache/fop/apps/FopFactory.java +++ b/src/java/org/apache/fop/apps/FopFactory.java @@ -46,7 +46,6 @@ import org.apache.fop.fo.ElementMapping; import org.apache.fop.fo.ElementMappingRegistry; import org.apache.fop.fonts.FontCache; import org.apache.fop.hyphenation.HyphenationTreeResolver; -import org.apache.fop.image.ImageFactory; import org.apache.fop.layoutmgr.LayoutManagerMaker; import org.apache.fop.render.RendererFactory; import org.apache.fop.render.XMLHandlerRegistry; @@ -82,9 +81,6 @@ public class FopFactory implements ImageContext { private ColorSpaceCache colorSpaceCache = null; - /** Image factory for creating fop image objects */ - private ImageFactory imageFactory; - /** Image manager for loading and caching image objects */ private ImageManager imageManager; @@ -155,7 +151,6 @@ public class FopFactory implements ImageContext { this.elementMappingRegistry = new ElementMappingRegistry(this); this.foURIResolver = new FOURIResolver(validateUserConfigStrictly()); this.colorSpaceCache = new ColorSpaceCache(foURIResolver); - this.imageFactory = new ImageFactory(); this.imageManager = new ImageManager(this); this.rendererFactory = new RendererFactory(); this.xmlHandlers = new XMLHandlerRegistry(); @@ -290,11 +285,6 @@ public class FopFactory implements ImageContext { return this.contentHandlerFactoryRegistry; } - /** @return the image factory */ - public ImageFactory getImageFactory() { - return this.imageFactory; - } - /** * Returns the image manager. * @return the image manager diff --git a/src/java/org/apache/fop/cli/InputHandler.java b/src/java/org/apache/fop/cli/InputHandler.java index 68af6ae51..4c38fa5c7 100644 --- a/src/java/org/apache/fop/cli/InputHandler.java +++ b/src/java/org/apache/fop/cli/InputHandler.java @@ -39,8 +39,13 @@ import javax.xml.transform.sax.SAXSource; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.XMLReader; + import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + import org.apache.fop.apps.FOPException; import org.apache.fop.apps.FOUserAgent; import org.apache.fop.apps.Fop; @@ -150,6 +155,7 @@ public class InputHandler implements ErrorListener, Renderable { try { InputSource is = new InputSource(new FileInputStream( this.sourcefile)); + is.setSystemId(this.sourcefile.toURI().toASCIIString()); SAXParserFactory spf = SAXParserFactory.newInstance(); spf.setFeature("http://xml.org/sax/features/namespaces", true); spf.setFeature("http://apache.org/xml/features/xinclude", true); @@ -221,7 +227,7 @@ public class InputHandler implements ErrorListener, Renderable { * {@inheritDoc} */ public void warning(TransformerException exc) { - log.warn(exc.toString()); + log.warn(exc.getLocalizedMessage()); } /** diff --git a/src/java/org/apache/fop/fo/ElementMapping.java b/src/java/org/apache/fop/fo/ElementMapping.java index 0f436ae28..495983750 100644 --- a/src/java/org/apache/fop/fo/ElementMapping.java +++ b/src/java/org/apache/fop/fo/ElementMapping.java @@ -19,7 +19,7 @@ package org.apache.fop.fo; -import java.util.HashMap; +import java.util.Map; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; @@ -37,7 +37,7 @@ public abstract class ElementMapping { public static final String DEFAULT = "<default>"; /** The HashMap table of formatting objects defined by the ElementMapping */ - protected HashMap foObjs = null; + protected Map foObjs = null; /** The namespace for the ElementMapping */ protected String namespaceURI = null; @@ -47,7 +47,7 @@ public abstract class ElementMapping { * * @return Table of Maker objects for this ElementMapping */ - public HashMap getTable() { + public Map getTable() { if (foObjs == null) { initialize(); } diff --git a/src/java/org/apache/fop/fo/FOPropertyMapping.java b/src/java/org/apache/fop/fo/FOPropertyMapping.java index 9910f1ce7..539648f5a 100644 --- a/src/java/org/apache/fop/fo/FOPropertyMapping.java +++ b/src/java/org/apache/fop/fo/FOPropertyMapping.java @@ -412,6 +412,7 @@ public final class FOPropertyMapping implements Constants { l.setInherited(false); l.addEnum("auto", getEnumProperty(EN_AUTO, "AUTO")); l.setDefault("auto"); + l.setPercentBase(LengthBase.CONTAINING_BLOCK_HEIGHT); addPropertyMaker("top", l); // right @@ -419,6 +420,7 @@ public final class FOPropertyMapping implements Constants { l.setInherited(false); l.addEnum("auto", getEnumProperty(EN_AUTO, "AUTO")); l.setDefault("auto"); + l.setPercentBase(LengthBase.CONTAINING_BLOCK_WIDTH); addPropertyMaker("right", l); // bottom @@ -426,6 +428,7 @@ public final class FOPropertyMapping implements Constants { l.setInherited(false); l.addEnum("auto", getEnumProperty(EN_AUTO, "AUTO")); l.setDefault("auto"); + l.setPercentBase(LengthBase.CONTAINING_BLOCK_HEIGHT); addPropertyMaker("bottom", l); // left @@ -433,6 +436,7 @@ public final class FOPropertyMapping implements Constants { l.setInherited(false); l.addEnum("auto", getEnumProperty(EN_AUTO, "AUTO")); l.setDefault("auto"); + l.setPercentBase(LengthBase.CONTAINING_BLOCK_WIDTH); addPropertyMaker("left", l); } @@ -1161,6 +1165,7 @@ public final class FOPropertyMapping implements Constants { m = new LengthProperty.Maker(PR_START_INDENT); m.setInherited(true); m.setDefault("0pt"); + m.setPercentBase(LengthBase.CONTAINING_REFAREA_WIDTH); IndentPropertyMaker sCorr = new IndentPropertyMaker(m); sCorr.setCorresponding(PR_MARGIN_LEFT, PR_MARGIN_RIGHT, PR_MARGIN_TOP); sCorr.setUseParent(false); @@ -1177,6 +1182,7 @@ public final class FOPropertyMapping implements Constants { m = new LengthProperty.Maker(PR_END_INDENT); m.setInherited(true); m.setDefault("0pt"); + m.setPercentBase(LengthBase.CONTAINING_REFAREA_WIDTH); IndentPropertyMaker eCorr = new IndentPropertyMaker(m); eCorr.setCorresponding(PR_MARGIN_RIGHT, PR_MARGIN_LEFT, PR_MARGIN_BOTTOM); eCorr.setUseParent(false); diff --git a/src/java/org/apache/fop/fo/FOTreeBuilder.java b/src/java/org/apache/fop/fo/FOTreeBuilder.java index 63fc5cb5b..d02a058fe 100644 --- a/src/java/org/apache/fop/fo/FOTreeBuilder.java +++ b/src/java/org/apache/fop/fo/FOTreeBuilder.java @@ -38,7 +38,6 @@ import org.apache.fop.area.AreaTreeHandler; import org.apache.fop.fo.ElementMapping.Maker; import org.apache.fop.fo.extensions.ExtensionElementMapping; import org.apache.fop.fo.pagination.Root; -import org.apache.fop.image.ImageFactory; import org.apache.fop.util.ContentHandlerFactory; import org.apache.fop.util.ContentHandlerFactory.ObjectBuiltListener; import org.apache.fop.util.ContentHandlerFactory.ObjectSource; @@ -57,9 +56,7 @@ public class FOTreeBuilder extends DefaultHandler { /** The registry for ElementMapping instances */ protected ElementMappingRegistry elementMappingRegistry; - /** - * The root of the formatting object tree - */ + /** The root of the formatting object tree */ protected Root rootFObj = null; /** Main DefaultHandler that handles the FO namespace. */ @@ -68,10 +65,7 @@ public class FOTreeBuilder extends DefaultHandler { /** Current delegate ContentHandler to receive the SAX events */ protected ContentHandler delegate; - /** - * The class that handles formatting and rendering to a stream - * (mark-fop@inomial.com) - */ + /** The object that handles formatting and rendering to a stream */ private FOEventHandler foEventHandler; /** The SAX locator object managing the line and column counters */ @@ -86,14 +80,18 @@ public class FOTreeBuilder extends DefaultHandler { private int depth; /** - * FOTreeBuilder constructor + * <code>FOTreeBuilder</code> constructor + * * @param outputFormat the MIME type of the output format to use (ex. "application/pdf"). - * @param foUserAgent in effect for this process - * @param stream OutputStream to direct results - * @throws FOPException if the FOTreeBuilder cannot be properly created + * @param foUserAgent the {@link FOUserAgent} in effect for this process + * @param stream the <code>OutputStream</code> to direct the results to + * @throws FOPException if the <code>FOTreeBuilder</code> cannot be properly created */ - public FOTreeBuilder(String outputFormat, FOUserAgent foUserAgent, - OutputStream stream) throws FOPException { + public FOTreeBuilder( + String outputFormat, + FOUserAgent foUserAgent, + OutputStream stream) + throws FOPException { this.userAgent = foUserAgent; this.elementMappingRegistry = userAgent.getFactory().getElementMappingRegistry(); @@ -108,40 +106,25 @@ public class FOTreeBuilder extends DefaultHandler { }); } - /** - * This method enables to reduce memory consumption of the FO tree slightly. When it returns - * true no Locator is passed to the FO tree nodes which would copy the information into - * a SAX LocatorImpl instance. - * @return true if no context information should be stored on each node in the FO tree. - * @deprecated Use FOUserAgent.isLocatorEnabled() instead. - */ - protected boolean isLocatorDisabled() { - return !userAgent.isLocatorEnabled(); - } - - /** - * {@inheritDoc} - */ + /** {@inheritDoc} */ public void setDocumentLocator(Locator locator) { this.locator = locator; } - /** @return a Locator instance if it is available and not disabled */ + /** + * @return a {@link Locator} instance if it is available and not disabled + */ protected Locator getEffectiveLocator() { return (userAgent.isLocatorEnabled() ? this.locator : null); } - /** - * {@inheritDoc} - */ + /** {@inheritDoc} */ public void characters(char[] data, int start, int length) throws SAXException { delegate.characters(data, start, length); } - /** - * {@inheritDoc} - */ + /** {@inheritDoc} */ public void startDocument() throws SAXException { if (used) { throw new IllegalStateException("FOTreeBuilder (and the Fop class) cannot be reused." @@ -159,9 +142,7 @@ public class FOTreeBuilder extends DefaultHandler { this.delegate = this.mainFOHandler; } - /** - * {@inheritDoc} - */ + /** {@inheritDoc} */ public void endDocument() throws SAXException { this.delegate.endDocument(); if (this.rootFObj == null && empty) { @@ -173,24 +154,16 @@ public class FOTreeBuilder extends DefaultHandler { log.debug("Parsing of document complete"); } foEventHandler.endDocument(); - - //Notify the image factory that this user agent has expired. - ImageFactory imageFactory = userAgent.getFactory().getImageFactory(); - imageFactory.removeContext(this.userAgent); } - /** - * {@inheritDoc} - */ + /** {@inheritDoc} */ public void startElement(String namespaceURI, String localName, String rawName, Attributes attlist) throws SAXException { this.depth++; delegate.startElement(namespaceURI, localName, rawName, attlist); } - /** - * {@inheritDoc} - */ + /** {@inheritDoc} */ public void endElement(String uri, String localName, String rawName) throws SAXException { this.delegate.endElement(uri, localName, rawName); @@ -206,7 +179,8 @@ public class FOTreeBuilder extends DefaultHandler { } /** - * Finds the Maker used to create node objects of a particular type + * Finds the {@link Maker} used to create {@link FONode} objects of a particular type + * * @param namespaceURI URI for the namespace of the element * @param localName name of the Element * @return the ElementMapping.Maker that can create an FO object for this element @@ -218,7 +192,7 @@ public class FOTreeBuilder extends DefaultHandler { /** {@inheritDoc} */ public void warning(SAXParseException e) { - log.warn(e.toString()); + log.warn(e.getLocalizedMessage()); } /** {@inheritDoc} */ @@ -233,7 +207,8 @@ public class FOTreeBuilder extends DefaultHandler { } /** - * Provides access to the underlying FOEventHandler object. + * Provides access to the underlying {@link FOEventHandler} object. + * * @return the FOEventHandler object */ public FOEventHandler getEventHandler() { @@ -244,6 +219,7 @@ public class FOTreeBuilder extends DefaultHandler { * Returns the results of the rendering process. Information includes * the total number of pages generated and the number of pages per * page-sequence. + * * @return the results of the rendering process. */ public FormattingResults getResults() { @@ -257,23 +233,17 @@ public class FOTreeBuilder extends DefaultHandler { } /** - * Main DefaultHandler implementation which builds the FO tree. + * Main <code>DefaultHandler</code> implementation which builds the FO tree. */ private class MainFOHandler extends DefaultHandler { - /** - * Current formatting object being handled - */ + /** Current formatting object being handled */ protected FONode currentFObj = null; - /** - * Current propertyList for the node being handled. - */ + /** Current propertyList for the node being handled */ protected PropertyList currentPropertyList; - /** - * Current marker nesting-depth - */ + /** Current marker nesting-depth */ private int nestedMarkerDepth = 0; /** {@inheritDoc} */ @@ -298,11 +268,7 @@ public class FOTreeBuilder extends DefaultHandler { } else { // check that incoming node is valid for currentFObj if (namespaceURI.equals(FOElementMapping.URI) || namespaceURI.equals(ExtensionElementMapping.URI)) { - try { - currentFObj.validateChildNode(locator, namespaceURI, localName); - } catch (ValidationException e) { - throw e; - } + currentFObj.validateChildNode(locator, namespaceURI, localName); } } @@ -391,12 +357,11 @@ public class FOTreeBuilder extends DefaultHandler { if (currentFObj.getParent() == null) { log.debug("endElement for top-level " + currentFObj.getName()); } + currentFObj = currentFObj.getParent(); } - /** - * {@inheritDoc} - */ + /** {@inheritDoc} */ public void characters(char[] data, int start, int length) throws FOPException { if (currentFObj != null) { @@ -405,6 +370,7 @@ public class FOTreeBuilder extends DefaultHandler { } } + /** {@inheritDoc} */ public void endDocument() throws SAXException { currentFObj = null; } diff --git a/src/java/org/apache/fop/fo/flow/table/EmptyGridUnit.java b/src/java/org/apache/fop/fo/flow/table/EmptyGridUnit.java index 201029ff1..2c910d3f8 100644 --- a/src/java/org/apache/fop/fo/flow/table/EmptyGridUnit.java +++ b/src/java/org/apache/fop/fo/flow/table/EmptyGridUnit.java @@ -25,8 +25,6 @@ package org.apache.fop.fo.flow.table; */ public class EmptyGridUnit extends GridUnit { - private TableBody body; - /** * @param table the containing table * @param row the table-row element this grid unit belongs to (if any) @@ -57,11 +55,6 @@ public class EmptyGridUnit extends GridUnit { } /** {@inheritDoc} */ - public TableBody getBody() { - return this.body; - } - - /** {@inheritDoc} */ public boolean isLastGridUnitColSpan() { return true; } diff --git a/src/java/org/apache/fop/fo/flow/table/GridUnit.java b/src/java/org/apache/fop/fo/flow/table/GridUnit.java index 23d1cc001..b9394ff31 100644 --- a/src/java/org/apache/fop/fo/flow/table/GridUnit.java +++ b/src/java/org/apache/fop/fo/flow/table/GridUnit.java @@ -19,7 +19,6 @@ package org.apache.fop.fo.flow.table; -import org.apache.fop.fo.FONode; import org.apache.fop.fo.properties.CommonBorderPaddingBackground; import org.apache.fop.fo.properties.CommonBorderPaddingBackground.BorderInfo; import org.apache.fop.layoutmgr.table.CollapsingBorderModel; @@ -165,14 +164,6 @@ public class GridUnit { this.row = row; } - public TableBody getBody() { - FONode node = getCell(); - while (node != null && !(node instanceof TableBody)) { - node = node.getParent(); - } - return (TableBody) node; - } - /** * Returns the before-start grid unit of the cell containing this grid unit. * diff --git a/src/java/org/apache/fop/fo/flow/table/PrimaryGridUnit.java b/src/java/org/apache/fop/fo/flow/table/PrimaryGridUnit.java index 1a47a7dcf..8af896fa2 100644 --- a/src/java/org/apache/fop/fo/flow/table/PrimaryGridUnit.java +++ b/src/java/org/apache/fop/fo/flow/table/PrimaryGridUnit.java @@ -23,6 +23,7 @@ import java.util.LinkedList; import java.util.List; import org.apache.fop.fo.Constants; +import org.apache.fop.fo.FONode; import org.apache.fop.fo.properties.CommonBorderPaddingBackground; import org.apache.fop.layoutmgr.ElementListUtils; import org.apache.fop.layoutmgr.table.TableCellLayoutManager; @@ -71,6 +72,19 @@ public class PrimaryGridUnit extends GridUnit { .getValue() / 2; // TODO } + /** + * Returns the fo:table-header/footer/body element containing this cell. + * + * @return the enclosing table part + */ + public TableBody getTableBody() { + FONode node = cell.getParent(); + if (node instanceof TableRow) { + node = node.getParent(); + } + return (TableBody) node; + } + public TableCellLayoutManager getCellLM() { assert cellLM != null; return cellLM; @@ -224,18 +238,6 @@ public class PrimaryGridUnit extends GridUnit { return contentLength; } - /** @return true if cell/row has an explicit BPD/height */ - public boolean hasBPD() { - if (!getCell().getBlockProgressionDimension().getOptimum(null).isAuto()) { - return true; - } - if (getRow() != null - && !getRow().getBlockProgressionDimension().getOptimum(null).isAuto()) { - return true; - } - return false; - } - /** * Returns the grid units belonging to the same span as this one. * diff --git a/src/java/org/apache/fop/fo/properties/CommonHyphenation.java b/src/java/org/apache/fop/fo/properties/CommonHyphenation.java index 26747c24d..f47ddbe0c 100644 --- a/src/java/org/apache/fop/fo/properties/CommonHyphenation.java +++ b/src/java/org/apache/fop/fo/properties/CommonHyphenation.java @@ -21,6 +21,7 @@ package org.apache.fop.fo.properties; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + import org.apache.fop.fo.Constants; import org.apache.fop.fo.PropertyList; import org.apache.fop.fo.expr.PropertyException; @@ -142,7 +143,7 @@ public final class CommonHyphenation { FontMetrics metrics = font.getFontMetrics(); if (metrics instanceof Typeface) { Typeface typeface = (Typeface)metrics; - if ("SymbolEncoding".equals(typeface.getEncoding())) { + if ("SymbolEncoding".equals(typeface.getEncodingName())) { //SymbolEncoding doesn't have HYPHEN_MINUS, so replace by MINUS_SIGN } else { //only warn if the encoding is not SymbolEncoding @@ -154,7 +155,7 @@ public final class CommonHyphenation { FontMetrics metrics = font.getFontMetrics(); if (metrics instanceof Typeface) { Typeface typeface = (Typeface)metrics; - if ("ZapfDingbatsEncoding".equals(typeface.getEncoding())) { + if ("ZapfDingbatsEncoding".equals(typeface.getEncodingName())) { //ZapfDingbatsEncoding doesn't have HYPHEN_MINUS, so replace by ' ' } else { //only warn if the encoding is not ZapfDingbatsEncoding diff --git a/src/java/org/apache/fop/fo/properties/XMLLangShorthandParser.java b/src/java/org/apache/fop/fo/properties/XMLLangShorthandParser.java index 69ca372e5..5a5cf95c5 100644 --- a/src/java/org/apache/fop/fo/properties/XMLLangShorthandParser.java +++ b/src/java/org/apache/fop/fo/properties/XMLLangShorthandParser.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. * -/* $Id:$ */ +/* $Id$ */ package org.apache.fop.fo.properties; diff --git a/src/java/org/apache/fop/fonts/AbstractCodePointMapping.java b/src/java/org/apache/fop/fonts/AbstractCodePointMapping.java index 91d13da85..3a2ac5022 100644 --- a/src/java/org/apache/fop/fonts/AbstractCodePointMapping.java +++ b/src/java/org/apache/fop/fonts/AbstractCodePointMapping.java @@ -29,7 +29,7 @@ import org.apache.fop.util.CharUtilities; /** * Abstract base class for code point mapping classes (1-byte character encodings). */ -public class AbstractCodePointMapping { +public class AbstractCodePointMapping implements SingleByteEncoding { private String name; private char[] latin1Map; @@ -114,19 +114,12 @@ public class AbstractCodePointMapping { } } - /** - * Returns the encoding's name. - * @return the name of the encoding - */ + /** {@inheritDoc} */ public String getName() { return this.name; } - /** - * Maps a Unicode character to a code point in the encoding. - * @param c the Unicode character to map - * @return the coid point in the encoding or 0 (=.notdef) if not found - */ + /** {@inheritDoc} */ public final char mapChar(char c) { if (c < 256) { char latin1 = latin1Map[c]; @@ -172,8 +165,8 @@ public class AbstractCodePointMapping { } } - putFallbackCharacter(c, '\0'); - return 0; + putFallbackCharacter(c, NOT_FOUND_CODE_POINT); + return NOT_FOUND_CODE_POINT; } private void putFallbackCharacter(char c, char mapTo) { @@ -227,11 +220,7 @@ public class AbstractCodePointMapping { return -1; } - /** - * Returns the array of character names for this encoding. - * @return the array of character names - * (unmapped code points are represented by a ".notdef" value) - */ + /** {@inheritDoc} */ public String[] getCharNameMap() { if (this.charNameMap != null) { String[] copy = new String[this.charNameMap.length]; diff --git a/src/java/org/apache/fop/image/EmfImage.java b/src/java/org/apache/fop/fonts/Base14Font.java index 73bbad232..26c11e72b 100644 --- a/src/java/org/apache/fop/image/EmfImage.java +++ b/src/java/org/apache/fop/fonts/Base14Font.java @@ -16,36 +16,12 @@ */ /* $Id$ */ - -package org.apache.fop.image; + +package org.apache.fop.fonts; /** - * Enhanced metafile image. - * This supports loading a EMF image. - * - * @see AbstractFopImage - * @see FopImage + * Base class for all Base 14 fonts. */ -public class EmfImage extends AbstractFopImage { - - /** - * Create a bitmap image with the image data. - * - * @param imgInfo the image information - */ - public EmfImage(FopImage.ImageInfo imgInfo) { - super(imgInfo); - } +public abstract class Base14Font extends Typeface { - /** - * Load the original EMF data. - * This loads the original EMF data and reads the color space, - * and icc profile if any. - * - * @return true if loaded false for any error - */ - protected boolean loadOriginalData() { - return loadDefaultOriginalData(); - } } - diff --git a/src/java/org/apache/fop/fonts/CIDFont.java b/src/java/org/apache/fop/fonts/CIDFont.java index c554d2165..7216c8f15 100644 --- a/src/java/org/apache/fop/fonts/CIDFont.java +++ b/src/java/org/apache/fop/fonts/CIDFont.java @@ -20,31 +20,14 @@ package org.apache.fop.fonts; //Java -import java.util.Map; /** * Abstract base class for CID fonts. */ public abstract class CIDFont extends CustomFont { - /** - * usedGlyphs contains orginal, new glyph index - */ - public Map usedGlyphs = new java.util.HashMap(); - - /** - * usedGlyphsIndex contains new glyph, original index - */ - public Map usedGlyphsIndex = new java.util.HashMap(); - public int usedGlyphsCount = 0; - - /** - * usedCharsIndex contains new glyph, original char - */ - public Map usedCharsIndex = new java.util.HashMap(); - - //private PDFWArray warray = new PDFWArray(); - public int width[] = null; + /** Contains the character widths for all characters in the font */ + protected int[] width = null; // ---- Required ---- /** @@ -73,6 +56,11 @@ public abstract class CIDFont extends CustomFont { */ public abstract int getSupplement(); + /** + * Returns the subset information for this font. + * @return the subset information + */ + public abstract CIDSubset getCIDSubset(); // ---- Optional ---- /** @@ -88,9 +76,4 @@ public abstract class CIDFont extends CustomFont { return true; } - /** - * Returns a char array containing all Unicode characters that have been accessed. - * @return a char array with all used Unicode characters - */ - public abstract char[] getCharsUsed(); -}
\ No newline at end of file +} diff --git a/src/java/org/apache/fop/fonts/CIDSubset.java b/src/java/org/apache/fop/fonts/CIDSubset.java new file mode 100644 index 000000000..6bcfc0b71 --- /dev/null +++ b/src/java/org/apache/fop/fonts/CIDSubset.java @@ -0,0 +1,177 @@ +/* + * 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.fonts; + +import java.util.BitSet; +import java.util.Collections; +import java.util.Iterator; +import java.util.Map; + +import org.apache.fop.util.CharUtilities; + +//Naming: +//glyph index: original index of the glyph in the non-subset font (!= unicode index) +//character selector: index into a set of glyphs. For subset CID fonts, this starts at 0. For +// non-subset fonts, this is the same as the glyph index. +//Unicode index: The Unicode codepoint of a character. +//Glyph name: the Adobe glyph name (as found in Glyphs.java) + +/** + * Keeps track of the glyphs used in a document. This information is later used to build + * a subset of a font. + */ +public class CIDSubset { + + /** + * usedGlyphs contains orginal, new glyph index (glyph index -> char selector) + */ + private Map/*<Integer, Integer>*/ usedGlyphs = new java.util.HashMap(); + + /** + * usedGlyphsIndex contains new glyph, original index (char selector -> glyph index) + */ + private Map/*<Integer, Integer>*/ usedGlyphsIndex = new java.util.HashMap(); + private int usedGlyphsCount = 0; + + /** + * usedCharsIndex contains new glyph, original char (char selector -> Unicode) + */ + private Map/*<Integer, Character>*/ usedCharsIndex = new java.util.HashMap(); + + public CIDSubset() { + } + + /** + * Adds the initial 3 glyphs which are the same for all CID subsets. + */ + public void setupFirstThreeGlyphs() { + // Make sure that the 3 first glyphs are included + usedGlyphs.put(new Integer(0), new Integer(0)); + usedGlyphsIndex.put(new Integer(0), new Integer(0)); + usedGlyphsCount++; + usedGlyphs.put(new Integer(1), new Integer(1)); + usedGlyphsIndex.put(new Integer(1), new Integer(1)); + usedGlyphsCount++; + usedGlyphs.put(new Integer(2), new Integer(2)); + usedGlyphsIndex.put(new Integer(2), new Integer(2)); + usedGlyphsCount++; + } + + /** + * Returns the original index of the glyph inside the (non-subset) font's glyph list. This + * index can be used to access the character width information, for example. + * @param subsetIndex the subset index (character selector) to access the glyph + * @return the original index (or -1 if no glyph index is available for the subset index) + */ + public int getGlyphIndexForSubsetIndex(int subsetIndex) { + Integer glyphIndex = (Integer)usedGlyphsIndex.get(new Integer(subsetIndex)); + if (glyphIndex != null) { + return glyphIndex.intValue(); + } else { + return -1; + } + } + + /** + * Returns the Unicode value for a subset index (character selector). If there's no such + * Unicode value, the "NOT A CHARACTER" (0xFFFF) is returned. + * @param subsetIndex the subset index (character selector) + * @return the Unicode value or "NOT A CHARACTER" (0xFFFF) + */ + public char getUnicodeForSubsetIndex(int subsetIndex) { + Character mapValue = (Character)usedCharsIndex.get(new Integer(subsetIndex)); + if (mapValue != null) { + return mapValue.charValue(); + } else { + return CharUtilities.NOT_A_CHARACTER; + } + } + + /** + * Maps a character to a character selector for a font subset. If the character isn't in the + * subset, yet, it is added and a new character selector returned. Otherwise, the already + * allocated character selector is returned from the existing map/subset. + * @param glyphIndex the glyph index of the character + * @param unicode the Unicode index of the character + * @return the subset index + */ + public int mapSubsetChar(int glyphIndex, char unicode) { + // Reencode to a new subset font or get the reencoded value + // IOW, accumulate the accessed characters and build a character map for them + Integer subsetCharSelector = (Integer)usedGlyphs.get(new Integer(glyphIndex)); + if (subsetCharSelector == null) { + int selector = usedGlyphsCount; + usedGlyphs.put(new Integer(glyphIndex), + new Integer(selector)); + usedGlyphsIndex.put(new Integer(selector), + new Integer(glyphIndex)); + usedCharsIndex.put(new Integer(selector), + new Character(unicode)); + usedGlyphsCount++; + return selector; + } else { + return subsetCharSelector.intValue(); + } + } + + /** + * Returns an unmodifiable Map of the font subset. It maps from glyph index to + * character selector (i.e. the subset index in this case). + * @return Map Map<Integer, Integer> of the font subset + */ + public Map/*<Integer, Integer>*/ getSubsetGlyphs() { + return Collections.unmodifiableMap(this.usedGlyphs); + } + + /** + * Returns a char array containing all Unicode characters that are in the subset. + * @return a char array with all used Unicode characters + */ + public char[] getSubsetChars() { + char[] charArray = new char[usedGlyphsCount]; + for (int i = 0; i < usedGlyphsCount; i++) { + charArray[i] = getUnicodeForSubsetIndex(i); + } + return charArray; + } + + /** + * Returns the number of glyphs in the subset. + * @return the number of glyphs in the subset + */ + public int getSubsetSize() { + return this.usedGlyphsCount; + } + + /** + * Returns a BitSet with bits set for each available glyph index. + * @return a BitSet indicating available glyph indices + */ + public BitSet getGlyphIndexBitSet() { + BitSet bitset = new BitSet(); + Iterator iter = usedGlyphs.keySet().iterator(); + while (iter.hasNext()) { + Integer cid = (Integer)iter.next(); + bitset.set(cid.intValue()); + } + return bitset; + } + +} diff --git a/src/java/org/apache/fop/fonts/Font.java b/src/java/org/apache/fop/fonts/Font.java index e123513c2..ff71434c6 100644 --- a/src/java/org/apache/fop/fonts/Font.java +++ b/src/java/org/apache/fop/fonts/Font.java @@ -199,7 +199,7 @@ public class Font { // Use default CodePointMapping char d = CodePointMapping.getMapping("WinAnsiEncoding").mapChar(c); - if (d != 0) { + if (d != SingleByteEncoding.NOT_FOUND_CODE_POINT) { c = d; } else { log.warn("Glyph " + (int) c + " not available in font " + fontName); diff --git a/src/java/org/apache/fop/fonts/LazyFont.java b/src/java/org/apache/fop/fonts/LazyFont.java index e6ed7e881..8997069d4 100644 --- a/src/java/org/apache/fop/fonts/LazyFont.java +++ b/src/java/org/apache/fop/fonts/LazyFont.java @@ -64,11 +64,10 @@ public class LazyFont extends Typeface implements FontDescriptor { this.resolver = resolver; } - /** - * String representation of LazyFont - */ + /** {@inheritDoc} */ public String toString() { - return ( "metrics-url=" + metricsFileName + ", embed-url=" + fontEmbedPath + ", kerning=" + useKerning ); + return ( "metrics-url=" + metricsFileName + ", embed-url=" + fontEmbedPath + + ", kerning=" + useKerning ); } private void load(boolean fail) { @@ -80,8 +79,9 @@ public class LazyFont extends Typeface implements FontDescriptor { if (resolver != null) { Source source = resolver.resolve(metricsFileName); if (source == null) { - String err = "Cannot load font: failed to create Source from metrics file " - + metricsFileName; + String err + = "Cannot load font: failed to create Source from metrics file " + + metricsFileName; if (fail) { throw new RuntimeException(err); } else { @@ -112,8 +112,8 @@ public class LazyFont extends Typeface implements FontDescriptor { src.setSystemId(source.getSystemId()); reader = new FontReader(src); } else { - reader - = new FontReader(new InputSource(new URL(metricsFileName).openStream())); + reader = new FontReader(new InputSource( + new URL(metricsFileName).openStream())); } reader.setKerningEnabled(useKerning); reader.setFontEmbedPath(fontEmbedPath); @@ -153,12 +153,10 @@ public class LazyFont extends Typeface implements FontDescriptor { } // ---- Font ---- - /** - * {@inheritDoc} - */ - public String getEncoding() { + /** {@inheritDoc} */ + public String getEncodingName() { load(true); - return realFont.getEncoding(); + return realFont.getEncodingName(); } /** diff --git a/src/java/org/apache/fop/fonts/MultiByteFont.java b/src/java/org/apache/fop/fonts/MultiByteFont.java index e40c40985..5849379bd 100644 --- a/src/java/org/apache/fop/fonts/MultiByteFont.java +++ b/src/java/org/apache/fop/fonts/MultiByteFont.java @@ -39,6 +39,9 @@ public class MultiByteFont extends CIDFont { private String namePrefix = null; // Quasi unique prefix + private CIDSubset subset = new CIDSubset(); + + /** A map from Unicode indices to glyph indices */ private BFEntry[] bfentries = null; /** @@ -46,15 +49,7 @@ public class MultiByteFont extends CIDFont { */ public MultiByteFont() { // Make sure that the 3 first glyphs are included - usedGlyphs.put(new Integer(0), new Integer(0)); - usedGlyphsIndex.put(new Integer(0), new Integer(0)); - usedGlyphsCount++; - usedGlyphs.put(new Integer(1), new Integer(1)); - usedGlyphsIndex.put(new Integer(1), new Integer(1)); - usedGlyphsCount++; - usedGlyphs.put(new Integer(2), new Integer(2)); - usedGlyphsIndex.put(new Integer(2), new Integer(2)); - usedGlyphsCount++; + subset.setupFirstThreeGlyphs(); // Create a quasiunique prefix for fontname synchronized (this.getClass()) { @@ -77,37 +72,27 @@ public class MultiByteFont extends CIDFont { setFontType(FontType.TYPE0); } - /** - * {@inheritDoc} - */ + /** {@inheritDoc} */ public int getDefaultWidth() { return defaultWidth; } - /** - * {@inheritDoc} - */ + /** {@inheritDoc} */ public String getRegistry() { return "Adobe"; } - /** - * {@inheritDoc} - */ + /** {@inheritDoc} */ public String getOrdering() { return "UCS"; } - /** - * {@inheritDoc} - */ + /** {@inheritDoc} */ public int getSupplement() { return 0; } - /** - * {@inheritDoc} - */ + /** {@inheritDoc} */ public CIDFontType getCIDType() { return cidType; } @@ -133,68 +118,47 @@ public class MultiByteFont extends CIDFont { } } - /** - * {@inheritDoc} - */ + /** {@inheritDoc} */ public boolean isEmbeddable() { return !(getEmbedFileName() == null && getEmbedResourceName() == null); } - /** - * {@inheritDoc} - */ - public String getEncoding() { + /** {@inheritDoc} */ + public CIDSubset getCIDSubset() { + return this.subset; + } + + /** {@inheritDoc} */ + public String getEncodingName() { return encoding; } - /** - * {@inheritDoc} - */ + /** {@inheritDoc} */ public int getWidth(int i, int size) { if (isEmbeddable()) { - Integer idx = (Integer)usedGlyphsIndex.get(new Integer(i)); - return size * width[idx.intValue()]; + int glyphIndex = subset.getGlyphIndexForSubsetIndex(i); + return size * width[glyphIndex]; } else { return size * width[i]; } } - /** - * {@inheritDoc} - */ + /** {@inheritDoc} */ public int[] getWidths() { int[] arr = new int[width.length]; System.arraycopy(width, 0, arr, 0, width.length - 1); - /* - for (int i = 0; i < arr.length; i++) - arr[i] *= size; - */ return arr; } /** - * Remaps a codepoint based. - * @param i codepoint to remap - * @return new codepoint + * Returns the glyph index for a Unicode character. The method returns 0 if there's no + * such glyph in the character map. + * @param c the Unicode character index + * @return the glyph index (or 0 if the glyph is not available) */ -/* unused - public Integer reMap(Integer i) { - if (isEmbeddable()) { - Integer ret = (Integer)usedGlyphsIndex.get(i); - if (ret == null) { - ret = i; - } - return ret; - } else { - return i; - } - - } -*/ - private int findGlyphIndex(char c) { int idx = (int)c; - int retIdx = 0; + int retIdx = 0; //.notdef for (int i = 0; (i < bfentries.length) && retIdx == 0; i++) { if (bfentries[i].getUnicodeStart() <= idx @@ -208,48 +172,30 @@ public class MultiByteFont extends CIDFont { return retIdx; } - /** - * {@inheritDoc} - */ + /** {@inheritDoc} */ public char mapChar(char c) { notifyMapOperation(); - int retIdx = findGlyphIndex(c); + int glyphIndex = findGlyphIndex(c); if (isEmbeddable()) { - // Reencode to a new subset font or get - // the reencoded value - Integer newIdx = (Integer)usedGlyphs.get(new Integer(retIdx)); - if (newIdx == null) { - usedGlyphs.put(new Integer(retIdx), - new Integer(usedGlyphsCount)); - usedGlyphsIndex.put(new Integer(usedGlyphsCount), - new Integer(retIdx)); - usedCharsIndex.put(new Integer(usedGlyphsCount), - new Integer((int) c)); - retIdx = usedGlyphsCount; - usedGlyphsCount++; - } else { - retIdx = newIdx.intValue(); - } + glyphIndex = subset.mapSubsetChar(glyphIndex, c); } - return (char)retIdx; + return (char)glyphIndex; } - /** - * {@inheritDoc} - */ + /** {@inheritDoc} */ public boolean hasChar(char c) { return (findGlyphIndex(c) > 0); } - /** - * Sets the bfentries. - * @param bfentries The bfentries to set + * Sets the array of BFEntry instances which constitutes the Unicode to glyph index map for + * a font. ("BF" means "base font") + * @param entries the Unicode to glyph index map */ - public void setBFEntries(BFEntry[] bfentries) { - this.bfentries = bfentries; + public void setBFEntries(BFEntry[] entries) { + this.bfentries = entries; } /** @@ -277,17 +223,6 @@ public class MultiByteFont extends CIDFont { } /** - * Adds a new CID width entry to the font. - * @param cidWidthIndex index - * @param wds array of widths - */ - /* - public void addCIDWidthEntry(int cidWidthIndex, int[] wds) { - this.warray.addEntry(cidWidthIndex, wds); - }*/ - - - /** * Sets the width array. * @param wds array of widths. */ @@ -300,30 +235,15 @@ public class MultiByteFont extends CIDFont { * @return Map Map of used Glyphs */ public Map getUsedGlyphs() { - return usedGlyphs; + return subset.getSubsetGlyphs(); } - /** The invalid Unicode character, suitable as a return value in methods - * that need to return an invalid character. */ - public static final char INVALID_UNICODE_CHAR = 0xFFFF; - /** {@inheritDoc} */ public char[] getCharsUsed() { if (!isEmbeddable()) { return null; } - char[] charArray = new char[usedGlyphsCount]; - for (int i = 0; i < usedGlyphsCount; i++) { - Integer mapValue = (Integer)usedCharsIndex.get(new Integer(i)); - if (mapValue != null) { - char arrayItem = (char) mapValue.intValue(); - charArray[i] = arrayItem; - } - else { - charArray[i] = INVALID_UNICODE_CHAR; - } - } - return charArray; + return subset.getSubsetChars(); } } diff --git a/src/java/org/apache/fop/fonts/NamedCharacter.java b/src/java/org/apache/fop/fonts/NamedCharacter.java new file mode 100644 index 000000000..2c8007ba1 --- /dev/null +++ b/src/java/org/apache/fop/fonts/NamedCharacter.java @@ -0,0 +1,142 @@ +/* + * 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.fonts; + +import org.apache.xmlgraphics.fonts.Glyphs; + +import org.apache.fop.util.CharUtilities; + +/** + * Represents an named character with character name (from the Adobe glyph list) and a Unicode + * sequence that this character represents. + */ +public class NamedCharacter { + + private String charName; + private String unicodeSequence; + + /** + * Main constructor. + * @param charName the character name + * @param unicodeSequence the Unicode sequence associated with this character + */ + public NamedCharacter(String charName, String unicodeSequence) { + if (charName == null) { + throw new NullPointerException("charName must not be null"); + } + this.charName = charName; + if (unicodeSequence != null) { + this.unicodeSequence = unicodeSequence; + } else { + this.unicodeSequence = Glyphs.getUnicodeSequenceForGlyphName(charName); + } + } + + /** + * Simple constructor. + * @param charName the character name + */ + public NamedCharacter(String charName) { + this(charName, null); + } + + /** {@inheritDoc} */ + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((charName == null) ? 0 : charName.hashCode()); + return result; + } + + /** {@inheritDoc} */ + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final NamedCharacter other = (NamedCharacter)obj; + return charName.equals(other.charName); + } + + /** + * Returns the character name (as defined by the Adobe glyph list). + * @return the character name + */ + public String getName() { + return this.charName; + } + + /** + * Returns the Unicode sequence associated with this character. + * @return the Unicode sequence (or null if no Unicode sequence is associated) + */ + public String getUnicodeSequence() { + return this.unicodeSequence; + } + + /** + * Indicates whether a single Unicode value is associated with this character. + * @return true if exactly one Unicode value is associated with this character, false otherwise + */ + public boolean hasSingleUnicodeValue() { + return (this.unicodeSequence != null && this.unicodeSequence.length() == 1); + } + + /** + * Returns the single Unicode value associated with this named character. Check + * {@link #hasSingleUnicodeValue()} before you call this method because an + * IllegalStateException is thrown is a Unicode sequence with more than one character is + * associated with this character. + * @return the single Unicode value (or FFFF ("NOT A CHARACTER") if no Unicode value is + * available) + * @throws IllegalStateException if a Unicode sequence with more than one value is associated + * with the named character + */ + public char getSingleUnicodeValue() throws IllegalStateException { + if (this.unicodeSequence == null) { + return CharUtilities.NOT_A_CHARACTER; + } + if (this.unicodeSequence.length() > 1) { + throw new IllegalStateException("getSingleUnicodeValue() may not be called for a" + + " named character that has more than one Unicode value (a sequence)" + + " associated with the named character!"); + } + return this.unicodeSequence.charAt(0); + } + + /** {@inheritDoc} */ + public String toString() { + StringBuffer sb = new StringBuffer(this.unicodeSequence); + sb.append(" ("); + if (this.unicodeSequence != null) { + for (int i = 0, c = this.unicodeSequence.length(); i < c; i++) { + sb.append("0x").append(Integer.toHexString(this.unicodeSequence.charAt(0))); + } + sb.append(", "); + } + sb.append(getName()).append(')'); + return sb.toString(); + } +}
\ No newline at end of file diff --git a/src/java/org/apache/fop/fonts/SimpleSingleByteEncoding.java b/src/java/org/apache/fop/fonts/SimpleSingleByteEncoding.java new file mode 100644 index 000000000..a5ba1a33b --- /dev/null +++ b/src/java/org/apache/fop/fonts/SimpleSingleByteEncoding.java @@ -0,0 +1,145 @@ +/* + * 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.fonts; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +import org.apache.xmlgraphics.fonts.Glyphs; + +/** + * A simple implementation of the OneByteEncoding mostly used for encodings that are constructed + * on-the-fly. + */ +public class SimpleSingleByteEncoding implements SingleByteEncoding { + + private String name; + private List mapping = new java.util.ArrayList(); + //List<NamedCharacter> + private Map charMap = new java.util.HashMap(); + //Map<Character(Unicode), Character(code point)> + + /** + * Main constructor. + * @param name the encoding's name + */ + public SimpleSingleByteEncoding(String name) { + this.name = name; + } + + /** {@inheritDoc} */ + public String getName() { + return this.name; + } + + /** {@inheritDoc} */ + public char mapChar(char c) { + Character nc = (Character)charMap.get(new Character(c)); + if (nc != null) { + return nc.charValue(); + } + return NOT_FOUND_CODE_POINT; + } + + /** {@inheritDoc} */ + public String[] getCharNameMap() { + String[] map = new String[getSize()]; + Arrays.fill(map, Glyphs.NOTDEF); + for (int i = getFirstChar(); i <= getLastChar(); i++) { + NamedCharacter ch = (NamedCharacter)this.mapping.get(i - 1); + map[i] = ch.getName(); + } + return map; + } + + /** + * Returns the index of the first defined character. + * @return the index of the first defined character (always 1 for this class) + */ + public int getFirstChar() { + return 1; + } + + /** + * Returns the index of the last defined character. + * @return the index of the last defined character + */ + public int getLastChar() { + return this.mapping.size(); + } + + /** + * Returns the number of characters defined by this encoding. + * @return the number of characters + */ + public int getSize() { + return this.mapping.size() + 1; + } + + /** + * Indicates whether the encoding is full (with 256 code points). + * @return true if the encoding is full + */ + public boolean isFull() { + return (getSize() == 256); + } + + /** + * Adds a new character to the encoding. + * @param ch the named character + * @return the code point assigned to the character + */ + public char addCharacter(NamedCharacter ch) { + if (!ch.hasSingleUnicodeValue()) { + throw new IllegalArgumentException("Only NamedCharacters with a single Unicode value" + + " are currently supported!"); + } + if (isFull()) { + throw new IllegalStateException("Encoding is full!"); + } + char newSlot = (char)(getLastChar() + 1); + this.mapping.add(ch); + this.charMap.put(new Character(ch.getSingleUnicodeValue()), new Character(newSlot)); + return newSlot; + } + + /** + * Returns the named character at a given code point in the encoding. + * @param codePoint the code point of the character + * @return the NamedCharacter (or null if no character is at this position) + */ + public NamedCharacter getCharacterForIndex(int codePoint) { + if (codePoint < 0 || codePoint > 255) { + throw new IllegalArgumentException("codePoint must be between 0 and 255"); + } + if (codePoint <= getLastChar()) { + return (NamedCharacter)this.mapping.get(codePoint - 1); + } else { + return null; + } + } + + /** {@inheritDoc} */ + public String toString() { + return getName() + " (" + getSize() + " chars)"; + } + +} diff --git a/src/java/org/apache/fop/image/RegisterableImageProvider.java b/src/java/org/apache/fop/fonts/SingleByteEncoding.java index fd79ebd91..ac7241e24 100644 --- a/src/java/org/apache/fop/image/RegisterableImageProvider.java +++ b/src/java/org/apache/fop/fonts/SingleByteEncoding.java @@ -16,34 +16,35 @@ */ /* $Id$ */ - -package org.apache.fop.image; + +package org.apache.fop.fonts; /** - * This interface is used to dynamically register FopImage implementations. - * <p> - * NOTE: Please don't rely on this interface too much. It is a temporary measure - * until the whole image package can be redesigned. The redesign will likely - * provide a different mechanism to dynamically register new implementations. + * The interface defines a 1-byte character encoding (with 256 characters). */ -public interface RegisterableImageProvider { +public interface SingleByteEncoding { + + /** Code point that is used if no code point for a specific character has been found. */ + char NOT_FOUND_CODE_POINT = '\0'; /** - * Returns the MIME type the implementation supports. - * @return the MIME type - */ - String getSupportedMimeType(); - - /** - * Returns the name of the implementation. - * @return the name + * Returns the encoding's name. + * @return the name of the encoding */ String getName(); - + /** - * Returns the fully qualified class name for the implementing class. - * @return the class name + * Maps a Unicode character to a code point in the encoding. + * @param c the Unicode character to map + * @return the code point in the encoding or 0 (=.notdef) if not found */ - String getClassName(); + char mapChar(char c); + /** + * Returns the array of character names for this encoding. + * @return the array of character names + * (unmapped code points are represented by a ".notdef" value) + */ + String[] getCharNameMap(); + } diff --git a/src/java/org/apache/fop/fonts/SingleByteFont.java b/src/java/org/apache/fop/fonts/SingleByteFont.java index 814d05a2c..ac12b7615 100644 --- a/src/java/org/apache/fop/fonts/SingleByteFont.java +++ b/src/java/org/apache/fop/fonts/SingleByteFont.java @@ -19,6 +19,8 @@ package org.apache.fop.fonts; +import java.util.List; +import java.util.Map; import java.util.Set; import org.apache.commons.logging.Log; @@ -31,14 +33,21 @@ import org.apache.xmlgraphics.fonts.Glyphs; */ public class SingleByteFont extends CustomFont { + /** Code point that is used if no code point for a specific character has been found. */ + public static final char NOT_FOUND = '#'; + /** logger */ private static Log log = LogFactory.getLog(SingleByteFont.class); - private CodePointMapping mapping; + private SingleByteEncoding mapping; private int[] width = null; private Set warnedChars; + + private Map unencodedCharacters; + //Map<Character, UnencodedCharacter> + private List additionalEncodings; /** * Main constructor. @@ -54,7 +63,7 @@ public class SingleByteFont extends CustomFont { } /** {@inheritDoc} */ - public String getEncoding() { + public String getEncodingName() { return this.mapping.getName(); } @@ -62,18 +71,28 @@ public class SingleByteFont extends CustomFont { * Returns the code point mapping (encoding) of this font. * @return the code point mapping */ - public CodePointMapping getCodePointMapping() { + public SingleByteEncoding getEncoding() { return this.mapping; } /** {@inheritDoc} */ public int getWidth(int i, int size) { - int idx = i - getFirstChar(); - if (idx >= 0 && idx < width.length) { - return size * width[i - getFirstChar()]; - } else { - return 0; + if (i < 256) { + int idx = i - getFirstChar(); + if (idx >= 0 && idx < width.length) { + return size * width[i - getFirstChar()]; + } + } else if (this.additionalEncodings != null) { + int encodingIndex = (i / 256) - 1; + SimpleSingleByteEncoding encoding = getAdditionalEncoding(encodingIndex); + int codePoint = i % 256; + NamedCharacter nc = encoding.getCharacterForIndex(codePoint); + UnencodedCharacter uc + = (UnencodedCharacter)this.unencodedCharacters.get( + new Character(nc.getSingleUnicodeValue())); + return size * uc.getWidth(); } + return 0; } /** {@inheritDoc} */ @@ -87,30 +106,80 @@ public class SingleByteFont extends CustomFont { public char mapChar(char c) { notifyMapOperation(); char d = mapping.mapChar(c); - if (d != 0) { + if (d != SingleByteEncoding.NOT_FOUND_CODE_POINT) { return d; - } else { - Character ch = new Character(c); - if (warnedChars == null) { - warnedChars = new java.util.HashSet(); + } + + //Check unencoded characters which are available in the font by character name + d = mapUnencodedChar(c); + if (d != SingleByteEncoding.NOT_FOUND_CODE_POINT) { + return d; + } + + //Give up, character is not available + Character ch = new Character(c); + if (warnedChars == null) { + warnedChars = new java.util.HashSet(); + } + if (warnedChars.size() < 8 && !warnedChars.contains(ch)) { + warnedChars.add(ch); + if (warnedChars.size() == 8) { + log.warn("Many requested glyphs are not available in font " + getFontName()); + } else { + log.warn("Glyph " + (int)c + " (0x" + Integer.toHexString(c) + + ", " + Glyphs.charToGlyphName(c) + + ") not available in font " + getFontName()); } - if (warnedChars.size() < 8 && !warnedChars.contains(ch)) { - warnedChars.add(ch); - if (warnedChars.size() == 8) { - log.warn("Many requested glyphs are not available in font " + getFontName()); - } else { - log.warn("Glyph " + (int)c + " (0x" + Integer.toHexString(c) - + ", " + Glyphs.charToGlyphName(c) - + ") not available in font " + getFontName()); + } + return NOT_FOUND; + } + + private char mapUnencodedChar(char ch) { + if (this.unencodedCharacters != null) { + UnencodedCharacter unencoded + = (UnencodedCharacter)this.unencodedCharacters.get(new Character(ch)); + if (unencoded != null) { + if (this.additionalEncodings == null) { + this.additionalEncodings = new java.util.ArrayList(); + } + SimpleSingleByteEncoding encoding = null; + char mappedStart = 0; + int additionalsCount = this.additionalEncodings.size(); + for (int i = 0; i < additionalsCount; i++) { + mappedStart += 256; + encoding = getAdditionalEncoding(i); + char alt = encoding.mapChar(ch); + if (alt != 0) { + return (char)(mappedStart + alt); + } + } + if (encoding != null && encoding.isFull()) { + encoding = null; } + if (encoding == null) { + encoding = new SimpleSingleByteEncoding( + getFontName() + "EncodingSupp" + (additionalsCount + 1)); + this.additionalEncodings.add(encoding); + mappedStart += 256; + } + return (char)(mappedStart + encoding.addCharacter(unencoded.getCharacter())); } - return '#'; } + return 0; } /** {@inheritDoc} */ public boolean hasChar(char c) { - return (mapping.mapChar(c) > 0); + char d = mapping.mapChar(c); + if (d != SingleByteEncoding.NOT_FOUND_CODE_POINT) { + return true; + } + //Check unencoded characters which are available in the font by character name + d = mapUnencodedChar(c); + if (d != SingleByteEncoding.NOT_FOUND_CODE_POINT) { + return true; + } + return false; } /* ---- single byte font specific setters --- */ @@ -146,13 +215,106 @@ public class SingleByteFont extends CustomFont { /** * Sets a width for a character. * @param index index of the character - * @param width the width of the character + * @param w the width of the character */ - public void setWidth(int index, int width) { + public void setWidth(int index, int w) { if (this.width == null) { this.width = new int[getLastChar() - getFirstChar() + 1]; } - this.width[index - getFirstChar()] = width; + this.width[index - getFirstChar()] = w; + } + + /** + * Adds an unencoded character (one that is not supported by the primary encoding). + * @param ch the named character + * @param width the width of the character + */ + public void addUnencodedCharacter(NamedCharacter ch, int width) { + if (this.unencodedCharacters == null) { + this.unencodedCharacters = new java.util.HashMap(); + } + if (ch.hasSingleUnicodeValue()) { + UnencodedCharacter uc = new UnencodedCharacter(ch, width); + this.unencodedCharacters.put(new Character(ch.getSingleUnicodeValue()), uc); + } else { + //Cannot deal with unicode sequences, so ignore this character + } + } + + /** + * Indicates whether the encoding has additional encodings besides the primary encoding. + * @return true if there are additional encodings. + */ + public boolean hasAdditionalEncodings() { + return (this.additionalEncodings != null) && (this.additionalEncodings.size() > 0); + } + + /** + * Returns the number of additional encodings this single-byte font maintains. + * @return the number of additional encodings + */ + public int getAdditionalEncodingCount() { + if (hasAdditionalEncodings()) { + return this.additionalEncodings.size(); + } else { + return 0; + } + } + + /** + * Returns an additional encoding. + * @param index the index of the additional encoding + * @return the additional encoding + * @throws IndexOutOfBoundsException if the index is out of bounds + */ + public SimpleSingleByteEncoding getAdditionalEncoding(int index) + throws IndexOutOfBoundsException { + if (hasAdditionalEncodings()) { + return (SimpleSingleByteEncoding)this.additionalEncodings.get(index); + } else { + throw new IndexOutOfBoundsException("No additional encodings available"); + } + } + + /** + * Returns an array with the widths for an additional encoding. + * @param index the index of the additional encoding + * @return the width array + */ + public int[] getAdditionalWidths(int index) { + SimpleSingleByteEncoding enc = getAdditionalEncoding(index); + int[] arr = new int[enc.getLastChar() - enc.getFirstChar() + 1]; + for (int i = 0, c = arr.length; i < c; i++) { + NamedCharacter nc = enc.getCharacterForIndex(enc.getFirstChar() + i); + UnencodedCharacter uc = (UnencodedCharacter)this.unencodedCharacters.get( + new Character(nc.getSingleUnicodeValue())); + arr[i] = uc.getWidth(); + } + return arr; + } + + private static final class UnencodedCharacter { + + private NamedCharacter character; + private int width; + + public UnencodedCharacter(NamedCharacter character, int width) { + this.character = character; + this.width = width; + } + + public NamedCharacter getCharacter() { + return this.character; + } + + public int getWidth() { + return this.width; + } + + /** {@inheritDoc} */ + public String toString() { + return getCharacter().toString(); + } } } diff --git a/src/java/org/apache/fop/fonts/Typeface.java b/src/java/org/apache/fop/fonts/Typeface.java index 3bc3be772..173d2e8a3 100644 --- a/src/java/org/apache/fop/fonts/Typeface.java +++ b/src/java/org/apache/fop/fonts/Typeface.java @@ -19,11 +19,8 @@ package org.apache.fop.fonts; -// FOP - - /** - * Base class for PDF font classes + * Base class for font classes */ public abstract class Typeface implements FontMetrics { @@ -37,7 +34,7 @@ public abstract class Typeface implements FontMetrics { * Get the encoding of the font. * @return the encoding */ - public abstract String getEncoding(); + public abstract String getEncodingName(); /** * Map a Unicode character to a code point in the font. diff --git a/src/java/org/apache/fop/fonts/truetype/TTFSubSetFile.java b/src/java/org/apache/fop/fonts/truetype/TTFSubSetFile.java index 99fd10315..d593c4544 100644 --- a/src/java/org/apache/fop/fonts/truetype/TTFSubSetFile.java +++ b/src/java/org/apache/fop/fonts/truetype/TTFSubSetFile.java @@ -592,8 +592,8 @@ public class TTFSubSetFile extends TTFFile { + mtxTab[origIndex.intValue()].getOffset()) < 0) { // origIndex is a composite glyph allComposites.put(origIndex, glyphs.get(origIndex)); - List composites = - getIncludedGlyphs(in, (int)entry.getOffset(), + List composites + = getIncludedGlyphs(in, (int)entry.getOffset(), origIndex); // Iterate through all composites pointed to @@ -651,6 +651,9 @@ public class TTFSubSetFile extends TTFFile { if (!checkTTC(in, name)) { throw new IOException("Failed to read font"); } + + //Copy the Map as we're going to modify it + Map subsetGlyphs = new java.util.HashMap(glyphs); output = new byte[in.getFileSize()]; @@ -661,14 +664,14 @@ public class TTFSubSetFile extends TTFFile { readHorizontalMetrics(in); readIndexToLocation(in); - scanGlyphs(in, glyphs); + scanGlyphs(in, subsetGlyphs); createDirectory(); // Create the TrueType header and directory createHead(in); - createHhea(in, glyphs.size()); // Create the hhea table - createHmtx(in, glyphs); // Create hmtx table - createMaxp(in, glyphs.size()); // copy the maxp table + createHhea(in, subsetGlyphs.size()); // Create the hhea table + createHmtx(in, subsetGlyphs); // Create hmtx table + createMaxp(in, subsetGlyphs.size()); // copy the maxp table boolean optionalTableFound; optionalTableFound = createCvt(in); // copy the cvt table @@ -689,8 +692,8 @@ public class TTFSubSetFile extends TTFFile { log.debug("TrueType: prep table not present. Skipped."); } - createLoca(glyphs.size()); // create empty loca table - createGlyf(in, glyphs); //create glyf table and update loca table + createLoca(subsetGlyphs.size()); // create empty loca table + createGlyf(in, subsetGlyphs); //create glyf table and update loca table pad4(); createCheckSumAdjustment(); diff --git a/src/java/org/apache/fop/fonts/type1/AFMCharMetrics.java b/src/java/org/apache/fop/fonts/type1/AFMCharMetrics.java index 1b7f814b8..758078af4 100644 --- a/src/java/org/apache/fop/fonts/type1/AFMCharMetrics.java +++ b/src/java/org/apache/fop/fonts/type1/AFMCharMetrics.java @@ -21,6 +21,8 @@ package org.apache.fop.fonts.type1; import java.awt.geom.RectangularShape; +import org.apache.fop.fonts.NamedCharacter; + /** * Holds the metrics of a single character from an AFM file. @@ -28,8 +30,7 @@ import java.awt.geom.RectangularShape; public class AFMCharMetrics { private int charCode = -1; - private String unicodeSequence; - private String charName; + private NamedCharacter character; private double widthX; private double widthY; private RectangularShape bBox; @@ -59,36 +60,45 @@ public class AFMCharMetrics { } /** - * Returns the Unicode sequence for this character. - * @return the Unicode characters - * (or null if no such Unicode sequence exists for this character) + * Returns the named character represented by this instance. + * @return the named character (or null if no named character is associated) */ - public String getUnicodeSequence() { - return this.unicodeSequence; + public NamedCharacter getCharacter() { + return this.character; + } + + /** + * Sets the named character represented by this instance. + * @param ch the named character + */ + public void setCharacter(NamedCharacter ch) { + this.character = ch; } /** - * Sets the Unicode sequence for this character. + * Sets the named character represented by this instance. + * @param charName the character name (as defined in the Adobe glyph list) * @param unicodeSequence the Unicode sequence */ - public void setUnicodeSequence(String unicodeSequence) { - this.unicodeSequence = unicodeSequence; + public void setCharacter(String charName, String unicodeSequence) { + setCharacter(new NamedCharacter(charName, unicodeSequence)); } /** - * Returns the PostScript character name. - * @return the charName + * Returns the Unicode sequence for this character. + * @return the Unicode characters + * (or null if no such Unicode sequence exists for this character) */ - public String getCharName() { - return charName; + public String getUnicodeSequence() { + return (getCharacter() != null ? getCharacter().getUnicodeSequence() : null); } /** - * Sets the PostScript character name. - * @param charName the charName to set + * Returns the PostScript character name. + * @return the charName (or null if no character name is associated) */ - public void setCharName(String charName) { - this.charName = charName; + public String getCharName() { + return (getCharacter() != null ? getCharacter().getName() : null); } /** diff --git a/src/java/org/apache/fop/fonts/type1/AFMFile.java b/src/java/org/apache/fop/fonts/type1/AFMFile.java index b51485485..6a1973843 100644 --- a/src/java/org/apache/fop/fonts/type1/AFMFile.java +++ b/src/java/org/apache/fop/fonts/type1/AFMFile.java @@ -26,7 +26,6 @@ import java.util.Iterator; import java.util.List; import java.util.Map; -import org.apache.xmlgraphics.fonts.Glyphs; import org.apache.xmlgraphics.java2d.Dimension2DDouble; /** @@ -315,15 +314,8 @@ public class AFMFile { public void addCharMetrics(AFMCharMetrics metrics) { String name = metrics.getCharName(); if (metrics.getUnicodeSequence() == null) { - if (name != null) { - String u = Glyphs.getUnicodeSequenceForGlyphName(metrics.getCharName()); - if (u != null) { - metrics.setUnicodeSequence(u); - } - } else { - //Ignore as no Unicode assignment is possible - return; - } + //Ignore as no Unicode assignment is possible + return; } this.charMetrics.add(metrics); if (name != null) { diff --git a/src/java/org/apache/fop/fonts/type1/AFMParser.java b/src/java/org/apache/fop/fonts/type1/AFMParser.java index bb7ea3d30..2e63ea729 100644 --- a/src/java/org/apache/fop/fonts/type1/AFMParser.java +++ b/src/java/org/apache/fop/fonts/type1/AFMParser.java @@ -31,6 +31,8 @@ import java.util.Stack; import org.apache.commons.io.IOUtils; +import org.apache.fop.fonts.NamedCharacter; + /** * Parses the contents of a Type 1 AFM font metrics file into an object structure ({@link AFMFile}). */ @@ -126,7 +128,7 @@ public class AFMParser { VALUE_PARSERS.put(W, new NotImplementedYet(W)); VALUE_PARSERS.put(W0, new NotImplementedYet(W0)); VALUE_PARSERS.put(W1, new NotImplementedYet(W1)); - VALUE_PARSERS.put(N, new StringSetter("CharName")); + VALUE_PARSERS.put(N, new NamedCharacterSetter("Character")); VALUE_PARSERS.put(B, new CharBBox()); VALUE_PARSERS.put(START_TRACK_KERN, new NotImplementedYet(START_TRACK_KERN)); VALUE_PARSERS.put(END_TRACK_KERN, new NotImplementedYet(END_TRACK_KERN)); @@ -379,6 +381,19 @@ public class AFMParser { } } + private static class NamedCharacterSetter extends BeanSetter { + + public NamedCharacterSetter(String variable) { + super(variable); + } + + public void parse(String line, int startpos, Stack stack) throws IOException { + NamedCharacter ch = new NamedCharacter(getStringValue(line, startpos)); + Object obj = stack.peek(); + setValue(obj, ch); + } + } + private static class NumberSetter extends BeanSetter { public NumberSetter(String variable) { super(variable); diff --git a/src/java/org/apache/fop/fonts/type1/Type1FontLoader.java b/src/java/org/apache/fop/fonts/type1/Type1FontLoader.java index 910ee82cc..8cf6f2371 100644 --- a/src/java/org/apache/fop/fonts/type1/Type1FontLoader.java +++ b/src/java/org/apache/fop/fonts/type1/Type1FontLoader.java @@ -108,6 +108,11 @@ public class Type1FontLoader extends FontLoader { throw new java.io.FileNotFoundException( "Neither an AFM nor a PFM file was found for " + this.fontFileURI); } + if (pfm == null) { + //Cannot do without the PFM for now + throw new java.io.FileNotFoundException( + "No PFM file was found for " + this.fontFileURI); + } buildFont(afm, pfm); this.loaded = true; } @@ -122,12 +127,17 @@ public class Type1FontLoader extends FontLoader { singleFont.setEmbedFileName(this.fontFileURI); returnFont = singleFont; + handleEncoding(afm, pfm); + handleFontName(afm, pfm); + handleMetrics(afm, pfm); + } + + private void handleEncoding(AFMFile afm, PFMFile pfm) { //Encoding if (afm != null) { String encoding = afm.getEncodingScheme(); if ("AdobeStandardEncoding".equals(encoding)) { - //Use WinAnsi in this case as it better fits the usual character set people need - singleFont.setEncoding(CodePointMapping.WIN_ANSI_ENCODING); + singleFont.setEncoding(CodePointMapping.STANDARD_ENCODING); } else { String effEncodingName; if ("FontSpecific".equals(encoding)) { @@ -142,6 +152,14 @@ public class Type1FontLoader extends FontLoader { CodePointMapping mapping = buildCustomEncoding(effEncodingName, afm); singleFont.setEncoding(mapping); } + List charMetrics = afm.getCharMetrics(); + for (int i = 0, c = afm.getCharCount(); i < c; i++) { + AFMCharMetrics metrics = (AFMCharMetrics)charMetrics.get(i); + if (!metrics.hasCharCode() && metrics.getCharacter() != null) { + singleFont.addUnencodedCharacter(metrics.getCharacter(), + (int)Math.round(metrics.getWidthX())); + } + } } else { if (pfm.getCharSet() >= 0 && pfm.getCharSet() <= 2) { singleFont.setEncoding(pfm.getCharSetName() + "Encoding"); @@ -151,7 +169,9 @@ public class Type1FontLoader extends FontLoader { singleFont.setEncoding("WinAnsiEncoding"); //Try fallback, no guarantees! } } - + } + + private void handleFontName(AFMFile afm, PFMFile pfm) { //Font name if (afm != null) { returnFont.setFontName(afm.getFontName()); //PostScript font name @@ -168,7 +188,9 @@ public class Type1FontLoader extends FontLoader { names.add(pfm.getWindowsName()); //emulate afm.getFamilyName() returnFont.setFamilyNames(names); } - + } + + private void handleMetrics(AFMFile afm, PFMFile pfm) { //Basic metrics if (afm != null) { if (afm.getCapHeight() != null) { @@ -268,6 +290,7 @@ public class Type1FontLoader extends FontLoader { if (afm != null) { //TODO returnFont.setFlags(flags); + returnFont.setFirstChar(afm.getFirstChar()); returnFont.setLastChar(afm.getLastChar()); Iterator iter = afm.getCharMetrics().iterator(); @@ -279,7 +302,6 @@ public class Type1FontLoader extends FontLoader { } returnFont.replaceKerningMap(afm.createXKerningMapEncoded()); } else { - returnFont.setFlags(pfm.getFlags()); returnFont.setFirstChar(pfm.getFirstChar()); returnFont.setLastChar(pfm.getLastChar()); for (short i = pfm.getFirstChar(); i <= pfm.getLastChar(); i++) { @@ -287,6 +309,7 @@ public class Type1FontLoader extends FontLoader { } returnFont.replaceKerningMap(pfm.getKerning()); } + returnFont.setFlags(pfm.getFlags()); } private CodePointMapping buildCustomEncoding(String encodingName, AFMFile afm) { diff --git a/src/java/org/apache/fop/image/AbstractFopImage.java b/src/java/org/apache/fop/image/AbstractFopImage.java deleted file mode 100644 index 68949b9c9..000000000 --- a/src/java/org/apache/fop/image/AbstractFopImage.java +++ /dev/null @@ -1,376 +0,0 @@ -/* - * 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.image; - -// Java -import java.awt.color.ColorSpace; -import java.awt.color.ICC_ColorSpace; -import java.awt.color.ICC_Profile; -import java.io.InputStream; -import java.awt.Color; - -import org.apache.commons.io.IOUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.apache.fop.datatypes.Length; - -/** - * Base class to implement the FopImage interface. - * - * @see FopImage - */ -public abstract class AbstractFopImage implements FopImage { - - /** - * logging instance - */ - protected static Log log = LogFactory.getLog(AbstractFopImage.class); - - /** - * Keeps track of what has been loaded. - */ - protected int loaded = 0; - - /** - * Image width (in pixel). - */ - protected int width = 0; - - /** - * Image height (in pixel). - */ - protected int height = 0; - - /** Horizontal bitmap resolution (in dpi) */ - protected double dpiHorizontal = 72.0f; - - /** Vertical bitmap resolution (in dpi) */ - protected double dpiVertical = 72.0f; - - /** - * Image input stream. - */ - protected InputStream inputStream = null; - - /** - * ImageReader object (to obtain image header informations). - */ - protected FopImage.ImageInfo imageInfo = null; - - /** - * Image color space (java.awt.color.ColorSpace). - */ - protected ColorSpace colorSpace = null; - - /** - * Bits per pixel. - */ - protected int bitsPerPixel = 0; - - /** - * Image data (pixels, uncompressed). - */ - protected byte[] bitmaps = null; - - /** - * Image data (undecoded, compressed, for image formats that can be embedded without decoding. - */ - protected byte[] raw = null; - - /** - * Image transparency. - */ - protected boolean isTransparent = false; - - /** - * Transparent color (java.awt.Color). - */ - protected Color transparentColor = null; - - /** - * Photoshop generated CMYK JPEGs are inverted. - */ - protected boolean invertImage = false; - - /** - * Constructor. - * Construct a new FopImage object and initialize its default properties: - * <UL> - * <LI>image width - * <LI>image height - * </UL> - * The image data isn't kept in memory. - * @param info image information - */ - public AbstractFopImage(FopImage.ImageInfo info) { - this.inputStream = info.inputStream; - this.imageInfo = info; - if (this.imageInfo.width != -1) { - width = imageInfo.width; - height = imageInfo.height; - dpiHorizontal = imageInfo.dpiHorizontal; - dpiVertical = imageInfo.dpiVertical; - loaded = loaded | DIMENSIONS; - } - } - - /** - * Get the mime type for this image. - * - * @return the mime type for the image - */ - public String getMimeType() { - return imageInfo.mimeType; - } - - /** {@inheritDoc} */ - public String getOriginalURI() { - return this.imageInfo.originalURI; - } - - /** - * Load image data and initialize its properties. - * - * @param type the type of loading to do - * @return true if the loading was successful - */ - public synchronized boolean load(int type) { - if ((loaded & type) != 0) { - return true; - } - boolean success = true; - if (((type & DIMENSIONS) != 0) && ((loaded & DIMENSIONS) == 0)) { - success = success && loadDimensions(); - - if (!success) { - return false; - } - loaded = loaded | DIMENSIONS; - } - if (((type & BITMAP) != 0) && ((loaded & BITMAP) == 0)) { - success = success && loadBitmap(); - if (success) { - loaded = loaded | BITMAP; - } - } - if (((type & ORIGINAL_DATA) != 0) && ((loaded & ORIGINAL_DATA) == 0)) { - success = success && loadOriginalData(); - if (success) { - loaded = loaded | ORIGINAL_DATA; - } - } - return success; - } - - /** - * Load the dimensions of the image. - * All implementations should override this to get and - * return the dimensions. - * - * @return true if the loading was successful - */ - protected boolean loadDimensions() { - return false; - } - - /** - * Load a bitmap array of the image. - * If the renderer requires a bitmap image then the - * implementations should override this to load the bitmap. - * - * @return true if the loading was successful - */ - protected boolean loadBitmap() { - return false; - } - - /** - * Load the original image data. - * In some cases the original data can be used by the renderer. - * This should load the data and any other associated information. - * - * @return true if the loading was successful - */ - protected boolean loadOriginalData() { - return false; - } - - /** - * Load the original image data. This is generic code for use by any - * subclass that wants to use this from a loadOriginalData() implementation. - * - * @return true if the loading was successful - */ - protected boolean loadDefaultOriginalData() { - if (inputStream == null) { - throw new IllegalStateException("inputStream is already null or was never set"); - } - try { - this.raw = IOUtils.toByteArray(inputStream); - } catch (java.io.IOException ex) { - log.error("Error while reading raw image: " + ex.getMessage(), ex); - return false; - } finally { - IOUtils.closeQuietly(inputStream); - inputStream = null; - } - return true; - } - - /** - * @return the image width (in pixels) - */ - public int getWidth() { - return this.width; - } - - /** - * @return the image height (in pixels) - */ - public int getHeight() { - return this.height; - } - - /** {@inheritDoc} */ - public int getIntrinsicWidth() { - return (int)(getWidth() * 72000 / getHorizontalResolution()); - } - - /** {@inheritDoc} */ - public int getIntrinsicHeight() { - return (int)(getHeight() * 72000 / getVerticalResolution()); - } - - /** {@inheritDoc} */ - public Length getIntrinsicAlignmentAdjust() { - return this.imageInfo.alignmentAdjust; - } - - /** {@inheritDoc} */ - public double getHorizontalResolution() { - return this.dpiHorizontal; - } - - /** {@inheritDoc} */ - public double getVerticalResolution() { - return this.dpiVertical; - } - - /** - * Return the image color space. - * @return the image color space (java.awt.color.ColorSpace) - */ - public ColorSpace getColorSpace() { - return this.colorSpace; - } - - /** - * Get ICC profile for this image. - * @return the icc profile or null if not applicable - */ - public ICC_Profile getICCProfile() { - if (this.colorSpace != null && this.colorSpace instanceof ICC_ColorSpace) { - return ((ICC_ColorSpace)this.colorSpace).getProfile(); - } - return null; - } - - /** - * Return the number of bits per pixel. - * @return number of bits per pixel - */ - public int getBitsPerPixel() { - return this.bitsPerPixel; - } - - /** - * Return the image transparency. - * @return true if the image is transparent - */ - public boolean isTransparent() { - return this.isTransparent; - } - - /** - * Check if this image has a soft mask. - * - * @return true if the image also has a soft transparency mask - */ - public boolean hasSoftMask() { - return false; - } - - /** - * Get the soft mask. - * The soft mask should have the same bitdepth as the image data. - * - * @return the data array of soft mask values - */ - public byte[] getSoftMask() { - return null; - } - - /** - * Return the transparent color. - * @return the transparent color (java.awt.Color) - */ - public Color getTransparentColor() { - return this.transparentColor; - } - - /** @return true for CMYK images generated by Adobe Photoshop */ - public boolean isInverted() { - return this.invertImage; - } - - /** - * Return the image data (pixels, uncompressed). - * @return the image data - */ - public byte[] getBitmaps() { - return this.bitmaps; - } - - /** - * Return the image data size (number of bytes taken up by the uncompressed pixels). - * @return the image data size - */ - public int getBitmapsSize() { - return (bitmaps != null ? bitmaps.length : 0); - } - - /** - * Return the original image data (compressed). - * @return the original image data - */ - public byte[] getResourceBytes() { - return raw; - } - - /** - * Return the original image data size (compressed). - * @return the original image data size - */ - public int getResourceBytesSize() { - return (raw != null ? raw.length : 0); - } - -} - diff --git a/src/java/org/apache/fop/image/BmpImage.java b/src/java/org/apache/fop/image/BmpImage.java deleted file mode 100644 index 5ebf522e9..000000000 --- a/src/java/org/apache/fop/image/BmpImage.java +++ /dev/null @@ -1,220 +0,0 @@ -/* - * 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.image; - -// Java -import java.io.IOException; -import java.awt.color.ColorSpace; - -import org.apache.commons.io.IOUtils; - -/** - * Bitmap image. - * This supports loading a bitmap image into bitmap data. - * - * @see AbstractFopImage - * @see FopImage - */ -public class BmpImage extends AbstractFopImage { - /** - * Create a bitmap image with the image data. - * - * @param imgInfo the image information - */ - public BmpImage(FopImage.ImageInfo imgInfo) { - super(imgInfo); - } - - /** - * Load the bitmap. - * This laods the bitmap data from the bitmap image. - * - * @return true if it was loaded successfully - */ - protected boolean loadBitmap() { - int wpos = 18; - int hpos = 22; // offset positioning for w and height in bmp files - int[] headermap = new int[54]; - int filepos = 0; - byte[] palette = null; - try { - boolean eof = false; - while ((!eof) && (filepos < 54)) { - int input = inputStream.read(); - if (input == -1) { - eof = true; - } else { - headermap[filepos++] = input; - } - } - - if (headermap[28] == 4 || headermap[28] == 8) { - int palettesize = 1 << headermap[28]; - palette = new byte[palettesize * 3]; - int countr = 0; - while (!eof && countr < palettesize) { - int count2 = 2; - while (!eof && count2 >= -1) { - int input = inputStream.read(); - if (input == -1) { - eof = true; - } else if (count2 >= 0) { - palette[countr * 3 + count2] = (byte)(input & 0xFF); - } - count2--; - filepos++; - } - countr++; - } - } - } catch (IOException ex) { - log.error("Error while loading image (Bmp): " + ex.getMessage(), ex); - IOUtils.closeQuietly(inputStream); - inputStream = null; - return false; - } - // gets h & w from headermap - this.width = headermap[wpos] - + headermap[wpos + 1] * 256 - + headermap[wpos + 2] * 256 * 256 - + headermap[wpos + 3] * 256 * 256 * 256; - this.height = headermap[hpos] - + headermap[hpos + 1] * 256 - + headermap[hpos + 2] * 256 * 256 - + headermap[hpos + 3] * 256 * 256 * 256; - - int imagestart = headermap[10] - + headermap[11] * 256 - + headermap[12] * 256 * 256 - + headermap[13] * 256 * 256 * 256; - this.bitsPerPixel = headermap[28]; - this.colorSpace = ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB); - int bytes = 0; - if (this.bitsPerPixel == 1) { - bytes = (this.width + 7) / 8; - } else if (this.bitsPerPixel == 24) { - bytes = this.width * 3; - } else if (this.bitsPerPixel == 4 || this.bitsPerPixel == 8) { - bytes = this.width / (8 / this.bitsPerPixel); - } else { - log.error("Image (" + "" - + ") has " + this.bitsPerPixel - + " which is not a supported BMP format."); - return false; - } - if ((bytes & 0x03) != 0) { - bytes |= 0x03; - bytes++; - } - - // Should take care of the ColorSpace and bitsPerPixel - this.bitmaps = new byte[this.width * this.height * 3]; - - int[] temp = new int[bytes * this.height]; - try { - int input; - int count = 0; - inputStream.skip((long)(imagestart - filepos)); - while ((input = inputStream.read()) != -1) { - if (count >= temp.length) { - log.warn("Data longer than expected while loading image"); - break; - } else { - temp[count++] = input; - } - } - } catch (IOException ex) { - log.error("Error while loading image (Bmp): " + ex.getMessage(), ex); - return false; - } finally { - IOUtils.closeQuietly(inputStream); - inputStream = null; - } - - for (int i = 0; i < this.height; i++) { - int x = 0; - int j = 0; - while (j < bytes) { - int p = temp[(this.height - i - 1) * bytes + j]; - - if (this.bitsPerPixel == 24 && x < this.width) { - int countr = 2; - do { - this.bitmaps[3 * (i * this.width + x) + countr] - = (byte)(temp[(this.height - i - 1) * bytes + j] & 0xFF); - j++; - } while (--countr >= 0) - ; - x++; - } else if (this.bitsPerPixel == 1) { - for (int countr = 0; - countr < 8 && x < this.width; countr++) { - if ((p & 0x80) != 0) { - this.bitmaps[3 * (i * this.width + x)] = (byte) 0xFF; - this.bitmaps[3 * (i * this.width + x) + 1] = (byte) 0xFF; - this.bitmaps[3 * (i * this.width + x) + 2] = (byte) 0xFF; - } else { - this.bitmaps[3 * (i * this.width + x)] = (byte) 0; - this.bitmaps[3 * (i * this.width + x) + 1] = (byte) 0; - this.bitmaps[3 * (i * this.width + x) + 2] = (byte) 0; - } - p <<= 1; - x++; - } - j++; - } else if (this.bitsPerPixel == 4) { - for (int countr = 0; - countr < 2 && x < this.width; countr++) { - int pal = ((p & 0xF0) >> 4) * 3; - this.bitmaps[3 * (i * this.width + x)] = palette[pal]; - this.bitmaps[3 * (i * this.width + x) + 1] = palette[pal + 1]; - this.bitmaps[3 * (i * this.width + x) + 2] = palette[pal + 2]; - p <<= 4; - x++; - } - j++; - } else if (this.bitsPerPixel == 8) { - if (x < this.width) { - p *= 3; - this.bitmaps[3 * (i * this.width + x)] = palette[p]; - this.bitmaps[3 * (i * this.width + x) + 1] = palette[p + 1]; - this.bitmaps[3 * (i * this.width + x) + 2] = palette[p + 2]; - j++; - x++; - } else { - j = bytes; - } - } else { - j++; - } - } - } - - // This seems really strange to me, but I noticed that - // JimiImage hardcodes bitsPerPixel to 8. If I do not - // do this Acrobat is unable to read the resultant PDF, - // so we will hardcode this... - this.bitsPerPixel = 8; - - return true; - } - -} - diff --git a/src/java/org/apache/fop/image/EPSImage.java b/src/java/org/apache/fop/image/EPSImage.java deleted file mode 100644 index ab708f2a4..000000000 --- a/src/java/org/apache/fop/image/EPSImage.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * 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.image; - - -/** - * EPS image handler. - * This handles the Encapulated PostScript images. - * It gets the dimensions and original data from the analyser. - * - * @see AbstractFopImage - * @see FopImage - */ -public class EPSImage extends AbstractFopImage { - - private String docName; - private int[] bbox; - - private EPSData epsData = null; - - /** - * Create an EPS image with the image information. - * - * @param imgInfo the information containing the data and bounding box - */ - public EPSImage(FopImage.ImageInfo imgInfo) { - super(imgInfo); - init(""); - if (imgInfo.data instanceof EPSData) { - epsData = (EPSData) imgInfo.data; - bbox = new int[4]; - bbox[0] = (int) epsData.bbox[0]; - bbox[1] = (int) epsData.bbox[1]; - bbox[2] = (int) epsData.bbox[2]; - bbox[3] = (int) epsData.bbox[3]; - - loaded = loaded | ORIGINAL_DATA; - } - } - - /** - * Initialize docName and bounding box. - * @param name the document name - */ - private void init(String name) { - bbox = new int[4]; - bbox[0] = 0; - bbox[1] = 0; - bbox[2] = 0; - bbox[3] = 0; - - docName = name; - } - - /** - * Return the name of the eps - * @return the name of the eps - */ - public String getDocName() { - return docName; - } - - /** - * Return the bounding box - * @return an int array containing the bounding box - */ - public int[] getBBox() { - return bbox; - } - - /** - * Get the eps image. - * - * @return the original eps image data - */ - public byte[] getEPSImage() { - if (epsData.epsFile == null) { - //log.error("ERROR LOADING EXTERNAL EPS"); - } - return epsData.epsFile; - } - - /** - * Data for EPS image. - */ - public static class EPSData { - public long[] bbox; - public boolean isAscii; // True if plain ascii eps file - - // offsets if not ascii - public long psStart = 0; - public long psLength = 0; - public long wmfStart = 0; - public long wmfLength = 0; - public long tiffStart = 0; - public long tiffLength = 0; - - /** raw eps file */ - public byte[] rawEps; - /** eps part */ - public byte[] epsFile; - public byte[] preview = null; - } - -} diff --git a/src/java/org/apache/fop/image/FopImage.java b/src/java/org/apache/fop/image/FopImage.java deleted file mode 100644 index abe11ef25..000000000 --- a/src/java/org/apache/fop/image/FopImage.java +++ /dev/null @@ -1,207 +0,0 @@ -/* - * 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.image; - -import java.io.InputStream; -import java.awt.color.ColorSpace; -import java.awt.color.ICC_Profile; -import java.awt.Color; - -import org.apache.fop.datatypes.Length; - -/** - * Fop image interface for loading images. - */ -public interface FopImage { - /** - * Flag for loading dimensions. - */ - int DIMENSIONS = 1; - - /** - * Flag for loading original data. - */ - int ORIGINAL_DATA = 2; - - /** - * Flag for loading bitmap data. - */ - int BITMAP = 4; - - /** - * Get the mime type of this image. - * This is used so that when reading from the image it knows - * what type of image it is. - * - * @return the mime type string - */ - String getMimeType(); - - /** @return the original URI used to access this image. */ - String getOriginalURI(); - - /** - * Load particular inforamtion for this image - * This must be called before attempting to get - * the information. - * - * @param type the type of loading required - * @return boolean true if the information could be loaded - */ - boolean load(int type); - - /** - * Returns the image width. - * @return the width in pixels - */ - int getWidth(); - - /** - * Returns the image height. - * @return the height in pixels - */ - int getHeight(); - - /** - * @return the intrinsic image width (in millipoints) - */ - int getIntrinsicWidth(); - - /** - * @return the intrinsic image width (in millipoints) - */ - int getIntrinsicHeight(); - - /** - * @return the intrinsic alignment-adjust value or NULL if the image does - * not have one. - */ - Length getIntrinsicAlignmentAdjust(); - - /** - * @return the horizontal bitmap resolution (in dpi) - */ - double getHorizontalResolution(); - - /** - * @return the vertical bitmap resolution (in dpi) - */ - double getVerticalResolution(); - - /** - * Returns the color space of the image. - * @return the color space - */ - ColorSpace getColorSpace(); - - /** - * Returns the ICC profile. - * @return the ICC profile, null if none is available - */ - ICC_Profile getICCProfile(); - - /** - * Returns the number of bits per pixel for the image. - * @return the number of bits per pixel - */ - int getBitsPerPixel(); - - /** - * Indicates whether the image is transparent. - * @return True if it is transparent - */ - boolean isTransparent(); - - /** - * For transparent images. Returns the transparent color. - * @return the transparent color - */ - Color getTransparentColor(); - - /** - * Indicates whether the image has a Soft Mask (See section 7.5.4 in the - * PDF specs) - * @return True if a Soft Mask exists - */ - boolean hasSoftMask(); - - /** - * For images with a Soft Mask. Returns the Soft Mask as an array. - * @return the Soft Mask - */ - byte[] getSoftMask(); - - /** @return true for CMYK images generated by Adobe Photoshop */ - boolean isInverted(); - - /** - * Returns the decoded and uncompressed image as a array of - * width * height * [colorspace-multiplicator] pixels. - * @return the bitmap - */ - byte[] getBitmaps(); - /** - * Returns the size of the image. - * width * (bitsPerPixel / 8) * height, no ? - * @return the size - */ - int getBitmapsSize(); - - /** - * Returns the encoded/compressed image as an array of bytes. - * @return the raw image - */ - byte[] getResourceBytes(); - - /** - * Returns the number of bytes of the raw image. - * @return the size in bytes - */ - int getResourceBytesSize(); - - /** - * Image info class. - * Information loaded from analyser and passed to image object. - */ - public static class ImageInfo { - /** InputStream to load the image from */ - public InputStream inputStream; - /** Original URI the image was accessed with */ - public String originalURI; - /** image width (in pixels) */ - public int width; - /** image height (in pixels) */ - public int height; - /** horizontal bitmap resolution (in dpi) */ - public double dpiHorizontal = 72.0f; - /** vertical bitmap resolution (in dpi) */ - public double dpiVertical = 72.0f; - /** implementation-specific data object (ex. a SVG DOM for SVG images) */ - public Object data; - /** MIME type of the image */ - public String mimeType; - /** implementation-specific String (ex. the namespace for XML-based images) */ - public String str; - /** intrinsic alignment-adjust or null if there is none */ - public Length alignmentAdjust; - } - -} - diff --git a/src/java/org/apache/fop/image/FopImageConsumer.java b/src/java/org/apache/fop/image/FopImageConsumer.java deleted file mode 100644 index 4e4d7be48..000000000 --- a/src/java/org/apache/fop/image/FopImageConsumer.java +++ /dev/null @@ -1,211 +0,0 @@ -/* - * 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.image; - -// Java -import java.util.Hashtable; -import java.awt.image.ColorModel; -import java.awt.image.ImageConsumer; -import java.awt.image.ImageProducer; -import java.awt.image.PixelGrabber; - -/** - * ImageConsumer implementation for FopImage classes. - */ -public class FopImageConsumer implements ImageConsumer { - - /** Image width in pixels */ - protected int width = -1; - /** Image height in pixels */ - protected int height = -1; - /** Image status */ - protected Integer imageStatus = new Integer(-1); - /** hints */ - protected int hints = 0; - /** Image properties */ - protected Hashtable properties = null; - /** Color model */ - protected ColorModel cm = null; - /** Image producer */ - protected ImageProducer ip = null; - - /** - * Main constructor - * @param iprod ImageProducer to use - */ - public FopImageConsumer(ImageProducer iprod) { - this.ip = iprod; - } - - /** - * {@inheritDoc} - */ - public void imageComplete(int status) { - /* - * log.error("Status "); - * if (status == ImageConsumer.COMPLETESCANLINES) { - * log.error("CompleteScanLines"); - * } else if (status == ImageConsumer.IMAGEABORTED) { - * log.error("ImageAborted"); - * } else if (status == ImageConsumer.IMAGEERROR) { - * log.error("ImageError"); - * } else if (status == ImageConsumer.RANDOMPIXELORDER) { - * log.error("RandomPixelOrder"); - * } else if (status == ImageConsumer.SINGLEFRAME) { - * log.error("SingleFrame"); - * } else if (status == ImageConsumer.SINGLEFRAMEDONE) { - * log.error("SingleFrameDone"); - * } else if (status == ImageConsumer.SINGLEPASS) { - * log.error("SinglePass"); - * } else if (status == ImageConsumer.STATICIMAGEDONE) { - * log.error("StaticImageDone"); - * } else if (status == ImageConsumer.TOPDOWNLEFTRIGHT) { - * log.error("TopDownLeftRight"); - * } - */ - synchronized (this.imageStatus) { - // Need to stop status if image done - if (imageStatus.intValue() != ImageConsumer.STATICIMAGEDONE - && imageStatus.intValue() != ImageConsumer.SINGLEFRAMEDONE) { - this.imageStatus = new Integer(status); - } - } - } - - /** - * {@inheritDoc} - */ - public void setColorModel(ColorModel model) { - // log.error("setColorModel: " + model); - this.cm = model; - } - - /** - * {@inheritDoc} - */ - public void setDimensions(int width, int height) { - // log.error("setDimension: w=" + width + " h=" + height); - this.width = width; - this.height = height; - } - - /** - * {@inheritDoc} - */ - public void setHints(int hintflags) { - // log.error("setHints: " + hintflags); - this.hints = hintflags; - } - - /** - * {@inheritDoc} - */ - public void setProperties(Hashtable props) { - // log.error("setProperties: " + props); - this.properties = props; - } - - /** - * {@inheritDoc} - */ - public void setPixels(int x, int y, int w, int h, ColorModel model, - byte[] pixels, int off, int scansize) { - } - - /** - * {@inheritDoc} - */ - public void setPixels(int x, int y, int w, int h, ColorModel model, - int[] pixels, int off, int scansize) { - } - - /** - * Indicates whether the image is ready. - * @return boolean True if the image is ready, false if it's still loading - * @throws Exception If an error happened while loading the image - */ - public boolean isImageReady() throws Exception { - /**@todo Use a better exception than Exception */ - synchronized (this.imageStatus) { - if (this.imageStatus.intValue() == ImageConsumer.IMAGEABORTED) { - throw new Exception("Image aborted"); - } - if (this.imageStatus.intValue() == ImageConsumer.IMAGEERROR) { - throw new Exception("Image error"); - } - - if (imageStatus.intValue() == ImageConsumer.STATICIMAGEDONE - || imageStatus.intValue() == ImageConsumer.SINGLEFRAMEDONE) { - return true; - } - - return false; - } - } - - /** - * Returns the image width - * @return the width in pixels - */ - public int getWidth() { - return this.width; - } - - /** - * Returns the image height - * @return the height in pixels - */ - public int getHeight() { - return this.height; - } - - /** - * Returns the color model of the image - * @return the color model - */ - public ColorModel getColorModel() { - return this.cm; - } - - /** - * Returns the bitmap as an array. - * @return the bitmap as an array. - * @throws Exception if an error occured while generating the array - */ - public int[] getImage() throws Exception { - int tmpMap[] = new int[this.width * this.height]; - PixelGrabber pg = new PixelGrabber(this.ip, 0, 0, this.width, - this.height, tmpMap, 0, this.width); - pg.setDimensions(this.width, this.height); - pg.setColorModel(this.cm); - pg.setHints(this.hints); - pg.setProperties(this.properties); - try { - pg.grabPixels(); - } catch (InterruptedException intex) { - /**@todo Use a better exception than Exception */ - throw new Exception("Image grabbing interrupted : " - + intex.getMessage()); - } - return tmpMap; - } - -} - diff --git a/src/java/org/apache/fop/image/GifImage.java b/src/java/org/apache/fop/image/GifImage.java deleted file mode 100644 index 27e81c4fc..000000000 --- a/src/java/org/apache/fop/image/GifImage.java +++ /dev/null @@ -1,218 +0,0 @@ -/* - * 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.image; - -// Java -import java.awt.image.ImageProducer; -import java.awt.image.ColorModel; -import java.awt.image.IndexColorModel; -import java.awt.color.ColorSpace; -import java.awt.Color; -import java.io.InputStream; -import java.io.IOException; -import java.net.URLConnection; - -import org.apache.commons.io.IOUtils; - -/** - * FopImage object for GIF images, using Java native classes. - * - * @see AbstractFopImage - * @see FopImage - */ -public class GifImage extends AbstractFopImage { - - /** - * Create a new gif image. - * - * @param imgInfo the image info for this gif image - */ - public GifImage(FopImage.ImageInfo imgInfo) { - super(imgInfo); - } - - /** - * Load the bitmap for this gif image. - * This loads the data and creates a bitmap byte array - * of the image data. - * To decode the image a dummy URLConnection is used that - * will do the conversion. - * - * @return True if the load process succeeded - */ - protected boolean loadBitmap() { - int[] tmpMap = null; - try { - URLConnection con = new DummyConnection(inputStream); - - ImageProducer ip = (ImageProducer) con.getContent(); - if (ip == null) { - return false; - } - FopImageConsumer consumer = new FopImageConsumer(ip); - ip.startProduction(consumer); - - //Load the image into memory - while (!consumer.isImageReady()) { - Thread.sleep(500); - } - - this.height = consumer.getHeight(); - this.width = consumer.getWidth(); - - try { - tmpMap = consumer.getImage(); - } catch (Exception ex) { - log.error("Image grabbing interrupted : " - + ex.getMessage(), ex); - return false; - } - - ColorModel cm = consumer.getColorModel(); - this.bitsPerPixel = 8; - // this.bitsPerPixel = cm.getPixelSize(); - this.colorSpace = ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB); - if (cm.hasAlpha()) { - // java.awt.Transparency. BITMASK or OPAQUE or TRANSLUCENT - int transparencyType = cm.getTransparency(); - - if (transparencyType == java.awt.Transparency.OPAQUE) { - this.isTransparent = false; - } else if (transparencyType == java.awt.Transparency.BITMASK) { - if (cm instanceof IndexColorModel) { - IndexColorModel indexcm = (IndexColorModel) cm; - this.isTransparent = false; - byte[] alphas = new byte[indexcm.getMapSize()]; - byte[] reds = new byte[indexcm.getMapSize()]; - byte[] greens = new byte[indexcm.getMapSize()]; - byte[] blues = new byte[indexcm.getMapSize()]; - indexcm.getAlphas(alphas); - indexcm.getReds(reds); - indexcm.getGreens(greens); - indexcm.getBlues(blues); - for (int i = 0; - i < indexcm.getMapSize(); - i++) { - if ((alphas[i] & 0xFF) == 0) { - this.isTransparent = true; - this.transparentColor = new Color( - (int)(reds[i] & 0xFF), - (int)(greens[i] & 0xFF), - (int)(blues[i] & 0xFF)); - break; - } - } - } else { - // TRANSLUCENT - /* - * this.isTransparent = false; - * for (int i = 0; i < this.width * this.height; i++) { - * if (cm.getAlpha(tmpMap[i]) == 0) { - * this.isTransparent = true; - * this.transparentColor = new PDFColor(cm.getRed(tmpMap[i]), - * cm.getGreen(tmpMap[i]), cm.getBlue(tmpMap[i])); - * break; - * } - * } - */ - // use special API... - this.isTransparent = false; - } - } else { - this.isTransparent = false; - } - } else { - this.isTransparent = false; - } - } catch (Exception ex) { - log.error("Error while loading image (Gif): " + ex.getMessage(), ex); - return false; - } finally { - IOUtils.closeQuietly(inputStream); - inputStream = null; - } - - // Should take care of the ColorSpace and bitsPerPixel - this.bitmaps = new byte[this.width * this.height * 3]; - for (int i = 0; i < this.height; i++) { - for (int j = 0; j < this.width; j++) { - int p = tmpMap[i * this.width + j]; - int r = (p >> 16) & 0xFF; - int g = (p >> 8) & 0xFF; - int b = (p) & 0xFF; - this.bitmaps[3 * (i * this.width + j)] = (byte)(r & 0xFF); - this.bitmaps[3 * (i * this.width + j) + 1] = (byte)(g & 0xFF); - this.bitmaps[3 * (i * this.width + j) + 2] = (byte)(b & 0xFF); - } - } - return true; - } - - /** {@inheritDoc} */ - protected boolean loadOriginalData() { - return loadDefaultOriginalData(); - } - - /** - * A dummy url connection for a gif image in an input stream. - */ - protected static class DummyConnection extends URLConnection { - private InputStream inputStream; - - DummyConnection(InputStream is) { - super(null); - inputStream = is; - } - - /** - * {@inheritDoc} - */ - public InputStream getInputStream() throws IOException { - return inputStream; - } - - /** - * {@inheritDoc} - */ - public void connect() throws IOException { - // do nothing - } - - /** - * {@inheritDoc} - */ - public String getContentType() { - return "image/gif"; - } - - /** - * {@inheritDoc} - */ - public int getContentLength() { - try { - return inputStream.available(); - } catch (IOException e) { - return -1; - } - } - - } -} - diff --git a/src/java/org/apache/fop/image/ImageCache.java b/src/java/org/apache/fop/image/ImageCache.java deleted file mode 100644 index 3dd639823..000000000 --- a/src/java/org/apache/fop/image/ImageCache.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * 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.image; - -// FOP -import org.apache.fop.apps.FOUserAgent; - -/** - * Image cache holder. - * This interface is used for caching images. - */ -public interface ImageCache { - - /** - * Get an image from the cache. - * - * @param url the url and key for the image - * @param context the user agent context - * @return the requested image - */ - FopImage getImage(String url, FOUserAgent context); - - /** - * Release an image in the current context. - * - * @param url the url and key for the image - * @param context the user agent context - */ - void releaseImage(String url, FOUserAgent context); - - /** - * Invalidate image. - * If during loading this image is found to be invalid - * it will be invalidated to prevent further attempts at - * loading the image. - * - * @param url the url and key for the image - * @param context the user agent context - */ - void invalidateImage(String url, FOUserAgent context); - - /** - * Remove a context and handle all images in the context. - * - * @param context the user agent context - */ - void removeContext(FOUserAgent context); - - /** - * Forces the cache to fully cleared. - */ - void clearAll(); - -} - diff --git a/src/java/org/apache/fop/image/ImageFactory.java b/src/java/org/apache/fop/image/ImageFactory.java deleted file mode 100644 index 5c9f198e8..000000000 --- a/src/java/org/apache/fop/image/ImageFactory.java +++ /dev/null @@ -1,708 +0,0 @@ -/* - * 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.image; - -// Java -import java.io.InputStream; -import java.lang.ref.Reference; -import java.lang.ref.ReferenceQueue; -import java.lang.ref.SoftReference; -import java.lang.reflect.Constructor; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.Map.Entry; - -import javax.xml.transform.Source; -import javax.xml.transform.stream.StreamSource; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.apache.fop.apps.FOUserAgent; -import org.apache.fop.datatypes.URISpecification; -import org.apache.fop.image.analyser.ImageReaderFactory; -import org.apache.xmlgraphics.util.Service; - -/** - * Create FopImage objects (with a configuration file - not yet implemented). - * @author Eric SCHAEFFER - */ -public final class ImageFactory { - - /** - * logging instance - */ - protected static Log log = LogFactory.getLog(FopImage.class); - - private HashMap imageMimeTypes = new HashMap(); - - private ImageCache cache = new ContextImageCache(true); - - /** - * Main constructor for the ImageFactory. - */ - public ImageFactory() { - /* @todo The mappings set up below of image mime types to implementing - * classes should be made externally configurable - */ - ImageProvider jaiImage = new ImageProvider("JAIImage", "org.apache.fop.image.JAIImage"); - ImageProvider jimiImage = new ImageProvider("JIMIImage", "org.apache.fop.image.JimiImage"); - ImageProvider imageIoImage = new ImageProvider( - "ImageIOImage", "org.apache.fop.image.ImageIOImage"); - ImageProvider gifImage = new ImageProvider("GIFImage", "org.apache.fop.image.GifImage"); - ImageProvider jpegImage = new ImageProvider("JPEGImage", "org.apache.fop.image.JpegImage"); - ImageProvider jpegImageIOImage = new ImageProvider( - "JPEGImage", "org.apache.fop.image.JpegImageIOImage"); - ImageProvider bmpImage = new ImageProvider("BMPImage", "org.apache.fop.image.BmpImage"); - ImageProvider epsImage = new ImageProvider("EPSImage", "org.apache.fop.image.EPSImage"); - ImageProvider pngImage = new ImageProvider("PNGImage", "org.apache.fop.image.PNGImage"); - ImageProvider tiffImage = new ImageProvider("TIFFImage", "org.apache.fop.image.TIFFImage"); - ImageProvider xmlImage = new ImageProvider("XMLImage", "org.apache.fop.image.XMLImage"); - ImageProvider emfImage = new ImageProvider("EMFImage", "org.apache.fop.image.EmfImage"); - - ImageMimeType imt = new ImageMimeType("image/gif"); - imageMimeTypes.put(imt.getMimeType(), imt); - imt.addProvider(imageIoImage); - imt.addProvider(jaiImage); - imt.addProvider(jimiImage); - imt.addProvider(gifImage); - - imt = new ImageMimeType("image/jpeg"); - imageMimeTypes.put(imt.getMimeType(), imt); - imt.addProvider(jpegImageIOImage); - imt.addProvider(jpegImage); - - imt = new ImageMimeType("image/bmp"); - imageMimeTypes.put(imt.getMimeType(), imt); - imt.addProvider(bmpImage); - - imt = new ImageMimeType("image/eps"); - imageMimeTypes.put(imt.getMimeType(), imt); - imt.addProvider(epsImage); - - imt = new ImageMimeType("image/png"); - imageMimeTypes.put(imt.getMimeType(), imt); - //Image I/O is faster and more memory-efficient than own codec for PNG - imt.addProvider(imageIoImage); - imt.addProvider(pngImage); - - imt = new ImageMimeType("image/tga"); - imageMimeTypes.put(imt.getMimeType(), imt); - imt.addProvider(jaiImage); - imt.addProvider(imageIoImage); - imt.addProvider(jimiImage); - - imt = new ImageMimeType("image/tiff"); - imageMimeTypes.put(imt.getMimeType(), imt); - imt.addProvider(tiffImage); //Slower but supports CCITT embedding - imt.addProvider(imageIoImage); //Fast but doesn't support CCITT embedding - imt.addProvider(jaiImage); //Fast but doesn't support CCITT embedding - - imt = new ImageMimeType("image/svg+xml"); - imageMimeTypes.put(imt.getMimeType(), imt); - imt.addProvider(xmlImage); - - imt = new ImageMimeType("text/xml"); - imageMimeTypes.put(imt.getMimeType(), imt); - imt.addProvider(xmlImage); - - imt = new ImageMimeType("image/emf"); - imageMimeTypes.put(imt.getMimeType(), imt); - imt.addProvider(emfImage); - - Iterator iter = Service.providers(RegisterableImageProvider.class, true); - while (iter.hasNext()) { - RegisterableImageProvider impl = (RegisterableImageProvider)iter.next(); - imt = new ImageMimeType(impl.getSupportedMimeType()); - imageMimeTypes.put(imt.getMimeType(), imt); - imt.addProvider(new ImageProvider(impl.getName(), impl.getClassName())); - } - } - - /** - * Get the url string from a wrapped url. - * - * @param href the input wrapped url - * @return the raw url - */ - public static String getURL(String href) { - return URISpecification.getURL(href); - } - - /** - * Get the image from the cache or load. - * If this returns null then the image could not be loaded - * due to an error. Messages should be logged. - * Before calling this the getURL(url) must be used. - * - * @param url the url for the image - * @param context the user agent context - * @return the fop image instance - */ - public FopImage getImage(String url, FOUserAgent context) { - return cache.getImage(url, context); - } - - /** - * Release an image from the cache. - * This can be used if the renderer has its own cache of - * the image. - * The image should then be put into the weak cache. - * - * @param url the url for the image - * @param context the user agent context - */ - public void releaseImage(String url, FOUserAgent context) { - cache.releaseImage(url, context); - } - - /** - * Release the context and all images in the context. - * - * @param context the context to remove - */ - public void removeContext(FOUserAgent context) { - cache.removeContext(context); - } - - /** - * Create an FopImage objects. - * @param href the url for the image - * @param ua the user agent context - * @return the fop image instance - */ - public FopImage loadImage(String href, FOUserAgent ua) { - - Source source = ua.resolveURI(href); - if (source == null) { - return null; - } - - // Got a valid source, obtain an InputStream from it - InputStream in = null; - if (source instanceof StreamSource) { - in = ((StreamSource)source).getInputStream(); - } - if (in == null) { - try { - in = new java.net.URL(source.getSystemId()).openStream(); - } catch (Exception ex) { - log.error("Unable to obtain stream from id '" - + source.getSystemId() + "'"); - } - } - 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); - } - - // Check image type - FopImage.ImageInfo imgInfo = null; - try { - imgInfo = ImageReaderFactory.make(source.getSystemId(), in, ua); - } catch (Exception e) { - log.error("Error while recovering image information (" - + href + ") : " + e.getMessage(), e); - return null; - } - if (imgInfo == null) { - try { - in.close(); - in = null; - } catch (Exception e) { - log.debug("Error closing the InputStream for the image", e); - } - log.error("No ImageReader for this type of image (" + href + ")"); - return null; - } - // Associate mime-type to FopImage class - String imgMimeType = imgInfo.mimeType; - Class imageClass = getImageClass(imgMimeType); - if (imageClass == null) { - log.error("Unsupported image type (" + href + "): " + imgMimeType); - return null; - } else { - if (log.isDebugEnabled()) { - log.debug("Loading " + imgMimeType + " with " + imageClass.getName() - + ": " + href); - } - } - - // load the right image class - // return new <FopImage implementing class> - Object imageInstance = null; - try { - Class[] imageConstructorParameters = new Class[1]; - imageConstructorParameters[0] = org.apache.fop.image.FopImage.ImageInfo.class; - Constructor imageConstructor = imageClass.getDeclaredConstructor( - imageConstructorParameters); - Object[] initArgs = new Object[1]; - initArgs[0] = imgInfo; - imageInstance = imageConstructor.newInstance(initArgs); - } catch (java.lang.reflect.InvocationTargetException ex) { - Throwable t = ex.getTargetException(); - String msg; - if (t != null) { - msg = t.getMessage(); - } else { - msg = ex.getMessage(); - } - log.error("Error creating FopImage object (" - + href + "): " + msg, (t == null) ? ex : t); - return null; - } catch (InstantiationException ie) { - log.error("Error creating FopImage object (" - + href + "): Could not instantiate " + imageClass.getName() + " instance"); - return null; - } catch (Exception ex) { - log.error("Error creating FopImage object (" - + href + "): " + ex.getMessage(), ex); - return null; - } - if (!(imageInstance instanceof org.apache.fop.image.FopImage)) { - log.error("Error creating FopImage object (" + href + "): " + "class " - + imageClass.getName() - + " doesn't implement org.apache.fop.image.FopImage interface"); - return null; - } - return (FopImage) imageInstance; - } - - private Class getImageClass(String imgMimeType) { - ImageMimeType imt = (ImageMimeType)imageMimeTypes.get(imgMimeType); - if (imt == null) { - return null; - } - return imt.getFirstImplementingClass(); - } - - /** - * Forces all the image caches to be cleared. This should normally only be used in - * testing environments. If you happen to think that you need to call this yourself - * in a production environment, please notify the development team so we can look - * into the issue. A call like this shouldn't be necessary anymore like it may have - * been with FOP 0.20.5. - */ - public void clearCaches() { - cache.clearAll(); - } -} - -/** - * Basic image cache. - * This keeps track of invalid images. - */ -class BasicImageCache implements ImageCache { - - private Set invalid = Collections.synchronizedSet(new java.util.HashSet()); - //private Map contextStore = Collections.synchronizedMap(new java.util.HashMap()); - - public FopImage getImage(String url, FOUserAgent context) { - if (invalid.contains(url)) { - return null; - } - //TODO Doesn't seem to be fully implemented. Do we need it at all? Not referenced. - return null; - } - - public void releaseImage(String url, FOUserAgent context) { - // do nothing - } - - public void invalidateImage(String url, FOUserAgent context) { - // cap size of invalid list - if (invalid.size() > 100) { - invalid.clear(); - } - invalid.add(url); - } - - public void removeContext(FOUserAgent context) { - // do nothing - } - - /** {@inheritDoc} */ - public void clearAll() { - invalid.clear(); - } - -} - -/** - * This is the context image cache. - * This caches images on the basis of the given context. - * Common images in different contexts are currently not handled. - * There are two possiblities, each context handles its own images - * and renderers can cache information or images are shared and - * all information is retained. - * Once a context is removed then all images are placed into a - * weak hashmap so they may be garbage collected. - */ -class ContextImageCache implements ImageCache { - - // if this cache is collective then images can be shared - // among contexts, this implies that the base directory - // is either the same or does not effect the images being - // loaded - private boolean collective; - private Map contextStore = Collections.synchronizedMap(new java.util.HashMap()); - private Set invalid = null; - private Map refStore = null; - private ReferenceQueue refQueue = new ReferenceQueue(); - - public ContextImageCache(boolean col) { - collective = col; - if (collective) { - refStore = Collections.synchronizedMap(new java.util.HashMap()); - invalid = Collections.synchronizedSet(new java.util.HashSet()); - } - } - - // sync around lookups and puts - // another sync around load for a particular image - public FopImage getImage(String url, FOUserAgent context) { - ImageLoader im = null; - // this protects the finding or creating of a new - // ImageLoader for multi threads - synchronized (this) { - if (collective && invalid.contains(url)) { - return null; - } - Context con = (Context) contextStore.get(context); - if (con == null) { - con = new Context(context, collective); - contextStore.put(context, con); - } else { - if (con.invalid(url)) { - return null; - } - im = con.getImage(url); - } - if (im == null && collective) { - Iterator i = contextStore.values().iterator(); - while (i.hasNext()) { - Context c = (Context)i.next(); - if (c != con) { - im = c.getImage(url); - if (im != null) { - break; - } - } - } - if (im == null) { - Reference ref = (Reference)refStore.get(url); - if (ref != null) { - im = (ImageLoader) ref.get(); - if (im == null) { - //Remove key if its value has been garbage collected - refStore.remove(url); - } - } - } - } - - if (im != null) { - con.putImage(url, im); - } else { - im = con.getImage(url, this); - } - } - - // the ImageLoader is synchronized so images with the - // same url will not be loaded at the same time - if (im != null) { - return im.loadImage(); - } - return null; - } - - public void releaseImage(String url, FOUserAgent context) { - Context con = (Context) contextStore.get(context); - if (con != null) { - if (collective) { - ImageLoader im = con.getImage(url); - refStore.put(url, wrapInReference(im, url)); - } - con.releaseImage(url); - } - } - - public void invalidateImage(String url, FOUserAgent context) { - if (collective) { - // cap size of invalid list - if (invalid.size() > 100) { - invalid.clear(); - } - invalid.add(url); - } - Context con = (Context) contextStore.get(context); - if (con != null) { - con.invalidateImage(url); - } - } - - private Reference wrapInReference(Object obj, Object key) { - return new SoftReferenceWithKey(obj, key, refQueue); - } - - private static class SoftReferenceWithKey extends SoftReference { - - private Object key; - - public SoftReferenceWithKey(Object referent, Object key, ReferenceQueue q) { - super(referent, q); - this.key = key; - } - } - - public void removeContext(FOUserAgent context) { - Context con = (Context) contextStore.get(context); - if (con != null) { - if (collective) { - Map images = con.getImages(); - Iterator iter = images.entrySet().iterator(); - while (iter.hasNext()) { - Entry entry = (Entry)iter.next(); - refStore.put(entry.getKey(), - wrapInReference(entry.getValue(), entry.getKey())); - } - } - contextStore.remove(context); - } - //House-keeping (remove cleared references) - checkReferenceQueue(); - } - - /** - * Checks the reference queue if any references have been cleared and removes them from the - * cache. - */ - private void checkReferenceQueue() { - SoftReferenceWithKey ref; - while ((ref = (SoftReferenceWithKey)refQueue.poll()) != null) { - refStore.remove(ref.key); - } - } - - class Context { - private Map images = Collections.synchronizedMap(new java.util.HashMap()); - private Set invalid = null; - private FOUserAgent userAgent; - - public Context(FOUserAgent ua, boolean inv) { - userAgent = ua; - if (inv) { - invalid = Collections.synchronizedSet(new java.util.HashSet()); - } - } - - public ImageLoader getImage(String url, ImageCache c) { - if (images.containsKey(url)) { - return (ImageLoader) images.get(url); - } - ImageLoader loader = new ImageLoader(url, c, userAgent); - images.put(url, loader); - return loader; - } - - public void putImage(String url, ImageLoader image) { - images.put(url, image); - } - - public ImageLoader getImage(String url) { - return (ImageLoader) images.get(url); - } - - public void releaseImage(String url) { - images.remove(url); - } - - public Map getImages() { - return images; - } - - public void invalidateImage(String url) { - invalid.add(url); - } - - public boolean invalid(String url) { - return invalid.contains(url); - } - - } - - /** {@inheritDoc} */ - public void clearAll() { - this.refStore.clear(); - this.invalid.clear(); - //The context-sensitive caches are not cleared so there are no negative side-effects - //in a multi-threaded environment. Not that it's a good idea to use this method at - //all except in testing environments. If such a calls is necessary in normal environments - //we need to check on memory leaks! - } - -} - -/** - * Encapsulates a class of type FopImage by holding its class name. - * This allows dynamic loading of the class at runtime. - */ -class ImageProvider { - - private String name = null; - - private String className = null; - - private boolean checked = false; - - private Class clazz = null; - - /** - * Creates an ImageProvider with a given name and implementing class. - * The class name should refer to a class of type {@link FopImage}. - * However, this is not checked on construction. - * @param name The name of the provider - * @param className The full class name of the class implementing this provider - */ - public ImageProvider(String name, String className) { - setName(name); - setClassName(className); - } - - /** - * Returns the provider name. - * @return The provider name - */ - public String getName() { - return name; - } - - private void setName(String name) { - this.name = name; - } - - /** - * Returns the implementing class name. - * @return The implementing class name - */ - public String getClassName() { - return className; - } - - private void setClassName(String className) { - this.className = className; - } - - /** - * Returns the implementing class as a {@link Class} object. - * @return The implementing class or null if it couldn't be loaded. - */ - public Class getImplementingClass() { - if (!checked) { - try { - clazz = Class.forName(getClassName()); - } catch (ClassNotFoundException cnfe) { - //nop - } catch (LinkageError le) { - // This can happen if fop was build with support for a - // particular provider (e.g. a binary fop distribution) - // but the required support files (e.g. jai, jimi) are not - // available in the current runtime environment. - ImageFactory.log.debug("Image support provider " + getName() - + " could not be loaded. If " + getName() + " should be" - + " available please make sure all required external libraries" - + " are on the classpath."); - } - checked = true; - } - return clazz; - } -} - -/** - * Holds a mime type for a particular image format plus a list of - * {@link ImageProvider} objects which support the particular image format. - */ -class ImageMimeType { - - private String mimeType = null; - - private List providers = null; - - /** - * Constructor for a particular mime type. - * @param mimeType The mime type - */ - public ImageMimeType(String mimeType) { - setMimeType(mimeType); - } - - /** - * Returns the mime type. - * @return The mime type - */ - public String getMimeType() { - return mimeType; - } - - private void setMimeType(String mimeType) { - this.mimeType = mimeType; - } - - /** - * Returns the class from the first available provider. - * @return The first available class or null if none can be found - */ - public Class getFirstImplementingClass() { - if (providers == null) { - return null; - } - for (Iterator it = providers.iterator(); it.hasNext();) { - ImageProvider ip = (ImageProvider)it.next(); - Class clazz = ip.getImplementingClass(); - if (clazz != null) { - return clazz; - } - } - return null; - } - - /** - * Adds a new provider. - * The provider is added to the end of the current provider list. - * @param The new provider to add - */ - public void addProvider(ImageProvider provider) { - if (providers == null) { - providers = new ArrayList(4); // Assume we only have a few providers - } - if (!providers.contains(provider)) { - providers.add(provider); - } - } -}
\ No newline at end of file diff --git a/src/java/org/apache/fop/image/ImageLoader.java b/src/java/org/apache/fop/image/ImageLoader.java deleted file mode 100644 index 111ef919f..000000000 --- a/src/java/org/apache/fop/image/ImageLoader.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * 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.image; - -import org.apache.fop.apps.FOUserAgent; - -/** - * Class to load images. - */ -class ImageLoader { - - private String url; - private ImageCache cache; - private boolean valid = true; - private FOUserAgent userAgent; - private FopImage image = null; - - /** - * Main constructor. - * @param url URL to the image - * @param cache Image cache - * @param ua User agent - */ - public ImageLoader(String url, ImageCache cache, FOUserAgent ua) { - this.url = url; - this.cache = cache; - this.userAgent = ua; - } - - /** - * Loads the image. - * @return the loaded image - */ - public synchronized FopImage loadImage() { - if (!valid || image != null) { - return image; - } - ImageFactory imageFactory = userAgent.getFactory().getImageFactory(); - image = imageFactory.loadImage(url, userAgent); - if (image == null) { - cache.invalidateImage(url, userAgent); - valid = false; - } - return image; - } - -} diff --git a/src/java/org/apache/fop/image/JAIImage.java b/src/java/org/apache/fop/image/JAIImage.java deleted file mode 100644 index 8b3074052..000000000 --- a/src/java/org/apache/fop/image/JAIImage.java +++ /dev/null @@ -1,194 +0,0 @@ -/* - * 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.image; - -// AWT -import java.awt.image.ColorModel; -import java.awt.image.IndexColorModel; -import java.awt.image.BufferedImage; -import java.awt.Color; - -// JAI -import javax.media.jai.JAI; -import javax.media.jai.RenderedOp; - -import org.apache.commons.io.IOUtils; -// Sun codec -import com.sun.media.jai.codec.FileCacheSeekableStream; - -/** - * FopImage object using JAI. - * - * @see AbstractFopImage - * @see FopImage - */ -public class JAIImage extends AbstractFopImage { - - /** - * Create a new JAI image. - * - * @param imgInfo the image info for this JAI image - */ - public JAIImage(FopImage.ImageInfo imgInfo) { - super(imgInfo); - } - - /** - * {@inheritDoc} - */ - protected boolean loadDimensions() { - if (this.bitmaps == null) { - loadImage(); - } - return this.bitmaps != null; - } - - /** - * {@inheritDoc} - */ - protected boolean loadBitmap() { - if (this.bitmaps == null) { - loadImage(); - } - - return this.bitmaps != null; - } - - /** - * Loads the image from the inputstream - */ - protected void loadImage() { - com.sun.media.jai.codec.FileCacheSeekableStream seekableInput = null; - RenderedOp imageOp = null; - try { - seekableInput = new FileCacheSeekableStream(inputStream); - imageOp = JAI.create("stream", seekableInput); - - this.height = imageOp.getHeight(); - this.width = imageOp.getWidth(); - - ColorModel cm = imageOp.getColorModel(); - //this.bitsPerPixel = 8; - this.bitsPerPixel = cm.getPixelSize(); - - // TODO: the getRGB() function converts the image into the RGB - // colorspace. However, here we assume the image colorspace is kept. - // It should be either one of them, but not both. Unfortunately - // there are other hacks for images in the CMYK colorspace (e.g. in - // the PDF output) that would need to be changed as well. - - //this.colorSpace = ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB); - this.colorSpace = cm.getColorSpace(); - - BufferedImage imageData = imageOp.getAsBufferedImage(); - int[] tmpMap = imageData.getRGB(0, 0, this.width, - this.height, null, 0, this.width); - - if (cm.hasAlpha()) { - // java.awt.Transparency. BITMASK or OPAQUE or TRANSLUCENT - int transparencyType = cm.getTransparency(); - - if (transparencyType == java.awt.Transparency.OPAQUE) { - this.isTransparent = false; - } else if (transparencyType == java.awt.Transparency.BITMASK) { - if (cm instanceof IndexColorModel) { - this.isTransparent = false; - byte[] alphas = new byte[ - ((IndexColorModel) cm).getMapSize()]; - byte[] reds = new byte[ - ((IndexColorModel) cm).getMapSize()]; - byte[] greens = new byte[ - ((IndexColorModel) cm).getMapSize()]; - byte[] blues = new byte[ - ((IndexColorModel) cm).getMapSize()]; - ((IndexColorModel) cm).getAlphas(alphas); - ((IndexColorModel) cm).getReds(reds); - ((IndexColorModel) cm).getGreens(greens); - ((IndexColorModel) cm).getBlues(blues); - for (int i = 0; - i < ((IndexColorModel) cm).getMapSize(); - i++) { - if ((alphas[i] & 0xFF) == 0) { - this.isTransparent = true; - this.transparentColor = new Color( - (int)(reds[i] & 0xFF), - (int)(greens[i] & 0xFF), - (int)(blues[i] & 0xFF)); - break; - } - } - } else { - // TRANSLUCENT - /* - * this.isTransparent = false; - * for (int i = 0; i < this.width * this.height; i++) { - * if (cm.getAlpha(tmpMap[i]) == 0) { - * this.isTransparent = true; - * this.transparentColor = new PDFColor(cm.getRed(tmpMap[i]), - * cm.getGreen(tmpMap[i]), cm.getBlue(tmpMap[i])); - * break; - * } - * } - * // or use special API... - */ - this.isTransparent = false; - } - } else { - this.isTransparent = false; - } - } else { - this.isTransparent = false; - } - - // Should take care of the ColorSpace and bitsPerPixel - this.bitmaps = new byte[this.width * this.height * 3]; - for (int i = 0; i < this.height; i++) { - for (int j = 0; j < this.width; j++) { - int p = tmpMap[i * this.width + j]; - int r = (p >> 16) & 0xFF; - int g = (p >> 8) & 0xFF; - int b = (p) & 0xFF; - this.bitmaps[3 * (i * this.width + j)] = (byte)(r & 0xFF); - this.bitmaps[3 * (i * this.width + j) + 1] = (byte)(g & 0xFF); - this.bitmaps[3 * (i * this.width + j) + 2] = (byte)(b & 0xFF); - } - } - - } catch (Exception ex) { - log.error("Error while loading image (JAI): " + ex.getMessage(), ex); - } finally { - IOUtils.closeQuietly(inputStream); - inputStream = null; - if (imageOp != null) { - imageOp.dispose(); - } - if (seekableInput != null) { - IOUtils.closeQuietly(seekableInput); - } - } - } - - /** {@inheritDoc} */ - protected boolean loadOriginalData() { - return loadDefaultOriginalData(); - } - -} - diff --git a/src/java/org/apache/fop/image/JimiImage.java b/src/java/org/apache/fop/image/JimiImage.java deleted file mode 100644 index 49fb0b2e0..000000000 --- a/src/java/org/apache/fop/image/JimiImage.java +++ /dev/null @@ -1,186 +0,0 @@ -/* - * 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.image; - -// Java -import java.awt.image.ImageProducer; -import java.awt.image.ColorModel; -import java.awt.image.IndexColorModel; -import java.awt.color.ColorSpace; -import java.awt.Color; - -import org.apache.commons.io.IOUtils; - -// Jimi -import com.sun.jimi.core.Jimi; - -/** - * FopImage object for several images types, using Jimi. - * See Jimi documentation for supported image types. - * - * @see AbstractFopImage - * @see FopImage - */ -public class JimiImage extends AbstractFopImage { - - /** - * Create a new Jimi image. - * - * @param imgInfo the image info for this Jimi image - */ - public JimiImage(FopImage.ImageInfo imgInfo) { - super(imgInfo); - } - - /** - * {@inheritDoc} - */ - protected boolean loadDimensions() { - if (this.bitmaps == null) { - loadImage(); - } - - return this.bitmaps != null; - } - - /** - * {@inheritDoc} - */ - protected boolean loadBitmap() { - if (this.bitmaps == null) { - loadImage(); - } - - return this.bitmaps != null; - } - - /** - * Loads the image from the inputstream - */ - protected void loadImage() { - int[] tmpMap = null; - try { - ImageProducer ip = Jimi.getImageProducer(inputStream, - Jimi.SYNCHRONOUS | Jimi.IN_MEMORY); - FopImageConsumer consumer = new FopImageConsumer(ip); - ip.startProduction(consumer); - - while (!consumer.isImageReady()) { - Thread.sleep(500); - } - this.height = consumer.getHeight(); - this.width = consumer.getWidth(); - - try { - tmpMap = consumer.getImage(); - } catch (Exception ex) { - log.error("Image grabbing interrupted", ex); - return; - } - - ColorModel cm = consumer.getColorModel(); - this.bitsPerPixel = 8; - // this.bitsPerPixel = cm.getPixelSize(); - this.colorSpace = ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB); - if (cm.hasAlpha()) { - // java.awt.Transparency. BITMASK or OPAQUE or TRANSLUCENT - int transparencyType = cm.getTransparency(); - if (transparencyType == java.awt.Transparency.OPAQUE) { - this.isTransparent = false; - } else if (transparencyType == java.awt.Transparency.BITMASK) { - if (cm instanceof IndexColorModel) { - this.isTransparent = false; - byte[] alphas = new byte[ - ((IndexColorModel) cm).getMapSize()]; - byte[] reds = new byte[ - ((IndexColorModel) cm).getMapSize()]; - byte[] greens = new byte[ - ((IndexColorModel) cm).getMapSize()]; - byte[] blues = new byte[ - ((IndexColorModel) cm).getMapSize()]; - ((IndexColorModel) cm).getAlphas(alphas); - ((IndexColorModel) cm).getReds(reds); - ((IndexColorModel) cm).getGreens(greens); - ((IndexColorModel) cm).getBlues(blues); - for (int i = 0; - i < ((IndexColorModel) cm).getMapSize(); - i++) { - if ((alphas[i] & 0xFF) == 0) { - this.isTransparent = true; - this.transparentColor = new Color( - (int)(reds[i] & 0xFF), - (int)(greens[i] & 0xFF), - (int)(blues[i] & 0xFF)); - break; - } - } - } else { - // TRANSLUCENT - /* - * this.isTransparent = false; - * for (int i = 0; i < this.width * this.height; i++) { - * if (cm.getAlpha(tmpMap[i]) == 0) { - * this.isTransparent = true; - * this.transparentColor = new PDFColor(cm.getRed(tmpMap[i]), - * cm.getGreen(tmpMap[i]), cm.getBlue(tmpMap[i])); - * break; - * } - * } - */ - // use special API... - this.isTransparent = false; - } - } else { - this.isTransparent = false; - } - } else { - this.isTransparent = false; - } - } catch (Throwable ex) { - log.error("Error while loading image (Jimi): " + ex.getMessage(), ex); - return; - } finally { - IOUtils.closeQuietly(inputStream); - inputStream = null; - } - - - // Should take care of the ColorSpace and bitsPerPixel - this.bitmaps = new byte[this.width * this.height * 3]; - for (int i = 0; i < this.height; i++) { - for (int j = 0; j < this.width; j++) { - int p = tmpMap[i * this.width + j]; - int r = (p >> 16) & 0xFF; - int g = (p >> 8) & 0xFF; - int b = (p) & 0xFF; - this.bitmaps[3 * (i * this.width + j)] = (byte)(r & 0xFF); - this.bitmaps[3 * (i * this.width + j) + 1] = (byte)(g & 0xFF); - this.bitmaps[3 * (i * this.width + j) + 2] = (byte)(b & 0xFF); - } - } - } - - /** {@inheritDoc} */ - protected boolean loadOriginalData() { - return loadDefaultOriginalData(); - } - -} - diff --git a/src/java/org/apache/fop/image/JpegImage.java b/src/java/org/apache/fop/image/JpegImage.java deleted file mode 100644 index 1de6b0d48..000000000 --- a/src/java/org/apache/fop/image/JpegImage.java +++ /dev/null @@ -1,239 +0,0 @@ -/* - * 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.image; - -import java.awt.color.ColorSpace; -import java.awt.color.ICC_Profile; - -import org.apache.commons.io.IOUtils; -import org.apache.commons.io.output.ByteArrayOutputStream; -import org.apache.fop.util.CMYKColorSpace; - -/** - * FopImage object for JPEG images, Using Java native classes. - * - * @see AbstractFopImage - * @see FopImage - */ -public class JpegImage extends AbstractFopImage { - private ICC_Profile iccProfile = null; - private boolean foundICCProfile = false; - private boolean hasAPPEMarker = false; - - /** - * Create a jpeg image with the info. - * - * @param imgInfo the image info for this jpeg - */ - public JpegImage(FopImage.ImageInfo imgInfo) { - super(imgInfo); - } - - /** - * Load the original jpeg data. - * This loads the original jpeg data and reads the color space, - * and icc profile if any. - * - * @return true if loaded false for any error - */ - protected boolean loadOriginalData() { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - ByteArrayOutputStream iccStream = null; - int index = 0; - boolean cont = true; - - try { - byte[] readBuf = new byte[4096]; - int bytesRead; - while ((bytesRead = inputStream.read(readBuf)) != -1) { - baos.write(readBuf, 0, bytesRead); - } - } catch (java.io.IOException ex) { - log.error("Error while loading image (Jpeg): " + ex.getMessage(), ex); - return false; - } finally { - IOUtils.closeQuietly(inputStream); - inputStream = null; - } - - this.raw = baos.toByteArray(); - this.bitsPerPixel = 8; - this.isTransparent = false; - - //Check for SOI (Start of image) marker (FFD8) - if (this.raw.length > (index + 2) - && uByte(this.raw[index]) == 255 /*0xFF*/ - && uByte(this.raw[index + 1]) == 216 /*0xD8*/) { - index += 2; - - while (index < this.raw.length && cont) { - //check to be sure this is the begining of a header - if (this.raw.length > (index + 2) - && uByte(this.raw[index]) == 255 /*0xFF*/) { - - //192 or 194 are the header bytes that contain - // the jpeg width height and color depth. - if (uByte(this.raw[index + 1]) == 192 /*0xC0*/ - || uByte(this.raw[index + 1]) == 194 /*0xC2*/) { - - this.height = calcBytes(this.raw[index + 5], - this.raw[index + 6]); - this.width = calcBytes(this.raw[index + 7], - this.raw[index + 8]); - - int numComponents = this.raw[index + 9]; - if (numComponents == 1) { - this.colorSpace = ColorSpace.getInstance( - ColorSpace.CS_GRAY); - } else if (numComponents == 3) { - this.colorSpace = ColorSpace.getInstance( - ColorSpace.CS_LINEAR_RGB); - } else if (numComponents == 4) { - // howto create CMYK color space - /* - this.colorSpace = ColorSpace.getInstance( - ColorSpace.CS_CIEXYZ); - */ - this.colorSpace = CMYKColorSpace.getInstance(); - } else { - log.error("Unknown ColorSpace for image: " - + ""); - return false; - } - - if (foundICCProfile) { - cont = false; - break; - } - index += calcBytes(this.raw[index + 2], - this.raw[index + 3]) + 2; - - } else if (uByte(this.raw[index + 1]) == 226 /*0xE2*/ - && this.raw.length > (index + 60)) { - // Check if ICC profile - byte[] iccString = new byte[11]; - System.arraycopy(this.raw, index + 4, - iccString, 0, 11); - - if ("ICC_PROFILE".equals(new String(iccString))) { - int chunkSize = calcBytes( - this.raw[index + 2], - this.raw[index + 3]) + 2; - - if (iccStream == null) { - iccStream = new ByteArrayOutputStream(); - } - iccStream.write(this.raw, - index + 18, chunkSize - 18); - - } - - index += calcBytes(this.raw[index + 2], - this.raw[index + 3]) + 2; - // Check for Adobe APPE Marker - } else if ((uByte(this.raw[index]) == 0xff - && uByte(this.raw[index + 1]) == 0xee - && uByte(this.raw[index + 2]) == 0 - && uByte(this.raw[index + 3]) == 14 - && "Adobe".equals(new String(this.raw, index + 4, 5)))) { - // The reason for reading the APPE marker is that Adobe Photoshop - // generates CMYK JPEGs with inverted values. The correct thing - // to do would be to interpret the values in the marker, but for now - // only assume that if APPE marker is present and colorspace is CMYK, - // the image is inverted. - hasAPPEMarker = true; - - index += calcBytes(this.raw[index + 2], - this.raw[index + 3]) + 2; - } else { - index += calcBytes(this.raw[index + 2], - this.raw[index + 3]) + 2; - } - - } else { - cont = false; - } - } - } else { - log.error("Error while loading " - + "JpegImage - Invalid JPEG Header."); - return false; - } - if (iccStream != null && iccStream.size() > 0) { - int padding = (8 - (iccStream.size() % 8)) % 8; - if (padding != 0) { - try { - iccStream.write(new byte[padding]); - } catch (Exception ex) { - log.error("Error while aligning ICC stream: " + ex.getMessage(), ex); - return false; - } - } - try { - iccProfile = ICC_Profile.getInstance(iccStream.toByteArray()); - } catch (IllegalArgumentException iae) { - log.warn("An ICC profile is present but it is invalid (" - + iae.getMessage() + "). The color profile will be ignored. (" - + this.getOriginalURI() + ")"); - } - if (iccProfile.getNumComponents() != this.colorSpace.getNumComponents()) { - log.warn("The number of components of the ICC profile (" - + iccProfile.getNumComponents() - + ") doesn't match the image (" - + this.colorSpace.getNumComponents() - + "). Ignoring the ICC color profile."); - this.iccProfile = null; - } - } else if (this.colorSpace == null) { - log.error("ColorSpace not specified for JPEG image"); - return false; - } - if (hasAPPEMarker && this.colorSpace.getType() == ColorSpace.TYPE_CMYK) { - if (log.isDebugEnabled()) { - log.debug("JPEG has an Adobe APPE marker. Note: CMYK Image will be inverted. (" - + this.getOriginalURI() + ")"); - } - this.invertImage = true; - } - return true; - } - - /** - * Get the ICC profile for this Jpeg image. - * - * @return the icc profile or null if not found - */ - public ICC_Profile getICCProfile() { - return iccProfile; - } - - private int calcBytes(byte bOne, byte bTwo) { - return (uByte(bOne) * 256) + uByte(bTwo); - } - - private int uByte(byte bIn) { - if (bIn < 0) { - return 256 + bIn; - } else { - return bIn; - } - } -} - diff --git a/src/java/org/apache/fop/image/PNGImage.java b/src/java/org/apache/fop/image/PNGImage.java deleted file mode 100644 index c83d491f0..000000000 --- a/src/java/org/apache/fop/image/PNGImage.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * 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.image; - -import java.io.IOException; - -import org.apache.xmlgraphics.image.codec.png.PNGRed; -import org.apache.xmlgraphics.image.codec.png.PNGDecodeParam; -import org.apache.xmlgraphics.image.codec.util.SeekableStream; -import org.apache.xmlgraphics.image.rendered.CachableRed; -import org.apache.commons.io.IOUtils; - -/** - * FopImage object using PNG - * - * @see AbstractFopImage - * @see FopImage - */ -public class PNGImage extends XmlGraphicsCommonsImage { - - /** - * Constructs a new PNGImage instance. - * @param imgReader basic metadata for the image - */ - public PNGImage(FopImage.ImageInfo imgReader) { - super(imgReader); - this.loaded = 0; //TODO The PNGReader cannot read the resolution, yet. - } - - /** - * {@inheritDoc} - */ - protected CachableRed decodeImage(SeekableStream stream) throws IOException { - PNGDecodeParam param = new PNGDecodeParam(); - param.setPerformGammaCorrection(true); - param.setDisplayExponent(2.2f); // sRGB gamma - PNGRed red = new PNGRed(stream, param); - String unit = (String)red.getProperty("pixel_units"); - if ("Meters".equals(unit)) { - this.dpiHorizontal = ((Integer)red.getProperty("x_pixels_per_unit")).intValue() - * 25.4f / 1000f; - this.dpiVertical = ((Integer)red.getProperty("y_pixels_per_unit")).intValue() - * 25.4f / 1000f; - } - return red; - } - - /** - * Load the original PNG data. - * This loads the original PNG data as is into memory. - * - * @return true if loaded false for any error - */ - protected boolean loadOriginalData() { - try { - seekableInput.seek(0); - this.raw = IOUtils.toByteArray(seekableInput); - - } catch (java.io.IOException ex) { - log.error("Error while loading raw image: " + ex.getMessage(), ex); - return false; - } finally { - IOUtils.closeQuietly(inputStream); - inputStream = null; - } - - return true; - } - -} diff --git a/src/java/org/apache/fop/image/TIFFImage.java b/src/java/org/apache/fop/image/TIFFImage.java deleted file mode 100644 index d9a9fc022..000000000 --- a/src/java/org/apache/fop/image/TIFFImage.java +++ /dev/null @@ -1,207 +0,0 @@ -/* - * 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.image; - -import java.awt.color.ColorSpace; -import java.io.IOException; - -import org.apache.xmlgraphics.image.codec.util.SeekableStream; -import org.apache.xmlgraphics.image.codec.tiff.TIFFDirectory; -import org.apache.xmlgraphics.image.codec.tiff.TIFFField; -import org.apache.xmlgraphics.image.codec.tiff.TIFFImageDecoder; -import org.apache.xmlgraphics.image.rendered.CachableRed; -import org.apache.commons.io.IOUtils; - -/** - * TIFF implementation using the Batik codecs. - */ -public class TIFFImage extends XmlGraphicsCommonsImage { - - private int compression = 0; - private int stripCount = 0; - private long stripOffset = 0; - private long stripLength = 0; - private int fillOrder = 1; - - /** - * Constructs a new BatikImage instance. - * @param imgReader basic metadata for the image - */ - public TIFFImage(FopImage.ImageInfo imgReader) { - super(imgReader); - } - - /** - * The compression type set in the TIFF directory - * @return the TIFF compression type - */ - public int getCompression() { - return compression; - } - - /** - * The number of strips in the image - * @return the number of strips in the image - */ - public int getStripCount() { - return stripCount; - } - - /** - * {@inheritDoc} - * org.apache.xmlgraphics.image.codec.util.SeekableStream) - */ - protected CachableRed decodeImage(SeekableStream stream) throws IOException { - org.apache.xmlgraphics.image.codec.tiff.TIFFImage img - = new org.apache.xmlgraphics.image.codec.tiff.TIFFImage - (stream, null, 0); - TIFFDirectory dir = (TIFFDirectory)img.getProperty("tiff_directory"); - TIFFField fld = dir.getField(TIFFImageDecoder.TIFF_RESOLUTION_UNIT); - int resUnit = fld.getAsInt(0); - fld = dir.getField(TIFFImageDecoder.TIFF_X_RESOLUTION); - double xRes = fld.getAsDouble(0); - fld = dir.getField(TIFFImageDecoder.TIFF_Y_RESOLUTION); - double yRes = fld.getAsDouble(0); - switch (resUnit) { - case 2: //inch - this.dpiHorizontal = xRes; - this.dpiVertical = yRes; - break; - case 3: //cm - this.dpiHorizontal = xRes * 2.54f; - this.dpiVertical = yRes * 2.54f; - break; - default: - //ignored - log.warn("Cannot determine bitmap resolution." - + " Unimplemented resolution unit: " + resUnit); - } - fld = dir.getField(TIFFImageDecoder.TIFF_COMPRESSION); - if (fld != null) { - compression = fld.getAsInt(0); - } - fld = dir.getField(TIFFImageDecoder.TIFF_BITS_PER_SAMPLE); - if (fld != null) { - bitsPerPixel = fld.getAsInt(0); - } - fld = dir.getField(TIFFImageDecoder.TIFF_ROWS_PER_STRIP); - if (fld == null) { - stripCount = 1; - } else { - stripCount = (int)(dir.getFieldAsLong(TIFFImageDecoder.TIFF_IMAGE_LENGTH) - / fld.getAsLong(0)); - } - - fld = dir.getField(TIFFImageDecoder.TIFF_FILL_ORDER); - if (fld != null) { - fillOrder = fld.getAsInt(0); - } - - stripOffset = dir.getField(TIFFImageDecoder.TIFF_STRIP_OFFSETS).getAsLong(0); - stripLength = dir.getField(TIFFImageDecoder.TIFF_STRIP_BYTE_COUNTS).getAsLong(0); - - if (this.bitsPerPixel == 1) { - this.colorSpace = ColorSpace.getInstance(ColorSpace.CS_GRAY); - } - return img; - } - - /** - * Load the original TIFF data. - * This loads only strip 1 of the original TIFF data. - * - * @return true if loaded false for any error - * {@inheritDoc} - */ - protected boolean loadOriginalData() { - if (loadDimensions()) { - byte[] readBuf = new byte[(int)stripLength]; - int bytesRead; - - try { - this.seekableInput.reset(); - this.seekableInput.skip(stripOffset); - bytesRead = seekableInput.read(readBuf); - if (bytesRead != stripLength) { - log.error("Error while loading image: length mismatch on read"); - return false; - } - - // need to invert bytes if fill order = 2 - if (fillOrder == 2) { - for (int i = 0; i < (int)stripLength; i++) { - readBuf[i] = flipTable[readBuf[i] & 0xff]; - } - } - this.raw = readBuf; - - return true; - } catch (IOException ioe) { - log.error("Error while loading image strip 1 (TIFF): ", ioe); - return false; - } finally { - IOUtils.closeQuietly(seekableInput); - IOUtils.closeQuietly(inputStream); - this.seekableInput = null; - this.inputStream = null; - this.cr = null; - } - } - return false; - } - - // Table to be used when fillOrder = 2, for flipping bytes. - // Copied from XML Graphics Commons' TIFFFaxDecoder class - private static byte[] flipTable = { - 0, -128, 64, -64, 32, -96, 96, -32, - 16, -112, 80, -48, 48, -80, 112, -16, - 8, -120, 72, -56, 40, -88, 104, -24, - 24, -104, 88, -40, 56, -72, 120, -8, - 4, -124, 68, -60, 36, -92, 100, -28, - 20, -108, 84, -44, 52, -76, 116, -12, - 12, -116, 76, -52, 44, -84, 108, -20, - 28, -100, 92, -36, 60, -68, 124, -4, - 2, -126, 66, -62, 34, -94, 98, -30, - 18, -110, 82, -46, 50, -78, 114, -14, - 10, -118, 74, -54, 42, -86, 106, -22, - 26, -102, 90, -38, 58, -70, 122, -6, - 6, -122, 70, -58, 38, -90, 102, -26, - 22, -106, 86, -42, 54, -74, 118, -10, - 14, -114, 78, -50, 46, -82, 110, -18, - 30, -98, 94, -34, 62, -66, 126, -2, - 1, -127, 65, -63, 33, -95, 97, -31, - 17, -111, 81, -47, 49, -79, 113, -15, - 9, -119, 73, -55, 41, -87, 105, -23, - 25, -103, 89, -39, 57, -71, 121, -7, - 5, -123, 69, -59, 37, -91, 101, -27, - 21, -107, 85, -43, 53, -75, 117, -11, - 13, -115, 77, -51, 45, -83, 109, -19, - 29, -99, 93, -35, 61, -67, 125, -3, - 3, -125, 67, -61, 35, -93, 99, -29, - 19, -109, 83, -45, 51, -77, 115, -13, - 11, -117, 75, -53, 43, -85, 107, -21, - 27, -101, 91, -37, 59, -69, 123, -5, - 7, -121, 71, -57, 39, -89, 103, -25, - 23, -105, 87, -41, 55, -73, 119, -9, - 15, -113, 79, -49, 47, -81, 111, -17, - 31, -97, 95, -33, 63, -65, 127, -1, - }; - // end -} diff --git a/src/java/org/apache/fop/image/XMLImage.java b/src/java/org/apache/fop/image/XMLImage.java deleted file mode 100644 index 261dc8494..000000000 --- a/src/java/org/apache/fop/image/XMLImage.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * 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.image; - -// Java -import org.w3c.dom.Document; -import javax.xml.parsers.SAXParserFactory; - -/** - * This is an implementation for XML-based images such as SVG. - * - * @see AbstractFopImage - * @see FopImage - */ -public class XMLImage extends AbstractFopImage { - - private Document doc; - private String namespace = ""; - - /** - * @see org.apache.fop.image.AbstractFopImage#AbstractFopImage(FopImage.ImageInfo) - */ - public XMLImage(FopImage.ImageInfo imgInfo) { - super(imgInfo); - if (imgInfo.data instanceof Document) { - doc = (Document)imgInfo.data; - loaded = loaded | ORIGINAL_DATA; - } - namespace = imgInfo.str; - } - - /** - * Returns the fully qualified classname of an XML parser for - * Batik classes that apparently need it (error messages, perhaps) - * @return an XML parser classname - */ - public static String getParserName() { - try { - SAXParserFactory factory = SAXParserFactory.newInstance(); - return factory.newSAXParser().getXMLReader().getClass().getName(); - } catch (Exception e) { - return null; - } - } - - /** - * Returns the XML document as a DOM document. - * @return the DOM document - */ - public Document getDocument() { - return this.doc; - } - - /** - * Returns the namespace of the XML document. - * @return the namespace - */ - public String getNameSpace() { - return this.namespace; - } -} diff --git a/src/java/org/apache/fop/image/XmlGraphicsCommonsImage.java b/src/java/org/apache/fop/image/XmlGraphicsCommonsImage.java deleted file mode 100644 index 662673726..000000000 --- a/src/java/org/apache/fop/image/XmlGraphicsCommonsImage.java +++ /dev/null @@ -1,239 +0,0 @@ -/* - * 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.image; - -import java.awt.Color; -import java.awt.Transparency; -import java.awt.image.ColorModel; -import java.awt.image.IndexColorModel; -import java.awt.image.RenderedImage; -import java.awt.image.WritableRaster; -import java.awt.image.BufferedImage; -import java.io.IOException; - -import org.apache.xmlgraphics.image.GraphicsUtil; -import org.apache.xmlgraphics.image.codec.util.SeekableStream; -import org.apache.xmlgraphics.image.codec.util.MemoryCacheSeekableStream; -import org.apache.xmlgraphics.image.codec.util.FileCacheSeekableStream; -import org.apache.xmlgraphics.image.rendered.CachableRed; - -import org.apache.commons.io.IOUtils; - -/** - * Abstract FopImage implementation which uses the internal codecs from XML Graphics Commons. - * - * @see AbstractFopImage - * @see FopImage - */ -public abstract class XmlGraphicsCommonsImage extends AbstractFopImage { - - private byte[] softMask = null; - - /** - * The InputStream wrapped into a SeekableStream for decoding. - */ - protected SeekableStream seekableInput = null; - - /** - * The Batik representation of the image - */ - protected CachableRed cr = null; - - /** - * Constructs a new BatikImage instance. - * @param imgReader basic metadata for the image - */ - public XmlGraphicsCommonsImage(FopImage.ImageInfo imgReader) { - super(imgReader); - } - - /** - * {@inheritDoc} - */ - protected boolean loadDimensions() { - if (seekableInput == null && inputStream != null) { - try { - seekableInput = new FileCacheSeekableStream(inputStream); - } catch (IOException ioe) { - seekableInput = new MemoryCacheSeekableStream(inputStream); - } - try { - this.bitsPerPixel = 8; - cr = decodeImage(seekableInput); - this.height = cr.getHeight(); - this.width = cr.getWidth(); - this.isTransparent = false; - this.softMask = null; - ColorModel cm = cr.getColorModel(); - - this.height = cr.getHeight(); - this.width = cr.getWidth(); - this.isTransparent = false; - this.softMask = null; - - int transparencyType = cm.getTransparency(); - if (cm instanceof IndexColorModel) { - if (transparencyType == Transparency.BITMASK) { - // Use 'transparent color'. - IndexColorModel icm = (IndexColorModel)cm; - int numColor = icm.getMapSize(); - byte [] alpha = new byte[numColor]; - icm.getAlphas(alpha); - for (int i = 0; i < numColor; i++) { - if ((alpha[i] & 0xFF) == 0) { - this.isTransparent = true; - int red = (icm.getRed (i)) & 0xFF; - int grn = (icm.getGreen(i)) & 0xFF; - int blu = (icm.getBlue (i)) & 0xFF; - this.transparentColor = new Color(red, grn, blu); - break; - } - } - } - } else { - cr = GraphicsUtil.convertTosRGB(cr); - } - - // Get our current ColorModel - cm = cr.getColorModel(); - if (this.colorSpace == null) { - this.colorSpace = cm.getColorSpace(); - } - } catch (IOException ioe) { - log.error("Error while loading image (Batik): " + ioe.getMessage(), ioe); - IOUtils.closeQuietly(seekableInput); - IOUtils.closeQuietly(inputStream); - seekableInput = null; - inputStream = null; - return false; - } - } - return this.height != -1; - } - - /** - * {@inheritDoc} - */ - protected boolean loadBitmap() { - if (this.bitmaps == null) { - loadImage(); - } - - return this.bitmaps != null; - } - - /** - * {@inheritDoc} - */ - public boolean hasSoftMask() { - if (this.bitmaps == null && this.raw == null) { - loadImage(); - } - - return (this.softMask != null); - } - - /** - * {@inheritDoc} - */ - public byte[] getSoftMask() { - if (this.bitmaps == null) { - loadImage(); - } - - return this.softMask; - } - - /** - * Decodes the image from the stream. - * @param stream the stream to read the image from - * @return the decoded image - * @throws IOException in case an I/O problem occurs - */ - protected abstract CachableRed decodeImage(SeekableStream stream) throws IOException; - - /** - * Loads the image from the InputStream. - */ - protected void loadImage() { - if (loadDimensions()) { - try { - if (cr == null) { - throw new IllegalStateException( - "Can't load the bitmaps data without the CachableRed instance"); - } - - // Get our current ColorModel - ColorModel cm = cr.getColorModel(); - - // It has an alpha channel so generate a soft mask. - if (!this.isTransparent && cm.hasAlpha()) { - this.softMask = new byte[this.width * this.height]; - } - - this.bitmaps = new byte[this.width * this.height * 3]; - - constructBitmaps(cr, this.bitmaps, this.softMask); - } catch (Exception ex) { - log.error("Error while loading image (Batik): " + ex.getMessage(), ex); - } finally { - // Make sure we clean up - IOUtils.closeQuietly(seekableInput); - IOUtils.closeQuietly(inputStream); - seekableInput = null; - inputStream = null; - cr = null; - } - } - } - - private static void constructBitmaps(RenderedImage red, byte[] bitmaps, byte[] softMask) { - WritableRaster wr = (WritableRaster)red.getData(); - ColorModel cm = red.getColorModel(); - BufferedImage bi = new BufferedImage - (cm, wr.createWritableTranslatedChild(0, 0), - cm.isAlphaPremultiplied(), null); - int width = red.getWidth(); - int height = red.getHeight(); - int [] tmpMap = new int[width]; - int idx = 0; - int sfIdx = 0; - for (int y = 0; y < height; y++) { - tmpMap = bi.getRGB(0, y, width, 1, tmpMap, 0, width); - if (softMask != null) { - for (int x = 0; x < width; x++) { - int pix = tmpMap[x]; - softMask[sfIdx++] = (byte)(pix >>> 24); - bitmaps[idx++] = (byte)((pix >>> 16) & 0xFF); - bitmaps[idx++] = (byte)((pix >>> 8) & 0xFF); - bitmaps[idx++] = (byte)((pix) & 0xFF); - } - } else { - for (int x = 0; x < width; x++) { - int pix = tmpMap[x]; - bitmaps[idx++] = (byte)((pix >> 16) & 0xFF); - bitmaps[idx++] = (byte)((pix >> 8) & 0xFF); - bitmaps[idx++] = (byte)((pix) & 0xFF); - } - } - } - } - -}
\ No newline at end of file diff --git a/src/java/org/apache/fop/image/analyser/BMPReader.java b/src/java/org/apache/fop/image/analyser/BMPReader.java deleted file mode 100644 index caca6f3dd..000000000 --- a/src/java/org/apache/fop/image/analyser/BMPReader.java +++ /dev/null @@ -1,134 +0,0 @@ -/* - * 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.image.analyser; - -// Java -import java.io.InputStream; -import java.io.IOException; - -// FOP -import org.apache.fop.image.FopImage; -import org.apache.fop.apps.FOUserAgent; - -/** - * ImageReader object for BMP image type. - * - * @author Pankaj Narula - * @version $Id$ - */ -public class BMPReader implements ImageReader { - - /** Length of the BMP header */ - protected static final int BMP_SIG_LENGTH = 46; - - /** offset to width */ - private static final int WIDTH_OFFSET = 18; - /** offset to height */ - private static final int HEIGHT_OFFSET = 22; - /** offset to horizontal res */ - private static final int HRES_OFFSET = 38; - /** offset to vertical res */ - private static final int VRES_OFFSET = 42; - - /** {@inheritDoc} */ - public FopImage.ImageInfo verifySignature(String uri, InputStream bis, - FOUserAgent ua) throws IOException { - byte[] header = getDefaultHeader(bis); - boolean supported = ((header[0] == (byte) 0x42) - && (header[1] == (byte) 0x4d)); - if (supported) { - FopImage.ImageInfo info = new FopImage.ImageInfo(); - info.dpiHorizontal = ua.getFactory().getSourceResolution(); - info.dpiVertical = info.dpiHorizontal; - - getDimension(header, info); - info.originalURI = uri; - info.mimeType = getMimeType(); - info.inputStream = bis; - return info; - } else { - return null; - } - } - - /** - * Returns the MIME type supported by this implementation. - * - * @return The MIME type - */ - public String getMimeType() { - return "image/bmp"; - } - - private void getDimension(byte[] header, FopImage.ImageInfo info) { - // little endian notation - int byte1 = header[WIDTH_OFFSET] & 0xff; - int byte2 = header[WIDTH_OFFSET + 1] & 0xff; - int byte3 = header[WIDTH_OFFSET + 2] & 0xff; - int byte4 = header[WIDTH_OFFSET + 3] & 0xff; - long l = (long) ((byte4 << 24) | (byte3 << 16) - | (byte2 << 8) | byte1); - info.width = (int) (l & 0xffffffff); - - byte1 = header[HEIGHT_OFFSET] & 0xff; - byte2 = header[HEIGHT_OFFSET + 1] & 0xff; - byte3 = header[HEIGHT_OFFSET + 2] & 0xff; - byte4 = header[HEIGHT_OFFSET + 3] & 0xff; - l = (long) ((byte4 << 24) | (byte3 << 16) | (byte2 << 8) | byte1); - info.height = (int) (l & 0xffffffff); - - byte1 = header[HRES_OFFSET] & 0xff; - byte2 = header[HRES_OFFSET + 1] & 0xff; - byte3 = header[HRES_OFFSET + 2] & 0xff; - byte4 = header[HRES_OFFSET + 3] & 0xff; - l = (long) ((byte4 << 24) | (byte3 << 16) | (byte2 << 8) | byte1); - if (l > 0) { - info.dpiHorizontal = l / 39.37d; - } - - byte1 = header[VRES_OFFSET] & 0xff; - byte2 = header[VRES_OFFSET + 1] & 0xff; - byte3 = header[VRES_OFFSET + 2] & 0xff; - byte4 = header[VRES_OFFSET + 3] & 0xff; - l = (long) ((byte4 << 24) | (byte3 << 16) | (byte2 << 8) | byte1); - if (l > 0) { - info.dpiVertical = l / 39.37d; - } - } - - private byte[] getDefaultHeader(InputStream imageStream) - throws IOException { - byte[] header = new byte[BMP_SIG_LENGTH]; - try { - imageStream.mark(BMP_SIG_LENGTH + 1); - imageStream.read(header); - imageStream.reset(); - } catch (IOException ex) { - try { - imageStream.reset(); - } catch (IOException exbis) { - // throw the original exception, not this one - } - throw ex; - } - return header; - } - -} diff --git a/src/java/org/apache/fop/image/analyser/EMFReader.java b/src/java/org/apache/fop/image/analyser/EMFReader.java deleted file mode 100644 index d6e2f2975..000000000 --- a/src/java/org/apache/fop/image/analyser/EMFReader.java +++ /dev/null @@ -1,162 +0,0 @@ -/* - * 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.image.analyser; - -// Java -import java.io.InputStream; -import java.io.IOException; - -// FOP -import org.apache.fop.image.FopImage; -import org.apache.fop.apps.FOUserAgent; - -/** - * ImageReader object for EMF image type. - * - * @author Peter Herweg - */ -public class EMFReader implements ImageReader { - - /** Length of the EMF header */ - protected static final int EMF_SIG_LENGTH = 88; - - /** offset to signature */ - private static final int SIGNATURE_OFFSET = 40; - /** offset to width */ - private static final int WIDTH_OFFSET = 32; - /** offset to height */ - private static final int HEIGHT_OFFSET = 36; - /** offset to horizontal resolution in pixel */ - private static final int HRES_PIXEL_OFFSET = 72; - /** offset to vertical resolution in pixel */ - private static final int VRES_PIXEL_OFFSET = 76; - /** offset to horizontal resolution in mm */ - private static final int HRES_MM_OFFSET = 80; - /** offset to vertical resolution in mm */ - private static final int VRES_MM_OFFSET = 84; - - /** {@inheritDoc} */ - public FopImage.ImageInfo verifySignature(String uri, InputStream bis, - FOUserAgent ua) throws IOException { - byte[] header = getDefaultHeader(bis); - boolean supported - = ( (header[SIGNATURE_OFFSET + 0] == (byte) 0x20) - && (header[SIGNATURE_OFFSET + 1] == (byte) 0x45) - && (header[SIGNATURE_OFFSET + 2] == (byte) 0x4D) - && (header[SIGNATURE_OFFSET + 3] == (byte) 0x46) ); - - if (supported) { - FopImage.ImageInfo info = getDimension(header); - info.originalURI = uri; - info.mimeType = getMimeType(); - info.inputStream = bis; - return info; - } else { - return null; - } - } - - /** - * Returns the MIME type supported by this implementation. - * - * @return The MIME type - */ - public String getMimeType() { - return "image/emf"; - } - - private FopImage.ImageInfo getDimension(byte[] header) { - FopImage.ImageInfo info = new FopImage.ImageInfo(); - long value = 0; - int byte1; - int byte2; - int byte3; - int byte4; - - // little endian notation - - //resolution - byte1 = header[HRES_MM_OFFSET] & 0xff; - byte2 = header[HRES_MM_OFFSET + 1] & 0xff; - byte3 = header[HRES_MM_OFFSET + 2] & 0xff; - byte4 = header[HRES_MM_OFFSET + 3] & 0xff; - long hresMM = (long) ((byte4 << 24) | (byte3 << 16) | (byte2 << 8) | byte1); - - byte1 = header[VRES_MM_OFFSET] & 0xff; - byte2 = header[VRES_MM_OFFSET + 1] & 0xff; - byte3 = header[VRES_MM_OFFSET + 2] & 0xff; - byte4 = header[VRES_MM_OFFSET + 3] & 0xff; - long vresMM = (long) ((byte4 << 24) | (byte3 << 16) | (byte2 << 8) | byte1); - - byte1 = header[HRES_PIXEL_OFFSET] & 0xff; - byte2 = header[HRES_PIXEL_OFFSET + 1] & 0xff; - byte3 = header[HRES_PIXEL_OFFSET + 2] & 0xff; - byte4 = header[HRES_PIXEL_OFFSET + 3] & 0xff; - long hresPixel = (long) ((byte4 << 24) | (byte3 << 16) | (byte2 << 8) | byte1); - - byte1 = header[VRES_PIXEL_OFFSET] & 0xff; - byte2 = header[VRES_PIXEL_OFFSET + 1] & 0xff; - byte3 = header[VRES_PIXEL_OFFSET + 2] & 0xff; - byte4 = header[VRES_PIXEL_OFFSET + 3] & 0xff; - long vresPixel = (long) ((byte4 << 24) | (byte3 << 16) | (byte2 << 8) | byte1); - - info.dpiHorizontal = hresPixel / (hresMM / 25.4f); - info.dpiVertical = vresPixel / (vresMM / 25.4f); - - //width - byte1 = header[WIDTH_OFFSET] & 0xff; - byte2 = header[WIDTH_OFFSET + 1] & 0xff; - byte3 = header[WIDTH_OFFSET + 2] & 0xff; - byte4 = header[WIDTH_OFFSET + 3] & 0xff; - value = (long) ((byte4 << 24) | (byte3 << 16) - | (byte2 << 8) | byte1); - value = Math.round(value / 100f / 25.4f * info.dpiHorizontal); - info.width = (int) (value & 0xffffffff); - - //height - byte1 = header[HEIGHT_OFFSET] & 0xff; - byte2 = header[HEIGHT_OFFSET + 1] & 0xff; - byte3 = header[HEIGHT_OFFSET + 2] & 0xff; - byte4 = header[HEIGHT_OFFSET + 3] & 0xff; - value = (long) ((byte4 << 24) | (byte3 << 16) | (byte2 << 8) | byte1); - value = Math.round(value / 100f / 25.4f * info.dpiVertical); - info.height = (int) (value & 0xffffffff); - - return info; - } - - private byte[] getDefaultHeader(InputStream imageStream) - throws IOException { - byte[] header = new byte[EMF_SIG_LENGTH]; - try { - imageStream.mark(EMF_SIG_LENGTH + 1); - imageStream.read(header); - imageStream.reset(); - } catch (IOException ex) { - try { - imageStream.reset(); - } catch (IOException exbis) { - // throw the original exception, not this one - } - throw ex; - } - return header; - } -} diff --git a/src/java/org/apache/fop/image/analyser/EPSReader.java b/src/java/org/apache/fop/image/analyser/EPSReader.java deleted file mode 100644 index 92260d1ab..000000000 --- a/src/java/org/apache/fop/image/analyser/EPSReader.java +++ /dev/null @@ -1,253 +0,0 @@ -/* - * 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.image.analyser; - -// Java -import java.io.InputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; - -// FOP -import org.apache.commons.io.IOUtils; -import org.apache.fop.image.FopImage; -import org.apache.fop.image.EPSImage; -import org.apache.fop.apps.FOUserAgent; - -/** - * ImageReader object for EPS document image type. - * - * @version $Id$ - */ -public class EPSReader implements ImageReader { - - private static final byte[] EPS_HEADER_ASCII = "%!PS".getBytes(); - private static final byte[] BOUNDINGBOX = "%%BoundingBox: ".getBytes(); - //private static final byte[] HIRESBOUNDINGBOX = "%%HiResBoundingBox: ".getBytes(); - //TODO Implement HiResBoundingBox, ImageInfo probably needs some changes for that - - /** {@inheritDoc} */ - public FopImage.ImageInfo verifySignature(String uri, InputStream bis, - FOUserAgent ua) throws IOException { - - boolean isEPS = false; - - bis.mark(32); - byte[] header = new byte[30]; - bis.read(header, 0, 30); - bis.reset(); - - EPSImage.EPSData data = new EPSImage.EPSData(); - - // Check if binary header - if (getLong(header, 0) == 0xC6D3D0C5) { - data.isAscii = false; - isEPS = true; - - data.psStart = getLong(header, 4); - data.psLength = getLong(header, 8); - data.wmfStart = getLong(header, 12); - data.wmfLength = getLong(header, 16); - data.tiffStart = getLong(header, 20); - data.tiffLength = getLong(header, 24); - - } else { - // Check if plain ascii - byte[] epsh = "%!PS".getBytes(); - if (EPS_HEADER_ASCII[0] == header[0] - && EPS_HEADER_ASCII[1] == header[1] - && EPS_HEADER_ASCII[2] == header[2] - && EPS_HEADER_ASCII[3] == header[3]) { - data.isAscii = true; - isEPS = true; - } - } - - if (isEPS) { - FopImage.ImageInfo info = new FopImage.ImageInfo(); - info.originalURI = uri; - info.mimeType = getMimeType(); - info.data = data; - readEPSImage(bis, data); - data.bbox = readBBox(data); - - if (data.bbox != null) { - info.width = (int) (data.bbox[2] - data.bbox[0]); - info.height = (int) (data.bbox[3] - data.bbox[1]); - - // image data read - IOUtils.closeQuietly(bis); - info.inputStream = null; - - return info; - } else { - // Ain't eps if no BoundingBox - isEPS = false; - } - } - - return null; - } - - /** - * Returns the MIME type supported by this implementation. - * - * @return The MIME type - */ - public String getMimeType() { - return "image/eps"; - } - - private long getLong(byte[] buf, int idx) { - int b1 = buf[idx] & 0xff; - int b2 = buf[idx + 1] & 0xff; - int b3 = buf[idx + 2] & 0xff; - int b4 = buf[idx + 3] & 0xff; - - return (long) ((b4 << 24) | (b3 << 16) | (b2 << 8) | b1); - } - - /** - * Read the eps file and extract eps part. - * - * @param bis The InputStream - * @param data EPSData object to write the results to - * @exception IOException If an I/O error occurs - */ - private void readEPSImage(InputStream bis, EPSImage.EPSData data) - throws IOException { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - byte[] file; - byte[] readBuf = new byte[20480]; - int bytesRead; - int index = 0; - boolean cont = true; - - try { - while ((bytesRead = bis.read(readBuf)) != -1) { - baos.write(readBuf, 0, bytesRead); - } - } catch (java.io.IOException ex) { - throw new IOException("Error while loading EPS image: " - + ex.getMessage()); - } - - file = baos.toByteArray(); - - if (data.isAscii) { - data.rawEps = null; - data.epsFile = new byte[file.length]; - System.arraycopy(file, 0, data.epsFile, 0, data.epsFile.length); - } else { - data.rawEps = new byte[file.length]; - data.epsFile = new byte[(int) data.psLength]; - System.arraycopy(file, 0, data.rawEps, 0, data.rawEps.length); - System.arraycopy(data.rawEps, (int) data.psStart, data.epsFile, 0, - (int) data.psLength); - } - } - - /** - * Get embedded TIFF preview or null. - * - * @param data The EPS payload - * @return The embedded preview - */ - public byte[] getPreview(EPSImage.EPSData data) { - if (data.preview == null) { - if (data.tiffLength > 0) { - data.preview = new byte[(int) data.tiffLength]; - System.arraycopy(data.rawEps, (int) data.tiffStart, data.preview, 0, - (int) data.tiffLength); - } - } - return data.preview; - } - - /** - * Extract bounding box from eps part. - * - * @param data The EPS payload - * @return An Array of four coordinates making up the bounding box - */ - private long[] readBBox(EPSImage.EPSData data) { - long[] mbbox = null; - int idx = 0; - boolean found = false; - - while (!found && (data.epsFile.length > (idx + BOUNDINGBOX.length))) { - boolean sfound = true; - int i = idx; - for (i = idx; sfound && (i - idx) < BOUNDINGBOX.length; i++) { - if (BOUNDINGBOX[i - idx] != data.epsFile[i]) { - sfound = false; - } - } - if (sfound) { - found = true; - idx = i; - } else { - idx++; - } - } - - if (!found) { - return mbbox; - } - - mbbox = new long[4]; - idx += readLongString(data, mbbox, 0, idx); - idx += readLongString(data, mbbox, 1, idx); - idx += readLongString(data, mbbox, 2, idx); - idx += readLongString(data, mbbox, 3, idx); - - return mbbox; - } - - private int readLongString(EPSImage.EPSData data, long[] mbbox, int i, int idx) { - while (idx < data.epsFile.length && (data.epsFile[idx] == 32)) { - idx++; - } - - int nidx = idx; - - // check also for ANSI46(".") to identify floating point values - while (nidx < data.epsFile.length - && ((data.epsFile[nidx] >= 48 && data.epsFile[nidx] <= 57) - || (data.epsFile[nidx] == 45) - || (data.epsFile[nidx] == 46))) { - nidx++; - } - - byte[] num = new byte[nidx - idx]; - System.arraycopy(data.epsFile, idx, num, 0, nidx - idx); - String ns = new String(num); - - //if( ns.indexOf(".") != -1 ) { - // do something like logging a warning - //} - - // then parse the double and round off to the next math. Integer - mbbox[i] = (long) Math.ceil(Double.parseDouble(ns)); - - return (1 + nidx - idx); - } - -} - diff --git a/src/java/org/apache/fop/image/analyser/GIFReader.java b/src/java/org/apache/fop/image/analyser/GIFReader.java deleted file mode 100644 index d8da89607..000000000 --- a/src/java/org/apache/fop/image/analyser/GIFReader.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * 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.image.analyser; - -// Java -import java.io.InputStream; -import java.io.IOException; - -// FOP -import org.apache.fop.image.FopImage; -import org.apache.fop.apps.FOUserAgent; - -/** - * ImageReader object for GIF image type. - * - * @author Pankaj Narula - * @version $Id$ - */ -public class GIFReader implements ImageReader { - - private static final int GIF_SIG_LENGTH = 10; - - /** {@inheritDoc} */ - public FopImage.ImageInfo verifySignature(String uri, InputStream bis, - FOUserAgent ua) throws IOException { - byte[] header = getDefaultHeader(bis); - boolean supported = ((header[0] == 'G') - && (header[1] == 'I') - && (header[2] == 'F') - && (header[3] == '8') - && (header[4] == '7' || header[4] == '9') - && (header[5] == 'a')); - if (supported) { - FopImage.ImageInfo info = new FopImage.ImageInfo(); - info.dpiHorizontal = ua.getFactory().getSourceResolution(); - info.dpiVertical = info.dpiHorizontal; - - getDimension(header, info); - info.originalURI = uri; - info.mimeType = getMimeType(); - info.inputStream = bis; - return info; - } else { - return null; - } - } - - /** - * Returns the MIME type supported by this implementation. - * - * @return The MIME type - */ - public String getMimeType() { - return "image/gif"; - } - - private void getDimension(byte[] header, FopImage.ImageInfo info) { - // little endian notation - int byte1 = header[6] & 0xff; - int byte2 = header[7] & 0xff; - info.width = ((byte2 << 8) | byte1) & 0xffff; - - byte1 = header[8] & 0xff; - byte2 = header[9] & 0xff; - info.height = ((byte2 << 8) | byte1) & 0xffff; - } - - private byte[] getDefaultHeader(InputStream imageStream) - throws IOException { - byte[] header = new byte[GIF_SIG_LENGTH]; - try { - imageStream.mark(GIF_SIG_LENGTH + 1); - imageStream.read(header); - imageStream.reset(); - } catch (IOException ex) { - try { - imageStream.reset(); - } catch (IOException exbis) { - // throw the original exception, not this one - } - throw ex; - } - return header; - } - -} - diff --git a/src/java/org/apache/fop/image/analyser/ImageReader.java b/src/java/org/apache/fop/image/analyser/ImageReader.java deleted file mode 100644 index 980d1e489..000000000 --- a/src/java/org/apache/fop/image/analyser/ImageReader.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * 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.image.analyser; - -// Java -import java.io.InputStream; -import java.io.IOException; - -// FOP -import org.apache.fop.image.FopImage; -import org.apache.fop.apps.FOUserAgent; - -/** - * ImageReader objects read image headers to determine the image size. - * - * @author Pankaj Narula - * @version $Id$ - */ -public interface ImageReader { - - /** - * Verify image type. If the stream does not contain image data expected by - * the reader it must reset the stream to the start. This is so that the - * next reader can start reading from the start. The reader must not close - * the stream unless it can handle the image and it has read the - * information. - * - * @param bis Image buffered input stream - * @param uri URI to the image - * @param ua The user agent - * @return <code>true</code> if image type is the handled one - * @exception IOException if an I/O error occurs - */ - FopImage.ImageInfo verifySignature(String uri, InputStream bis, - FOUserAgent ua) - throws IOException; - -} - diff --git a/src/java/org/apache/fop/image/analyser/ImageReaderFactory.java b/src/java/org/apache/fop/image/analyser/ImageReaderFactory.java deleted file mode 100644 index c07d68d39..000000000 --- a/src/java/org/apache/fop/image/analyser/ImageReaderFactory.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * 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.image.analyser; - -// Java -import java.io.IOException; -import java.io.InputStream; -import java.util.Iterator; -import java.util.List; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.apache.fop.apps.FOUserAgent; -import org.apache.fop.image.FopImage; -import org.apache.xmlgraphics.util.Service; - -/** - * Factory for ImageReader objects. - * - * @author Pankaj Narula - * @version $Id$ - */ -public class ImageReaderFactory { - - private static List formats = new java.util.ArrayList(); - - /** logger */ - protected static Log log = LogFactory.getLog(ImageReaderFactory.class); - - static { - registerFormat(new JPEGReader()); - registerFormat(new BMPReader()); - registerFormat(new GIFReader()); - registerFormat(new PNGReader()); - registerFormat(new TIFFReader()); - registerFormat(new EPSReader()); - registerFormat(new EMFReader()); - - //Dynamic registration of ImageReaders - Iterator iter = Service.providers(ImageReader.class, true); - while (iter.hasNext()) { - registerFormat((ImageReader)iter.next()); - } - - // the xml parser through batik closes the stream when finished - // so there is a workaround in the SVGReader - registerFormat(new SVGReader()); - registerFormat(new SVGZReader()); - registerFormat(new XMLReader()); - } - - /** - * Registers a new ImageReader. - * - * @param reader An ImageReader instance - */ - public static void registerFormat(ImageReader reader) { - formats.add(reader); - } - - /** - * ImageReader maker. - * - * @param uri URI to the image - * @param in image input stream - * @param ua user agent - * @return An ImageInfo object describing the image - */ - public static FopImage.ImageInfo make(String uri, InputStream in, - FOUserAgent ua) { - - ImageReader reader; - try { - for (int count = 0; count < formats.size(); count++) { - reader = (ImageReader) formats.get(count); - FopImage.ImageInfo info = reader.verifySignature(uri, in, ua); - if (info != null) { - return info; - } - } - log.warn("No ImageReader found for " + uri); - in.close(); - } catch (IOException ex) { - log.error("Error while recovering Image Informations (" - + uri + ")", ex); - } - return null; - } - -} - diff --git a/src/java/org/apache/fop/image/analyser/JPEGReader.java b/src/java/org/apache/fop/image/analyser/JPEGReader.java deleted file mode 100644 index c90f6b000..000000000 --- a/src/java/org/apache/fop/image/analyser/JPEGReader.java +++ /dev/null @@ -1,264 +0,0 @@ -/* - * 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.image.analyser; - -// Java -import java.io.InputStream; -import java.io.IOException; - -// FOP -import org.apache.fop.image.FopImage; -import org.apache.fop.apps.FOUserAgent; - -/** - * ImageReader object for JPEG image type. - * - * @author Pankaj Narula - * @version $Id$ - */ -public class JPEGReader implements ImageReader { - - /** - * Only SOFn and APPn markers are defined as SOFn is needed for the height and - * width search. APPn is also defined because if the JPEG contains thumbnails - * the dimensions of the thumnail would also be after the SOFn marker enclosed - * inside the APPn marker. And we don't want to confuse those dimensions with - * the image dimensions. - */ - private static final int MARK = 0xff; // Beginning of a Marker - private static final int NULL = 0x00; // Special case for 0xff00 - private static final int SOF1 = 0xc0; // Baseline DCT - private static final int SOF2 = 0xc1; // Extended Sequential DCT - private static final int SOF3 = 0xc2; // Progrssive DCT only PDF 1.3 - private static final int SOFA = 0xca; // Progressice DCT only PDF 1.3 - private static final int APP0 = 0xe0; // Application marker, JFIF - private static final int APPF = 0xef; // Application marker - private static final int SOS = 0xda; // Start of Scan - private static final int SOI = 0xd8; // start of Image - private static final int JPG_SIG_LENGTH = 2; - - /** {@inheritDoc} */ - public FopImage.ImageInfo verifySignature(String uri, InputStream fis, - FOUserAgent ua) throws IOException { - byte[] header = getDefaultHeader(fis); - boolean supported = ((header[0] == (byte) 0xff) - && (header[1] == (byte) 0xd8)); - if (supported) { - FopImage.ImageInfo info = new FopImage.ImageInfo(); - info.dpiHorizontal = ua.getFactory().getSourceResolution(); - info.dpiVertical = info.dpiHorizontal; - - getDimension(fis, info); - info.originalURI = uri; - info.mimeType = getMimeType(); - info.inputStream = fis; - return info; - } else { - return null; - } - } - - /** - * Returns the MIME type supported by this implementation. - * - * @return The MIME type - */ - public String getMimeType() { - return "image/jpeg"; - } - - private byte[] getDefaultHeader(InputStream imageStream) throws IOException { - byte[] header = new byte[JPG_SIG_LENGTH]; - try { - imageStream.mark(JPG_SIG_LENGTH + 1); - imageStream.read(header); - imageStream.reset(); - } catch (IOException ex) { - try { - imageStream.reset(); - } catch (IOException exbis) { - // throw the original exception, not this one - } - throw ex; - } - return header; - } - - private void getDimension(InputStream imageStream, - FopImage.ImageInfo info) - throws IOException { - try { - int pos=0, avail = imageStream.available(); - imageStream.mark(avail); - int marker = NULL; - long length, skipped; -outer: - while (true) { - do { - if (avail == 0) { - imageStream.reset(); - avail = 2*pos; - imageStream.mark(avail); - pos = (int)this.skip(imageStream, pos); - avail -= pos; - } - //Marker first byte (FF) - marker = imageStream.read(); - pos++; avail--; - } while (marker != MARK); - - do { - if (avail == 0) { - imageStream.reset(); - avail = 2*pos; - imageStream.mark(avail); - pos = (int)this.skip(imageStream, pos); - avail -= pos; - } - //Marker second byte - marker = imageStream.read(); - pos++; avail--; - } while (marker == MARK); - - switch (marker) { - case SOI: - break; - case NULL: - break; - case APP0: - if (avail < 14) { - imageStream.reset(); - avail = 2 * pos; - imageStream.mark(avail); - pos = (int)this.skip(imageStream, pos); - avail -= pos; - } - int reclen = this.read2bytes(imageStream); - pos += 2; avail -= 2; - this.skip(imageStream, 7); - pos += 7; avail -= 7; - int densityUnits = imageStream.read(); - pos++; avail--; - int xdensity = this.read2bytes(imageStream); - pos += 2; avail -= 2; - int ydensity = this.read2bytes(imageStream); - pos += 2; avail -= 2; - - if (densityUnits == 2) { - info.dpiHorizontal = xdensity * 28.3464567 / 72; //dpi - info.dpiVertical = ydensity * 28.3464567 / 72; //dpi - } else if (densityUnits == 1) { - info.dpiHorizontal = xdensity; - info.dpiVertical = ydensity; - } else { - // Use resolution specified in - // FOUserAgent.getFactory() (default 72dpi). - } - - int restlen = reclen - 12; - if (avail < restlen) { - imageStream.reset(); - avail = 2 * pos; - if (avail < pos + restlen + 10) { - avail = (int)(pos + restlen + 10); - } - imageStream.mark(avail); - pos = (int)this.skip(imageStream, pos); - avail -= pos; - } - skipped = this.skip(imageStream, restlen - 2); - pos += skipped; avail -= skipped; - if (skipped != restlen - 2) { - throw new IOException("Skipping Error"); - } - break; - case SOF1: - case SOF2: - case SOF3: // SOF3 and SOFA are only supported by PDF 1.3 - case SOFA: - while (avail < 7) { - imageStream.reset(); - avail = 2*pos; - imageStream.mark(avail); - pos = (int)this.skip(imageStream, pos); - avail -= pos; - } - this.skip(imageStream, 3); - pos+=3; avail-=3; - info.height = this.read2bytes(imageStream); - pos+=2; avail-=2; - info.width = this.read2bytes(imageStream); - pos+=2; avail-=2; - break outer; - default: - while (avail < 2) { - imageStream.reset(); - avail = 2*pos; - imageStream.mark(avail); - pos = (int)this.skip(imageStream, pos); - avail -= pos; - } - length = this.read2bytes(imageStream); - pos+=2; avail-=2; - if (avail < length) { - imageStream.reset(); - avail = 2*pos; - if (avail < pos+length+10) { - avail = (int)(pos+length+10); - } - imageStream.mark(avail); - pos = (int)this.skip(imageStream, pos); - avail -= pos; - } - skipped = this.skip(imageStream, length - 2); - pos += skipped; avail -= skipped; - if (skipped != length - 2) { - throw new IOException("Skipping Error"); - } - } - } - imageStream.reset(); - } catch (IOException ioe) { - try { - imageStream.reset(); - } catch (IOException exbis) { - // throw the original exception, not this one - } - throw ioe; - } - } - - private int read2bytes(InputStream imageStream) throws IOException { - int byte1 = imageStream.read(); - int byte2 = imageStream.read(); - return (int) ((byte1 << 8) | byte2); - } - - private long skip(InputStream imageStream, long n) throws IOException { - long discarded = 0; - while (discarded != n) { - imageStream.read(); - discarded++; - } - return discarded; // scope for exception - } - -} - diff --git a/src/java/org/apache/fop/image/analyser/PNGReader.java b/src/java/org/apache/fop/image/analyser/PNGReader.java deleted file mode 100644 index ab6c64775..000000000 --- a/src/java/org/apache/fop/image/analyser/PNGReader.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * 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.image.analyser; - -// Java -import java.io.InputStream; -import java.io.IOException; - -// FOP -import org.apache.fop.image.FopImage; -import org.apache.fop.apps.FOUserAgent; - -/** - * ImageReader object for PNG image type. - * - * @author Pankaj Narula - * @version $Id$ - */ -public class PNGReader implements ImageReader { - - private static final int PNG_SIG_LENGTH = 24; - - /** {@inheritDoc} */ - public FopImage.ImageInfo verifySignature(String uri, InputStream bis, - FOUserAgent ua) throws IOException { - byte[] header = getDefaultHeader(bis); - boolean supported = ((header[0] == (byte) 0x89) - && (header[1] == (byte) 0x50) - && (header[2] == (byte) 0x4e) - && (header[3] == (byte) 0x47) - && (header[4] == (byte) 0x0d) - && (header[5] == (byte) 0x0a) - && (header[6] == (byte) 0x1a) - && (header[7] == (byte) 0x0a)); - - if (supported) { - FopImage.ImageInfo info = new FopImage.ImageInfo(); - info.dpiHorizontal = ua.getFactory().getSourceResolution(); - info.dpiVertical = info.dpiHorizontal; - - getDimension(header, info); - info.originalURI = uri; - info.mimeType = getMimeType(); - info.inputStream = bis; - return info; - } else { - return null; - } - } - - /** - * Returns the MIME type supported by this implementation. - * - * @return The MIME type - */ - public String getMimeType() { - return "image/png"; - } - - private void getDimension(byte[] header, FopImage.ImageInfo info) { - // png is always big endian - int byte1 = header[16] & 0xff; - int byte2 = header[17] & 0xff; - int byte3 = header[18] & 0xff; - int byte4 = header[19] & 0xff; - long l = (long) ((byte1 << 24) - | (byte2 << 16) - | (byte3 << 8) - | (byte4)); - info.width = (int) l; - - byte1 = header[20] & 0xff; - byte2 = header[21] & 0xff; - byte3 = header[22] & 0xff; - byte4 = header[23] & 0xff; - l = (long) ((byte1 << 24) | (byte2 << 16) | (byte3 << 8) | byte4); - info.height = (int) l; - } - - private byte[] getDefaultHeader(InputStream imageStream) - throws IOException { - byte[] header = new byte[PNG_SIG_LENGTH]; - try { - imageStream.mark(PNG_SIG_LENGTH + 1); - imageStream.read(header); - imageStream.reset(); - } catch (IOException ex) { - try { - imageStream.reset(); - } catch (IOException exbis) { - // throw the original exception, not this one - } - throw ex; - } - return header; - } - -} diff --git a/src/java/org/apache/fop/image/analyser/SVGReader.java b/src/java/org/apache/fop/image/analyser/SVGReader.java deleted file mode 100644 index cffaa365c..000000000 --- a/src/java/org/apache/fop/image/analyser/SVGReader.java +++ /dev/null @@ -1,188 +0,0 @@ -/* - * 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.image.analyser; - -// Java -import java.io.InputStream; -import java.io.IOException; -import java.awt.geom.AffineTransform; - -// XML -import org.w3c.dom.Element; -import org.w3c.dom.svg.SVGDocument; - -// Batik -import org.apache.batik.dom.svg.SAXSVGDocumentFactory; -import org.apache.batik.dom.svg.SVGOMDocument; -import org.apache.batik.bridge.BridgeContext; -import org.apache.batik.bridge.UnitProcessor; -import org.apache.batik.dom.svg.SVGDOMImplementation; -import org.apache.commons.io.IOUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -// FOP -import org.apache.fop.image.XMLImage; -import org.apache.fop.image.FopImage; -import org.apache.fop.apps.FOUserAgent; -import org.apache.fop.svg.SVGUserAgent; -import org.apache.fop.util.UnclosableInputStream; - -/** - * ImageReader object for SVG document image type. - */ -public class SVGReader implements ImageReader { - - /** Logger instance */ - protected static Log log = LogFactory.getLog(SVGReader.class); - - /** SVG's MIME type */ - public static final String SVG_MIME_TYPE = "image/svg+xml"; - - private boolean batik = true; - - /** {@inheritDoc} */ - public FopImage.ImageInfo verifySignature(String uri, InputStream fis, - FOUserAgent ua) throws IOException { - FopImage.ImageInfo info = loadImage(uri, fis, ua); - if (info != null) { - IOUtils.closeQuietly(fis); - } - return info; - } - - /** - * Returns the MIME type supported by this implementation. - * - * @return The MIME type - */ - public String getMimeType() { - return SVG_MIME_TYPE; - } - - /** - * This means the external svg document will be loaded twice. Possibly need - * a slightly different design for the image stuff. - * - * @param uri @todo Description of the Parameter - * @param fis @todo Description of the Parameter - * @param ua @todo Description of the Parameter - * @return @todo Description of the Return Value - */ - private FopImage.ImageInfo loadImage(String uri, InputStream bis, - FOUserAgent ua) { - if (batik) { - try { - Loader loader = new Loader(); - return loader.getImage(uri, bis, - ua.getSourcePixelUnitToMillimeter()); - } catch (NoClassDefFoundError e) { - batik = false; - log.warn("Batik not in class path", e); - return null; - } - } - return null; - } - - /** - * This method is put in another class so that the classloader does not - * attempt to load batik related classes when constructing the SVGReader - * class. - */ - class Loader { - private FopImage.ImageInfo getImage(String uri, InputStream fis, - float pixelUnitToMM) { - // parse document and get the size attributes of the svg element - - try { - fis = new UnclosableInputStream(fis); - - FopImage.ImageInfo info = new FopImage.ImageInfo(); - - //Set the resolution to that of the FOUserAgent - info.dpiHorizontal = 25.4f / pixelUnitToMM; - info.dpiVertical = info.dpiHorizontal; - - info.originalURI = uri; - info.mimeType = getMimeType(); - info.str = SVGDOMImplementation.SVG_NAMESPACE_URI; - - int length = fis.available(); - fis.mark(length + 1); - SAXSVGDocumentFactory factory = new SAXSVGDocumentFactory( - XMLImage.getParserName()); - SVGDocument doc = (SVGDocument) factory.createSVGDocument(uri, fis); - info.data = doc; - - Element e = doc.getRootElement(); - String s; - SVGUserAgent userAg = new SVGUserAgent(pixelUnitToMM, - new AffineTransform()); - BridgeContext ctx = new BridgeContext(userAg); - UnitProcessor.Context uctx = UnitProcessor.createContext(ctx, e); - - // 'width' attribute - default is 100% - s = e.getAttributeNS(null, - SVGOMDocument.SVG_WIDTH_ATTRIBUTE); - if (s.length() == 0) { - s = SVGOMDocument.SVG_SVG_WIDTH_DEFAULT_VALUE; - } - info.width = Math.round(UnitProcessor.svgHorizontalLengthToUserSpace( - s, SVGOMDocument.SVG_WIDTH_ATTRIBUTE, uctx)); - - // 'height' attribute - default is 100% - s = e.getAttributeNS(null, - SVGOMDocument.SVG_HEIGHT_ATTRIBUTE); - if (s.length() == 0) { - s = SVGOMDocument.SVG_SVG_HEIGHT_DEFAULT_VALUE; - } - info.height = Math.round(UnitProcessor.svgVerticalLengthToUserSpace( - s, SVGOMDocument.SVG_HEIGHT_ATTRIBUTE, uctx)); - - return info; - } catch (NoClassDefFoundError ncdfe) { - try { - fis.reset(); - } catch (IOException ioe) { - // we're more interested in the original exception - } - batik = false; - log.warn("Batik not in class path", ncdfe); - return null; - } catch (IOException e) { - // If the svg is invalid then it throws an IOException - // so there is no way of knowing if it is an svg document - - log.debug("Error while trying to load stream as an SVG file: " - + e.getMessage()); - // assuming any exception means this document is not svg - // or could not be loaded for some reason - try { - fis.reset(); - } catch (IOException ioe) { - // we're more interested in the original exception - } - return null; - } - } - } - -} diff --git a/src/java/org/apache/fop/image/analyser/TIFFReader.java b/src/java/org/apache/fop/image/analyser/TIFFReader.java deleted file mode 100644 index 08d702780..000000000 --- a/src/java/org/apache/fop/image/analyser/TIFFReader.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * 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.image.analyser; - -// Java -import java.io.InputStream; -import java.io.IOException; - -// FOP -import org.apache.fop.image.FopImage; -import org.apache.fop.apps.FOUserAgent; - -/** - * ImageReader object for TIFF image type. - * - * @author Pankaj Narula, Michael Lee - * @version $Id$ - */ -public class TIFFReader implements ImageReader { - - private static final int TIFF_SIG_LENGTH = 8; - - /** {@inheritDoc} */ - public FopImage.ImageInfo verifySignature(String uri, InputStream bis, - FOUserAgent ua) throws IOException { - byte[] header = getDefaultHeader(bis); - boolean supported = false; - - // first 2 bytes = II (little endian encoding) - if (header[0] == (byte) 0x49 && header[1] == (byte) 0x49) { - - // look for '42' in byte 3 and '0' in byte 4 - if (header[2] == 42 && header[3] == 0) { - supported = true; - } - } - - // first 2 bytes == MM (big endian encoding) - if (header[0] == (byte) 0x4D && header[1] == (byte) 0x4D) { - - // look for '42' in byte 4 and '0' in byte 3 - if (header[2] == 0 && header[3] == 42) { - supported = true; - } - } - - if (supported) { - FopImage.ImageInfo info = new FopImage.ImageInfo(); - info.dpiHorizontal = ua.getFactory().getSourceResolution(); - info.dpiVertical = info.dpiHorizontal; - - getDimension(header, info); - info.originalURI = uri; - info.mimeType = getMimeType(); - info.inputStream = bis; - return info; - } else { - return null; - } - } - - /** - * Returns the MIME type supported by this implementation. - * - * @return The MIME type - */ - public String getMimeType() { - return "image/tiff"; - } - - private void getDimension(byte[] header, FopImage.ImageInfo info) { - // currently not setting the width and height - // these are set again by the Jimi image reader. - // I suppose I'll do it one day to be complete. Or - // someone else will. - // Note: bytes 4,5,6,7 contain the byte offset in the stream of the first IFD block - info.width = -1; - info.height = -1; - } - - private byte[] getDefaultHeader(InputStream imageStream) - throws IOException { - byte[] header = new byte[TIFF_SIG_LENGTH]; - try { - imageStream.mark(TIFF_SIG_LENGTH + 1); - imageStream.read(header); - imageStream.reset(); - } catch (IOException ex) { - try { - imageStream.reset(); - } catch (IOException exbis) { - // throw the original exception, not this one - } - throw ex; - } - return header; - } - -} - diff --git a/src/java/org/apache/fop/image/analyser/XMLReader.java b/src/java/org/apache/fop/image/analyser/XMLReader.java deleted file mode 100644 index 4d0145eed..000000000 --- a/src/java/org/apache/fop/image/analyser/XMLReader.java +++ /dev/null @@ -1,167 +0,0 @@ -/* - * 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.image.analyser; - -// Java -import java.io.InputStream; -import java.io.IOException; -import java.util.Map; - -// XML -import javax.xml.parsers.DocumentBuilderFactory; -import org.w3c.dom.Document; -import org.w3c.dom.Element; - -// FOP -import org.apache.fop.image.FopImage; -import org.apache.fop.util.UnclosableInputStream; -import org.apache.fop.apps.FOUserAgent; - -// Commons-Logging -import org.apache.commons.io.IOUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -/** ImageReader object for XML document image type. */ -public class XMLReader implements ImageReader { - - /** - * logging instance - */ - private Log log = LogFactory.getLog(XMLReader.class); - - private static Map converters = new java.util.HashMap(); - - /** - * Registers a Converter implementation with XMLReader. - * - * @param ns The namespace to associate with this converter - * @param conv The actual Converter implementation - */ - public static void setConverter(String ns, Converter conv) { - converters.put(ns, conv); - } - - /** {@inheritDoc} */ - public FopImage.ImageInfo verifySignature(String uri, InputStream fis, - FOUserAgent ua) - throws IOException { - FopImage.ImageInfo info = loadImage(uri, fis, ua); - if (info != null) { - info.originalURI = uri; - IOUtils.closeQuietly(fis); - } - return info; - } - - /** - * Returns the MIME type supported by this implementation. - * - * @return The MIME type - */ - public String getMimeType() { - return "text/xml"; - } - - /** - * Creates an ImageInfo object from an XML image read from a stream. - * - * (todo) This means the external svg document will be loaded twice. Possibly need - * a slightly different design for the image stuff. - * - * @param uri The URI to the image - * @param bis The InputStream - * @param ua The user agent - * @return An ImageInfo object describing the image - */ - protected FopImage.ImageInfo loadImage(String uri, InputStream bis, - FOUserAgent ua) { - return createDocument(bis, ua); - } - - /** - * Creates an ImageInfo object from an XML image read from a stream. - * - * @param input The InputStream - * @param ua The user agent - * @return An ImageInfo object describing the image - */ - public FopImage.ImageInfo createDocument(final InputStream input, final FOUserAgent ua) { - Document doc = null; - FopImage.ImageInfo info = new FopImage.ImageInfo(); - info.mimeType = getMimeType(); - - try { - final InputStream is = new UnclosableInputStream(input); - int length = is.available(); - is.mark(length); - - DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); - doc = dbf.newDocumentBuilder().parse(is); - info.data = doc; - - Element root = doc.getDocumentElement(); - log.debug("XML image namespace: " + root.getAttribute("xmlns")); - String ns = root.getAttribute("xmlns"); - info.str = ns; - - Converter conv = (Converter) converters.get(ns); - if (conv != null) { - FopImage.ImageInfo i = conv.convert(doc); - if (i != null) { - info = i; - } - } - } catch (Exception e) { - log.debug("Error while constructing image from XML", e); - try { - input.reset(); - } catch (IOException ioe) { - // throw the original exception, not this one - } - return null; - } - if (info != null) { - try { - input.close(); - } catch (IOException io) { - // ignore - } - } - return info; - } - - /** - * This interface is to be implemented for XML to image converters. - */ - public static interface Converter { - - /** - * This method is called for a DOM document to be converted into an - * ImageInfo object. - * - * @param doc The DOM document to convert - * @return An ImageInfo object describing the image - */ - FopImage.ImageInfo convert(Document doc); - } - -} - diff --git a/src/java/org/apache/fop/image/analyser/package.html b/src/java/org/apache/fop/image/analyser/package.html deleted file mode 100644 index 677a625b2..000000000 --- a/src/java/org/apache/fop/image/analyser/package.html +++ /dev/null @@ -1,23 +0,0 @@ -<!-- - 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$ --> -<HTML> -<TITLE>org.apache.fop.image.analyser Package</TITLE> -<BODY> -<P>Image analyzers for determining the format of an image and to preload its intrinsic size.</P> -</BODY> -</HTML>
\ No newline at end of file diff --git a/src/java/org/apache/fop/image/package.html b/src/java/org/apache/fop/image/package.html index 0145864b2..cbd2d7c5e 100644 --- a/src/java/org/apache/fop/image/package.html +++ b/src/java/org/apache/fop/image/package.html @@ -18,6 +18,6 @@ <HTML> <TITLE>org.apache.fop.image Package</TITLE> <BODY> -<P>Contains image loading adapters for various image sources and the image cache.</P> +<P>Contains image loading adapters for various image sources.</P> </BODY> </HTML>
\ No newline at end of file diff --git a/src/java/org/apache/fop/layoutmgr/AbstractBreaker.java b/src/java/org/apache/fop/layoutmgr/AbstractBreaker.java index 54fd315b1..65d537bcd 100644 --- a/src/java/org/apache/fop/layoutmgr/AbstractBreaker.java +++ b/src/java/org/apache/fop/layoutmgr/AbstractBreaker.java @@ -272,7 +272,7 @@ public abstract class AbstractBreaker { */ public void doLayout(int flowBPD, boolean autoHeight) { LayoutContext childLC = createLayoutContext(); - childLC.setStackLimit(new MinOptMax(flowBPD)); + childLC.setStackLimitBP(new MinOptMax(flowBPD)); if (getCurrentDisplayAlign() == Constants.EN_X_FILL) { //EN_X_FILL is non-standard (by LF) @@ -495,7 +495,7 @@ public abstract class AbstractBreaker { int averageLineLength = optimizeLineLength(effectiveList, startElementIndex, endElementIndex); if (averageLineLength != 0) { - childLC.setStackLimit(new MinOptMax(averageLineLength)); + childLC.setStackLimitBP(new MinOptMax(averageLineLength)); } } /* *** *** non-standard extension *** *** */ diff --git a/src/java/org/apache/fop/layoutmgr/AbstractLayoutManager.java b/src/java/org/apache/fop/layoutmgr/AbstractLayoutManager.java index f75fffc0d..656b5e2df 100644 --- a/src/java/org/apache/fop/layoutmgr/AbstractLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/AbstractLayoutManager.java @@ -113,7 +113,7 @@ public abstract class AbstractLayoutManager extends AbstractBaseLayoutManager if (curChildLM != null && !curChildLM.isFinished()) { return curChildLM; } - while (childLMiter.hasNext()) { + if (childLMiter.hasNext()) { curChildLM = (LayoutManager) childLMiter.next(); curChildLM.initialize(); return curChildLM; @@ -350,6 +350,10 @@ public abstract class AbstractLayoutManager extends AbstractBaseLayoutManager /** * Registers the FO's markers on the current PageViewport + * + * @param isStarting boolean indicating whether the markers qualify as 'starting' + * @param isFirst boolean indicating whether the markers qualify as 'first' + * @param isLast boolean indicating whether the markers qualify as 'last' */ protected void addMarkersToPage(boolean isStarting, boolean isFirst, boolean isLast) { if (this.markers != null) { @@ -361,10 +365,18 @@ public abstract class AbstractLayoutManager extends AbstractBaseLayoutManager } } + /** + * Registers the FO's id on the current PageViewport + */ + protected void addId() { + if (fobj != null) { + getPSLM().addIDToPage(fobj.getId()); + } + } + /** {@inheritDoc} */ public String toString() { return (super.toString() + (fobj != null ? "[fobj=" + fobj.toString() + "]" : "")); } - } diff --git a/src/java/org/apache/fop/layoutmgr/AreaAdditionUtil.java b/src/java/org/apache/fop/layoutmgr/AreaAdditionUtil.java index 80e0b74cc..1b0d02639 100644 --- a/src/java/org/apache/fop/layoutmgr/AreaAdditionUtil.java +++ b/src/java/org/apache/fop/layoutmgr/AreaAdditionUtil.java @@ -117,7 +117,7 @@ public class AreaAdditionUtil { // set space after for each LM, in order to implement // display-align = distribute lc.setSpaceAfter(layoutContext.getSpaceAfter()); - lc.setStackLimit(layoutContext.getStackLimit()); + lc.setStackLimitsFrom(layoutContext); childLM.addAreas(childPosIter, lc); } diff --git a/src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java b/src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java index fc60b561e..e8ca88c1c 100644 --- a/src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java @@ -38,7 +38,6 @@ import org.apache.fop.datatypes.Length; import org.apache.fop.fo.FONode; import org.apache.fop.fo.flow.BlockContainer; import org.apache.fop.fo.properties.CommonAbsolutePosition; -import org.apache.fop.layoutmgr.inline.InlineLayoutManager; import org.apache.fop.traits.MinOptMax; import org.apache.fop.traits.SpaceVal; @@ -201,7 +200,7 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager = (getBlockContainerFO().getReferenceOrientation() % 180 != 0); autoHeight = false; //boolean rotated = (getBlockContainerFO().getReferenceOrientation() % 180 != 0); - int maxbpd = context.getStackLimit().opt; + int maxbpd = context.getStackLimitBP().opt; int allocBPD; if (height.getEnum() == EN_AUTO || (!height.isAbsolute() && getAncestorBlockAreaBPD() <= 0)) { @@ -249,7 +248,7 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager MinOptMax stackLimit = new MinOptMax(relDims.bpd); - LinkedList returnedList = null; + LinkedList returnedList; LinkedList contentList = new LinkedList(); LinkedList returnList = new LinkedList(); @@ -280,8 +279,7 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager LayoutContext childLC = new LayoutContext(0); childLC.copyPendingMarksFrom(context); // curLM is a ? - childLC.setStackLimit(MinOptMax.subtract(context - .getStackLimit(), stackLimit)); + childLC.setStackLimitBP(MinOptMax.subtract(context.getStackLimitBP(), stackLimit)); childLC.setRefIPD(relDims.ipd); childLC.setWritingMode(getBlockContainerFO().getWritingMode()); @@ -388,6 +386,9 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager } addKnuthElementsForBorderPaddingAfter(returnList, true); addKnuthElementsForSpaceAfter(returnList, alignment); + + //All child content is processed. Only break-after can occur now, so... + context.clearPendingMarks(); addKnuthElementsForBreakAfter(returnList, context); setFinished(true); @@ -411,7 +412,7 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager if (isFixed()) { availHeight = (int)getCurrentPV().getViewArea().getHeight(); } else { - availHeight = context.getStackLimit().opt; + availHeight = context.getStackLimitBP().opt; } allocBPD = availHeight; allocBPD -= offset.y; @@ -444,7 +445,7 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager } } } else { - int maxbpd = context.getStackLimit().opt; + int maxbpd = context.getStackLimitBP().opt; allocBPD = maxbpd; if (!switchedProgressionDirection) { autoHeight = true; @@ -601,11 +602,7 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager } public boolean isOverflow() { - if (isEmpty()) { - return false; - } else { - return (deferredAlg.getPageBreaks().size() > 1); - } + return !isEmpty() && (deferredAlg.getPageBreaks().size() > 1); } protected LayoutManager getTopLevelLM() { @@ -625,7 +622,7 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager while ((curLM = getChildLM()) != null) { LayoutContext childLC = new LayoutContext(0); - childLC.setStackLimit(context.getStackLimit()); + childLC.setStackLimitBP(context.getStackLimitBP()); childLC.setRefIPD(context.getRefIPD()); childLC.setWritingMode(getBlockContainerFO().getWritingMode()); @@ -707,7 +704,7 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager addBlockSpacing(0.0, new MinOptMax(layoutContext.getSpaceBefore())); } - LayoutManager childLM = null; + LayoutManager childLM; LayoutManager lastLM = null; LayoutContext lc = new LayoutContext(0); lc.setSpaceAdjust(layoutContext.getSpaceAdjust()); @@ -736,7 +733,7 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager } Position innerPosition = pos; if (pos instanceof NonLeafPosition) { - innerPosition = ((NonLeafPosition)pos).getPosition(); + innerPosition = pos.getPosition(); } if (pos instanceof BlockContainerPosition) { if (bcpos != null) { @@ -772,7 +769,7 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager } } - getPSLM().addIDToPage(getBlockContainerFO().getId()); + addId(); addMarkersToPage(true, isFirst(firstPos), isLast(lastPos)); @@ -854,7 +851,7 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager // set last area flag lc.setFlags(LayoutContext.LAST_AREA, (layoutContext.isLastArea() && childLM == lastLM)); - /*LF*/lc.setStackLimit(layoutContext.getStackLimit()); + /*LF*/lc.setStackLimitBP(layoutContext.getStackLimitBP()); // Add the line areas to Area childLM.addAreas(childPosIter, lc); } @@ -873,7 +870,7 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager referenceArea = null; resetSpaces(); - getPSLM().notifyEndOfLayout(((BlockContainer)getFObj()).getId()); + getPSLM().notifyEndOfLayout(fobj.getId()); } /** @@ -992,30 +989,21 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager } - /** - * {@inheritDoc} - */ + /** {@inheritDoc} */ public boolean mustKeepTogether() { //TODO Keeps will have to be more sophisticated sooner or later - return (!getBlockContainerFO().getKeepTogether().getWithinPage().isAuto() - || !getBlockContainerFO().getKeepTogether().getWithinColumn().isAuto() - || (getParent() instanceof BlockLevelLayoutManager - && ((BlockLevelLayoutManager) getParent()).mustKeepTogether()) - || (getParent() instanceof InlineLayoutManager - && ((InlineLayoutManager) getParent()).mustKeepTogether())); + return super.mustKeepTogether() + || !getBlockContainerFO().getKeepTogether().getWithinPage().isAuto() + || !getBlockContainerFO().getKeepTogether().getWithinColumn().isAuto(); } - /** - * {@inheritDoc} - */ + /** {@inheritDoc} */ public boolean mustKeepWithPrevious() { return !getBlockContainerFO().getKeepWithPrevious().getWithinPage().isAuto() || !getBlockContainerFO().getKeepWithPrevious().getWithinColumn().isAuto(); } - /** - * {@inheritDoc} - */ + /** {@inheritDoc} */ public boolean mustKeepWithNext() { return !getBlockContainerFO().getKeepWithNext().getWithinPage().isAuto() || !getBlockContainerFO().getKeepWithNext().getWithinColumn().isAuto(); diff --git a/src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java b/src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java index bb39def8d..f5270107c 100644 --- a/src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java @@ -25,6 +25,7 @@ import java.util.ListIterator; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + import org.apache.fop.area.Area; import org.apache.fop.area.Block; import org.apache.fop.area.LineArea; @@ -146,7 +147,7 @@ public class BlockLayoutManager extends BlockStackingLayoutManager * @return true if there are more child lms */ public boolean hasNext() { - return (curPos < listLMs.size()) ? true : createNextChildLMs(curPos); + return (curPos < listLMs.size()) || createNextChildLMs(curPos); } /** @@ -249,7 +250,7 @@ public class BlockLayoutManager extends BlockStackingLayoutManager addBlockSpacing(0.0, new MinOptMax(layoutContext.getSpaceBefore())); } - LayoutManager childLM = null; + LayoutManager childLM; LayoutManager lastLM = null; LayoutContext lc = new LayoutContext(0); lc.setSpaceAdjust(layoutContext.getSpaceAdjust()); @@ -279,7 +280,7 @@ public class BlockLayoutManager extends BlockStackingLayoutManager Position innerPosition = pos; if (pos instanceof NonLeafPosition) { //Not all elements are wrapped - innerPosition = ((NonLeafPosition) pos).getPosition(); + innerPosition = pos.getPosition(); } if (innerPosition == null) { // pos was created by this BlockLM and was inside an element @@ -308,7 +309,7 @@ public class BlockLayoutManager extends BlockStackingLayoutManager } } - getPSLM().addIDToPage(getBlockFO().getId()); + addId(); addMarkersToPage(true, isFirst(firstPos), isLast(lastPos)); @@ -389,7 +390,7 @@ public class BlockLayoutManager extends BlockStackingLayoutManager // set last area flag lc.setFlags(LayoutContext.LAST_AREA, (layoutContext.isLastArea() && childLM == lastLM)); - lc.setStackLimit(layoutContext.getStackLimit()); + lc.setStackLimitBP(layoutContext.getStackLimitBP()); // Add the line areas to Area childLM.addAreas(childPosIter, lc); } diff --git a/src/java/org/apache/fop/layoutmgr/BlockStackingLayoutManager.java b/src/java/org/apache/fop/layoutmgr/BlockStackingLayoutManager.java index 5faad623c..67ed1de9f 100644 --- a/src/java/org/apache/fop/layoutmgr/BlockStackingLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/BlockStackingLayoutManager.java @@ -274,13 +274,14 @@ public abstract class BlockStackingLayoutManager extends AbstractLayoutManager if (curLM instanceof LineLayoutManager) { // curLM is a LineLayoutManager // set stackLimit for lines (stack limit is now i-p-direction, not b-p-direction!) - childLC.setStackLimit(new MinOptMax(getContentAreaIPD())); + childLC.setStackLimitBP(context.getStackLimitBP()); + childLC.setStackLimitIP(new MinOptMax(getContentAreaIPD())); childLC.setRefIPD(getContentAreaIPD()); } else { // curLM is a ? //childLC.setStackLimit(MinOptMax.subtract(context // .getStackLimit(), stackSize)); - childLC.setStackLimit(context.getStackLimit()); + childLC.setStackLimitBP(context.getStackLimitBP()); childLC.setRefIPD(referenceIPD); } @@ -293,15 +294,22 @@ public abstract class BlockStackingLayoutManager extends AbstractLayoutManager if (returnedList != null && returnedList.size() == 1 && ((ListElement) returnedList.getFirst()).isForcedBreak()) { - // a descendant of this block has break-before - contentList.addAll(returnedList); if (curLM.isFinished() && !hasNextChildLM()) { - forcedBreakAfterLast = (BreakElement)contentList.removeLast(); + // a descendant of this block has break-before + forcedBreakAfterLast = (BreakElement) returnedList.getFirst(); context.clearPendingMarks(); break; } + if (contentList.size() == 0) { + // Empty fo:block, zero-length box makes sure the IDs and/or markers + // are registered and borders/padding are painted. + returnList.add(new KnuthBox(0, notifyPos(new Position(this)), false)); + } + // a descendant of this block has break-before + contentList.addAll(returnedList); + /* extension: conversione di tutta la sequenza fin'ora ottenuta */ if (bpUnit > 0) { storedList = contentList; @@ -392,6 +400,9 @@ public abstract class BlockStackingLayoutManager extends AbstractLayoutManager addKnuthElementsForBorderPaddingAfter(returnList, true); addKnuthElementsForSpaceAfter(returnList, alignment); + + //All child content is processed. Only break-after can occur now, so... + context.clearPendingMarks(); if (forcedBreakAfterLast == null) { addKnuthElementsForBreakAfter(returnList, context); } diff --git a/src/java/org/apache/fop/layoutmgr/FlowLayoutManager.java b/src/java/org/apache/fop/layoutmgr/FlowLayoutManager.java index a70dd0883..115532cf1 100644 --- a/src/java/org/apache/fop/layoutmgr/FlowLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/FlowLayoutManager.java @@ -99,7 +99,7 @@ public class FlowLayoutManager extends BlockStackingLayoutManager //MinOptMax bpd = context.getStackLimit(); LayoutContext childLC = new LayoutContext(0); - childLC.setStackLimit(context.getStackLimit()); + childLC.setStackLimitBP(context.getStackLimitBP()); childLC.setRefIPD(context.getRefIPD()); childLC.setWritingMode(getCurrentPage().getSimplePageMaster().getWritingMode()); diff --git a/src/java/org/apache/fop/layoutmgr/LayoutContext.java b/src/java/org/apache/fop/layoutmgr/LayoutContext.java index 9eb38600b..79b5e232f 100644 --- a/src/java/org/apache/fop/layoutmgr/LayoutContext.java +++ b/src/java/org/apache/fop/layoutmgr/LayoutContext.java @@ -74,18 +74,29 @@ public class LayoutContext { private int flags; // Contains some set of flags defined above /** * Total available stacking dimension for a "galley-level" layout - * manager (Line or Flow). It is passed by the parent LM. For LineLM, - * the block LM determines this based on indent properties. + * manager in block-progression-direction. It is passed by the + * parent LM. * These LM <b>may</b> wish to pass this information down to lower * level LM to allow them to optimize returned break possibilities. */ - private MinOptMax stackLimit; + private MinOptMax stackLimitBP; + /** + * Total available stacking dimension for a "galley-level" layout + * manager in inline-progression-direction. It is passed by the + * parent LM. For LineLM, the block LM determines this based on + * indent properties. + * These LM <b>may</b> wish to pass this information down to lower + * level LM to allow them to optimize returned break possibilities. + */ + private MinOptMax stackLimitIP; /** True if current element list is spanning in multi-column layout. */ private int nextSpan = Constants.NOT_SET; /** inline-progression-dimension of nearest ancestor reference area */ private int refIPD; + //TODO After the split of stackLimit into stackLimitBP and stackLimitIP there's now some + //overlap with refIPD. Need to investigate how best to refactor that. /** the writing mode established by the nearest ancestor reference area */ private int writingMode = Constants.EN_LR_TB; @@ -145,7 +156,7 @@ public class LayoutContext { this.flags = parentLC.flags; this.refIPD = parentLC.refIPD; this.writingMode = parentLC.writingMode; - this.stackLimit = null; // Don't reference parent MinOptMax! + setStackLimitsFrom(parentLC); this.leadingSpace = parentLC.leadingSpace; //??? this.trailingSpace = parentLC.trailingSpace; //??? this.hyphContext = parentLC.hyphContext; @@ -166,7 +177,8 @@ public class LayoutContext { public LayoutContext(int flags) { this.flags = flags; this.refIPD = 0; - stackLimit = new MinOptMax(0); + stackLimitBP = new MinOptMax(0); + stackLimitIP = new MinOptMax(0); leadingSpace = null; trailingSpace = null; } @@ -273,12 +285,8 @@ public class LayoutContext { * Clears all pending marks on the LayoutContext. */ public void clearPendingMarks() { - if (this.pendingBeforeMarks != null) { - this.pendingBeforeMarks.clear(); - } - if (this.pendingAfterMarks != null) { - this.pendingAfterMarks.clear(); - } + this.pendingBeforeMarks = null; + this.pendingAfterMarks = null; } /** @@ -306,15 +314,48 @@ public class LayoutContext { } } - public void setStackLimit(MinOptMax limit) { - stackLimit = limit; + /** + * Sets the stack limit in block-progression-dimension. + * @param limit the stack limit + */ + public void setStackLimitBP(MinOptMax limit) { + stackLimitBP = limit; } - public MinOptMax getStackLimit() { - return stackLimit; + /** + * Returns the stack limit in block-progression-dimension. + * @return the stack limit + */ + public MinOptMax getStackLimitBP() { + return stackLimitBP; } /** + * Sets the stack limit in inline-progression-dimension. + * @param limit the stack limit + */ + public void setStackLimitIP(MinOptMax limit) { + stackLimitIP = limit; + } + + /** + * Returns the stack limit in inline-progression-dimension. + * @return the stack limit + */ + public MinOptMax getStackLimitIP() { + return stackLimitIP; + } + + /** + * Sets (Copies) the stack limits in both directions from another layout context. + * @param context the layout context to taje the values from + */ + public void setStackLimitsFrom(LayoutContext context) { + setStackLimitBP(context.getStackLimitBP()); + setStackLimitIP(context.getStackLimitIP()); + } + + /** * Sets the inline-progression-dimension of the nearest ancestor reference area. */ public void setRefIPD(int ipd) { @@ -536,22 +577,27 @@ public class LayoutContext { /** {@inheritDoc} */ public String toString() { - return "Layout Context:" + - "\nStack Limit: \t" + (getStackLimit() == null ? "null" : getStackLimit().toString()) + - "\nTrailing Space: \t" + (getTrailingSpace() == null ? "null" : getTrailingSpace().toString()) + - "\nLeading Space: \t" + (getLeadingSpace() == null ? "null" : getLeadingSpace().toString()) + - "\nReference IPD: \t" + getRefIPD() + - "\nSpace Adjust: \t" + getSpaceAdjust() + - "\nIPD Adjust: \t" + getIPDAdjust() + - "\nResolve Leading Space: \t" + resolveLeadingSpace() + - "\nSuppress Leading Space: \t" + suppressLeadingSpace() + - "\nIs First Area: \t" + isFirstArea() + - "\nStarts New Area: \t" + startsNewArea() + - "\nIs Last Area: \t" + isLastArea() + - "\nTry Hyphenate: \t" + tryHyphenate() + - "\nKeeps: \t[" + (isKeepWithNextPending() ? "keep-with-next" : "") + "][" - + (isKeepWithPreviousPending() ? "keep-with-previous" : "") + "] pending" + - "\nBreaks: \tforced [" + (breakBefore != Constants.EN_AUTO ? "break-before" : "") + "][" + return "Layout Context:" + + "\nStack Limit BPD: \t" + + (getStackLimitBP() == null ? "null" : getStackLimitBP().toString()) + + "\nStack Limit IPD: \t" + + (getStackLimitIP() == null ? "null" : getStackLimitIP().toString()) + + "\nTrailing Space: \t" + + (getTrailingSpace() == null ? "null" : getTrailingSpace().toString()) + + "\nLeading Space: \t" + + (getLeadingSpace() == null ? "null" : getLeadingSpace().toString()) + + "\nReference IPD: \t" + getRefIPD() + + "\nSpace Adjust: \t" + getSpaceAdjust() + + "\nIPD Adjust: \t" + getIPDAdjust() + + "\nResolve Leading Space: \t" + resolveLeadingSpace() + + "\nSuppress Leading Space: \t" + suppressLeadingSpace() + + "\nIs First Area: \t" + isFirstArea() + + "\nStarts New Area: \t" + startsNewArea() + + "\nIs Last Area: \t" + isLastArea() + + "\nTry Hyphenate: \t" + tryHyphenate() + + "\nKeeps: \t[" + (isKeepWithNextPending() ? "keep-with-next" : "") + "][" + + (isKeepWithPreviousPending() ? "keep-with-previous" : "") + "] pending" + + "\nBreaks: \tforced [" + (breakBefore != Constants.EN_AUTO ? "break-before" : "") + "][" + (breakAfter != Constants.EN_AUTO ? "break-after" : "") + "]"; } diff --git a/src/java/org/apache/fop/layoutmgr/MinOptMaxUtil.java b/src/java/org/apache/fop/layoutmgr/MinOptMaxUtil.java index ff9bdb1d9..b58af1cfe 100644 --- a/src/java/org/apache/fop/layoutmgr/MinOptMaxUtil.java +++ b/src/java/org/apache/fop/layoutmgr/MinOptMaxUtil.java @@ -68,19 +68,16 @@ public class MinOptMaxUtil { } /** - * Extend the minimum length to the given length. + * Extends the minimum length to the given length if necessary, and adjusts opt and + * max accordingly. + * * @param mom the min/opt/max trait * @param len the new minimum length - * @param optToLen if set adjusts the optimum length to be the smaller of the - * minimum length and the given length */ - public static void extendMinimum(MinOptMax mom, int len, boolean optToLen) { + public static void extendMinimum(MinOptMax mom, int len) { if (mom.min < len) { mom.min = len; mom.opt = Math.max(mom.min, mom.opt); - if (optToLen) { - mom.opt = Math.min(mom.min, len); - } mom.max = Math.max(mom.opt, mom.max); } } @@ -111,7 +108,7 @@ public class MinOptMaxUtil { ? 0 : prop.getMinimum(context).getLength().getValue(context)), (prop.getOptimum(context).isAuto() ? 0 : prop.getOptimum(context).getLength().getValue(context)), - (prop.getMinimum(context).isAuto() + (prop.getMaximum(context).isAuto() ? Integer.MAX_VALUE : prop.getMaximum(context).getLength().getValue(context))); return mom; diff --git a/src/java/org/apache/fop/layoutmgr/PageBreaker.java b/src/java/org/apache/fop/layoutmgr/PageBreaker.java index cf830a7ec..3e100cd50 100644 --- a/src/java/org/apache/fop/layoutmgr/PageBreaker.java +++ b/src/java/org/apache/fop/layoutmgr/PageBreaker.java @@ -156,7 +156,7 @@ public class PageBreaker extends AbstractBreaker { // element represents a line with footnote citations bFootnotesPresent = true; LayoutContext footnoteContext = new LayoutContext(context); - footnoteContext.setStackLimit(context.getStackLimit()); + footnoteContext.setStackLimitBP(context.getStackLimitBP()); footnoteContext.setRefIPD(pslm.getCurrentPV() .getRegionReference(Constants.FO_REGION_BODY).getIPD()); LinkedList footnoteBodyLMs = ((KnuthBlockBox) element).getFootnoteBodyLMs(); diff --git a/src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java b/src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java index 57fc4600e..d98d29b5c 100644 --- a/src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java +++ b/src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java @@ -364,7 +364,13 @@ class PageBreakingAlgorithm extends BreakingAlgorithm { } else { // there are no footnotes } - return getLineWidth(activeNode.line) - actualWidth; + int diff = getLineWidth(activeNode.line) - actualWidth; + if (autoHeight && diff < 0) { + //getLineWidth() for auto-height parts return 0 so the diff will be negative + return 0; //...but we don't want to shrink in this case. Stick to optimum. + } else { + return diff; + } } /** Checks whether footnotes from preceding pages may be deferred to the page after diff --git a/src/java/org/apache/fop/layoutmgr/StaticContentLayoutManager.java b/src/java/org/apache/fop/layoutmgr/StaticContentLayoutManager.java index c8b89e6af..b1e414527 100644 --- a/src/java/org/apache/fop/layoutmgr/StaticContentLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/StaticContentLayoutManager.java @@ -93,7 +93,7 @@ public class StaticContentLayoutManager extends BlockStackingLayoutManager { //TODO Empty this method?!? // set layout dimensions setContentAreaIPD(context.getRefIPD()); - setContentAreaBPD(context.getStackLimit().opt); + setContentAreaBPD(context.getStackLimitBP().opt); //TODO Copied from elsewhere. May be worthwhile to factor out the common parts. // currently active LM @@ -111,10 +111,10 @@ public class StaticContentLayoutManager extends BlockStackingLayoutManager { } // Set up a LayoutContext - MinOptMax bpd = context.getStackLimit(); + MinOptMax bpd = context.getStackLimitBP(); LayoutContext childLC = new LayoutContext(0); - childLC.setStackLimit(MinOptMax.subtract(bpd, stackSize)); + childLC.setStackLimitBP(MinOptMax.subtract(bpd, stackSize)); childLC.setRefIPD(context.getRefIPD()); // get elements from curLM @@ -307,7 +307,7 @@ public class StaticContentLayoutManager extends BlockStackingLayoutManager { while ((curLM = getChildLM()) != null) { LayoutContext childLC = new LayoutContext(0); - childLC.setStackLimit(context.getStackLimit()); + childLC.setStackLimitBP(context.getStackLimitBP()); childLC.setRefIPD(context.getRefIPD()); childLC.setWritingMode(context.getWritingMode()); diff --git a/src/java/org/apache/fop/layoutmgr/TraitSetter.java b/src/java/org/apache/fop/layoutmgr/TraitSetter.java index 841a94705..dfc8c99f2 100644 --- a/src/java/org/apache/fop/layoutmgr/TraitSetter.java +++ b/src/java/org/apache/fop/layoutmgr/TraitSetter.java @@ -29,6 +29,7 @@ import org.apache.fop.datatypes.SimplePercentBaseContext; import org.apache.fop.fo.Constants; import org.apache.fop.fo.properties.CommonBorderPaddingBackground; import org.apache.fop.fo.properties.CommonMarginBlock; +import org.apache.fop.fo.properties.CommonMarginBlock; import org.apache.fop.fo.properties.CommonTextDecoration; import org.apache.fop.fo.properties.CommonBorderPaddingBackground.BorderInfo; import org.apache.fop.fonts.Font; @@ -281,6 +282,90 @@ public class TraitSetter { } /** + * Add background to an area. This method is mainly used by table-related layout + * managers to add background for column, body or row. Since the area corresponding to + * border-separation must be filled with the table's background, for every cell an + * additional area with the same dimensions is created to hold the background for the + * corresponding column/body/row. An additional shift must then be added to + * background-position-horizontal/vertical to ensure the background images are + * correctly placed. Indeed the placement of images must be made WRT the + * column/body/row and not the cell. + * + * <p>Note: The area's IPD and BPD must be set before calling this method.</p> + * + * <p>TODO the regular + * {@link #addBackground(Area, CommonBorderPaddingBackground, PercentBaseContext)} + * method should be used instead, and a means to retrieve the original area's + * dimensions must be found.</p> + * + * <p>TODO the placement of images in the x- or y-direction will be incorrect if + * background-repeat is set for that direction.</p> + * + * @param area the area to set the traits on + * @param backProps the background properties + * @param context Property evaluation context + * @param ipdShift horizontal shift to affect to the background, in addition to the + * value of the background-position-horizontal property + * @param bpdShift vertical shift to affect to the background, in addition to the + * value of the background-position-vertical property + * @param referenceIPD value to use as a reference for percentage calculation + * @param referenceBPD value to use as a reference for percentage calculation + */ + public static void addBackground(Area area, + CommonBorderPaddingBackground backProps, + PercentBaseContext context, + int ipdShift, int bpdShift, int referenceIPD, int referenceBPD) { + if (!backProps.hasBackground()) { + return; + } + Trait.Background back = new Trait.Background(); + back.setColor(backProps.backgroundColor); + + if (backProps.getImageInfo() != null) { + back.setURL(backProps.backgroundImage); + back.setImageInfo(backProps.getImageInfo()); + back.setRepeat(backProps.backgroundRepeat); + if (backProps.backgroundPositionHorizontal != null) { + if (back.getRepeat() == Constants.EN_NOREPEAT + || back.getRepeat() == Constants.EN_REPEATY) { + if (area.getIPD() > 0) { + PercentBaseContext refContext = new SimplePercentBaseContext(context, + LengthBase.IMAGE_BACKGROUND_POSITION_HORIZONTAL, + (referenceIPD - back.getImageInfo().getSize().getWidthMpt())); + + back.setHoriz(ipdShift + + backProps.backgroundPositionHorizontal.getValue(refContext)); + } else { + // TODO Area IPD has to be set for this to work + log.warn("Horizontal background image positioning ignored" + + " because the IPD was not set on the area." + + " (Yes, it's a bug in FOP)"); + } + } + } + if (backProps.backgroundPositionVertical != null) { + if (back.getRepeat() == Constants.EN_NOREPEAT + || back.getRepeat() == Constants.EN_REPEATX) { + if (area.getBPD() > 0) { + PercentBaseContext refContext = new SimplePercentBaseContext(context, + LengthBase.IMAGE_BACKGROUND_POSITION_VERTICAL, + (referenceBPD - back.getImageInfo().getSize().getHeightMpt())); + back.setVertical(bpdShift + + backProps.backgroundPositionVertical.getValue(refContext)); + } else { + // TODO Area BPD has to be set for this to work + log.warn("Vertical background image positioning ignored" + + " because the BPD was not set on the area." + + " (Yes, it's a bug in FOP)"); + } + } + } + } + + area.addTrait(Trait.BACKGROUND, back); + } + + /** * Add background to an area. * Layout managers that create areas with a background can use this to * add the background to the area. @@ -312,7 +397,7 @@ public class TraitSetter { back.setHoriz(backProps.backgroundPositionHorizontal.getValue( new SimplePercentBaseContext(context, LengthBase.IMAGE_BACKGROUND_POSITION_HORIZONTAL, - (width - back.getImageInfo().getSize().getHeightMpt()) + (width - back.getImageInfo().getSize().getWidthMpt()) ) )); } else { diff --git a/src/java/org/apache/fop/layoutmgr/inline/AbstractGraphicsLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/AbstractGraphicsLayoutManager.java index 6426f15db..38e0c35bc 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/AbstractGraphicsLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/AbstractGraphicsLayoutManager.java @@ -124,13 +124,6 @@ public abstract class AbstractGraphicsLayoutManager extends LeafNodeLayoutManage } /** - * {@inheritDoc} - */ - protected void addId() { - getPSLM().addIDToPage(fobj.getId()); - } - - /** * Returns the image of foreign object area to be put into * the viewport. * @return the appropriate area diff --git a/src/java/org/apache/fop/layoutmgr/inline/AbstractPageNumberCitationLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/AbstractPageNumberCitationLayoutManager.java index 41d4af1c1..c92bdb6fc 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/AbstractPageNumberCitationLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/AbstractPageNumberCitationLayoutManager.java @@ -51,7 +51,7 @@ public abstract class AbstractPageNumberCitationLayoutManager extends LeafNodeLa * Constructor * * @param node the formatting object that creates this area - * @todo better retrieval of font info + * TODO better retrieval of font info */ public AbstractPageNumberCitationLayoutManager(AbstractPageNumberCitation node) { super(node); @@ -83,7 +83,7 @@ public abstract class AbstractPageNumberCitationLayoutManager extends LeafNodeLa /** {@inheritDoc} */ public InlineArea get(LayoutContext context) { - curArea = getPageNumberCitationInlineArea(parentLM); + curArea = getPageNumberCitationInlineArea(); return curArea; } @@ -99,12 +99,15 @@ public abstract class AbstractPageNumberCitationLayoutManager extends LeafNodeLa } /** - * if id can be resolved then simply return a word, otherwise + * If id can be resolved then simply return a word, otherwise * return a resolvable area + * + * @param parentLM the parent LayoutManager + * @return a corresponding InlineArea */ - private InlineArea getPageNumberCitationInlineArea(LayoutManager parentLM) { + private InlineArea getPageNumberCitationInlineArea() { PageViewport page = getPSLM().getFirstPVWithID(fobj.getRefId()); - TextArea text = null; + TextArea text; if (page != null) { String str = page.getPageNumberString(); // get page string from parent, build area @@ -150,9 +153,5 @@ public abstract class AbstractPageNumberCitationLayoutManager extends LeafNodeLa return width; } - /** {@inheritDoc} */ - protected void addId() { - getPSLM().addIDToPage(fobj.getId()); - } } diff --git a/src/java/org/apache/fop/layoutmgr/inline/CharacterLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/CharacterLayoutManager.java index 0430eef0c..b53e442d4 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/CharacterLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/CharacterLayoutManager.java @@ -45,7 +45,6 @@ import org.apache.fop.util.CharUtilities; * LayoutManager for the fo:character formatting object */ public class CharacterLayoutManager extends LeafNodeLayoutManager { - private Character fobj; private MinOptMax letterSpaceIPD; private int hyphIPD; private Font font; @@ -57,13 +56,13 @@ public class CharacterLayoutManager extends LeafNodeLayoutManager { * @param node the fo:character formatting object */ public CharacterLayoutManager(Character node) { - // @todo better null checking of node super(node); - fobj = node; } /** {@inheritDoc} */ public void initialize() { + Character fobj = (Character)this.fobj; + FontInfo fi = fobj.getFOEventHandler().getFontInfo(); FontTriplet[] fontkeys = fobj.getCommonFont().getFontState(fi); font = fi.getFontInstance(fontkeys[0], fobj.getCommonFont().fontSize.getValue(this)); @@ -90,7 +89,7 @@ public class CharacterLayoutManager extends LeafNodeLayoutManager { text.addWord(String.valueOf(ch), 0); } TraitSetter.setProducerID(text, node.getId()); - TraitSetter.addTextDecoration(text, fobj.getTextDecoration()); + TraitSetter.addTextDecoration(text, node.getTextDecoration()); return text; } @@ -105,6 +104,8 @@ public class CharacterLayoutManager extends LeafNodeLayoutManager { return null; } + Character fobj = (Character)this.fobj; + ipd = new MinOptMax(font.getCharWidth(fobj.getCharacter())); curArea.setIPD(ipd.opt); @@ -178,14 +179,7 @@ public class CharacterLayoutManager extends LeafNodeLayoutManager { /** {@inheritDoc} */ public boolean applyChanges(List oldList) { setFinished(false); - if (isSomethingChanged) { - // there is nothing to do, - // possible changes have already been applied - // in the hyphenate() method - return true; - } else { - return false; - } + return isSomethingChanged; } /** {@inheritDoc} */ @@ -238,10 +232,5 @@ public class CharacterLayoutManager extends LeafNodeLayoutManager { return returnList; } - /** {@inheritDoc} */ - protected void addId() { - getPSLM().addIDToPage(fobj.getId()); - } - } diff --git a/src/java/org/apache/fop/layoutmgr/inline/ContentLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/ContentLayoutManager.java index 03e9b382a..ff7c5b3ce 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/ContentLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/ContentLayoutManager.java @@ -19,9 +19,20 @@ package org.apache.fop.layoutmgr.inline; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.ListIterator; + import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + import org.apache.fop.apps.FOUserAgent; +import org.apache.fop.area.Area; +import org.apache.fop.area.Block; +import org.apache.fop.area.LineArea; +import org.apache.fop.area.inline.InlineArea; import org.apache.fop.fo.Constants; import org.apache.fop.fo.pagination.Title; import org.apache.fop.layoutmgr.AbstractBaseLayoutManager; @@ -34,19 +45,8 @@ import org.apache.fop.layoutmgr.PageSequenceLayoutManager; import org.apache.fop.layoutmgr.Position; import org.apache.fop.layoutmgr.PositionIterator; import org.apache.fop.layoutmgr.SpaceSpecifier; -import org.apache.fop.area.Area; -import org.apache.fop.area.LineArea; -import org.apache.fop.area.inline.InlineArea; - -import java.util.LinkedList; -import java.util.List; -import java.util.ListIterator; -import java.util.ArrayList; -import java.util.Iterator; import org.apache.fop.traits.MinOptMax; -import org.apache.fop.area.Block; - /** * Content Layout Manager. * For use with objects that contain inline areas such as @@ -115,7 +115,7 @@ public class ContentLayoutManager extends AbstractBaseLayoutManager childLC.setLeadingSpace(new SpaceSpecifier(false)); childLC.setTrailingSpace(new SpaceSpecifier(false)); // set stackLimit for lines - childLC.setStackLimit(new MinOptMax(ipd)); + childLC.setStackLimitIP(new MinOptMax(ipd)); childLC.setRefIPD(ipd); int lineHeight = 14000; diff --git a/src/java/org/apache/fop/layoutmgr/inline/ICLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/ICLayoutManager.java index 4ffafb061..7ca9e0d5e 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/ICLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/ICLayoutManager.java @@ -31,12 +31,10 @@ import org.apache.fop.fo.flow.InlineContainer; * and id areas are maintained for later retrieval. */ public class ICLayoutManager extends LeafNodeLayoutManager { - private InlineContainer fobj; private List childrenLM; public ICLayoutManager(InlineContainer node, List childLM) { super(node); - fobj = node; childrenLM = childLM; } @@ -44,7 +42,4 @@ public class ICLayoutManager extends LeafNodeLayoutManager { return null; } - protected void addId() { - getPSLM().addIDToPage(fobj.getId()); - } } diff --git a/src/java/org/apache/fop/layoutmgr/inline/InlineLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/InlineLayoutManager.java index b449b6689..e9919e02f 100755 --- a/src/java/org/apache/fop/layoutmgr/inline/InlineLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/InlineLayoutManager.java @@ -19,12 +19,13 @@ package org.apache.fop.layoutmgr.inline; -import java.util.ListIterator; import java.util.LinkedList; import java.util.List; +import java.util.ListIterator; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + import org.apache.fop.area.Area; import org.apache.fop.area.inline.InlineArea; import org.apache.fop.area.inline.InlineBlockParent; @@ -47,12 +48,12 @@ import org.apache.fop.layoutmgr.InlineKnuthSequence; import org.apache.fop.layoutmgr.KnuthBox; import org.apache.fop.layoutmgr.KnuthSequence; import org.apache.fop.layoutmgr.LayoutContext; -import org.apache.fop.layoutmgr.NonLeafPosition; -import org.apache.fop.layoutmgr.SpaceSpecifier; -import org.apache.fop.layoutmgr.TraitSetter; import org.apache.fop.layoutmgr.LayoutManager; +import org.apache.fop.layoutmgr.NonLeafPosition; import org.apache.fop.layoutmgr.Position; import org.apache.fop.layoutmgr.PositionIterator; +import org.apache.fop.layoutmgr.SpaceSpecifier; +import org.apache.fop.layoutmgr.TraitSetter; import org.apache.fop.traits.MinOptMax; import org.apache.fop.traits.SpaceVal; @@ -296,7 +297,7 @@ public class InlineLayoutManager extends InlineStackingLayoutManager { ); } - while ((curLM = (LayoutManager) getChildLM()) != null) { + while ((curLM = getChildLM()) != null) { if (!(curLM instanceof InlineLevelLayoutManager)) { // A block LM @@ -435,7 +436,7 @@ public class InlineLayoutManager extends InlineStackingLayoutManager { // layout context given to lastLM, but must be cleared in the // layout context given to the other LMs. LinkedList positionList = new LinkedList(); - NonLeafPosition pos = null; + NonLeafPosition pos; LayoutManager lastLM = null;// last child LM in this iterator Position lastPos = null; while (parentIter.hasNext()) { @@ -599,9 +600,4 @@ public class InlineLayoutManager extends InlineStackingLayoutManager { return this.auxiliaryPosition; } - /** {@inheritDoc} */ - protected void addId() { - getPSLM().addIDToPage(fobj.getId()); - } - } diff --git a/src/java/org/apache/fop/layoutmgr/inline/InlineStackingLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/InlineStackingLayoutManager.java index 4d0872a6d..bf1538a7c 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/InlineStackingLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/InlineStackingLayoutManager.java @@ -75,15 +75,9 @@ public abstract class InlineStackingLayoutManager extends AbstractLayoutManager private Area currentArea; // LineArea or InlineParent - //private BreakPoss prevBP; - /** The child layout context */ protected LayoutContext childLC; - private boolean bAreaCreated = false; - - //private LayoutManager currentLM = null; - /** Used to store previous content IPD for each child LM. */ private HashMap hmPrevIPD = new HashMap(); @@ -171,14 +165,6 @@ public abstract class InlineStackingLayoutManager extends AbstractLayoutManager } /** - * This method is called by addAreas() so IDs can be added to a page for FOs that - * support the 'id' property. - */ - protected void addId() { - // Do nothing here, overriden in subclasses that have an 'id' property. - } - - /** * Returns the current area. * @return the current area */ @@ -255,7 +241,7 @@ public abstract class InlineStackingLayoutManager extends AbstractLayoutManager // "unwrap" the Position stored in each element of oldList while (oldListIterator.hasNext()) { element = (KnuthElement) oldListIterator.next(); - element.setPosition(((NonLeafPosition)element.getPosition()).getPosition()); + element.setPosition(element.getPosition().getPosition()); } // The last element may not have a layout manager (its position == null); @@ -288,7 +274,7 @@ public abstract class InlineStackingLayoutManager extends AbstractLayoutManager // "unwrap" the Position stored in each element of oldList while (oldListIterator.hasNext()) { element = (KnuthElement) oldListIterator.next(); - element.setPosition(((NonLeafPosition)element.getPosition()).getPosition()); + element.setPosition(element.getPosition().getPosition()); } ((InlineLevelLayoutManager) @@ -298,14 +284,14 @@ public abstract class InlineStackingLayoutManager extends AbstractLayoutManager /** {@inheritDoc} */ public void getWordChars(StringBuffer sbChars, Position pos) { - Position newPos = ((NonLeafPosition) pos).getPosition(); + Position newPos = pos.getPosition(); ((InlineLevelLayoutManager) newPos.getLM()).getWordChars(sbChars, newPos); } /** {@inheritDoc} */ public void hyphenate(Position pos, HyphContext hc) { - Position newPos = ((NonLeafPosition) pos).getPosition(); + Position newPos = pos.getPosition(); ((InlineLevelLayoutManager) newPos.getLM()).hyphenate(newPos, hc); } @@ -318,7 +304,7 @@ public abstract class InlineStackingLayoutManager extends AbstractLayoutManager while (oldListIterator.hasNext()) { oldElement = (KnuthElement) oldListIterator.next(); oldElement.setPosition - (((NonLeafPosition) oldElement.getPosition()).getPosition()); + (oldElement.getPosition().getPosition()); } // reset the iterator oldListIterator = oldList.listIterator(); @@ -385,7 +371,7 @@ public abstract class InlineStackingLayoutManager extends AbstractLayoutManager while (oldListIterator.hasNext()) { oldElement = (KnuthElement) oldListIterator.next(); oldElement.setPosition - (((NonLeafPosition) oldElement.getPosition()).getPosition()); + (oldElement.getPosition().getPosition()); } // reset the iterator oldListIterator = oldList.listIterator(); diff --git a/src/java/org/apache/fop/layoutmgr/inline/LeaderLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/LeaderLayoutManager.java index 5f93ab98c..f4bcde96f 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/LeaderLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/LeaderLayoutManager.java @@ -339,11 +339,6 @@ public class LeaderLayoutManager extends LeafNodeLayoutManager { return returnList; } - /** {@inheritDoc} */ - protected void addId() { - getPSLM().addIDToPage(fobj.getId()); - } - /** * {@inheritDoc} */ diff --git a/src/java/org/apache/fop/layoutmgr/inline/LeafNodeLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/LeafNodeLayoutManager.java index 78d126194..fb5e9ee4d 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/LeafNodeLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/LeafNodeLayoutManager.java @@ -65,8 +65,6 @@ public abstract class LeafNodeLayoutManager extends AbstractLayoutManager /** The alignment context applying to this area */ protected AlignmentContext alignmentContext = null; - private MinOptMax ipd; - /** Flag to indicate if something was changed as part of the getChangeKnuthElements sequence */ protected boolean isSomethingChanged = false; /** Our area info for the Knuth elements */ @@ -205,14 +203,6 @@ public abstract class LeafNodeLayoutManager extends AbstractLayoutManager } /** - * This method is called by addAreas() so IDs can be added to a page for FOs that - * support the 'id' property. - */ - protected void addId() { - // Do nothing here, overriden in subclasses that have an 'id' property. - } - - /** * Offset this area. * Offset the inline area in the bpd direction when adding the * inline area. diff --git a/src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java index 6df7ac00c..9a818232c 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java @@ -26,6 +26,7 @@ import java.util.ListIterator; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + import org.apache.fop.area.Area; import org.apache.fop.area.LineArea; import org.apache.fop.area.Trait; @@ -582,9 +583,6 @@ public class LineLayoutManager extends InlineStackingLayoutManager // Get a break from currently active child LM // Set up constraints for inline level managers - // IPD remaining in line - MinOptMax availIPD = context.getStackLimit(); - clearPrevIPD(); //PHASE 1: Create Knuth elements @@ -646,7 +644,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager InlineLevelLayoutManager curLM; LinkedList returnedList = null; - iLineWidth = context.getStackLimit().opt; + iLineWidth = context.getStackLimitIP().opt; // convert all the text in a sequence of paragraphs made // of KnuthBox, KnuthGlue and KnuthPenalty objects @@ -1687,7 +1685,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager */ if (false && textAlignment == EN_JUSTIFY) { // re-compute space adjust ratio - int updatedDifference = context.getStackLimit().opt + int updatedDifference = context.getStackLimitIP().opt - lbp.lineWidth + lbp.difference; double updatedRatio = 0.0; if (updatedDifference > 0) { @@ -1701,12 +1699,12 @@ public class LineLayoutManager extends InlineStackingLayoutManager } else if (false && textAlignment == EN_CENTER) { // re-compute indent int updatedIndent = lbp.startIndent - + (context.getStackLimit().opt - lbp.lineWidth) / 2; + + (context.getStackLimitIP().opt - lbp.lineWidth) / 2; lineArea.addTrait(Trait.START_INDENT, new Integer(updatedIndent)); } else if (false && textAlignment == EN_END) { // re-compute indent int updatedIndent = lbp.startIndent - + (context.getStackLimit().opt - lbp.lineWidth); + + (context.getStackLimitIP().opt - lbp.lineWidth); lineArea.addTrait(Trait.START_INDENT, new Integer(updatedIndent)); } @@ -1770,7 +1768,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager // set last area flag blocklc.setFlags(LayoutContext.LAST_AREA, (context.isLastArea() && childLM == lastLM)); - blocklc.setStackLimit(context.getStackLimit()); + blocklc.setStackLimitsFrom(context); // Add the line areas to Area childLM.addAreas(childPosIter, blocklc); blocklc.setLeadingSpace(blocklc.getTrailingSpace()); diff --git a/src/java/org/apache/fop/layoutmgr/inline/PageNumberLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/PageNumberLayoutManager.java index 88f1d283c..1aaaaf527 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/PageNumberLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/PageNumberLayoutManager.java @@ -42,7 +42,7 @@ public class PageNumberLayoutManager extends LeafNodeLayoutManager { * Constructor * * @param node the fo:page-number formatting object that creates the area - * @todo better null checking of node, font + * TODO better null checking of node, font */ public PageNumberLayoutManager(PageNumber node) { super(node); @@ -131,9 +131,5 @@ public class PageNumberLayoutManager extends LeafNodeLayoutManager { return width; } - /** {@inheritDoc} */ - protected void addId() { - getPSLM().addIDToPage(fobj.getId()); - } } diff --git a/src/java/org/apache/fop/layoutmgr/list/ListBlockLayoutManager.java b/src/java/org/apache/fop/layoutmgr/list/ListBlockLayoutManager.java index 470cbbe9c..c17ddc711 100644 --- a/src/java/org/apache/fop/layoutmgr/list/ListBlockLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/list/ListBlockLayoutManager.java @@ -19,29 +19,29 @@ package org.apache.fop.layoutmgr.list; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + +import org.apache.fop.area.Area; +import org.apache.fop.area.Block; import org.apache.fop.fo.flow.ListBlock; -import org.apache.fop.layoutmgr.BlockLevelLayoutManager; import org.apache.fop.layoutmgr.BlockStackingLayoutManager; import org.apache.fop.layoutmgr.ConditionalElementListener; import org.apache.fop.layoutmgr.ElementListUtils; -import org.apache.fop.layoutmgr.LayoutManager; import org.apache.fop.layoutmgr.LayoutContext; -import org.apache.fop.layoutmgr.PositionIterator; -import org.apache.fop.layoutmgr.Position; +import org.apache.fop.layoutmgr.LayoutManager; import org.apache.fop.layoutmgr.NonLeafPosition; +import org.apache.fop.layoutmgr.Position; +import org.apache.fop.layoutmgr.PositionIterator; import org.apache.fop.layoutmgr.RelSide; import org.apache.fop.layoutmgr.TraitSetter; -import org.apache.fop.area.Area; -import org.apache.fop.area.Block; import org.apache.fop.traits.MinOptMax; import org.apache.fop.traits.SpaceVal; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; - /** * LayoutManager for a list-block FO. * A list block contains list items which are stacked within @@ -156,11 +156,11 @@ public class ListBlockLayoutManager extends BlockStackingLayoutManager addBlockSpacing(0.0, new MinOptMax(layoutContext.getSpaceBefore())); } - getPSLM().addIDToPage(getListBlockFO().getId()); + addId(); // the list block contains areas stacked from each list item - LayoutManager childLM = null; + LayoutManager childLM; LayoutContext lc = new LayoutContext(0); LayoutManager firstLM = null; LayoutManager lastLM = null; @@ -181,10 +181,10 @@ public class ListBlockLayoutManager extends BlockStackingLayoutManager } if (pos instanceof NonLeafPosition && (pos.getPosition() != null) - && ((NonLeafPosition) pos).getPosition().getLM() != this) { + && pos.getPosition().getLM() != this) { // pos was created by a child of this ListBlockLM - positionList.add(((NonLeafPosition) pos).getPosition()); - lastLM = ((NonLeafPosition) pos).getPosition().getLM(); + positionList.add(pos.getPosition()); + lastLM = pos.getPosition().getLM(); if (firstLM == null) { firstLM = lastLM; } @@ -200,7 +200,7 @@ public class ListBlockLayoutManager extends BlockStackingLayoutManager lc.setSpaceAdjust(layoutContext.getSpaceAdjust()); lc.setFlags(LayoutContext.FIRST_AREA, childLM == firstLM); lc.setFlags(LayoutContext.LAST_AREA, childLM == lastLM); - lc.setStackLimit(layoutContext.getStackLimit()); + lc.setStackLimitBP(layoutContext.getStackLimitBP()); childLM.addAreas(childPosIter, lc); } @@ -218,7 +218,7 @@ public class ListBlockLayoutManager extends BlockStackingLayoutManager curBlockArea = null; resetSpaces(); - getPSLM().notifyEndOfLayout(((ListBlock)getFObj()).getId()); + getPSLM().notifyEndOfLayout(fobj.getId()); } /** @@ -280,7 +280,7 @@ public class ListBlockLayoutManager extends BlockStackingLayoutManager /** {@inheritDoc} */ public boolean mustKeepTogether() { //TODO Keeps will have to be more sophisticated sooner or later - return ((BlockLevelLayoutManager)getParent()).mustKeepTogether() + return super.mustKeepTogether() || !getListBlockFO().getKeepTogether().getWithinPage().isAuto() || !getListBlockFO().getKeepTogether().getWithinColumn().isAuto(); } diff --git a/src/java/org/apache/fop/layoutmgr/list/ListItemContentLayoutManager.java b/src/java/org/apache/fop/layoutmgr/list/ListItemContentLayoutManager.java index 853b1a128..8b0028a8f 100644 --- a/src/java/org/apache/fop/layoutmgr/list/ListItemContentLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/list/ListItemContentLayoutManager.java @@ -19,24 +19,24 @@ package org.apache.fop.layoutmgr.list; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +import org.apache.fop.area.Area; +import org.apache.fop.area.Block; import org.apache.fop.fo.flow.AbstractListItemPart; import org.apache.fop.fo.flow.ListItemBody; import org.apache.fop.fo.flow.ListItemLabel; import org.apache.fop.layoutmgr.BlockLevelLayoutManager; import org.apache.fop.layoutmgr.BlockStackingLayoutManager; -import org.apache.fop.layoutmgr.LayoutManager; import org.apache.fop.layoutmgr.LayoutContext; -import org.apache.fop.layoutmgr.PositionIterator; -import org.apache.fop.layoutmgr.Position; +import org.apache.fop.layoutmgr.LayoutManager; import org.apache.fop.layoutmgr.NonLeafPosition; +import org.apache.fop.layoutmgr.Position; +import org.apache.fop.layoutmgr.PositionIterator; import org.apache.fop.layoutmgr.TraitSetter; import org.apache.fop.layoutmgr.SpaceResolver.SpaceHandlingBreakPosition; -import org.apache.fop.area.Area; -import org.apache.fop.area.Block; - -import java.util.Iterator; -import java.util.List; -import java.util.LinkedList; /** * LayoutManager for a list-item-label or list-item-body FO. @@ -115,9 +115,9 @@ public class ListItemContentLayoutManager extends BlockStackingLayoutManager { LayoutContext layoutContext) { getParentArea(null); - getPSLM().addIDToPage(getPartFO().getId()); + addId(); - LayoutManager childLM = null; + LayoutManager childLM; LayoutContext lc = new LayoutContext(0); LayoutManager firstLM = null; LayoutManager lastLM = null; @@ -141,8 +141,8 @@ public class ListItemContentLayoutManager extends BlockStackingLayoutManager { } if (pos instanceof NonLeafPosition) { // pos was created by a child of this ListBlockLM - positionList.add(((NonLeafPosition) pos).getPosition()); - lastLM = ((NonLeafPosition) pos).getPosition().getLM(); + positionList.add(pos.getPosition()); + lastLM = pos.getPosition().getLM(); if (firstLM == null) { firstLM = lastLM; } @@ -162,7 +162,7 @@ public class ListItemContentLayoutManager extends BlockStackingLayoutManager { lc.setFlags(LayoutContext.LAST_AREA, childLM == lastLM); // set the space adjustment ratio lc.setSpaceAdjust(layoutContext.getSpaceAdjust()); - lc.setStackLimit(layoutContext.getStackLimit()); + lc.setStackLimitBP(layoutContext.getStackLimitBP()); childLM.addAreas(childPosIter, lc); } @@ -172,7 +172,7 @@ public class ListItemContentLayoutManager extends BlockStackingLayoutManager { curBlockArea = null; - getPSLM().notifyEndOfLayout(((AbstractListItemPart)getFObj()).getId()); + getPSLM().notifyEndOfLayout(fobj.getId()); } /** diff --git a/src/java/org/apache/fop/layoutmgr/list/ListItemLayoutManager.java b/src/java/org/apache/fop/layoutmgr/list/ListItemLayoutManager.java index dc28e98e2..c6b5b8cf9 100644 --- a/src/java/org/apache/fop/layoutmgr/list/ListItemLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/list/ListItemLayoutManager.java @@ -19,8 +19,16 @@ package org.apache.fop.layoutmgr.list; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.ListIterator; + import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + +import org.apache.fop.area.Area; +import org.apache.fop.area.Block; import org.apache.fop.fo.flow.ListItem; import org.apache.fop.fo.flow.ListItemBody; import org.apache.fop.fo.flow.ListItemLabel; @@ -30,28 +38,21 @@ import org.apache.fop.layoutmgr.BreakElement; import org.apache.fop.layoutmgr.ConditionalElementListener; import org.apache.fop.layoutmgr.ElementListObserver; import org.apache.fop.layoutmgr.ElementListUtils; -import org.apache.fop.layoutmgr.LayoutManager; +import org.apache.fop.layoutmgr.KnuthBox; +import org.apache.fop.layoutmgr.KnuthElement; +import org.apache.fop.layoutmgr.KnuthPenalty; +import org.apache.fop.layoutmgr.KnuthPossPosIter; import org.apache.fop.layoutmgr.LayoutContext; -import org.apache.fop.layoutmgr.PositionIterator; -import org.apache.fop.layoutmgr.Position; +import org.apache.fop.layoutmgr.LayoutManager; import org.apache.fop.layoutmgr.NonLeafPosition; +import org.apache.fop.layoutmgr.Position; +import org.apache.fop.layoutmgr.PositionIterator; import org.apache.fop.layoutmgr.RelSide; import org.apache.fop.layoutmgr.SpaceResolver; import org.apache.fop.layoutmgr.TraitSetter; -import org.apache.fop.layoutmgr.KnuthElement; -import org.apache.fop.layoutmgr.KnuthBox; -import org.apache.fop.layoutmgr.KnuthPenalty; -import org.apache.fop.layoutmgr.KnuthPossPosIter; -import org.apache.fop.area.Area; -import org.apache.fop.area.Block; import org.apache.fop.traits.MinOptMax; import org.apache.fop.traits.SpaceVal; -import java.util.ArrayList; -import java.util.List; -import java.util.LinkedList; -import java.util.ListIterator; - /** * LayoutManager for a list-item FO. * The list item contains a list item label and a list item body. @@ -72,8 +73,6 @@ public class ListItemLayoutManager extends BlockStackingLayoutManager private LinkedList labelList = null; private LinkedList bodyList = null; - private int listItemHeight; - private boolean discardBorderBefore; private boolean discardBorderAfter; private boolean discardPaddingBefore; @@ -83,7 +82,9 @@ public class ListItemLayoutManager extends BlockStackingLayoutManager private boolean keepWithNextPendingOnLabel; private boolean keepWithNextPendingOnBody; - + + private int listItemHeight; + private class ListItemPosition extends Position { private int iLabelFirstIndex; private int iLabelLastIndex; @@ -116,6 +117,11 @@ public class ListItemLayoutManager extends BlockStackingLayoutManager } /** {@inheritDoc} */ + public boolean generatesAreas() { + return true; + } + + /** {@inheritDoc} */ public String toString() { StringBuffer sb = new StringBuffer("ListItemPosition:"); sb.append(getIndex()).append("("); @@ -301,12 +307,12 @@ public class ListItemLayoutManager extends BlockStackingLayoutManager int additionalPenaltyHeight = 0; KnuthElement endEl = (KnuthElement)elementLists[0].get(end[0]); if (endEl instanceof KnuthPenalty) { - additionalPenaltyHeight = ((KnuthPenalty)endEl).getW(); + additionalPenaltyHeight = endEl.getW(); } endEl = (KnuthElement)elementLists[1].get(end[1]); if (endEl instanceof KnuthPenalty) { additionalPenaltyHeight = Math.max( - additionalPenaltyHeight, ((KnuthPenalty)endEl).getW()); + additionalPenaltyHeight, endEl.getW()); } int boxHeight = step - addedBoxHeight - penaltyHeight; @@ -413,10 +419,10 @@ public class ListItemLayoutManager extends BlockStackingLayoutManager // body // "unwrap" the Positions stored in the elements ListIterator oldListIterator = oldList.listIterator(); - KnuthElement oldElement = null; + KnuthElement oldElement; while (oldListIterator.hasNext()) { oldElement = (KnuthElement)oldListIterator.next(); - Position innerPosition = ((NonLeafPosition) oldElement.getPosition()).getPosition(); + Position innerPosition = oldElement.getPosition().getPosition(); //log.debug(" BLM> unwrapping: " + (oldElement.isBox() // ? "box " : (oldElement.isGlue() ? "glue " : "penalty")) // + " creato da " + oldElement.getLayoutManager().getClass().getName()); @@ -459,7 +465,7 @@ public class ListItemLayoutManager extends BlockStackingLayoutManager LayoutContext layoutContext) { getParentArea(null); - getPSLM().addIDToPage(getListItemFO().getId()); + addId(); LayoutContext lc = new LayoutContext(0); Position firstPos = null; @@ -478,7 +484,7 @@ public class ListItemLayoutManager extends BlockStackingLayoutManager } if (pos instanceof NonLeafPosition && pos.getPosition() != null) { // pos contains a ListItemPosition created by this ListBlockLM - positionList.add(((NonLeafPosition) pos).getPosition()); + positionList.add(pos.getPosition()); } } @@ -510,7 +516,7 @@ public class ListItemLayoutManager extends BlockStackingLayoutManager // set the space adjustment ratio lc.setSpaceAdjust(layoutContext.getSpaceAdjust()); // TO DO: use the right stack limit for the label - lc.setStackLimit(layoutContext.getStackLimit()); + lc.setStackLimitBP(layoutContext.getStackLimitBP()); label.addAreas(labelIter, lc); } @@ -531,7 +537,7 @@ public class ListItemLayoutManager extends BlockStackingLayoutManager // set the space adjustment ratio lc.setSpaceAdjust(layoutContext.getSpaceAdjust()); // TO DO: use the right stack limit for the body - lc.setStackLimit(layoutContext.getStackLimit()); + lc.setStackLimitBP(layoutContext.getStackLimitBP()); body.addAreas(bodyIter, lc); } @@ -554,7 +560,7 @@ public class ListItemLayoutManager extends BlockStackingLayoutManager curBlockArea = null; resetSpaces(); - getPSLM().notifyEndOfLayout(((ListItem)getFObj()).getId()); + getPSLM().notifyEndOfLayout(fobj.getId()); } /** diff --git a/src/java/org/apache/fop/layoutmgr/table/ActiveCell.java b/src/java/org/apache/fop/layoutmgr/table/ActiveCell.java index c1e4ae619..61d0f8e6f 100644 --- a/src/java/org/apache/fop/layoutmgr/table/ActiveCell.java +++ b/src/java/org/apache/fop/layoutmgr/table/ActiveCell.java @@ -33,6 +33,8 @@ import org.apache.fop.layoutmgr.ElementListUtils; import org.apache.fop.layoutmgr.KnuthBox; import org.apache.fop.layoutmgr.KnuthElement; import org.apache.fop.layoutmgr.KnuthPenalty; +import org.apache.fop.layoutmgr.MinOptMaxUtil; +import org.apache.fop.traits.MinOptMax; /** * A cell playing in the construction of steps for a row-group. @@ -133,6 +135,49 @@ class ActiveCell { } } + // TODO to be removed along with the RowPainter#computeContentLength method + /** See {@link ActiveCell#handleExplicitHeight(MinOptMax, MinOptMax)}. */ + private static class FillerPenalty extends KnuthPenalty { + + private int contentLength; + + FillerPenalty(KnuthPenalty p, int length) { + super(length, p.getP(), p.isFlagged(), p.getBreakClass(), + p.getPosition(), p.isAuxiliary()); + contentLength = p.getW(); + } + + FillerPenalty(int length) { + super(length, 0, false, null, true); + contentLength = 0; + } + } + + /** See {@link ActiveCell#handleExplicitHeight(MinOptMax, MinOptMax)}. */ + private static class FillerBox extends KnuthBox { + FillerBox(int length) { + super(length, null, true); + } + } + + /** + * Returns the actual length of the content represented by the given element. In the + * case where this element is used as a filler to match a row's fixed height, the + * value returned by the getW() method will be higher than the actual content. + * + * @param el an element + * @return the actual content length corresponding to the element + */ + static int getElementContentLength(KnuthElement el) { + if (el instanceof FillerPenalty) { + return ((FillerPenalty) el).contentLength; + } else if (el instanceof FillerBox) { + return 0; + } else { + return el.getW(); + } + } + ActiveCell(PrimaryGridUnit pgu, EffRow row, int rowIndex, int previousRowsLength, TableLayoutManager tableLM) { this.pgu = pgu; @@ -149,23 +194,10 @@ class ActiveCell { + pgu.getBeforeBorderWidth(0, ConditionalBorder.REST); bpAfterNormal = paddingAfterNormal + pgu.getAfterBorderWidth(ConditionalBorder.NORMAL); bpAfterTrailing = paddingAfterTrailing + pgu.getAfterBorderWidth(0, ConditionalBorder.REST); - boolean makeBoxForWholeRow = false; - if (row.getExplicitHeight().min > 0) { - boolean contentsSmaller = ElementListUtils.removeLegalBreaks( - pgu.getElements(), row.getExplicitHeight()); - if (contentsSmaller) { - makeBoxForWholeRow = true; - } - } - if (makeBoxForWholeRow) { - elementList = new java.util.ArrayList(1); - int height = row.getHeight().opt; - height -= 2 * tableLM.getHalfBorderSeparationBPD(); - height -= bpBeforeNormal + bpAfterNormal; - elementList.add(new KnuthBoxCellWithBPD(height)); - } else { - elementList = pgu.getElements(); - } + elementList = pgu.getElements(); + handleExplicitHeight( + MinOptMaxUtil.toMinOptMax(pgu.getCell().getBlockProgressionDimension(), tableLM), + row.getExplicitHeight()); knuthIter = elementList.listIterator(); includedLength = -1; // Avoid troubles with cells having content of zero length totalLength = previousRowsLength + ElementListUtils.calcContentLength(elementList); @@ -182,6 +214,46 @@ class ActiveCell { } } + /** + * Modifies the cell's element list by putting filler elements, so that the cell's or + * row's explicit height is always reached. + * + * TODO this will work properly only for the first break. Then the limitation + * explained on http://wiki.apache.org/xmlgraphics-fop/TableLayout/KnownProblems + * occurs. The list of elements needs to be re-adjusted after each break. + */ + private void handleExplicitHeight(MinOptMax cellBPD, MinOptMax rowBPD) { + int minBPD = Math.max(cellBPD.min, rowBPD.min); + if (minBPD > 0) { + ListIterator iter = elementList.listIterator(); + int cumulateLength = 0; + boolean prevIsBox = false; + while (iter.hasNext() && cumulateLength < minBPD) { + KnuthElement el = (KnuthElement) iter.next(); + if (el.isBox()) { + prevIsBox = true; + cumulateLength += el.getW(); + } else if (el.isGlue()) { + if (prevIsBox) { + elementList.add(iter.nextIndex() - 1, + new FillerPenalty(minBPD - cumulateLength)); + } + prevIsBox = false; + cumulateLength += el.getW(); + } else { + prevIsBox = false; + if (cumulateLength + el.getW() < minBPD) { + iter.set(new FillerPenalty((KnuthPenalty) el, minBPD - cumulateLength)); + } + } + } + } + int optBPD = Math.max(minBPD, Math.max(cellBPD.opt, rowBPD.opt)); + if (pgu.getContentLength() < optBPD) { + elementList.add(new FillerBox(optBPD - pgu.getContentLength())); + } + } + PrimaryGridUnit getPrimaryGridUnit() { return pgu; } @@ -265,14 +337,22 @@ class ActiveCell { } /** - * Returns the last step for this cell. + * Returns the last step for this cell. This includes the normal border- and + * padding-before, the whole content, the normal padding-after, and the + * <em>trailing</em> after border. Indeed, if the normal border is taken instead, + * and appears to be smaller than the trailing one, the last step may be smaller than + * the current step (see TableStepper#considerRowLastStep). This will produce a wrong + * infinite penalty, plus the cell's content won't be taken into account since the + * final step will be smaller than the current one (see {@link #signalNextStep(int)}). + * This actually means that the content will be swallowed. * - * @return the step including all of the cell's content plus the normal borders and paddings + * @return the length of last step */ int getLastStep() { assert nextStep.end == elementList.size() - 1; assert nextStep.contentLength == totalLength && nextStep.penaltyLength == 0; - int lastStep = bpBeforeNormal + totalLength + bpAfterNormal; + int lastStep = bpBeforeNormal + totalLength + paddingAfterNormal + + pgu.getAfterBorderWidth(ConditionalBorder.LEADING_TRAILING); log.debug(this + ": last step = " + lastStep); return lastStep; } @@ -394,8 +474,12 @@ class ActiveCell { */ void endRow(int rowIndex) { if (endsOnRow(rowIndex)) { + // Subtract the old value of bpAfterTrailing... + nextStep.totalLength -= bpAfterTrailing; bpAfterTrailing = paddingAfterNormal + pgu.getAfterBorderWidth(ConditionalBorder.LEADING_TRAILING); + // ... and add the new one + nextStep.totalLength += bpAfterTrailing; lastCellPart = true; } else { bpBeforeLeading = paddingBeforeLeading @@ -445,13 +529,6 @@ class ActiveCell { return new CellPart(pgu, nextStep.start, previousStep.end, lastCellPart, 0, 0, previousStep.penaltyLength, bpBeforeNormal, bpBeforeFirst, bpAfterNormal, bpAfterTrailing); - } else if (nextStep.start == 0 && nextStep.end == 0 - && elementList.size() == 1 - && elementList.get(0) instanceof KnuthBoxCellWithBPD) { - //Special case: Cell with fixed BPD - return new CellPart(pgu, 0, pgu.getElements().size() - 1, lastCellPart, - nextStep.condBeforeContentLength, length, nextStep.penaltyLength, - bpBeforeNormal, bpBeforeFirst, bpAfterNormal, bpAfterTrailing); } else { return new CellPart(pgu, nextStep.start, nextStep.end, lastCellPart, nextStep.condBeforeContentLength, length, nextStep.penaltyLength, @@ -463,20 +540,9 @@ class ActiveCell { return keepWithNextSignal; } - + /** {@inheritDoc} */ public String toString() { return "Cell " + (pgu.getRowIndex() + 1) + "." + (pgu.getColIndex() + 1); } - - - /** - * Marker class denoting table cells fitting in just one box (no legal break inside). - */ - private static class KnuthBoxCellWithBPD extends KnuthBox { - - public KnuthBoxCellWithBPD(int w) { - super(w, null, true); - } - } } diff --git a/src/java/org/apache/fop/layoutmgr/table/RowGroupLayoutManager.java b/src/java/org/apache/fop/layoutmgr/table/RowGroupLayoutManager.java index c2e26e18d..9c97ca827 100644 --- a/src/java/org/apache/fop/layoutmgr/table/RowGroupLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/table/RowGroupLayoutManager.java @@ -19,16 +19,16 @@ package org.apache.fop.layoutmgr.table; +import java.util.Iterator; import java.util.LinkedList; -import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.fop.fo.Constants; -import org.apache.fop.fo.FONode; import org.apache.fop.fo.flow.table.EffRow; import org.apache.fop.fo.flow.table.GridUnit; import org.apache.fop.fo.flow.table.PrimaryGridUnit; +import org.apache.fop.fo.flow.table.TableColumn; import org.apache.fop.fo.flow.table.TableRow; import org.apache.fop.fo.properties.CommonBorderPaddingBackground; import org.apache.fop.fo.properties.LengthRangeProperty; @@ -94,137 +94,119 @@ class RowGroupLayoutManager { private void createElementsForRowGroup(LayoutContext context, int alignment, int bodyType, LinkedList returnList) { log.debug("Handling row group with " + rowGroup.length + " rows..."); + EffRow row; + for (int rgi = 0; rgi < rowGroup.length; rgi++) { + row = rowGroup[rgi]; + for (Iterator iter = row.getGridUnits().iterator(); iter.hasNext();) { + GridUnit gu = (GridUnit) iter.next(); + if (gu.isPrimary()) { + PrimaryGridUnit primary = gu.getPrimary(); + // TODO a new LM must be created for every new static-content + primary.createCellLM(); + primary.getCellLM().setParent(tableLM); + //Calculate width of cell + int spanWidth = 0; + Iterator colIter = tableLM.getTable().getColumns().listIterator( + primary.getColIndex()); + for (int i = 0, c = primary.getCell().getNumberColumnsSpanned(); i < c; i++) { + spanWidth += ((TableColumn) colIter.next()).getColumnWidth().getValue( + tableLM); + } + LayoutContext childLC = new LayoutContext(0); + childLC.setStackLimitBP(context.getStackLimitBP()); //necessary? + childLC.setRefIPD(spanWidth); + + //Get the element list for the cell contents + LinkedList elems = primary.getCellLM().getNextKnuthElements( + childLC, alignment); + ElementListObserver.observe(elems, "table-cell", primary.getCell().getId()); + primary.setElements(elems); + } + } + } + computeRowHeights(); + LinkedList elements = tableStepper.getCombinedKnuthElementsForRowGroup(context, + rowGroup, bodyType); + returnList.addAll(elements); + } + + /** + * Calculate the heights of the rows in the row group, see CSS21, 17.5.3 Table height + * algorithms. + * + * TODO this method will need to be adapted once clarification has been made by the + * W3C regarding whether borders or border-separation must be included or not + */ + private void computeRowHeights() { + log.debug("rowGroup:"); MinOptMax[] rowHeights = new MinOptMax[rowGroup.length]; - MinOptMax[] explicitRowHeights = new MinOptMax[rowGroup.length]; EffRow row; - List pgus = new java.util.ArrayList(); //holds a list of a row's primary grid units for (int rgi = 0; rgi < rowGroup.length; rgi++) { row = rowGroup[rgi]; - rowHeights[rgi] = new MinOptMax(0, 0, Integer.MAX_VALUE); - explicitRowHeights[rgi] = new MinOptMax(0, 0, Integer.MAX_VALUE); - - pgus.clear(); - TableRow tableRow = null; - // The row's minimum content height; 0 if the row's height is auto, otherwise - // the .minimum component of the explicitly specified value - int minRowBPD = 0; // The BPD of the biggest cell in the row - int maxCellBPD = 0; - for (int j = 0; j < row.getGridUnits().size(); j++) { - GridUnit gu = row.getGridUnit(j); - if ((gu.isPrimary() || (gu.getColSpanIndex() == 0 && gu.isLastGridUnitRowSpan())) - && !gu.isEmpty()) { +// int maxCellBPD = 0; + MinOptMax explicitRowHeight; + TableRow tableRowFO = rowGroup[rgi].getTableRow(); + if (tableRowFO == null) { + rowHeights[rgi] = new MinOptMax(0, 0, Integer.MAX_VALUE); + explicitRowHeight = new MinOptMax(0, 0, Integer.MAX_VALUE); + } else { + LengthRangeProperty rowBPD = tableRowFO.getBlockProgressionDimension(); + rowHeights[rgi] = MinOptMaxUtil.toMinOptMax(rowBPD, tableLM); + explicitRowHeight = MinOptMaxUtil.toMinOptMax(rowBPD, tableLM); + } + for (Iterator iter = row.getGridUnits().iterator(); iter.hasNext();) { + GridUnit gu = (GridUnit) iter.next(); + if (!gu.isEmpty() && gu.getColSpanIndex() == 0 && gu.isLastGridUnitRowSpan()) { PrimaryGridUnit primary = gu.getPrimary(); - - if (gu.isPrimary()) { - // TODO a new LM must be created for every new static-content - primary.createCellLM(); - primary.getCellLM().setParent(tableLM); - - //Determine the table-row if any - if (tableRow == null && primary.getRow() != null) { - tableRow = primary.getRow(); - - //Check for bpd on row, see CSS21, 17.5.3 Table height algorithms - LengthRangeProperty rowBPD = tableRow.getBlockProgressionDimension(); - if (!rowBPD.getMinimum(tableLM).isAuto()) { - minRowBPD = Math.max(minRowBPD, - rowBPD.getMinimum(tableLM).getLength().getValue(tableLM)); - } - MinOptMaxUtil.restrict(explicitRowHeights[rgi], rowBPD, tableLM); - - } - - //Calculate width of cell - int spanWidth = 0; - for (int i = primary.getColIndex(); - i < primary.getColIndex() - + primary.getCell().getNumberColumnsSpanned(); - i++) { - if (tableLM.getColumns().getColumn(i + 1) != null) { - spanWidth += tableLM.getColumns().getColumn(i + 1) - .getColumnWidth().getValue(tableLM); - } - } - LayoutContext childLC = new LayoutContext(0); - childLC.setStackLimit(context.getStackLimit()); //necessary? - childLC.setRefIPD(spanWidth); - - //Get the element list for the cell contents - LinkedList elems = primary.getCellLM().getNextKnuthElements( - childLC, alignment); - ElementListObserver.observe(elems, "table-cell", primary.getCell().getId()); - primary.setElements(elems); + int effectiveCellBPD = 0; + LengthRangeProperty cellBPD = primary.getCell().getBlockProgressionDimension(); + if (!cellBPD.getMinimum(tableLM).isAuto()) { + effectiveCellBPD = cellBPD.getMinimum(tableLM).getLength() + .getValue(tableLM); } - - //Calculate height of row, see CSS21, 17.5.3 Table height algorithms - if (gu.isLastGridUnitRowSpan()) { - // The effective cell's bpd, after taking into account bpd - // (possibly explicitly) set on the row or on the cell, and the - // cell's content length - int effectiveCellBPD = minRowBPD; - LengthRangeProperty cellBPD = primary.getCell() - .getBlockProgressionDimension(); - if (!cellBPD.getMinimum(tableLM).isAuto()) { - effectiveCellBPD = Math.max(effectiveCellBPD, - cellBPD.getMinimum(tableLM).getLength().getValue(tableLM)); - } - if (!cellBPD.getOptimum(tableLM).isAuto()) { - effectiveCellBPD = Math.max(effectiveCellBPD, - cellBPD.getOptimum(tableLM).getLength().getValue(tableLM)); - } - if (gu.getRowSpanIndex() == 0) { - //TODO ATM only non-row-spanned cells are taken for this - MinOptMaxUtil.restrict(explicitRowHeights[rgi], cellBPD, tableLM); - } - effectiveCellBPD = Math.max(effectiveCellBPD, - primary.getContentLength()); - - int borderWidths = primary.getBeforeAfterBorderWidth(); - int padding = 0; - maxCellBPD = Math.max(maxCellBPD, effectiveCellBPD); - CommonBorderPaddingBackground cbpb - = primary.getCell().getCommonBorderPaddingBackground(); - padding += cbpb.getPaddingBefore(false, primary.getCellLM()); - padding += cbpb.getPaddingAfter(false, primary.getCellLM()); - int effRowHeight = effectiveCellBPD - + padding + borderWidths; - for (int previous = 0; previous < gu.getRowSpanIndex(); previous++) { - effRowHeight -= rowHeights[rgi - previous - 1].opt; - } - if (effRowHeight > rowHeights[rgi].min) { - //This is the new height of the (grid) row - MinOptMaxUtil.extendMinimum(rowHeights[rgi], effRowHeight, false); - } + if (!cellBPD.getOptimum(tableLM).isAuto()) { + effectiveCellBPD = cellBPD.getOptimum(tableLM).getLength() + .getValue(tableLM); } - - if (gu.isPrimary()) { - pgus.add(primary); + if (gu.getRowSpanIndex() == 0) { + effectiveCellBPD = Math.max(effectiveCellBPD, explicitRowHeight.opt); + } + effectiveCellBPD = Math.max(effectiveCellBPD, primary.getContentLength()); + int borderWidths = primary.getBeforeAfterBorderWidth(); + int padding = 0; + CommonBorderPaddingBackground cbpb = primary.getCell() + .getCommonBorderPaddingBackground(); + padding += cbpb.getPaddingBefore(false, primary.getCellLM()); + padding += cbpb.getPaddingAfter(false, primary.getCellLM()); + int effRowHeight = effectiveCellBPD + padding + borderWidths; + for (int prev = rgi - 1; prev >= rgi - gu.getRowSpanIndex(); prev--) { + effRowHeight -= rowHeights[prev].opt; + } + if (effRowHeight > rowHeights[rgi].min) { + // This is the new height of the (grid) row + MinOptMaxUtil.extendMinimum(rowHeights[rgi], effRowHeight); } } } row.setHeight(rowHeights[rgi]); - row.setExplicitHeight(explicitRowHeights[rgi]); - if (maxCellBPD > row.getExplicitHeight().max) { - log.warn(FONode.decorateWithContextInfo( - "The contents of row " + (row.getIndex() + 1) - + " are taller than they should be (there is a" - + " block-progression-dimension or height constraint on the indicated row)." - + " Due to its contents the row grows" - + " to " + maxCellBPD + " millipoints, but the row shouldn't get" - + " any taller than " + row.getExplicitHeight() + " millipoints.", - row.getTableRow())); - } - } - if (log.isDebugEnabled()) { - log.debug("rowGroup:"); - for (int i = 0; i < rowHeights.length; i++) { - log.debug(" height=" + rowHeights[i] + " explicit=" + explicitRowHeights[i]); + row.setExplicitHeight(explicitRowHeight); + // TODO re-enable and improve after clarification +// if (maxCellBPD > row.getExplicitHeight().max) { +// log.warn(FONode.decorateWithContextInfo( +// "The contents of row " + (row.getIndex() + 1) +// + " are taller than they should be (there is a" +// + " block-progression-dimension or height constraint +// + " on the indicated row)." +// + " Due to its contents the row grows" +// + " to " + maxCellBPD + " millipoints, but the row shouldn't get" +// + " any taller than " + row.getExplicitHeight() + " millipoints.", +// row.getTableRow())); +// } + if (log.isDebugEnabled()) { + log.debug(" height=" + rowHeights[rgi] + " explicit=" + explicitRowHeight); } } - LinkedList elements = tableStepper.getCombinedKnuthElementsForRowGroup(context, - rowGroup, bodyType); - returnList.addAll(elements); } } diff --git a/src/java/org/apache/fop/layoutmgr/table/RowPainter.java b/src/java/org/apache/fop/layoutmgr/table/RowPainter.java index 022ff0589..bed9c53ae 100644 --- a/src/java/org/apache/fop/layoutmgr/table/RowPainter.java +++ b/src/java/org/apache/fop/layoutmgr/table/RowPainter.java @@ -23,25 +23,27 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; +import java.util.ListIterator; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.apache.fop.area.Block; import org.apache.fop.fo.flow.table.ConditionalBorder; import org.apache.fop.fo.flow.table.EffRow; import org.apache.fop.fo.flow.table.GridUnit; import org.apache.fop.fo.flow.table.PrimaryGridUnit; -import org.apache.fop.fo.flow.table.TableRow; +import org.apache.fop.fo.flow.table.TableBody; +import org.apache.fop.fo.properties.CommonBorderPaddingBackground; import org.apache.fop.layoutmgr.ElementListUtils; import org.apache.fop.layoutmgr.KnuthElement; import org.apache.fop.layoutmgr.KnuthPossPosIter; import org.apache.fop.layoutmgr.LayoutContext; import org.apache.fop.layoutmgr.SpaceResolver; +import org.apache.fop.layoutmgr.TraitSetter; import org.apache.fop.traits.BorderProps; class RowPainter { private static Log log = LogFactory.getLog(RowPainter.class); - /** The fo:table-row containing the currently handled grid rows. */ - private TableRow rowFO = null; private int colCount; private int currentRowOffset = 0; /** Currently handled row (= last encountered row). */ @@ -71,6 +73,13 @@ class RowPainter { private CellPart[] firstCellParts; private CellPart[] lastCellParts; + /** y-offset of the current table part. */ + private int tablePartOffset = 0; + /** See {@link RowPainter#registerPartBackgroundArea(Block)}. */ + private CommonBorderPaddingBackground tablePartBackground; + /** See {@link RowPainter#registerPartBackgroundArea(Block)}. */ + private List tablePartBackgroundAreas; + private TableContentLayoutManager tclm; RowPainter(TableContentLayoutManager tclm, LayoutContext layoutContext) { @@ -85,6 +94,44 @@ class RowPainter { this.firstRowOnPageIndex = -1; } + void startTablePart(TableBody tablePart) { + CommonBorderPaddingBackground background = tablePart.getCommonBorderPaddingBackground(); + if (background.hasBackground()) { + tablePartBackground = background; + if (tablePartBackgroundAreas == null) { + tablePartBackgroundAreas = new ArrayList(); + } + } + tablePartOffset = currentRowOffset; + } + + /** + * Signals that the end of the current table part is reached. + * + * @param lastInBody true if the part is the last table-body element to be displayed + * on the current page. In which case all the cells must be flushed even if they + * aren't finished, plus the proper collapsed borders must be selected (trailing + * instead of normal, or rest if the cell is unfinished) + * @param lastOnPage true if the part is the last to be displayed on the current page. + * In which case collapsed after borders for the cells on the last row must be drawn + * in the outer mode + */ + void endTablePart(boolean lastInBody, boolean lastOnPage) { + addAreasAndFlushRow(lastInBody, lastOnPage); + + if (tablePartBackground != null) { + TableLayoutManager tableLM = tclm.getTableLM(); + for (Iterator iter = tablePartBackgroundAreas.iterator(); iter.hasNext();) { + Block backgroundArea = (Block) iter.next(); + TraitSetter.addBackground(backgroundArea, tablePartBackground, tableLM, + -backgroundArea.getXOffset(), tablePartOffset - backgroundArea.getYOffset(), + tableLM.getContentAreaIPD(), currentRowOffset - tablePartOffset); + } + tablePartBackground = null; + tablePartBackgroundAreas.clear(); + } + } + int getAccumulatedBPD() { return currentRowOffset; } @@ -108,7 +155,6 @@ class RowPainter { currentRow = row; } } - rowFO = currentRow.getTableRow(); if (firstRowIndex < 0) { firstRowIndex = currentRow.getIndex(); if (firstRowOnPageIndex < 0) { @@ -148,7 +194,7 @@ class RowPainter { * displayed on the current page. In which case collapsed after borders must be drawn * in the outer mode */ - void addAreasAndFlushRow(boolean lastInPart, boolean lastOnPage) { + private void addAreasAndFlushRow(boolean lastInPart, boolean lastOnPage) { if (log.isDebugEnabled()) { log.debug("Remembering yoffset for row " + currentRow.getIndex() + ": " + currentRowOffset); @@ -182,8 +228,6 @@ class RowPainter { } // Then add areas for cells finishing on the current row - tclm.addRowBackgroundArea(rowFO, actualRowHeight, layoutContext.getRefIPD(), - currentRowOffset); for (int i = 0; i < colCount; i++) { GridUnit currentGU = currentRow.getGridUnit(i); if (!currentGU.isEmpty() && currentGU.getColSpanIndex() == 0 @@ -255,18 +299,22 @@ class RowPainter { // cell, in most cases) return 0; } else { - int actualStart = startIndex; + ListIterator iter = pgu.getElements().listIterator(startIndex); // Skip from the content length calculation glues and penalties occurring at the // beginning of the page - while (actualStart <= endIndex - && !((KnuthElement) pgu.getElements().get(actualStart)).isBox()) { - actualStart++; + boolean nextIsBox = false; + while (iter.nextIndex() <= endIndex && !nextIsBox) { + nextIsBox = ((KnuthElement) iter.next()).isBox(); } - int len = ElementListUtils.calcContentLength( - pgu.getElements(), actualStart, endIndex); - KnuthElement el = (KnuthElement)pgu.getElements().get(endIndex); - if (el.isPenalty()) { - len += el.getW(); + int len = 0; + if (((KnuthElement) iter.previous()).isBox()) { + while (iter.nextIndex() < endIndex) { + KnuthElement el = (KnuthElement) iter.next(); + if (el.isBox() || el.isGlue()) { + len += el.getW(); + } + } + len += ActiveCell.getElementContentLength((KnuthElement) iter.next()); } return len; } @@ -278,8 +326,20 @@ class RowPainter { * Determine the index of the first row of this cell that will be displayed on the * current page. */ - int startRowIndex = Math.max(pgu.getRowIndex(), firstRowIndex); int currentRowIndex = currentRow.getIndex(); + int startRowIndex; + int firstRowHeight; + if (pgu.getRowIndex() >= firstRowIndex) { + startRowIndex = pgu.getRowIndex(); + if (startRowIndex < currentRowIndex) { + firstRowHeight = getRowOffset(startRowIndex + 1) - getRowOffset(startRowIndex); + } else { + firstRowHeight = rowHeight; + } + } else { + startRowIndex = firstRowIndex; + firstRowHeight = 0; + } /* * In collapsing-border model, if the cell spans over several columns/rows then @@ -319,7 +379,25 @@ class RowPainter { cellLM.addAreas(new KnuthPossPosIter(pgu.getElements(), startPos, endPos + 1), layoutContext, spannedGridRowHeights, startRowIndex - pgu.getRowIndex(), currentRowIndex - pgu.getRowIndex(), borderBeforeWhich, borderAfterWhich, - startRowIndex == firstRowOnPageIndex, lastOnPage); + startRowIndex == firstRowOnPageIndex, lastOnPage, this, firstRowHeight); + } + + + /** + * Registers the given area, that will be used to render the part of + * table-header/footer/body background covered by a table-cell. If percentages are + * used to place the background image, the final bpd of the (fraction of) table part + * that will be rendered on the current page must be known. The traits can't then be + * set when the areas for the cell are created since at that moment this bpd is yet + * unknown. So they will instead be set in + * {@link #addAreasAndFlushRow(boolean, boolean)}. + * + * @param backgroundArea the block of the cell's dimensions that will hold the part + * background + */ + void registerPartBackgroundArea(Block backgroundArea) { + tclm.getTableLM().addBackgroundArea(backgroundArea); + tablePartBackgroundAreas.add(backgroundArea); } /** @@ -357,11 +435,13 @@ class RowPainter { } // TODO get rid of that + /** Signals that the first table-body instance has started. */ void startBody() { Arrays.fill(firstCellOnPage, true); } // TODO get rid of that + /** Signals that the last table-body instance has ended. */ void endBody() { Arrays.fill(firstCellOnPage, false); } diff --git a/src/java/org/apache/fop/layoutmgr/table/TableAndCaptionLayoutManager.java b/src/java/org/apache/fop/layoutmgr/table/TableAndCaptionLayoutManager.java index 6d2e49e96..2e5bbdf1f 100644 --- a/src/java/org/apache/fop/layoutmgr/table/TableAndCaptionLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/table/TableAndCaptionLayoutManager.java @@ -23,7 +23,6 @@ import org.apache.fop.fo.flow.table.TableAndCaption; import org.apache.fop.layoutmgr.BlockStackingLayoutManager; import org.apache.fop.layoutmgr.LayoutContext; import org.apache.fop.layoutmgr.PositionIterator; -import org.apache.fop.layoutmgr.Position; import org.apache.fop.area.Area; import org.apache.fop.area.Block; @@ -36,8 +35,7 @@ import org.apache.fop.area.Block; * @todo Implement getNextKnuthElements() */ public class TableAndCaptionLayoutManager extends BlockStackingLayoutManager { - private TableAndCaption fobj; - + private Block curBlockArea; //private List childBreaks = new java.util.ArrayList(); @@ -48,7 +46,6 @@ public class TableAndCaptionLayoutManager extends BlockStackingLayoutManager { */ public TableAndCaptionLayoutManager(TableAndCaption node) { super(node); - fobj = node; } /** @@ -134,7 +131,7 @@ public class TableAndCaptionLayoutManager extends BlockStackingLayoutManager { public void addAreas(PositionIterator parentIter, LayoutContext layoutContext) { getParentArea(null); - getPSLM().addIDToPage(fobj.getId()); + addId(); /* TODO: Reimplement using Knuth approach LayoutManager childLM; diff --git a/src/java/org/apache/fop/layoutmgr/table/TableCaptionLayoutManager.java b/src/java/org/apache/fop/layoutmgr/table/TableCaptionLayoutManager.java index 4c21df937..674fd9a90 100644 --- a/src/java/org/apache/fop/layoutmgr/table/TableCaptionLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/table/TableCaptionLayoutManager.java @@ -23,7 +23,6 @@ import org.apache.fop.fo.flow.table.TableCaption; import org.apache.fop.layoutmgr.BlockStackingLayoutManager; import org.apache.fop.layoutmgr.LayoutContext; import org.apache.fop.layoutmgr.PositionIterator; -import org.apache.fop.layoutmgr.Position; import org.apache.fop.area.Area; import org.apache.fop.area.Block; @@ -34,7 +33,6 @@ import org.apache.fop.area.Block; * @todo Implement getNextKnuthElements() */ public class TableCaptionLayoutManager extends BlockStackingLayoutManager { - private TableCaption fobj; private Block curBlockArea; @@ -46,7 +44,6 @@ public class TableCaptionLayoutManager extends BlockStackingLayoutManager { */ public TableCaptionLayoutManager(TableCaption node) { super(node); - fobj = node; } /** @@ -133,7 +130,7 @@ public class TableCaptionLayoutManager extends BlockStackingLayoutManager { public void addAreas(PositionIterator parentIter, LayoutContext layoutContext) { getParentArea(null); - getPSLM().addIDToPage(fobj.getId()); + addId(); /* TODO: Reimplement using Knuth approach LayoutManager childLM; diff --git a/src/java/org/apache/fop/layoutmgr/table/TableCellLayoutManager.java b/src/java/org/apache/fop/layoutmgr/table/TableCellLayoutManager.java index 289785d68..8acfebca0 100644 --- a/src/java/org/apache/fop/layoutmgr/table/TableCellLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/table/TableCellLayoutManager.java @@ -23,16 +23,19 @@ import java.util.LinkedList; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + import org.apache.fop.area.Area; import org.apache.fop.area.Block; import org.apache.fop.area.Trait; import org.apache.fop.datatypes.PercentBaseContext; -import org.apache.fop.fo.FONode; import org.apache.fop.fo.flow.table.ConditionalBorder; import org.apache.fop.fo.flow.table.GridUnit; import org.apache.fop.fo.flow.table.PrimaryGridUnit; import org.apache.fop.fo.flow.table.Table; +import org.apache.fop.fo.flow.table.TableBody; import org.apache.fop.fo.flow.table.TableCell; +import org.apache.fop.fo.flow.table.TableColumn; +import org.apache.fop.fo.flow.table.TableRow; import org.apache.fop.fo.properties.CommonBorderPaddingBackground; import org.apache.fop.fo.properties.CommonBorderPaddingBackground.BorderInfo; import org.apache.fop.layoutmgr.AreaAdditionUtil; @@ -71,9 +74,8 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager private int xoffset; private int yoffset; private int cellIPD; - private int rowHeight; + private int totalHeight; private int usedBPD; - private int borderAndPaddingBPD; private boolean emptyCell = true; /** @@ -96,31 +98,11 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager return getTable().isSeparateBorderModel(); } - /** {@inheritDoc} */ - public void initialize() { - borderAndPaddingBPD = 0; - borderAndPaddingBPD += getTableCell() - .getCommonBorderPaddingBackground().getBorderBeforeWidth(false); - borderAndPaddingBPD += getTableCell() - .getCommonBorderPaddingBackground().getBorderAfterWidth(false); - if (!isSeparateBorderModel()) { - borderAndPaddingBPD /= 2; - } - borderAndPaddingBPD += getTableCell().getCommonBorderPaddingBackground() - .getPaddingBefore(false, this); - borderAndPaddingBPD += getTableCell().getCommonBorderPaddingBackground() - .getPaddingAfter(false, this); - } - /** * @return the table owning this cell */ public Table getTable() { - FONode node = fobj.getParent(); - while (!(node instanceof Table)) { - node = node.getParent(); - } - return (Table)node; + return getTableCell().getTable(); } @@ -148,13 +130,13 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager * {@inheritDoc} */ public LinkedList getNextKnuthElements(LayoutContext context, int alignment) { - MinOptMax stackLimit = new MinOptMax(context.getStackLimit()); + MinOptMax stackLimit = new MinOptMax(context.getStackLimitBP()); referenceIPD = context.getRefIPD(); cellIPD = referenceIPD; cellIPD -= getIPIndents(); - LinkedList returnedList = null; + LinkedList returnedList; LinkedList contentList = new LinkedList(); LinkedList returnList = new LinkedList(); @@ -163,8 +145,8 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager while ((curLM = (BlockLevelLayoutManager) getChildLM()) != null) { LayoutContext childLC = new LayoutContext(0); // curLM is a ? - childLC.setStackLimit(MinOptMax.subtract(context - .getStackLimit(), stackLimit)); + childLC.setStackLimitBP(MinOptMax.subtract(context + .getStackLimitBP(), stackLimit)); childLC.setRefIPD(cellIPD); // get elements from curLM @@ -260,7 +242,7 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager p.setP(0); } - getPSLM().notifyEndOfLayout(((TableCell)getFObj()).getId()); + getPSLM().notifyEndOfLayout(fobj.getId()); setFinished(true); return returnList; @@ -303,7 +285,7 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager * @param h the height of cell */ public void setTotalHeight(int h) { - rowHeight = h; + totalHeight = h; } /** @@ -330,6 +312,10 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager * which case collapsed before borders must be drawn in the outer mode * @param lastOnPage true if the cell will be the very last one on the page, in which * case collapsed after borders must be drawn in the outer mode + * @param painter painter + * @param firstRowHeight height of the first row spanned by this cell (may be zero if + * this row is placed on a previous page). Used to calculate the placement of the + * row's background image if any */ public void addAreas(PositionIterator parentIter, LayoutContext layoutContext, @@ -339,13 +325,25 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager int borderBeforeWhich, int borderAfterWhich, boolean firstOnPage, - boolean lastOnPage) { + boolean lastOnPage, + RowPainter painter, + int firstRowHeight) { getParentArea(null); - getPSLM().addIDToPage(getTableCell().getId()); + addId(); int borderBeforeWidth = primaryGridUnit.getBeforeBorderWidth(startRow, borderBeforeWhich); int borderAfterWidth = primaryGridUnit.getAfterBorderWidth(endRow, borderAfterWhich); + + CommonBorderPaddingBackground padding = primaryGridUnit.getCell() + .getCommonBorderPaddingBackground(); + int paddingRectBPD = totalHeight - borderBeforeWidth - borderAfterWidth; + int cellBPD = paddingRectBPD; + cellBPD -= padding.getPaddingBefore(borderBeforeWhich == ConditionalBorder.REST, this); + cellBPD -= padding.getPaddingAfter(borderAfterWhich == ConditionalBorder.REST, this); + + addBackgroundAreas(painter, firstRowHeight, borderBeforeWidth, paddingRectBPD); + if (isSeparateBorderModel()) { if (!emptyCell || getTableCell().showEmptyCells()) { if (borderBeforeWidth > 0) { @@ -421,7 +419,7 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager int dx = xoffset; for (int x = 0; x < gridUnits.length; x++) { int ipd = getTable().getColumn(primaryGridUnit.getColIndex() + x) - .getColumnWidth().getValue((PercentBaseContext) getParent()); + .getColumnWidth().getValue(getParent()); if (blocks[y][x] != null) { Block block = blocks[y][x]; adjustYOffset(block, dy); @@ -437,18 +435,12 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager } } - CommonBorderPaddingBackground padding = primaryGridUnit.getCell() - .getCommonBorderPaddingBackground(); TraitSetter.addPadding(curBlockArea, padding, borderBeforeWhich == ConditionalBorder.REST, borderAfterWhich == ConditionalBorder.REST, false, false, this); - int cellBPD = rowHeight - borderBeforeWidth - borderAfterWidth; - cellBPD -= padding.getPaddingBefore(borderBeforeWhich == ConditionalBorder.REST, this); - cellBPD -= padding.getPaddingAfter(borderAfterWhich == ConditionalBorder.REST, this); - //Handle display-align if (usedBPD < cellBPD) { if (getTableCell().getDisplayAlign() == EN_CENTER) { @@ -468,16 +460,9 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager curBlockArea.setBPD(cellBPD); // Add background after we know the BPD - if (isSeparateBorderModel()) { - if (!emptyCell || getTableCell().showEmptyCells()) { - TraitSetter.addBackground(curBlockArea, - getTableCell().getCommonBorderPaddingBackground(), - this); - } - } else { + if (!isSeparateBorderModel() || !emptyCell || getTableCell().showEmptyCells()) { TraitSetter.addBackground(curBlockArea, - getTableCell().getCommonBorderPaddingBackground(), - this); + getTableCell().getCommonBorderPaddingBackground(), this); } flush(); @@ -485,6 +470,33 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager curBlockArea = null; } + /** Adds background areas for the column, body and row, if any. */ + private void addBackgroundAreas(RowPainter painter, int firstRowHeight, int borderBeforeWidth, + int paddingRectBPD) { + TableColumn column = getTable().getColumn(primaryGridUnit.getColIndex()); + if (column.getCommonBorderPaddingBackground().hasBackground()) { + Block colBackgroundArea = getBackgroundArea(paddingRectBPD, borderBeforeWidth); + ((TableLayoutManager) parentLM).registerColumnBackgroundArea(column, colBackgroundArea, + -startIndent); + } + + TableBody body = primaryGridUnit.getTableBody(); + if (body.getCommonBorderPaddingBackground().hasBackground()) { + painter.registerPartBackgroundArea( + getBackgroundArea(paddingRectBPD, borderBeforeWidth)); + } + + TableRow row = primaryGridUnit.getRow(); + if (row != null && row.getCommonBorderPaddingBackground().hasBackground()) { + Block rowBackgroundArea = getBackgroundArea(paddingRectBPD, borderBeforeWidth); + ((TableLayoutManager) parentLM).addBackgroundArea(rowBackgroundArea); + TraitSetter.addBackground(rowBackgroundArea, row.getCommonBorderPaddingBackground(), + parentLM, + -xoffset - startIndent, -borderBeforeWidth, + parentLM.getContentAreaIPD(), firstRowHeight); + } + } + private void addBorder(Block[][] blocks, int i, int j, Integer side, BorderInfo border, boolean outer) { if (blocks[i][j] == null) { @@ -513,6 +525,21 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager block.setBPD(block.getBPD() + amount); } + private Block getBackgroundArea(int bpd, int borderBeforeWidth) { + CommonBorderPaddingBackground padding = getTableCell().getCommonBorderPaddingBackground(); + int paddingStart = padding.getPaddingStart(false, this); + int paddingEnd = padding.getPaddingEnd(false, this); + + Block block = new Block(); + TraitSetter.setProducerID(block, getTable().getId()); + block.setPositioning(Block.ABSOLUTE); + block.setIPD(cellIPD + paddingStart + paddingEnd); + block.setBPD(bpd); + block.setXOffset(xoffset + startIndent - paddingStart); + block.setYOffset(yoffset + borderBeforeWidth); + return block; + } + /** * Return an Area which can contain the passed childArea. The childArea * may not yet have any content, but it has essential traits set. diff --git a/src/java/org/apache/fop/layoutmgr/table/TableContentLayoutManager.java b/src/java/org/apache/fop/layoutmgr/table/TableContentLayoutManager.java index b9a118e28..7cdeb79d5 100644 --- a/src/java/org/apache/fop/layoutmgr/table/TableContentLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/table/TableContentLayoutManager.java @@ -27,8 +27,6 @@ import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.apache.fop.area.Block; -import org.apache.fop.area.Trait; import org.apache.fop.datatypes.PercentBaseContext; import org.apache.fop.fo.Constants; import org.apache.fop.fo.FObj; @@ -36,7 +34,6 @@ import org.apache.fop.fo.flow.table.EffRow; import org.apache.fop.fo.flow.table.PrimaryGridUnit; import org.apache.fop.fo.flow.table.Table; import org.apache.fop.fo.flow.table.TableBody; -import org.apache.fop.fo.flow.table.TableRow; import org.apache.fop.layoutmgr.BreakElement; import org.apache.fop.layoutmgr.ElementListUtils; import org.apache.fop.layoutmgr.KnuthBox; @@ -46,7 +43,6 @@ import org.apache.fop.layoutmgr.LayoutContext; import org.apache.fop.layoutmgr.ListElement; import org.apache.fop.layoutmgr.Position; import org.apache.fop.layoutmgr.PositionIterator; -import org.apache.fop.layoutmgr.TraitSetter; import org.apache.fop.layoutmgr.SpaceResolver.SpaceHandlingBreakPosition; import org.apache.fop.util.BreakUtil; @@ -293,7 +289,7 @@ public class TableContentLayoutManager implements PercentBaseContext { this.usedBPD = 0; RowPainter painter = new RowPainter(this, layoutContext); - List positions = new java.util.ArrayList(); + List tablePositions = new ArrayList(); List headerElements = null; List footerElements = null; Position firstPos = null; @@ -330,7 +326,7 @@ public class TableContentLayoutManager implements PercentBaseContext { //ignore for now, see special handling below if break is at a penalty //Only if the last position in this part/page us such a position it will be used } else if (pos instanceof TableContentPosition) { - positions.add(pos); + tablePositions.add(pos); } else { if (log.isDebugEnabled()) { log.debug("Ignoring position: " + pos); @@ -359,21 +355,23 @@ public class TableContentLayoutManager implements PercentBaseContext { if (headerElements != null) { //header positions for the last part are the second-to-last element and need to //be handled first before all other TableContentPositions - PositionIterator nestedIter = new KnuthPossPosIter(headerElements); - iterateAndPaintPositions(nestedIter, painter, false); + addHeaderFooterAreas(headerElements, tableLM.getTable().getTableHeader(), painter, + false); } - //Iterate over all steps - Iterator posIter = positions.iterator(); - painter.startBody(); - // Here we are sure that posIter iterates only over TableContentPosition instances - iterateAndPaintPositions(posIter, painter, footerElements == null); - painter.endBody(); + if (tablePositions.isEmpty()) { + // TODO make sure this actually never happens + log.error("tablePositions empty." + + " Please send your FO file to fop-users@xmlgraphics.apache.org"); + } else { + // Here we are sure that posIter iterates only over TableContentPosition instances + addBodyAreas(tablePositions.iterator(), painter, footerElements == null); + } if (footerElements != null) { //Positions for footers are simply added at the end - PositionIterator nestedIter = new KnuthPossPosIter(footerElements); - iterateAndPaintPositions(nestedIter, painter, true); + addHeaderFooterAreas(footerElements, tableLM.getTable().getTableFooter(), painter, + true); } this.usedBPD += painter.getAccumulatedBPD(); @@ -384,106 +382,74 @@ public class TableContentLayoutManager implements PercentBaseContext { } } + private void addHeaderFooterAreas(List elements, TableBody part, RowPainter painter, + boolean lastOnPage) { + List lst = new ArrayList(elements.size()); + for (Iterator iter = new KnuthPossPosIter(elements); iter.hasNext();) { + Position pos = (Position) iter.next(); + /* + * Unlike for the body the Positions associated to the glues generated by + * TableStepper haven't been removed yet. + */ + if (pos instanceof TableContentPosition) { + lst.add((TableContentPosition) pos); + } + } + addTablePartAreas(lst, painter, part, true, true, true, lastOnPage); + } + /** - * Iterates over a part of the table (header, footer, body) and paints the related - * elements. + * Iterates over the positions corresponding to the table's body (which may contain + * several table-body elements!) and adds the corresponding areas. * - * @param iterator iterator over Position elements. Those positions correspond to the - * elements of the table present on the current page + * @param iterator iterator over TableContentPosition elements. Those positions + * correspond to the elements of the body present on the current page * @param painter - * @param lastOnPage true if the corresponding part will be the last on the page - * (either body or footer, obviously) + * @param lastOnPage true if the table has no footer (then the last line of the table + * that will be present on the page belongs to the body) */ - private void iterateAndPaintPositions(Iterator iterator, RowPainter painter, + private void addBodyAreas(Iterator iterator, RowPainter painter, boolean lastOnPage) { + painter.startBody(); List lst = new ArrayList(); - boolean firstPos = false; - TableBody body = null; + TableContentPosition pos = (TableContentPosition) iterator.next(); + boolean isFirstPos = pos.getFlag(TableContentPosition.FIRST_IN_ROWGROUP) + && pos.getRow().getFlag(EffRow.FIRST_IN_PART); + TableBody body = pos.getTableBody(); + lst.add(pos); while (iterator.hasNext()) { - Position pos = (Position)iterator.next(); - if (pos instanceof TableContentPosition) { - TableContentPosition tcpos = (TableContentPosition)pos; - lst.add(tcpos); - CellPart part = (CellPart)tcpos.cellParts.get(0); - if (body == null) { - body = part.pgu.getBody(); - } - if (tcpos.getFlag(TableContentPosition.FIRST_IN_ROWGROUP) - && tcpos.getRow().getFlag(EffRow.FIRST_IN_PART)) { - firstPos = true; - - } - if (tcpos.getFlag(TableContentPosition.LAST_IN_ROWGROUP) - && tcpos.getRow().getFlag(EffRow.LAST_IN_PART)) { - log.trace("LAST_IN_ROWGROUP + LAST_IN_PART"); - handleMarkersAndPositions(lst, body, firstPos, true, painter); - //reset - firstPos = false; - body = null; - lst.clear(); - } + pos = (TableContentPosition) iterator.next(); + if (pos.getTableBody() != body) { + addTablePartAreas(lst, painter, body, isFirstPos, true, false, false); + isFirstPos = true; + lst.clear(); + body = pos.getTableBody(); } + lst.add(pos); } - if (body != null) { - // Entering this block means that the end of the current table-part hasn't - // been reached (otherwise it would have been caught by the test above). So - // lastPos is necessarily false - handleMarkersAndPositions(lst, body, firstPos, false, painter); - } - painter.addAreasAndFlushRow(true, lastOnPage); - } - - private void handleMarkersAndPositions(List positions, TableBody body, boolean firstPos, - boolean lastPos, RowPainter painter) { - getTableLM().getCurrentPV().addMarkers(body.getMarkers(), - true, firstPos, lastPos); - int size = positions.size(); - for (int i = 0; i < size; i++) { - painter.handleTableContentPosition((TableContentPosition)positions.get(i)); - } - getTableLM().getCurrentPV().addMarkers(body.getMarkers(), - false, firstPos, lastPos); + boolean isLastPos = pos.getFlag(TableContentPosition.LAST_IN_ROWGROUP) + && pos.getRow().getFlag(EffRow.LAST_IN_PART); + addTablePartAreas(lst, painter, body, isFirstPos, isLastPos, true, lastOnPage); + painter.endBody(); } /** - * Get the area for a row for background. - * @param row the table-row object or null - * @return the row area or null if there's no background to paint + * Adds the areas corresponding to a single fo:table-header/footer/body element. */ - Block getRowArea(TableRow row) { - if (row == null || !row.getCommonBorderPaddingBackground().hasBackground()) { - return null; - } else { - Block block = new Block(); - block.addTrait(Trait.IS_REFERENCE_AREA, Boolean.TRUE); - block.setPositioning(Block.ABSOLUTE); - return block; + private void addTablePartAreas(List positions, RowPainter painter, TableBody body, + boolean isFirstPos, boolean isLastPos, boolean lastInBody, boolean lastOnPage) { + getTableLM().getCurrentPV().addMarkers(body.getMarkers(), + true, isFirstPos, isLastPos); + painter.startTablePart(body); + for (Iterator iter = positions.iterator(); iter.hasNext();) { + painter.handleTableContentPosition((TableContentPosition) iter.next()); } + getTableLM().getCurrentPV().addMarkers(body.getMarkers(), + false, isFirstPos, isLastPos); + painter.endTablePart(lastInBody, lastOnPage); } /** - * Adds the area for the row background if any. - * @param row row for which to generate the background - * @param bpd block-progression-dimension of the row - * @param ipd inline-progression-dimension of the row - * @param yoffset Y offset at which to paint - */ - void addRowBackgroundArea(TableRow row, int bpd, int ipd, int yoffset) { - //Add row background if any - Block rowBackground = getRowArea(row); - if (rowBackground != null) { - rowBackground.setBPD(bpd); - rowBackground.setIPD(ipd); - rowBackground.setXOffset(this.startXOffset); - rowBackground.setYOffset(yoffset); - getTableLM().addChildArea(rowBackground); - TraitSetter.addBackground(rowBackground, - row.getCommonBorderPaddingBackground(), getTableLM()); - } - } - - - /** * Sets the overall starting x-offset. Used for proper placement of cells. * @param startXOffset starting x-offset (table's start-indent) */ diff --git a/src/java/org/apache/fop/layoutmgr/table/TableContentPosition.java b/src/java/org/apache/fop/layoutmgr/table/TableContentPosition.java index e702c58a9..260b8cfdf 100644 --- a/src/java/org/apache/fop/layoutmgr/table/TableContentPosition.java +++ b/src/java/org/apache/fop/layoutmgr/table/TableContentPosition.java @@ -22,6 +22,7 @@ package org.apache.fop.layoutmgr.table; import java.util.List; import org.apache.fop.fo.flow.table.EffRow; +import org.apache.fop.fo.flow.table.TableBody; import org.apache.fop.layoutmgr.LayoutManager; import org.apache.fop.layoutmgr.Position; @@ -79,6 +80,10 @@ class TableContentPosition extends Position { return row; } + TableBody getTableBody() { + return ((CellPart) cellParts.get(0)).pgu.getTableBody(); + } + /** * Returns a flag for this GridUnit. * @param which the requested flag @@ -101,10 +106,12 @@ class TableContentPosition extends Position { } } + /** {@inheritDoc} */ public boolean generatesAreas() { return true; } + /** {@inheritDoc} */ public String toString() { StringBuffer sb = new StringBuffer("TableContentPosition:"); sb.append(getIndex()); diff --git a/src/java/org/apache/fop/layoutmgr/table/TableHFPenaltyPosition.java b/src/java/org/apache/fop/layoutmgr/table/TableHFPenaltyPosition.java index afa166985..3e504a45c 100644 --- a/src/java/org/apache/fop/layoutmgr/table/TableHFPenaltyPosition.java +++ b/src/java/org/apache/fop/layoutmgr/table/TableHFPenaltyPosition.java @@ -43,6 +43,11 @@ class TableHFPenaltyPosition extends Position { super(lm); } + /** {@inheritDoc} */ + public boolean generatesAreas() { + return true; + } + public String toString() { StringBuffer sb = new StringBuffer("TableHFPenaltyPosition:"); sb.append(getIndex()).append("("); diff --git a/src/java/org/apache/fop/layoutmgr/table/TableHeaderFooterPosition.java b/src/java/org/apache/fop/layoutmgr/table/TableHeaderFooterPosition.java index c3ae72c74..8d3b993b2 100644 --- a/src/java/org/apache/fop/layoutmgr/table/TableHeaderFooterPosition.java +++ b/src/java/org/apache/fop/layoutmgr/table/TableHeaderFooterPosition.java @@ -48,6 +48,11 @@ class TableHeaderFooterPosition extends Position { this.nestedElements = nestedElements; } + /** {@inheritDoc} */ + public boolean generatesAreas() { + return true; + } + public String toString() { StringBuffer sb = new StringBuffer("Table"); sb.append(header ? "Header" : "Footer"); diff --git a/src/java/org/apache/fop/layoutmgr/table/TableLayoutManager.java b/src/java/org/apache/fop/layoutmgr/table/TableLayoutManager.java index 1cbc3e50a..d6bba5cb5 100644 --- a/src/java/org/apache/fop/layoutmgr/table/TableLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/table/TableLayoutManager.java @@ -19,8 +19,10 @@ package org.apache.fop.layoutmgr.table; +import java.util.ArrayList; import java.util.Iterator; import java.util.LinkedList; +import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -79,7 +81,27 @@ public class TableLayoutManager extends BlockStackingLayoutManager private int halfBorderSeparationBPD; private int halfBorderSeparationIPD; - + + /** See {@link TableLayoutManager#registerColumnBackgroundArea(TableColumn, Block, int)}. */ + private List columnBackgroundAreas; + + /** + * Temporary holder of column background informations for a table-cell's area. + * + * @see TableLayoutManager#registerColumnBackgroundArea(TableColumn, Block, int) + */ + private static final class ColumnBackgroundInfo { + private TableColumn column; + private Block backgroundArea; + private int xShift; + + private ColumnBackgroundInfo(TableColumn column, Block backgroundArea, int xShift) { + this.column = column; + this.backgroundArea = backgroundArea; + this.xShift = xShift; + } + } + /** * Create a new table layout manager. * @param node the table FO @@ -211,7 +233,7 @@ public class TableLayoutManager extends BlockStackingLayoutManager // Elements for the table-header/footer/body - LinkedList contentKnuthElements = null; + LinkedList contentKnuthElements; contentLM = new TableContentLayoutManager(this); LayoutContext childLC = new LayoutContext(0); /* @@ -263,7 +285,30 @@ public class TableLayoutManager extends BlockStackingLayoutManager resetSpaces(); return returnList; } - + + /** + * Registers the given area, that will be used to render the part of column background + * covered by a table-cell. If percentages are used to place the background image, the + * final bpd of the (fraction of) table that will be rendered on the current page must + * be known. The traits can't then be set when the areas for the cell are created + * since at that moment this bpd is yet unknown. So they will instead be set in + * TableLM's {@link #addAreas(PositionIterator, LayoutContext)} method. + * + * @param column the table-column element from which the cell gets background + * informations + * @param backgroundArea the block of the cell's dimensions that will hold the column + * background + * @param xShift additional amount by which the image must be shifted to be correctly + * placed (to counterbalance the cell's start border) + */ + void registerColumnBackgroundArea(TableColumn column, Block backgroundArea, int xShift) { + addBackgroundArea(backgroundArea); + if (columnBackgroundAreas == null) { + columnBackgroundAreas = new ArrayList(); + } + columnBackgroundAreas.add(new ColumnBackgroundInfo(column, backgroundArea, xShift)); + } + /** * The table area is a reference area that contains areas for * columns, bodies, rows and the contents are in cells. @@ -274,7 +319,7 @@ public class TableLayoutManager extends BlockStackingLayoutManager public void addAreas(PositionIterator parentIter, LayoutContext layoutContext) { getParentArea(null); - getPSLM().addIDToPage(getTable().getId()); + addId(); // add space before, in order to implement display-align = "center" or "after" if (layoutContext.getSpaceBefore() != 0) { @@ -298,6 +343,17 @@ public class TableLayoutManager extends BlockStackingLayoutManager curBlockArea.setBPD(tableHeight); + if (columnBackgroundAreas != null) { + for (Iterator iter = columnBackgroundAreas.iterator(); iter.hasNext();) { + ColumnBackgroundInfo b = (ColumnBackgroundInfo) iter.next(); + TraitSetter.addBackground(b.backgroundArea, + b.column.getCommonBorderPaddingBackground(), this, + b.xShift, -b.backgroundArea.getYOffset(), + b.column.getColumnWidth().getValue(this), tableHeight); + } + columnBackgroundAreas.clear(); + } + if (getTable().isSeparateBorderModel()) { TraitSetter.addBorders(curBlockArea, getTable().getCommonBorderPaddingBackground(), @@ -323,7 +379,7 @@ public class TableLayoutManager extends BlockStackingLayoutManager resetSpaces(); curBlockArea = null; - getPSLM().notifyEndOfLayout(((Table)getFObj()).getId()); + getPSLM().notifyEndOfLayout(fobj.getId()); } /** @@ -366,6 +422,15 @@ public class TableLayoutManager extends BlockStackingLayoutManager } } + /** + * Adds the given area to this layout manager's area, without updating the used bpd. + * + * @param background an area + */ + void addBackgroundArea(Block background) { + curBlockArea.addChildArea(background); + } + /** {@inheritDoc} */ public int negotiateBPDAdjustment(int adj, KnuthElement lastElement) { // TODO Auto-generated method stub diff --git a/src/java/org/apache/fop/pdf/PDFCMap.java b/src/java/org/apache/fop/pdf/PDFCMap.java index bdf8108c2..1be5e9dc4 100644 --- a/src/java/org/apache/fop/pdf/PDFCMap.java +++ b/src/java/org/apache/fop/pdf/PDFCMap.java @@ -21,7 +21,6 @@ package org.apache.fop.pdf; import java.io.IOException; import java.io.OutputStream; -import java.io.StringWriter; import java.io.Writer; /** @@ -425,10 +424,8 @@ public class PDFCMap extends PDFStream { /** {@inheritDoc} */ protected int output(OutputStream stream) throws IOException { - StringWriter writer = new StringWriter(); - CMapBuilder builder = createCMapBuilder(writer); + CMapBuilder builder = createCMapBuilder(getBufferWriter()); builder.writeCMap(); - add(writer.getBuffer().toString()); //TODO Could be optimized by not buffering return super.output(stream); } } diff --git a/src/java/org/apache/fop/pdf/PDFEncoding.java b/src/java/org/apache/fop/pdf/PDFEncoding.java index b2fba6e53..6dc448b40 100644 --- a/src/java/org/apache/fop/pdf/PDFEncoding.java +++ b/src/java/org/apache/fop/pdf/PDFEncoding.java @@ -132,6 +132,14 @@ public class PDFEncoding extends PDFDictionary { } /** + * Indicates whether any differences have been recorded. + * @return true if there are differences. + */ + public boolean hasDifferences() { + return (this.differences.length() > 0); + } + + /** * Creates and returns the PDFArray representing the Differences entry. * @return the Differences entry */ diff --git a/src/java/org/apache/fop/pdf/PDFFactory.java b/src/java/org/apache/fop/pdf/PDFFactory.java index 687b32016..f4474331f 100644 --- a/src/java/org/apache/fop/pdf/PDFFactory.java +++ b/src/java/org/apache/fop/pdf/PDFFactory.java @@ -43,6 +43,7 @@ import org.apache.commons.logging.LogFactory; import org.apache.xmlgraphics.xmp.Metadata; import org.apache.fop.fonts.CIDFont; +import org.apache.fop.fonts.CIDSubset; import org.apache.fop.fonts.CodePointMapping; import org.apache.fop.fonts.CustomFont; import org.apache.fop.fonts.FontDescriptor; @@ -50,6 +51,8 @@ import org.apache.fop.fonts.FontMetrics; import org.apache.fop.fonts.FontType; import org.apache.fop.fonts.LazyFont; import org.apache.fop.fonts.MultiByteFont; +import org.apache.fop.fonts.SimpleSingleByteEncoding; +import org.apache.fop.fonts.SingleByteEncoding; import org.apache.fop.fonts.SingleByteFont; import org.apache.fop.fonts.Typeface; import org.apache.fop.fonts.truetype.FontFileReader; @@ -1166,7 +1169,7 @@ public class PDFFactory { } /** - * make a Type1 /Font object + * Make a Type1 /Font object. * * @param fontname internal name to use for this font (eg "F1") * @param basefont name of the base font (eg "Helvetica") @@ -1217,10 +1220,12 @@ public class PDFFactory { (PDFCIDFontDescriptor)pdfdesc); getDocument().registerObject(cidFont); - PDFCMap cmap = new PDFToUnicodeCMap(cidMetrics.getCharsUsed(), "fop-ucs-H", - new PDFCIDSystemInfo("Adobe", - "Identity", - 0)); + PDFCMap cmap = new PDFToUnicodeCMap( + cidMetrics.getCIDSubset().getSubsetChars(), + "fop-ucs-H", + new PDFCIDSystemInfo("Adobe", + "Identity", + 0)); getDocument().registerObject(cmap); ((PDFFontType0)font).setCMAP(cmap); ((PDFFontType0)font).setDescendantFonts(cidFont); @@ -1238,35 +1243,20 @@ public class PDFFactory { int lastChar = singleByteFont.getLastChar(); nonBase14.setWidthMetrics(firstChar, lastChar, - makeArray(metrics.getWidths())); + new PDFArray(null, metrics.getWidths())); //Handle encoding - CodePointMapping mapping = singleByteFont.getCodePointMapping(); + SingleByteEncoding mapping = singleByteFont.getEncoding(); if (PDFEncoding.isPredefinedEncoding(mapping.getName())) { font.setEncoding(mapping.getName()); } else { - CodePointMapping winansi = CodePointMapping.getMapping( - CodePointMapping.WIN_ANSI_ENCODING); - PDFEncoding pdfEncoding = new PDFEncoding(winansi.getName()); - PDFEncoding.DifferencesBuilder builder - = pdfEncoding.createDifferencesBuilder(); - int start = -1; - String[] winansiNames = winansi.getCharNameMap(); - String[] charNameMap = mapping.getCharNameMap(); - for (int i = 0; i < 256; i++) { - String wac = winansiNames[i]; - String c = charNameMap[i]; - if (!wac.equals(c)) { - if (start != i) { - builder.addDifference(i); - start = i; - } - builder.addName(c); - start++; - } + Object pdfEncoding = createPDFEncoding(mapping, + singleByteFont.getFontName()); + if (pdfEncoding instanceof PDFEncoding) { + font.setEncoding((PDFEncoding)pdfEncoding); + } else { + font.setEncoding((String)pdfEncoding); } - pdfEncoding.setDifferences(builder.toPDFArray()); - font.setEncoding(pdfEncoding); /* JM: What I thought would be a necessity with custom encodings turned out to * be a bug in Adobe Acrobat 8. The following section just demonstrates how @@ -1278,21 +1268,88 @@ public class PDFFactory { nonBase14.setToUnicode(cmap); */ } + + //Handle additional encodings (characters outside the primary encoding) + if (singleByteFont.hasAdditionalEncodings()) { + for (int i = 0, c = singleByteFont.getAdditionalEncodingCount(); i < c; i++) { + SimpleSingleByteEncoding addEncoding + = singleByteFont.getAdditionalEncoding(i); + String name = fontname + "_" + (i + 1); + Object pdfenc = createPDFEncoding(addEncoding, + singleByteFont.getFontName()); + PDFFontNonBase14 addFont = (PDFFontNonBase14)PDFFont.createFont( + name, fonttype, + basefont, pdfenc); + addFont.setDescriptor(pdfdesc); + addFont.setWidthMetrics( + addEncoding.getFirstChar(), + addEncoding.getLastChar(), + new PDFArray(null, singleByteFont.getAdditionalWidths(i))); + getDocument().registerObject(addFont); + getDocument().getResources().addFont(addFont); + } + } } return font; } } + /** + * Creates a PDFEncoding instance from a CodePointMapping instance. + * @param encoding the code point mapping (encoding) + * @return the PDF Encoding dictionary (or a String with the predefined encoding) + */ + public Object createPDFEncoding(SingleByteEncoding encoding, String fontNameHint) { + SingleByteEncoding baseEncoding; + if (fontNameHint.indexOf("Symbol") >= 0) { + baseEncoding = CodePointMapping.getMapping( + CodePointMapping.SYMBOL_ENCODING); + } else { + baseEncoding = CodePointMapping.getMapping( + CodePointMapping.STANDARD_ENCODING); + } + PDFEncoding pdfEncoding = new PDFEncoding(baseEncoding.getName()); + PDFEncoding.DifferencesBuilder builder + = pdfEncoding.createDifferencesBuilder(); + int start = -1; + String[] baseNames = baseEncoding.getCharNameMap(); + String[] charNameMap = encoding.getCharNameMap(); + for (int i = 0, ci = charNameMap.length; i < ci; i++) { + String basec = baseNames[i]; + String c = charNameMap[i]; + if (!basec.equals(c)) { + if (start != i) { + builder.addDifference(i); + start = i; + } + builder.addName(c); + start++; + } + } + if (builder.hasDifferences()) { + pdfEncoding.setDifferences(builder.toPDFArray()); + return pdfEncoding; + } else { + return baseEncoding.getName(); + } + } + + /** + * Creates and returns a width array with the widths of all the characters in the subset. + * @param cidFont the font + * @return the width array + */ public PDFWArray getSubsetWidths(CIDFont cidFont) { // Create widths for reencoded chars PDFWArray warray = new PDFWArray(); - int[] tmpWidth = new int[cidFont.usedGlyphsCount]; + int[] widths = cidFont.getWidths(); + CIDSubset subset = cidFont.getCIDSubset(); + int[] tmpWidth = new int[subset.getSubsetSize()]; - for (int i = 0; i < cidFont.usedGlyphsCount; i++) { - Integer nw = (Integer)cidFont.usedGlyphsIndex.get(new Integer(i)); - int nwx = (nw == null) ? 0 : nw.intValue(); - tmpWidth[i] = cidFont.width[nwx]; + for (int i = 0, c = subset.getSubsetSize(); i < c; i++) { + int nwx = Math.max(0, subset.getGlyphIndexForSubsetIndex(i)); + tmpWidth[i] = widths[nwx]; } warray.addEntry(0, tmpWidth); return warray; @@ -1345,12 +1402,7 @@ public class PDFFactory { } private void buildCIDSet(PDFFontDescriptor descriptor, CIDFont cidFont) { - BitSet cidSubset = new BitSet(); - Iterator iter = cidFont.usedGlyphs.keySet().iterator(); - while (iter.hasNext()) { - Integer cid = (Integer)iter.next(); - cidSubset.set(cid.intValue()); - } + BitSet cidSubset = cidFont.getCIDSubset().getGlyphIndexBitSet(); PDFStream cidSet = makeStream(null, true); ByteArrayOutputStream baout = new ByteArrayOutputStream(cidSubset.length() / 8 + 1); int value = 0; @@ -1548,14 +1600,13 @@ public class PDFFactory { } /** - * make an Array object (ex. Widths array for a font) + * Make an Array object (ex. Widths array for a font). * * @param values the int array values * @return the PDF Array with the int values */ public PDFArray makeArray(int[] values) { PDFArray array = new PDFArray(null, values); - getDocument().registerObject(array); return array; } diff --git a/src/java/org/apache/fop/pdf/PDFResources.java b/src/java/org/apache/fop/pdf/PDFResources.java index 66ccdc78b..b0c0128e1 100644 --- a/src/java/org/apache/fop/pdf/PDFResources.java +++ b/src/java/org/apache/fop/pdf/PDFResources.java @@ -110,7 +110,7 @@ public class PDFResources extends PDFObject { desc = (FontDescriptor)font; } addFont(doc.getFactory().makeFont( - f, font.getEmbedFontName(), font.getEncoding(), font, desc)); + f, font.getEmbedFontName(), font.getEncodingName(), font, desc)); } } } diff --git a/src/java/org/apache/fop/pdf/PDFStream.java b/src/java/org/apache/fop/pdf/PDFStream.java index 59a8336c5..a213340e3 100644 --- a/src/java/org/apache/fop/pdf/PDFStream.java +++ b/src/java/org/apache/fop/pdf/PDFStream.java @@ -19,8 +19,9 @@ package org.apache.fop.pdf; -import java.io.OutputStream; import java.io.IOException; +import java.io.OutputStream; +import java.io.Writer; /** * Class representing a PDF stream. @@ -37,6 +38,8 @@ public class PDFStream extends AbstractPDFStream { */ protected StreamCache data; + private transient Writer streamWriter; + /** * Create an empty stream object */ @@ -44,6 +47,10 @@ public class PDFStream extends AbstractPDFStream { super(); try { data = StreamCacheFactory.getInstance().createStreamCache(); + this.streamWriter = new java.io.OutputStreamWriter( + getBufferOutputStream(), PDFDocument.ENCODING); + //Buffer to minimize calls to the converter + this.streamWriter = new java.io.BufferedWriter(this.streamWriter); } catch (IOException ex) { //TODO throw the exception and catch it elsewhere ex.printStackTrace(); @@ -57,21 +64,35 @@ public class PDFStream extends AbstractPDFStream { */ public void add(String s) { try { - data.getOutputStream().write(PDFDocument.encode(s)); + this.streamWriter.write(s); } catch (IOException ex) { //TODO throw the exception and catch it elsewhere ex.printStackTrace(); } - + } + + private void flush() throws IOException { + this.streamWriter.flush(); } /** + * Returns a Writer that writes to the OutputStream of the buffer. + * @return the Writer + */ + public Writer getBufferWriter() { + return this.streamWriter; + } + + /** * Returns an OutputStream that can be used to write to the buffer which is used * to build up the PDF stream. * @return the OutputStream * @throws IOException In case of an I/O problem */ public OutputStream getBufferOutputStream() throws IOException { + if (this.streamWriter != null) { + flush(); //Just to be sure + } return this.data.getOutputStream(); } @@ -91,6 +112,7 @@ public class PDFStream extends AbstractPDFStream { */ public int getDataLength() { try { + flush(); return data.getSize(); } catch (Exception e) { //TODO throw the exception and catch it elsewhere @@ -99,17 +121,15 @@ public class PDFStream extends AbstractPDFStream { } } - /** - * {@inheritDoc} - */ + /** {@inheritDoc} */ protected int getSizeHint() throws IOException { + flush(); return data.getSize(); } - /** - * {@inheritDoc} - */ + /** {@inheritDoc} */ protected void outputRawStreamData(OutputStream out) throws IOException { + flush(); data.outputContents(out); } diff --git a/src/java/org/apache/fop/pdf/PDFTTFStream.java b/src/java/org/apache/fop/pdf/PDFTTFStream.java index b3488f6a1..5570c62f3 100644 --- a/src/java/org/apache/fop/pdf/PDFTTFStream.java +++ b/src/java/org/apache/fop/pdf/PDFTTFStream.java @@ -67,7 +67,7 @@ public class PDFTTFStream extends PDFStream { */ public void setData(byte[] data, int size) throws IOException { this.data.clear(); - this.data.getOutputStream().write(data, 0, size); + getBufferOutputStream().write(data, 0, size); } } diff --git a/src/java/org/apache/fop/pdf/PDFTextUtil.java b/src/java/org/apache/fop/pdf/PDFTextUtil.java new file mode 100644 index 000000000..224bb6a1d --- /dev/null +++ b/src/java/org/apache/fop/pdf/PDFTextUtil.java @@ -0,0 +1,295 @@ +/* + * 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.pdf; + +import java.awt.geom.AffineTransform; + +/** + * Utility class for generating PDF text objects. It needs to be subclassed to add writing + * functionality (see {@link #write(String)}). + */ +public abstract class PDFTextUtil { + + /** The number of decimal places. */ + private static final int DEC = 8; + + /** PDF text rendering mode: Fill text */ + public static final int TR_FILL = 0; + /** PDF text rendering mode: Stroke text */ + public static final int TR_STROKE = 1; + /** PDF text rendering mode: Fill, then stroke text */ + public static final int TR_FILL_STROKE = 2; + /** PDF text rendering mode: Neither fill nor stroke text (invisible) */ + public static final int TR_INVISIBLE = 3; + /** PDF text rendering mode: Fill text and add to path for clipping */ + public static final int TR_FILL_CLIP = 4; + /** PDF text rendering mode: Stroke text and add to path for clipping */ + public static final int TR_STROKE_CLIP = 5; + /** PDF text rendering mode: Fill, then stroke text and add to path for clipping */ + public static final int TR_FILL_STROKE_CLIP = 6; + /** PDF text rendering mode: Add text to path for clipping */ + public static final int TR_CLIP = 7; + + private boolean inTextObject = false; + private String startText; + private String endText; + private boolean useMultiByte; + private StringBuffer bufTJ; + private int textRenderingMode = TR_FILL; + + private String currentFontName; + private double currentFontSize; + + /** + * Main constructor. + */ + public PDFTextUtil() { + //nop + } + + /** + * Writes PDF code. + * @param code the PDF code to write + */ + protected abstract void write(String code); + + private void writeAffineTransform(AffineTransform at, StringBuffer sb) { + double[] lt = new double[6]; + at.getMatrix(lt); + sb.append(PDFNumber.doubleOut(lt[0], DEC)).append(" "); + sb.append(PDFNumber.doubleOut(lt[1], DEC)).append(" "); + sb.append(PDFNumber.doubleOut(lt[2], DEC)).append(" "); + sb.append(PDFNumber.doubleOut(lt[3], DEC)).append(" "); + sb.append(PDFNumber.doubleOut(lt[4], DEC)).append(" "); + sb.append(PDFNumber.doubleOut(lt[5], DEC)); + } + + private void writeChar(char ch, StringBuffer sb) { + if (!useMultiByte) { + if (ch < 32 || ch > 127) { + sb.append("\\").append(Integer.toOctalString((int)ch)); + } else { + switch (ch) { + case '(': + case ')': + case '\\': + sb.append("\\"); + break; + default: + } + sb.append(ch); + } + } else { + sb.append(PDFText.toUnicodeHex(ch)); + } + } + + private void checkInTextObject() { + if (!inTextObject) { + throw new IllegalStateException("Not in text object"); + } + } + + /** + * Indicates whether we are in a text object or not. + * @return true if we are in a text object + */ + public boolean isInTextObject() { + return inTextObject; + } + + /** + * Called when a new text object should be started. Be sure to call setFont() before + * issuing any text painting commands. + */ + public void beginTextObject() { + if (inTextObject) { + throw new IllegalStateException("Already in text object"); + } + write("BT\n"); + this.inTextObject = true; + } + + /** + * Called when a text object should be ended. + */ + public void endTextObject() { + checkInTextObject(); + write("ET\n"); + this.inTextObject = false; + initValues(); + } + + /** + * Resets the state fields. + */ + protected void initValues() { + this.currentFontName = null; + this.currentFontSize = 0.0; + this.textRenderingMode = TR_FILL; + } + + /** + * Creates a "q" command, pushing a copy of the entire graphics state onto the stack. + */ + public void saveGraphicsState() { + write("q\n"); + } + + /** + * Creates a "Q" command, restoring the entire graphics state to its former value by popping + * it from the stack. + */ + public void restoreGraphicsState() { + write("Q\n"); + } + + /** + * Creates a "cm" command. + * @param at the transformation matrix + */ + public void concatMatrix(AffineTransform at) { + if (!at.isIdentity()) { + writeTJ(); + StringBuffer sb = new StringBuffer(); + writeAffineTransform(at, sb); + sb.append(" cm\n"); + write(sb.toString()); + } + } + + /** + * Writes a "Tf" command, setting a new current font. + * @param fontName the name of the font to select + * @param fontSize the font size (in points) + */ + public void writeTf(String fontName, double fontSize) { + checkInTextObject(); + write("/" + fontName + " " + PDFNumber.doubleOut(fontSize) + " Tf\n"); + + this.startText = useMultiByte ? "<" : "("; + this.endText = useMultiByte ? ">" : ")"; + } + + /** + * Updates the current font. This method only writes a "Tf" if the current font changes. + * @param fontName the name of the font to select + * @param fontSize the font size (in points) + * @param multiByte true indicates the font is a multi-byte font, false means single-byte + */ + public void updateTf(String fontName, double fontSize, boolean multiByte) { + checkInTextObject(); + if (!fontName.equals(this.currentFontName) || (fontSize != this.currentFontSize)) { + writeTJ(); + this.currentFontName = fontName; + this.currentFontSize = fontSize; + this.useMultiByte = multiByte; + writeTf(fontName, fontSize); + } + } + + /** + * Sets the text rendering mode. + * @param mode the rendering mode (value 0 to 7, see PDF Spec, constants: TR_*) + */ + public void setTextRenderingMode(int mode) { + if (mode < 0 || mode > 7) { + throw new IllegalArgumentException( + "Illegal value for text rendering mode. Expected: 0-7"); + } + if (mode != this.textRenderingMode) { + writeTJ(); + this.textRenderingMode = mode; + write(this.textRenderingMode + " Tr\n"); + } + } + + /** + * Sets the text rendering mode. + * @param fill true if the text should be filled + * @param stroke true if the text should be stroked + * @param addToClip true if the path should be added for clipping + */ + public void setTextRenderingMode(boolean fill, boolean stroke, boolean addToClip) { + int mode; + if (fill) { + mode = (stroke ? 2 : 0); + } else { + mode = (stroke ? 1 : 3); + } + if (addToClip) { + mode += 4; + } + setTextRenderingMode(mode); + } + + /** + * Writes a "Tm" command, setting a new text transformation matrix. + * @param localTransform the new text transformation matrix + */ + public void writeTextMatrix(AffineTransform localTransform) { + StringBuffer sb = new StringBuffer(); + writeAffineTransform(localTransform, sb); + sb.append(" Tm "); + write(sb.toString()); + } + + /** + * Writes a char to the "TJ-Buffer". + * @param codepoint the mapped character (code point/character code) + */ + public void writeTJMappedChar(char codepoint) { + if (bufTJ == null) { + bufTJ = new StringBuffer(); + } + if (bufTJ.length() == 0) { + bufTJ.append("[").append(startText); + } + writeChar(codepoint, bufTJ); + } + + /** + * Writes a glyph adjust value to the "TJ-Buffer". + * @param adjust the glyph adjust value in thousands of text unit space. + */ + public void adjustGlyphTJ(double adjust) { + bufTJ.append(endText).append(" "); + bufTJ.append(PDFNumber.doubleOut(adjust, DEC - 4)); + bufTJ.append(" "); + bufTJ.append(startText); + } + + /** + * Writes a "TJ" command, writing out the accumulated buffer with the characters and glyph + * positioning values. The buffer is reset afterwards. + */ + public void writeTJ() { + if (isInString()) { + bufTJ.append(endText).append("] TJ\n"); + write(bufTJ.toString()); + bufTJ.setLength(0); + } + } + + private boolean isInString() { + return bufTJ != null && bufTJ.length() > 0; + } + +} diff --git a/src/java/org/apache/fop/render/AbstractGenericSVGHandler.java b/src/java/org/apache/fop/render/AbstractGenericSVGHandler.java index f74699fd5..c0c9ce88c 100644 --- a/src/java/org/apache/fop/render/AbstractGenericSVGHandler.java +++ b/src/java/org/apache/fop/render/AbstractGenericSVGHandler.java @@ -47,7 +47,7 @@ import org.apache.fop.svg.SVGUserAgent; public abstract class AbstractGenericSVGHandler implements XMLHandler, RendererContextConstants { /** logging instance */ - private static Log log = LogFactory.getLog(AbstractGenericSVGHandler.class); + protected static Log log = LogFactory.getLog(AbstractGenericSVGHandler.class); /** {@inheritDoc} */ public void handleXML(RendererContext context, diff --git a/src/java/org/apache/fop/render/afp/AFPRenderer.java b/src/java/org/apache/fop/render/afp/AFPRenderer.java index b10f4b30f..cb3d3a433 100644 --- a/src/java/org/apache/fop/render/afp/AFPRenderer.java +++ b/src/java/org/apache/fop/render/afp/AFPRenderer.java @@ -940,10 +940,7 @@ public class AFPRenderer extends AbstractPathOrientedRenderer { int x = origin.x + posInt.x; int y = origin.y + posInt.y; - String name = null; - if (pageSegmentsMap != null) { - name = (String) pageSegmentsMap.get(uri); - } + String name = (String)getPageSegments().get(uri); if (name != null) { afpDataStream.createIncludePageSegment(name, mpts2units(x), mpts2units(y)); } else { @@ -1543,6 +1540,13 @@ public class AFPRenderer extends AbstractPathOrientedRenderer { return MimeConstants.MIME_AFP; } + private Map getPageSegments() { + if (pageSegmentsMap == null) { + pageSegmentsMap = new java.util.HashMap(); + } + return pageSegmentsMap; + } + /** * Method to render the page extension. * <p> @@ -1571,17 +1575,11 @@ public class AFPRenderer extends AbstractPathOrientedRenderer { .equals(element)) { String name = aps.getName(); String source = aps.getValue(); - if (pageSegmentsMap == null) { - pageSegmentsMap = new java.util.HashMap(); - } - pageSegmentsMap.put(source, name); + getPageSegments().put(source, name); } else if (AFPElementMapping.TAG_LOGICAL_ELEMENT .equals(element)) { String name = aps.getName(); String value = aps.getValue(); - if (pageSegmentsMap == null) { - pageSegmentsMap = new java.util.HashMap(); - } afpDataStream.createTagLogicalElement(name, value); } else if (AFPElementMapping.NO_OPERATION.equals(element)) { String content = aps.getContent(); @@ -1589,7 +1587,7 @@ public class AFPRenderer extends AbstractPathOrientedRenderer { afpDataStream.createNoOperation(content); } } else if (AFPElementMapping.RESOURCE.equals(element)) { - System.out.println("resource: " + attachment); + log.info("resource: " + attachment); } } } diff --git a/src/java/org/apache/fop/render/afp/AFPRendererConfigurator.java b/src/java/org/apache/fop/render/afp/AFPRendererConfigurator.java index 4c8a3dc9f..cde464ba5 100644 --- a/src/java/org/apache/fop/render/afp/AFPRendererConfigurator.java +++ b/src/java/org/apache/fop/render/afp/AFPRendererConfigurator.java @@ -234,8 +234,7 @@ public class AFPRendererConfigurator extends PrintRendererConfigurator { Configuration rendererResolutionCfg = cfg.getChild("renderer-resolution", false); if (rendererResolutionCfg != null) { - afpRenderer.setResolution(rendererResolutionCfg.getValueAsInteger( - AFPRenderer.DPI_240_RESOLUTION)); + afpRenderer.setResolution(rendererResolutionCfg.getValueAsInteger(240)); } Configuration gocaSupportCfg = cfg.getChild("goca-enabled", false); diff --git a/src/java/org/apache/fop/render/afp/AFPSVGHandler.java b/src/java/org/apache/fop/render/afp/AFPSVGHandler.java index 2641e6fbe..1d469ea94 100644 --- a/src/java/org/apache/fop/render/afp/AFPSVGHandler.java +++ b/src/java/org/apache/fop/render/afp/AFPSVGHandler.java @@ -49,9 +49,6 @@ import org.w3c.dom.Document; */ public class AFPSVGHandler extends AbstractGenericSVGHandler { - /** logging instance */ - private static Log log = LogFactory.getLog(AFPSVGHandler.class); - /** {@inheritDoc} */ public void handleXML(RendererContext context, Document doc, String ns) throws Exception { @@ -196,7 +193,10 @@ public class AFPSVGHandler extends AbstractGenericSVGHandler { } /** {@inheritDoc} */ - public String getNamespace() { - return SVGDOMImplementation.SVG_NAMESPACE_URI; + protected void updateRendererContext(RendererContext context) { + //Work around a problem in Batik: Gradients cannot be done in ColorSpace.CS_GRAY + context.setProperty(AFPRendererContextConstants.AFP_GRAYSCALE, + Boolean.FALSE); } + } diff --git a/src/java/org/apache/fop/render/afp/extensions/AFPElement.java b/src/java/org/apache/fop/render/afp/extensions/AFPElement.java index 808cd88f1..659c79a8e 100755 --- a/src/java/org/apache/fop/render/afp/extensions/AFPElement.java +++ b/src/java/org/apache/fop/render/afp/extensions/AFPElement.java @@ -55,7 +55,8 @@ public class AFPElement extends AbstractAFPExtensionObject { } } - protected ExtensionAttachment instantiateExtensionAttachment() { - return null; - } + protected ExtensionAttachment instantiateExtensionAttachment() { + return new AFPPageSetup(getName()); + } + } diff --git a/src/java/org/apache/fop/render/afp/extensions/AFPElementMapping.java b/src/java/org/apache/fop/render/afp/extensions/AFPElementMapping.java index 8d4b0ae5e..ad80399e3 100755 --- a/src/java/org/apache/fop/render/afp/extensions/AFPElementMapping.java +++ b/src/java/org/apache/fop/render/afp/extensions/AFPElementMapping.java @@ -19,8 +19,6 @@ package org.apache.fop.render.afp.extensions; -import java.util.HashMap; - import org.apache.fop.fo.ElementMapping; import org.apache.fop.fo.FONode; @@ -39,7 +37,7 @@ public class AFPElementMapping extends ElementMapping { public static final String PAGE = "page"; /** page group element */ - public static final String PAGE_GROUP = "page-group"; +// public static final String PAGE_GROUP = "page-group"; /** tag logical element */ public static final String TAG_LOGICAL_ELEMENT = "tag-logical-element"; @@ -78,9 +76,12 @@ public class AFPElementMapping extends ElementMapping { protected void initialize() { if (foObjs == null) { - foObjs = new HashMap(); + super.foObjs = new java.util.HashMap(); foObjs.put(PAGE, new AFPPageSetupMaker()); - // foObjs.put(PAGE_GROUP, new AFPMaker()); +// foObjs.put( +// PAGE_GROUP, +// new AFPPageGroupMaker() +// ); foObjs.put( TAG_LOGICAL_ELEMENT, new AFPTagLogicalElementMaker()); @@ -94,7 +95,6 @@ public class AFPElementMapping extends ElementMapping { NO_OPERATION, new AFPNoOperationMaker()); } - } static class AFPPageSetupMaker extends ElementMapping.Maker { @@ -126,4 +126,10 @@ public class AFPElementMapping extends ElementMapping { return new AFPElement(parent, NO_OPERATION); } } + +// static class AFPPageGroupMaker extends ElementMapping.Maker { +// public FONode make(FONode parent) { +// return new AFPElement(parent, PAGE_GROUP); +// } +// } } diff --git a/src/java/org/apache/fop/render/afp/extensions/AFPExtensionHandler.java b/src/java/org/apache/fop/render/afp/extensions/AFPExtensionHandler.java index 7fdf7cd11..fca486279 100644 --- a/src/java/org/apache/fop/render/afp/extensions/AFPExtensionHandler.java +++ b/src/java/org/apache/fop/render/afp/extensions/AFPExtensionHandler.java @@ -54,7 +54,7 @@ public class AFPExtensionHandler extends DefaultHandler || localName.equals(AFPElementMapping.INCLUDE_PAGE_OVERLAY) || localName.equals(AFPElementMapping.INCLUDE_PAGE_SEGMENT) || localName.equals(AFPElementMapping.PAGE) - || localName.equals(AFPElementMapping.PAGE_GROUP)) { + /*|| localName.equals(AFPElementMapping.PAGE_GROUP)*/) { //handled in endElement } else { handled = false; diff --git a/src/java/org/apache/fop/render/afp/fonts/OutlineFont.java b/src/java/org/apache/fop/render/afp/fonts/OutlineFont.java index 57886f89c..f8dd9a1ed 100644 --- a/src/java/org/apache/fop/render/afp/fonts/OutlineFont.java +++ b/src/java/org/apache/fop/render/afp/fonts/OutlineFont.java @@ -174,11 +174,8 @@ public class OutlineFont extends AFPFont { return charSet.mapChar(c); } - /** - * Get the encoding of the font. - * @return the encoding - */ - public String getEncoding() { + /** {@inheritDoc} */ + public String getEncodingName() { return charSet.getEncoding(); } }
\ No newline at end of file diff --git a/src/java/org/apache/fop/render/afp/fonts/RasterFont.java b/src/java/org/apache/fop/render/afp/fonts/RasterFont.java index a61f97ab5..44dcd0e9f 100644 --- a/src/java/org/apache/fop/render/afp/fonts/RasterFont.java +++ b/src/java/org/apache/fop/render/afp/fonts/RasterFont.java @@ -229,11 +229,8 @@ public class RasterFont extends AFPFont { return charSet.mapChar(c); } - /** - * Get the encoding of the font. - * @return the encoding - */ - public String getEncoding() { + /** {@inheritDoc} */ + public String getEncodingName() { return charSet.getEncoding(); } diff --git a/src/java/org/apache/fop/render/afp/modca/AbstractAFPObject.java b/src/java/org/apache/fop/render/afp/modca/AbstractAFPObject.java index 26d42b2d4..106454ad6 100644 --- a/src/java/org/apache/fop/render/afp/modca/AbstractAFPObject.java +++ b/src/java/org/apache/fop/render/afp/modca/AbstractAFPObject.java @@ -44,7 +44,7 @@ public abstract class AbstractAFPObject { * DataStream objects must implement the writeDataStream() * method to write its data to the given OutputStream * @param os The outputsteam stream - * @throws java.io.IOException in the event that an I/O exception occurred + * @throws java.io.IOException */ public abstract void writeDataStream(OutputStream os) throws IOException; @@ -52,10 +52,10 @@ public abstract class AbstractAFPObject { * Help method to write a set of AFPObjects to the AFP datastream. * @param objects a list of AFPObjects * @param os The stream to write to - * @throws java.io.IOException in the event that an I/O exception occurred + * @throws java.io.IOException */ protected void writeObjects(Collection/*<AbstractAFPObject>*/ objects, OutputStream os) - throws IOException { + throws IOException { if (objects != null) { for (Iterator it = objects.iterator(); it.hasNext();) { Object obj1 = it.next(); @@ -63,5 +63,7 @@ public abstract class AbstractAFPObject { obj.writeDataStream(os); } } + } -}
\ No newline at end of file +} + diff --git a/src/java/org/apache/fop/render/afp/modca/AbstractDescriptor.java b/src/java/org/apache/fop/render/afp/modca/AbstractDescriptor.java index 29c619d32..6d827766e 100644 --- a/src/java/org/apache/fop/render/afp/modca/AbstractDescriptor.java +++ b/src/java/org/apache/fop/render/afp/modca/AbstractDescriptor.java @@ -28,22 +28,22 @@ public abstract class AbstractDescriptor extends AbstractAFPObject { /** height of this descriptor */ protected int height = 0; /** width resolution of this descriptor */ - protected int widthRes = 0; + protected int widthResolution = 0; /** height resolution of this descriptor */ - protected int heightRes = 0; + protected int heightResolution = 0; /** * Constructor a PresentationTextDescriptor for the specified * width and height. * @param width The width of the page. * @param height The height of the page. - * @param widthRes The width resolution of the page. - * @param heightRes The height resolution of the page. + * @param widthResolution The width resolution of the page. + * @param heightResolution The height resolution of the page. */ - public AbstractDescriptor(int width, int height, int widthRes, int heightRes) { + public AbstractDescriptor(int width, int height, int widthResolution, int heightResolution) { this.width = width; this.height = height; - this.widthRes = widthRes; - this.heightRes = heightRes; + this.widthResolution = widthResolution; + this.heightResolution = heightResolution; } } diff --git a/src/java/org/apache/fop/render/afp/modca/AbstractNamedAFPObject.java b/src/java/org/apache/fop/render/afp/modca/AbstractNamedAFPObject.java index 393cff84b..09fa33f8f 100644 --- a/src/java/org/apache/fop/render/afp/modca/AbstractNamedAFPObject.java +++ b/src/java/org/apache/fop/render/afp/modca/AbstractNamedAFPObject.java @@ -38,7 +38,7 @@ public abstract class AbstractNamedAFPObject extends AbstractStructuredAFPObject * The name of the object in EBCIDIC bytes */ protected byte[] nameBytes; - + /** * Default constructor */ @@ -51,19 +51,17 @@ public abstract class AbstractNamedAFPObject extends AbstractStructuredAFPObject * @param name the object name */ protected AbstractNamedAFPObject(String name) { - int nameLen = getNameLength(); - if (name.length() < nameLen) { - this.name = (name + " ").substring(0, nameLen); - } else if (name.length() > nameLen) { - log.warn("Constructor:: name truncated to " + nameLen + " chars: " + name); - this.name = name.substring(0, nameLen); - } else { - this.name = name; + this.name = name; + if (name.length() < 8) { + name = (name + " ").substring(0, 8); + } else if (name.length() > 8) { + log.warn("Constructor:: name truncated to 8 chars" + name); + name = name.substring(0, 8); } try { - this.nameBytes = name.getBytes(AFPConstants.EBCIDIC_ENCODING); + nameBytes = name.getBytes(AFPConstants.EBCIDIC_ENCODING); } catch (UnsupportedEncodingException usee) { - this.nameBytes = name.getBytes(); + nameBytes = name.getBytes(); log.warn( "Constructor:: UnsupportedEncodingException translating the name " + name); @@ -82,5 +80,6 @@ public abstract class AbstractNamedAFPObject extends AbstractStructuredAFPObject */ public String getName() { return name; - } + } + } diff --git a/src/java/org/apache/fop/render/afp/modca/AbstractPageObject.java b/src/java/org/apache/fop/render/afp/modca/AbstractPageObject.java index f373b5bf7..984aa44c9 100644 --- a/src/java/org/apache/fop/render/afp/modca/AbstractPageObject.java +++ b/src/java/org/apache/fop/render/afp/modca/AbstractPageObject.java @@ -310,7 +310,7 @@ public abstract class AbstractPageObject extends AbstractResourceGroupContainer private PresentationTextObject getPresentationTextObject() { if (presentationTextObject == null) { this.presentationTextObject = new PresentationTextObject(); - addObject(this.presentationTextObject); + super.addObject(this.presentationTextObject); } return presentationTextObject; } diff --git a/src/java/org/apache/fop/render/afp/modca/ImageContent.java b/src/java/org/apache/fop/render/afp/modca/ImageContent.java index 4aea02901..726c58762 100644 --- a/src/java/org/apache/fop/render/afp/modca/ImageContent.java +++ b/src/java/org/apache/fop/render/afp/modca/ImageContent.java @@ -18,13 +18,11 @@ /* $Id$ */ package org.apache.fop.render.afp.modca; - import java.io.IOException; import java.io.OutputStream; import org.apache.fop.render.afp.tools.BinaryUtils; /** - * Image content IOCA object */ public class ImageContent extends AbstractStructuredAFPObject { @@ -55,27 +53,27 @@ public class ImageContent extends AbstractStructuredAFPObject { /** * The image size parameter */ - private ImageSizeParameter imageSizeParam = null; + private ImageSizeParameter _imageSizeParameter = null; /** * The image encoding */ - private byte encoding = 0x03; + private byte _encoding = 0x03; /** * The image ide size */ - private byte size = 1; + private byte _size = 1; /** * The image compression */ - private byte compression = (byte)0xC0; + private byte _compression = (byte)0xC0; /** * The image color model */ - private byte colorModel = 0x01; + private byte _colorModel = 0x01; /** * The image data @@ -97,39 +95,39 @@ public class ImageContent extends AbstractStructuredAFPObject { * @param vsize The vertival size of the image. */ public void setImageSize(int hresol, int vresol, int hsize, int vsize) { - this.imageSizeParam = new ImageSizeParameter(hresol, vresol, hsize, vsize); + _imageSizeParameter = new ImageSizeParameter(hresol, vresol, hsize, vsize); } /** * Sets the image encoding. - * @param enc The image encoding. + * @param encoding The image encoding. */ - public void setImageEncoding(byte enc) { - this.encoding = enc; + public void setImageEncoding(byte encoding) { + _encoding = encoding; } /** * Sets the image compression. - * @param comp The image compression. + * @param compression The image compression. */ - public void setImageCompression(byte comp) { - this.compression = comp; + public void setImageCompression(byte compression) { + _compression = compression; } /** * Sets the image IDE size. - * @param siz The IDE size. + * @param size The IDE size. */ - public void setImageIDESize(byte siz) { - this.size = siz; + public void setImageIDESize(byte size) { + _size = size; } /** * Sets the image IDE color model. - * @param model the IDE color model. + * @param colorModel the IDE color model. */ - public void setImageIDEColorModel(byte model) { - this.colorModel = model; + public void setImageIDEColorModel(byte colorModel) { + _colorModel = colorModel; } /** @@ -144,8 +142,8 @@ public class ImageContent extends AbstractStructuredAFPObject { * {@inheritDoc} */ protected void writeContent(OutputStream os) throws IOException { - if (imageSizeParam != null) { - imageSizeParam.writeDataStream(os); + if (_imageSizeParameter != null) { + _imageSizeParameter.writeDataStream(os); } os.write(getImageEncodingParameter()); os.write(getImageIDESizeParameter()); @@ -190,16 +188,21 @@ public class ImageContent extends AbstractStructuredAFPObject { * @return byte[] The data stream. */ private byte[] getImageDataStart(int len) { - byte[] imageDataStartData = new byte[] { + + byte[] data = new byte[] { (byte)0xFE, // ID (byte)0x92, // ID 0x00, // Length 0x00, // Length }; + byte[] l = BinaryUtils.convert(len, 2); - imageDataStartData[2] = l[0]; - imageDataStartData[3] = l[1]; - return imageDataStartData; + data[2] = l[0]; + data[3] = l[1]; + + + return data; + } /** @@ -207,13 +210,16 @@ public class ImageContent extends AbstractStructuredAFPObject { * @return byte[] The data stream. */ private byte[] getImageEncodingParameter() { - byte[] imageEncParamData = new byte[] { + + byte[] data = new byte[] { (byte)0x95, // ID 0x02, // Length - encoding, + _encoding, 0x01, // RECID }; - return imageEncParamData; + + return data; + } /** @@ -221,8 +227,9 @@ public class ImageContent extends AbstractStructuredAFPObject { * @return byte[] The data stream. */ private byte[] getExternalAlgorithmParameter() { - if (encoding == (byte)0x83 && compression != 0) { - byte[] extAlgParamData = new byte[] { + + if (_encoding == (byte)0x83 && _compression != 0) { + byte[] data = new byte[] { (byte)0x95, // ID 0x00, // Length 0x10, // ALGTYPE = Compression Algorithm @@ -231,13 +238,13 @@ public class ImageContent extends AbstractStructuredAFPObject { 0x00, // Reserved 0x00, // Reserved 0x00, // Reserved - compression, // MARKER + _compression, // MARKER 0x00, // Reserved 0x00, // Reserved 0x00, // Reserved }; - extAlgParamData[1] = (byte)(extAlgParamData.length - 2); - return extAlgParamData; + data[1] = (byte)(data.length - 2); + return data; } return new byte[0]; } @@ -247,12 +254,15 @@ public class ImageContent extends AbstractStructuredAFPObject { * @return byte[] The data stream. */ private byte[] getImageIDESizeParameter() { - byte[] imageIDESizeParamData = new byte[] { + + byte[] data = new byte[] { (byte)0x96, // ID 0x01, // Length - size, + _size, }; - return imageIDESizeParamData; + + return data; + } /** @@ -260,14 +270,15 @@ public class ImageContent extends AbstractStructuredAFPObject { * @return byte[] The data stream. */ private byte[] getIDEStructureParameter() { - if (colorModel != 0 && size == 24) { - byte bits = (byte)(size / 3); - byte[] ideStructParamData = new byte[] { + + if (_colorModel != 0 && _size == 24) { + byte bits = (byte)(_size / 3); + byte[] data = new byte[] { (byte)0x9B, // ID 0x00, // Length 0x00, // FLAGS 0x00, // Reserved - colorModel, // COLOR MODEL + _colorModel, // COLOR MODEL 0x00, // Reserved 0x00, // Reserved 0x00, // Reserved @@ -275,9 +286,10 @@ public class ImageContent extends AbstractStructuredAFPObject { bits, bits, }; - ideStructParamData[1] = (byte)(ideStructParamData.length - 2); - return ideStructParamData; + data[1] = (byte)(data.length - 2); + return data; } return new byte[0]; } + } diff --git a/src/java/org/apache/fop/render/afp/modca/ImageDataDescriptor.java b/src/java/org/apache/fop/render/afp/modca/ImageDataDescriptor.java index d1c2c11c7..9250f0c7f 100644 --- a/src/java/org/apache/fop/render/afp/modca/ImageDataDescriptor.java +++ b/src/java/org/apache/fop/render/afp/modca/ImageDataDescriptor.java @@ -27,17 +27,10 @@ import org.apache.fop.render.afp.tools.BinaryUtils; */ public class ImageDataDescriptor extends AbstractAFPObject { - /** x resolution */ - private int xresol = 0; - - /** y resolution */ - private int yresol = 0; - - /** width */ - private int width = 0; - - /** height */ - private int height = 0; + private int _xresol = 0; + private int _yresol = 0; + private int _width = 0; + private int _height = 0; /** * Constructor for a ImageDataDescriptor for the specified @@ -48,25 +41,26 @@ public class ImageDataDescriptor extends AbstractAFPObject { * @param height The height of the height. */ public ImageDataDescriptor(int xresol, int yresol, int width, int height) { - this.xresol = xresol; - this.yresol = yresol; - this.width = width; - this.height = height; + + _xresol = xresol; + _yresol = yresol; + _width = width; + _height = height; + } /** - * {@inheritDoc} + * Accessor method to write the AFP datastream for the Image Data Descriptor + * @param os The stream to write to + * @throws java.io.IOException */ - public void writeDataStream(OutputStream os) throws IOException { - byte[] len = BinaryUtils.convert(21, 2); - byte[] xres = BinaryUtils.convert(xresol, 2); - byte[] yres = BinaryUtils.convert(yresol, 2); - byte[] w = BinaryUtils.convert(width, 2); - byte[] h = BinaryUtils.convert(height, 2); + public void writeDataStream(OutputStream os) + throws IOException { + byte[] data = new byte[] { 0x5A, - len[0], - len[1], + 0x00, + 0x20, (byte) 0xD3, (byte) 0xA6, (byte) 0xFB, @@ -74,19 +68,42 @@ public class ImageDataDescriptor extends AbstractAFPObject { 0x00, // Reserved 0x00, // Reserved 0x00, // Unit base - 10 Inches - xres[0], // XRESOL - xres[1], // - yres[0], // YRESOL - yres[1], // - w[0], // XSIZE - w[1], // - h[0], // YSIZE - h[1], // + 0x00, // XRESOL + 0x00, // + 0x00, // YRESOL + 0x00, // + 0x00, // XSIZE + 0x00, // + 0x00, // YSIZE + 0x00, // (byte)0xF7, // ID = Set IOCA Function Set 0x02, // Length 0x01, // Category = Function set identifier 0x0B, // FCNSET = IOCA FS 11 }; + + byte[] l = BinaryUtils.convert(data.length - 1, 2); + data[1] = l[0]; + data[2] = l[1]; + + byte[] x = BinaryUtils.convert(_xresol, 2); + data[10] = x[0]; + data[11] = x[1]; + + byte[] y = BinaryUtils.convert(_yresol, 2); + data[12] = y[0]; + data[13] = y[1]; + + byte[] w = BinaryUtils.convert(_width, 2); + data[14] = w[0]; + data[15] = w[1]; + + byte[] h = BinaryUtils.convert(_height, 2); + data[16] = h[0]; + data[17] = h[1]; + os.write(data); + } + } diff --git a/src/java/org/apache/fop/render/afp/modca/ImageSegment.java b/src/java/org/apache/fop/render/afp/modca/ImageSegment.java index 2876e8348..ea8eb334b 100644 --- a/src/java/org/apache/fop/render/afp/modca/ImageSegment.java +++ b/src/java/org/apache/fop/render/afp/modca/ImageSegment.java @@ -18,7 +18,6 @@ /* $Id$ */ package org.apache.fop.render.afp.modca; - import java.io.IOException; import java.io.OutputStream; @@ -165,6 +164,7 @@ public class ImageSegment extends AbstractNamedAFPObject { * {@inheritDoc} */ protected void writeEnd(OutputStream os) throws IOException { + byte[] data = new byte[] { 0x71, // ID 0x00, // Length diff --git a/src/java/org/apache/fop/render/afp/modca/ObjectAreaDescriptor.java b/src/java/org/apache/fop/render/afp/modca/ObjectAreaDescriptor.java index aa35db10e..7c940148b 100644 --- a/src/java/org/apache/fop/render/afp/modca/ObjectAreaDescriptor.java +++ b/src/java/org/apache/fop/render/afp/modca/ObjectAreaDescriptor.java @@ -35,11 +35,11 @@ public class ObjectAreaDescriptor extends AbstractDescriptor { * and object height. * @param width The page width. * @param height The page height. - * @param widthRes The page width resolution. - * @param heightRes The page height resolution. + * @param widthResolution The page width resolution. + * @param heightResolution The page height resolution. */ - public ObjectAreaDescriptor(int width, int height, int widthRes, int heightRes) { - super(width, height, widthRes, heightRes); + public ObjectAreaDescriptor(int width, int height, int widthResolution, int heightResolution) { + super(width, height, widthResolution, heightResolution); } /** @@ -47,7 +47,8 @@ public class ObjectAreaDescriptor extends AbstractDescriptor { * @param os The stream to write to * @throws java.io.IOException thrown if an I/O exception of some sort has occurred */ - public void writeDataStream(OutputStream os) throws IOException { + public void writeDataStream(OutputStream os) + throws IOException { byte[] data = new byte[29]; data[0] = 0x5A; @@ -71,12 +72,12 @@ public class ObjectAreaDescriptor extends AbstractDescriptor { data[15] = 0x00; // YaoBase = 10 inches // XaoUnits - byte[] xdpi = BinaryUtils.convert(this.widthRes * 10, 2); + byte[] xdpi = BinaryUtils.convert(widthResolution * 10, 2); data[16] = xdpi[0]; data[17] = xdpi[1]; // YaoUnits - byte[] ydpi = BinaryUtils.convert(this.heightRes * 10, 2); + byte[] ydpi = BinaryUtils.convert(heightResolution * 10, 2); data[18] = ydpi[0]; data[19] = ydpi[1]; @@ -84,16 +85,18 @@ public class ObjectAreaDescriptor extends AbstractDescriptor { data[21] = 0x4C; // tid = Object Area Size data[22] = 0x02; // Size Type - byte[] x = BinaryUtils.convert(this.width, 3); + byte[] x = BinaryUtils.convert(width, 3); data[23] = x[0]; data[24] = x[1]; data[25] = x[2]; - byte[] y = BinaryUtils.convert(this.height, 3); + byte[] y = BinaryUtils.convert(height, 3); data[26] = y[0]; data[27] = y[1]; data[28] = y[2]; os.write(data); + } + }
\ No newline at end of file diff --git a/src/java/org/apache/fop/render/afp/modca/PageDescriptor.java b/src/java/org/apache/fop/render/afp/modca/PageDescriptor.java index ffa5f3c85..1cdec7616 100644 --- a/src/java/org/apache/fop/render/afp/modca/PageDescriptor.java +++ b/src/java/org/apache/fop/render/afp/modca/PageDescriptor.java @@ -35,11 +35,11 @@ public class PageDescriptor extends AbstractDescriptor { * and page height. * @param width The page width. * @param height The page height. - * @param widthRes The page width resolution - * @param heightRes The page height resolution + * @param widthResolution The page width resolution + * @param heightResolution The page height resolution */ - public PageDescriptor(int width, int height, int widthRes, int heightRes) { - super(width, height, widthRes, heightRes); + public PageDescriptor(int width, int height, int widthResolution, int heightResolution) { + super(width, height, widthResolution, heightResolution); } /** @@ -68,12 +68,12 @@ public class PageDescriptor extends AbstractDescriptor { data[10] = 0x00; // YpgBase = 10 inches // XpgUnits - byte[] xdpi = BinaryUtils.convert(widthRes * 10, 2); + byte[] xdpi = BinaryUtils.convert(widthResolution * 10, 2); data[11] = xdpi[0]; data[12] = xdpi[1]; // YpgUnits - byte[] ydpi = BinaryUtils.convert(heightRes * 10, 2); + byte[] ydpi = BinaryUtils.convert(heightResolution * 10, 2); data[13] = ydpi[0]; data[14] = ydpi[1]; diff --git a/src/java/org/apache/fop/render/afp/modca/PageObject.java b/src/java/org/apache/fop/render/afp/modca/PageObject.java index bad80bcc1..9a8646ba0 100644 --- a/src/java/org/apache/fop/render/afp/modca/PageObject.java +++ b/src/java/org/apache/fop/render/afp/modca/PageObject.java @@ -56,14 +56,14 @@ public class PageObject extends AbstractPageObject { * the height of the page. * @param rotation * the rotation of the page. - * @param widthRes + * @param widthResolution * the width resolution of the page. - * @param heightRes + * @param heightResolution * the height resolution of the page. */ public PageObject(String name, int width, int height, int rotation, - int widthRes, int heightRes) { - super(name, width, height, rotation, widthRes, heightRes); + int widthResolution, int heightResolution) { + super(name, width, height, rotation, widthResolution, heightResolution); } /** diff --git a/src/java/org/apache/fop/render/afp/modca/PresentationTextData.java b/src/java/org/apache/fop/render/afp/modca/PresentationTextData.java index 30a78d6c9..98beb96f7 100644 --- a/src/java/org/apache/fop/render/afp/modca/PresentationTextData.java +++ b/src/java/org/apache/fop/render/afp/modca/PresentationTextData.java @@ -56,42 +56,42 @@ public class PresentationTextData extends AbstractAFPObject { /** * The afp data relating to this presentaion text data. */ - private ByteArrayOutputStream baos = new ByteArrayOutputStream(1024); + private ByteArrayOutputStream _baos = new ByteArrayOutputStream(1024); /** * The current x coordinate. */ - private int currentXCoordinate = -1; + private int _currentXCoordinate = -1; /** * The current y cooridnate */ - private int currentYCoordinate = -1; + private int _currentYCoordinate = -1; /** * The current font */ - private String currentFont = ""; + private String _currentFont = ""; /** * The current orientation */ - private int currentOrientation = 0; + private int _currentOrientation = 0; /** * The current color */ - private Color currentColor = new Color(0, 0, 0); + private Color _currentColor = new Color(0, 0, 0); /** * The current variable space increment */ - private int currentVariableSpaceCharacterIncrement = 0; + private int _currentVariableSpaceCharacterIncrement = 0; /** * The current inter character adjustment */ - private int currentInterCharacterAdjustment = 0; + private int _currentInterCharacterAdjustment = 0; /** * Default constructor for the PresentationTextData. @@ -112,7 +112,7 @@ public class PresentationTextData extends AbstractAFPObject { */ public PresentationTextData(boolean controlInd) { - baos.write(new byte[] {0x5A, // Structured field identifier + _baos.write(new byte[] { 0x5A, // Structured field identifier 0x00, // Record length byte 1 0x00, // Record length byte 2 (byte) 0xD3, // PresentationTextData identifier byte 1 @@ -124,7 +124,7 @@ public class PresentationTextData extends AbstractAFPObject { }, 0, 9); if (controlInd) { - baos.write(new byte[] {0x2B, (byte) 0xD3}, 0, 2); + _baos.write(new byte[] { 0x2B, (byte) 0xD3 }, 0, 2); } } @@ -142,13 +142,13 @@ public class PresentationTextData extends AbstractAFPObject { private void setCodedFont(byte font, ByteArrayOutputStream afpdata) { // Avoid unnecessary specification of the font - if (String.valueOf(font).equals(currentFont)) { + if (String.valueOf(font).equals(_currentFont)) { return; } else { - currentFont = String.valueOf(font); + _currentFont = String.valueOf(font); } - afpdata.write(new byte[] {0x03, (byte) 0xF1, font}, 0, 3); + afpdata.write(new byte[] { 0x03, (byte) 0xF1, font, }, 0, 3); } @@ -167,9 +167,9 @@ public class PresentationTextData extends AbstractAFPObject { byte[] b = BinaryUtils.convert(coordinate, 2); - afpdata.write(new byte[] {0x04, (byte) 0xC7, b[0], b[1]}, 0, 4); + afpdata.write(new byte[] { 0x04, (byte) 0xC7, b[0], b[1], }, 0, 4); - currentXCoordinate = coordinate; + _currentXCoordinate = coordinate; } @@ -188,9 +188,9 @@ public class PresentationTextData extends AbstractAFPObject { byte[] b = BinaryUtils.convert(coordinate, 2); - afpdata.write(new byte[] {0x04, (byte) 0xD3, b[0], b[1]}, 0, 4); + afpdata.write(new byte[] { 0x04, (byte) 0xD3, b[0], b[1], }, 0, 4); - currentYCoordinate = coordinate; + _currentYCoordinate = coordinate; } @@ -214,7 +214,7 @@ public class PresentationTextData extends AbstractAFPObject { "Transparent data is longer than 253 bytes: " + data); } - afpdata.write(new byte[] {BinaryUtils.convert(l)[0], (byte) 0xDB}, + afpdata.write(new byte[] { BinaryUtils.convert(l)[0], (byte) 0xDB, }, 0, 2); afpdata.write(data, 0, data.length); @@ -236,7 +236,7 @@ public class PresentationTextData extends AbstractAFPObject { private void drawBaxisRule(int length, int width, ByteArrayOutputStream afpdata) { - afpdata.write(new byte[] {0x07, // Length + afpdata.write(new byte[] { 0x07, // Length (byte) 0xE7, // Type }, 0, 2); @@ -266,7 +266,7 @@ public class PresentationTextData extends AbstractAFPObject { private void drawIaxisRule(int length, int width, ByteArrayOutputStream afpdata) { - afpdata.write(new byte[] {0x07, // Length + afpdata.write(new byte[] { 0x07, // Length (byte) 0xE5, // Type }, 0, 2); @@ -284,7 +284,7 @@ public class PresentationTextData extends AbstractAFPObject { /** * Create the presentation text data for the byte array of data. * - * @param fontReference + * @param fontNumber * The font resource identifier. * @param x * The x coordinate for the text data. @@ -300,64 +300,64 @@ public class PresentationTextData extends AbstractAFPObject { * The inter character adjustment. * @param data * The text data to be created. - * @throws MaximumSizeExceededException if the maximum size is exceeded + * @throws MaximumSizeExceededException */ - public void createTextData(int fontReference, int x, int y, int orientation, + public void createTextData(int fontNumber, int x, int y, int orientation, Color col, int vsci, int ica, byte[] data) throws MaximumSizeExceededException { ByteArrayOutputStream afpdata = new ByteArrayOutputStream(); - if (currentOrientation != orientation) { + if (_currentOrientation != orientation) { setTextOrientation(orientation, afpdata); - currentOrientation = orientation; - currentXCoordinate = -1; - currentYCoordinate = -1; + _currentOrientation = orientation; + _currentXCoordinate = -1; + _currentYCoordinate = -1; } // Avoid unnecessary specification of the Y co-ordinate - if (y != currentYCoordinate) { + if (y != _currentYCoordinate) { absoluteMoveBaseline(y, afpdata); - currentXCoordinate = -1; + _currentXCoordinate = -1; } // Avoid unnecessary specification of the X co-ordinate - if (x != currentXCoordinate) { + if (x != _currentXCoordinate) { absoluteMoveInline(x, afpdata); } // Avoid unnecessary specification of the variable space increment - if (vsci != currentVariableSpaceCharacterIncrement) { + if (vsci != _currentVariableSpaceCharacterIncrement) { setVariableSpaceCharacterIncrement(vsci, afpdata); - currentVariableSpaceCharacterIncrement = vsci; + _currentVariableSpaceCharacterIncrement = vsci; } // Avoid unnecessary specification of the inter character adjustment - if (ica != currentInterCharacterAdjustment) { + if (ica != _currentInterCharacterAdjustment) { setInterCharacterAdjustment(ica, afpdata); - currentInterCharacterAdjustment = ica; + _currentInterCharacterAdjustment = ica; } // Avoid unnecessary specification of the text color - if (!col.equals(currentColor)) { + if (!col.equals(_currentColor)) { setExtendedTextColor(col, afpdata); - currentColor = col; + _currentColor = col; } - setCodedFont(BinaryUtils.convert(fontReference)[0], afpdata); + setCodedFont(BinaryUtils.convert(fontNumber)[0], afpdata); addTransparentData(data, afpdata); - currentXCoordinate = -1; + _currentXCoordinate = -1; int s = afpdata.size(); - if (baos.size() + s > MAX_SIZE) { - currentXCoordinate = -1; - currentYCoordinate = -1; + if (_baos.size() + s > MAX_SIZE) { + _currentXCoordinate = -1; + _currentYCoordinate = -1; throw new MaximumSizeExceededException(); } byte[] outputdata = afpdata.toByteArray(); - baos.write(outputdata, 0, outputdata.length); + _baos.write(outputdata, 0, outputdata.length); } @@ -379,31 +379,30 @@ public class PresentationTextData extends AbstractAFPObject { * The orientation of the text data. * @param col * The text color. - * @throws MaximumSizeExceededException if the maximum size is exceeded */ public void createLineData(int x1, int y1, int x2, int y2, int thickness, int orientation, Color col) throws MaximumSizeExceededException { ByteArrayOutputStream afpdata = new ByteArrayOutputStream(); - if (currentOrientation != orientation) { + if (_currentOrientation != orientation) { setTextOrientation(orientation, afpdata); - currentOrientation = orientation; + _currentOrientation = orientation; } // Avoid unnecessary specification of the Y coordinate - if (y1 != currentYCoordinate) { + if (y1 != _currentYCoordinate) { absoluteMoveBaseline(y1, afpdata); } // Avoid unnecessary specification of the X coordinate - if (x1 != currentXCoordinate) { + if (x1 != _currentXCoordinate) { absoluteMoveInline(x1, afpdata); } - if (!col.equals(currentColor)) { + if (!col.equals(_currentColor)) { setExtendedTextColor(col, afpdata); - currentColor = col; + _currentColor = col; } if (y1 == y2) { @@ -416,14 +415,14 @@ public class PresentationTextData extends AbstractAFPObject { int s = afpdata.size(); - if (baos.size() + s > MAX_SIZE) { - currentXCoordinate = -1; - currentYCoordinate = -1; + if (_baos.size() + s > MAX_SIZE) { + _currentXCoordinate = -1; + _currentYCoordinate = -1; throw new MaximumSizeExceededException(); } byte[] outputdata = afpdata.toByteArray(); - baos.write(outputdata, 0, outputdata.length); + _baos.write(outputdata, 0, outputdata.length); } @@ -444,7 +443,7 @@ public class PresentationTextData extends AbstractAFPObject { private void setTextOrientation(int orientation, ByteArrayOutputStream afpdata) { - afpdata.write(new byte[] {0x06, (byte) 0xF7}, 0, 2); + afpdata.write(new byte[] { 0x06, (byte) 0xF7, }, 0, 2); switch (orientation) { case 90: @@ -560,11 +559,12 @@ public class PresentationTextData extends AbstractAFPObject { * Accessor method to write the AFP datastream for * the text data. * @param os The stream to write to - * @throws java.io.IOException if an I/O exception occurred + * @throws java.io.IOException */ - public void writeDataStream(OutputStream os) throws IOException { + public void writeDataStream(OutputStream os) + throws IOException { - byte[] data = baos.toByteArray(); + byte[] data = _baos.toByteArray(); byte[] size = BinaryUtils.convert(data.length - 1, 2); data[1] = size[0]; data[2] = size[1]; @@ -580,7 +580,7 @@ public class PresentationTextData extends AbstractAFPObject { * presentation text data objects, but must eventually be terminated. This * method terminates the control sequence. * - * @throws MaximumSizeExceededException if the maximum size is exceeded + * @throws MaximumSizeExceededException */ public void endControlSequence() throws MaximumSizeExceededException { @@ -588,10 +588,12 @@ public class PresentationTextData extends AbstractAFPObject { data[0] = 0x02; data[1] = (byte) 0xF8; - if (data.length + baos.size() > MAX_SIZE) { + if (data.length + _baos.size() > MAX_SIZE) { throw new MaximumSizeExceededException(); } - baos.write(data, 0, data.length); + + _baos.write(data, 0, data.length); + } }
\ No newline at end of file diff --git a/src/java/org/apache/fop/render/afp/modca/PresentationTextDescriptor.java b/src/java/org/apache/fop/render/afp/modca/PresentationTextDescriptor.java index 27b3de5ca..3858f4169 100644 --- a/src/java/org/apache/fop/render/afp/modca/PresentationTextDescriptor.java +++ b/src/java/org/apache/fop/render/afp/modca/PresentationTextDescriptor.java @@ -52,11 +52,12 @@ public class PresentationTextDescriptor extends AbstractDescriptor { * width and height. * @param width The width of the page. * @param height The height of the page. - * @param widthRes The width resolution of the page. - * @param heightRes The height resolution of the page. + * @param widthResolution The width resolution of the page. + * @param heightResolution The height resolution of the page. */ - public PresentationTextDescriptor(int width, int height, int widthRes, int heightRes) { - super(width, height, widthRes, heightRes); + public PresentationTextDescriptor(int width, int height, + int widthResolution, int heightResolution) { + super(width, height, widthResolution, heightResolution); } /** @@ -80,11 +81,11 @@ public class PresentationTextDescriptor extends AbstractDescriptor { data[9] = 0x00; data[10] = 0x00; - byte[] xdpi = BinaryUtils.convert(widthRes * 10, 2); + byte[] xdpi = BinaryUtils.convert(widthResolution * 10, 2); data[11] = xdpi[0]; // xdpi data[12] = xdpi[1]; - byte[] ydpi = BinaryUtils.convert(heightRes * 10, 2); + byte[] ydpi = BinaryUtils.convert(heightResolution * 10, 2); data[13] = ydpi[0]; // ydpi data[14] = ydpi[1]; diff --git a/src/java/org/apache/fop/render/afp/modca/PresentationTextObject.java b/src/java/org/apache/fop/render/afp/modca/PresentationTextObject.java index 49f7c81a8..5df9fdb4c 100644 --- a/src/java/org/apache/fop/render/afp/modca/PresentationTextObject.java +++ b/src/java/org/apache/fop/render/afp/modca/PresentationTextObject.java @@ -117,7 +117,6 @@ public class PresentationTextObject extends AbstractNamedAFPObject { startPresentationTextData(); } try { - currentPresentationTextData.createTextData(fontRef, x, y, orientation, col, vsci, ica, data); } catch (MaximumSizeExceededException msee) { @@ -279,4 +278,4 @@ public class PresentationTextObject extends AbstractNamedAFPObject { endControlSequence(); } } -}
\ No newline at end of file +} diff --git a/src/java/org/apache/fop/render/afp/modca/TagLogicalElementBean.java b/src/java/org/apache/fop/render/afp/modca/TagLogicalElementBean.java index 99f31ba14..29ac9eb5d 100644 --- a/src/java/org/apache/fop/render/afp/modca/TagLogicalElementBean.java +++ b/src/java/org/apache/fop/render/afp/modca/TagLogicalElementBean.java @@ -47,7 +47,7 @@ public class TagLogicalElementBean { * @return the key */ public String getKey() { - return this.key; + return key; } /** @@ -55,7 +55,7 @@ public class TagLogicalElementBean { * @return the value */ public String getValue() { - return this.value; + return value; } } diff --git a/src/java/org/apache/fop/render/java2d/CustomFontMetricsMapper.java b/src/java/org/apache/fop/render/java2d/CustomFontMetricsMapper.java index 208bc303a..3a773a2db 100644 --- a/src/java/org/apache/fop/render/java2d/CustomFontMetricsMapper.java +++ b/src/java/org/apache/fop/render/java2d/CustomFontMetricsMapper.java @@ -112,7 +112,7 @@ public class CustomFontMetricsMapper extends Typeface implements FontMetricsMapp } /** {@inheritDoc} */ - public final String getEncoding() { + public final String getEncodingName() { return null; //Not applicable to Java2D rendering } diff --git a/src/java/org/apache/fop/render/java2d/Java2DRenderer.java b/src/java/org/apache/fop/render/java2d/Java2DRenderer.java index 8442af229..583d2ad2f 100644 --- a/src/java/org/apache/fop/render/java2d/Java2DRenderer.java +++ b/src/java/org/apache/fop/render/java2d/Java2DRenderer.java @@ -312,6 +312,8 @@ public abstract class Java2DRenderer extends AbstractPathOrientedRenderer implem graphics.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); } + graphics.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, + RenderingHints.VALUE_STROKE_PURE); // transform page based on scale factor supplied AffineTransform at = graphics.getTransform(); diff --git a/src/java/org/apache/fop/render/java2d/SystemFontMetricsMapper.java b/src/java/org/apache/fop/render/java2d/SystemFontMetricsMapper.java index ed7391383..19e9a4d1d 100644 --- a/src/java/org/apache/fop/render/java2d/SystemFontMetricsMapper.java +++ b/src/java/org/apache/fop/render/java2d/SystemFontMetricsMapper.java @@ -172,7 +172,7 @@ public class SystemFontMetricsMapper extends Typeface implements FontMetricsMapp } /** {@inheritDoc} */ - public String getEncoding() { + public String getEncodingName() { return null; //Not applicable to Java2D rendering } diff --git a/src/java/org/apache/fop/render/pdf/AbstractImageAdapter.java b/src/java/org/apache/fop/render/pdf/AbstractImageAdapter.java index b676ad6d5..caf8928ac 100644 --- a/src/java/org/apache/fop/render/pdf/AbstractImageAdapter.java +++ b/src/java/org/apache/fop/render/pdf/AbstractImageAdapter.java @@ -81,7 +81,7 @@ public abstract class AbstractImageAdapter implements PDFImage { /** {@inheritDoc} */ public void setup(PDFDocument doc) { - ICC_Profile prof = image.getICCProfile(); + ICC_Profile prof = getEffectiveICCProfile(); PDFDeviceColorSpace pdfCS = toPDFColorSpace(getImageColorSpace()); if (prof != null) { pdfICCStream = setupColorProfile(doc, prof, pdfCS); @@ -100,6 +100,14 @@ public abstract class AbstractImageAdapter implements PDFImage { } } + /** + * Returns the effective ICC profile for the image. + * @return an ICC profile or null + */ + protected ICC_Profile getEffectiveICCProfile() { + return image.getICCProfile(); + } + private static PDFICCStream setupColorProfile(PDFDocument doc, ICC_Profile prof, PDFDeviceColorSpace pdfCS) { boolean defaultsRGB = ColorProfileUtil.isDefaultsRGB(prof); diff --git a/src/java/org/apache/fop/render/pdf/ImageRawJPEGAdapter.java b/src/java/org/apache/fop/render/pdf/ImageRawJPEGAdapter.java index 4b0ce4a85..1c8fceb50 100644 --- a/src/java/org/apache/fop/render/pdf/ImageRawJPEGAdapter.java +++ b/src/java/org/apache/fop/render/pdf/ImageRawJPEGAdapter.java @@ -18,19 +18,33 @@ /* $Id$ */ package org.apache.fop.render.pdf; +import java.awt.color.ICC_Profile; +import java.io.DataInput; import java.io.IOException; +import java.io.InputStream; import java.io.OutputStream; +import org.apache.commons.io.IOUtils; + import org.apache.xmlgraphics.image.loader.impl.ImageRawJPEG; +import org.apache.xmlgraphics.image.loader.impl.JPEGConstants; +import org.apache.xmlgraphics.image.loader.impl.JPEGFile; +import org.apache.xmlgraphics.image.loader.util.ImageUtil; import org.apache.fop.pdf.DCTFilter; import org.apache.fop.pdf.PDFDeviceColorSpace; import org.apache.fop.pdf.PDFDocument; import org.apache.fop.pdf.PDFFilter; import org.apache.fop.pdf.PDFFilterList; +import org.apache.fop.util.ColorProfileUtil; /** * PDFImage implementation for the PDF renderer which handles raw JPEG images. + * <p> + * The JPEG is copied to the XObject's stream as-is but some elements (marker segments) are + * filtered. For example, an embedded color profile is filtered since it is already added as + * a PDF object and associated with the XObject. This way, the PDF file size is kept as small + * as possible. */ public class ImageRawJPEGAdapter extends AbstractImageAdapter { @@ -68,6 +82,21 @@ public class ImageRawJPEGAdapter extends AbstractImageAdapter { } /** {@inheritDoc} */ + protected ICC_Profile getEffectiveICCProfile() { + ICC_Profile profile = super.getEffectiveICCProfile(); + if (profile != null + && profile.getNumComponents() == 3 + && !ColorProfileUtil.isDefaultsRGB(profile)) { + //RGB profiles which are not sRGB don't seem to work. + //Without this override, the image drifts into yellow for an unknown reason. + //TODO Find out why this happens. + //Test using a JPEG images with, for example, "Adobe RGB 1998" color profile. + profile = null; + } + return profile; + } + + /** {@inheritDoc} */ public int getBitsPerComponent() { return 8; } @@ -84,7 +113,77 @@ public class ImageRawJPEGAdapter extends AbstractImageAdapter { /** {@inheritDoc} */ public void outputContents(OutputStream out) throws IOException { - getImage().writeTo(out); + InputStream in = getImage().createInputStream(); + in = ImageUtil.decorateMarkSupported(in); + try { + JPEGFile jpeg = new JPEGFile(in); + DataInput din = jpeg.getDataInput(); + + //Copy the whole JPEG file except: + // - the ICC profile + //TODO Thumbnails could safely be skipped, too. + //TODO Metadata (XMP, IPTC, EXIF) could safely be skipped, too. + while (true) { + int reclen; + int segID = jpeg.readMarkerSegment(); + switch (segID) { + case JPEGConstants.SOI: + out.write(0xFF); + out.write(segID); + break; + case JPEGConstants.EOI: + case JPEGConstants.SOS: + out.write(0xFF); + out.write(segID); + IOUtils.copy(in, out); //Just copy the rest! + return; + /* + case JPEGConstants.APP1: //Metadata + case JPEGConstants.APPD: + jpeg.skipCurrentMarkerSegment(); + break;*/ + case JPEGConstants.APP2: //ICC (see ICC1V42.pdf) + boolean skipICCProfile = false; + in.mark(16); + try { + reclen = jpeg.readSegmentLength(); + // Check for ICC profile + byte[] iccString = new byte[11]; + din.readFully(iccString); + din.skipBytes(1); //string terminator (null byte) + + if ("ICC_PROFILE".equals(new String(iccString, "US-ASCII"))) { + skipICCProfile = (this.image.getICCProfile() != null); + } + } finally { + in.reset(); + } + if (skipICCProfile) { + //ICC profile is skipped as it is already embedded as a PDF object + jpeg.skipCurrentMarkerSegment(); + break; + } + default: + out.write(0xFF); + out.write(segID); + + reclen = jpeg.readSegmentLength(); + //write short + out.write((reclen >>> 8) & 0xFF); + out.write((reclen >>> 0) & 0xFF); + int left = reclen - 2; + byte[] buf = new byte[2048]; + while (left > 0) { + int part = Math.min(buf.length, left); + din.readFully(buf, 0, part); + out.write(buf, 0, part); + left -= part; + } + } + } + } finally { + IOUtils.closeQuietly(in); + } } /** {@inheritDoc} */ diff --git a/src/java/org/apache/fop/render/pdf/PDFGraphics2DAdapter.java b/src/java/org/apache/fop/render/pdf/PDFGraphics2DAdapter.java index 1a121e294..c47b944d0 100644 --- a/src/java/org/apache/fop/render/pdf/PDFGraphics2DAdapter.java +++ b/src/java/org/apache/fop/render/pdf/PDFGraphics2DAdapter.java @@ -89,8 +89,8 @@ public class PDFGraphics2DAdapter extends AbstractGraphics2DAdapter { PDFGraphics2D graphics = new PDFGraphics2D(textAsShapes, pdfInfo.fi, pdfInfo.pdfDoc, pdfInfo.pdfContext, pdfInfo.pdfPage.referencePDF(), - renderer.currentFontName, - renderer.currentFontSize); + pdfInfo.currentFontName, + pdfInfo.currentFontSize); graphics.setGraphicContext(new org.apache.xmlgraphics.java2d.GraphicContext()); AffineTransform transform = new AffineTransform(); diff --git a/src/java/org/apache/fop/render/pdf/PDFRenderer.java b/src/java/org/apache/fop/render/pdf/PDFRenderer.java index 19f274902..55524534e 100644 --- a/src/java/org/apache/fop/render/pdf/PDFRenderer.java +++ b/src/java/org/apache/fop/render/pdf/PDFRenderer.java @@ -63,7 +63,6 @@ import org.apache.fop.area.OffDocumentExtensionAttachment; import org.apache.fop.area.OffDocumentItem; import org.apache.fop.area.PageSequence; import org.apache.fop.area.PageViewport; -import org.apache.fop.area.RegionViewport; import org.apache.fop.area.Trait; import org.apache.fop.area.inline.AbstractTextArea; import org.apache.fop.area.inline.Image; @@ -78,6 +77,8 @@ import org.apache.fop.fo.Constants; import org.apache.fop.fo.extensions.ExtensionAttachment; import org.apache.fop.fo.extensions.xmp.XMPMetadata; import org.apache.fop.fonts.Font; +import org.apache.fop.fonts.LazyFont; +import org.apache.fop.fonts.SingleByteFont; import org.apache.fop.fonts.Typeface; import org.apache.fop.pdf.PDFAMode; import org.apache.fop.pdf.PDFAction; @@ -106,7 +107,7 @@ import org.apache.fop.pdf.PDFResourceContext; import org.apache.fop.pdf.PDFResources; import org.apache.fop.pdf.PDFState; import org.apache.fop.pdf.PDFStream; -import org.apache.fop.pdf.PDFText; +import org.apache.fop.pdf.PDFTextUtil; import org.apache.fop.pdf.PDFXMode; import org.apache.fop.pdf.PDFXObject; import org.apache.fop.render.AbstractPathOrientedRenderer; @@ -250,21 +251,14 @@ public class PDFRenderer extends AbstractPathOrientedRenderer { /** drawing state */ protected PDFState currentState = null; - /** Name of currently selected font */ - protected String currentFontName = ""; - /** Size of currently selected font */ - protected int currentFontSize = 0; + /** Text generation utility holding the current font status */ + protected PDFTextUtil textutil; /** page height */ protected int pageHeight; /** Registry of PDF filters */ protected Map filterMap; - /** - * true if a BT command has been written. - */ - protected boolean inTextMode = false; - /** Image handler registry */ private PDFImageHandlerRegistry imageHandlerRegistry = new PDFImageHandlerRegistry(); @@ -527,7 +521,7 @@ public class PDFRenderer extends AbstractPathOrientedRenderer { currentContext = null; currentPage = null; currentState = null; - currentFontName = ""; + this.textutil = null; idPositions.clear(); idGoTos.clear(); @@ -665,19 +659,15 @@ public class PDFRenderer extends AbstractPathOrientedRenderer { /** Indicates the beginning of a text object. */ protected void beginTextObject() { - if (!inTextMode) { - currentStream.add("BT\n"); - currentFontName = ""; - inTextMode = true; + if (!textutil.isInTextObject()) { + textutil.beginTextObject(); } } /** Indicates the end of a text object. */ protected void endTextObject() { - closeText(); - if (inTextMode) { - currentStream.add("ET\n"); - inTextMode = false; + if (textutil.isInTextObject()) { + textutil.endTextObject(); } } @@ -787,6 +777,11 @@ public class PDFRenderer extends AbstractPathOrientedRenderer { currentStream = this.pdfDoc.getFactory() .makeStream(PDFFilterList.CONTENT_FILTER, false); + this.textutil = new PDFTextUtil() { + protected void write(String code) { + currentStream.add(code); + } + }; currentState = new PDFState(); // Transform the PDF's default coordinate system (0,0 at lower left) to the PDFRenderer's @@ -795,9 +790,6 @@ public class PDFRenderer extends AbstractPathOrientedRenderer { currentState.concatenate(basicPageTransform); currentStream.add(CTMHelper.toPDFString(basicPageTransform, false) + " cm\n"); - - currentFontName = ""; - super.renderPage(page); this.pdfDoc.registerObject(currentStream); @@ -808,6 +800,7 @@ public class PDFRenderer extends AbstractPathOrientedRenderer { } this.pdfDoc.addObject(currentPage); this.pdfDoc.output(ostream); + this.textutil = null; } /** {@inheritDoc} */ @@ -841,17 +834,6 @@ public class PDFRenderer extends AbstractPathOrientedRenderer { } /** - * Handle the traits for a region - * This is used to draw the traits for the given page region. - * (See Sect. 6.4.1.2 of XSL-FO spec.) - * @param region the RegionViewport whose region is to be drawn - */ - protected void handleRegionTraits(RegionViewport region) { - currentFontName = ""; - super.handleRegionTraits(region); - } - - /** * Formats a float value (normally coordinates) as Strings. * @param value the value * @return the formatted value @@ -866,7 +848,8 @@ public class PDFRenderer extends AbstractPathOrientedRenderer { float w = x2 - x1; float h = y2 - y1; if ((w < 0) || (h < 0)) { - log.error("Negative extent received (w=" + w + ", h=" + h + "). Border won't be painted."); + log.error("Negative extent received (w=" + w + ", h=" + h + + "). Border won't be painted."); return; } switch (style) { @@ -1329,12 +1312,9 @@ public class PDFRenderer extends AbstractPathOrientedRenderer { super.renderBlock(block); } - /** - * {@inheritDoc} - */ + /** {@inheritDoc} */ protected void renderLineArea(LineArea line) { super.renderLineArea(line); - closeText(); } /** @@ -1423,11 +1403,20 @@ public class PDFRenderer extends AbstractPathOrientedRenderer { } } - /** - * {@inheritDoc} - */ + private Typeface getTypeface(String fontName) { + Typeface tf = (Typeface) fontInfo.getFonts().get(fontName); + if (tf instanceof LazyFont) { + tf = ((LazyFont)tf).getRealFont(); + } + return tf; + } + + /** {@inheritDoc} */ public void renderText(TextArea text) { renderInlineAreaBackAndBorders(text); + Color ct = (Color) text.getTrait(Trait.COLOR); + updateColor(ct, true); + beginTextObject(); StringBuffer pdf = new StringBuffer(); @@ -1435,12 +1424,10 @@ public class PDFRenderer extends AbstractPathOrientedRenderer { int size = ((Integer) text.getTrait(Trait.FONT_SIZE)).intValue(); // This assumes that *all* CIDFonts use a /ToUnicode mapping - Typeface tf = (Typeface) fontInfo.getFonts().get(fontName); - boolean useMultiByte = tf.isMultiByte(); + Typeface tf = getTypeface(fontName); + + textutil.updateTf(fontName, size / 1000f, tf.isMultiByte()); - updateFont(fontName, size, pdf); - Color ct = (Color) text.getTrait(Trait.COLOR); - updateColor(ct, true, pdf); // word.getOffset() = only height of text itself // currentBlockIPPosition: 0 for beginning of line; nonzero @@ -1448,66 +1435,46 @@ public class PDFRenderer extends AbstractPathOrientedRenderer { int rx = currentIPPosition + text.getBorderAndPaddingWidthStart(); int bl = currentBPPosition + text.getOffset() + text.getBaselineOffset(); - pdf.append("1 0 0 -1 " + format(rx / 1000f) + " " + format(bl / 1000f) + " Tm " - /*+ format(text.getTextLetterSpaceAdjust() / 1000f) + " Tc\n"*/ - /*+ format(text.getTextWordSpaceAdjust() / 1000f) + " Tw ["*/); + textutil.writeTextMatrix(new AffineTransform(1, 0, 0, -1, rx / 1000f, bl / 1000f)); - pdf.append("["); currentStream.add(pdf.toString()); super.renderText(text); - currentStream.add("] TJ\n"); + textutil.writeTJ(); renderTextDecoration(tf, size, text, bl, rx); } - - /** - * {@inheritDoc} - */ + + /** {@inheritDoc} */ public void renderWord(WordArea word) { Font font = getFontFromArea(word.getParentArea()); - Typeface tf = (Typeface) fontInfo.getFonts().get(font.getFontName()); - boolean useMultiByte = tf.isMultiByte(); - - StringBuffer pdf = new StringBuffer(); - String s = word.getWord(); - escapeText(s, word.getLetterAdjustArray(), - font, (AbstractTextArea)word.getParentArea(), useMultiByte, pdf); - currentStream.add(pdf.toString()); + escapeText(s, word.getLetterAdjustArray(), + font, (AbstractTextArea)word.getParentArea()); super.renderWord(word); } - /** - * {@inheritDoc} - */ + /** {@inheritDoc} */ public void renderSpace(SpaceArea space) { Font font = getFontFromArea(space.getParentArea()); - Typeface tf = (Typeface) fontInfo.getFonts().get(font.getFontName()); - boolean useMultiByte = tf.isMultiByte(); - String s = space.getSpace(); - StringBuffer pdf = new StringBuffer(); - AbstractTextArea textArea = (AbstractTextArea)space.getParentArea(); - escapeText(s, null, font, textArea, useMultiByte, pdf); + escapeText(s, null, font, textArea); if (space.isAdjustable()) { int tws = -((TextArea) space.getParentArea()).getTextWordSpaceAdjust() - 2 * textArea.getTextLetterSpaceAdjust(); if (tws != 0) { - pdf.append(format(tws / (font.getFontSize() / 1000f))); - pdf.append(" "); + float adjust = tws / (font.getFontSize() / 1000f); + textutil.adjustGlyphTJ(adjust); } } - currentStream.add(pdf.toString()); - super.renderSpace(space); } @@ -1515,101 +1482,77 @@ public class PDFRenderer extends AbstractPathOrientedRenderer { * Escapes text according to PDF rules. * @param s Text to escape * @param letterAdjust an array of widths for letter adjustment (may be null) - * @param fs Font state + * @param font to font in use + * @param parentArea the parent text area to retrieve certain traits from + */ + protected void escapeText(String s, + int[] letterAdjust, + Font font, AbstractTextArea parentArea) { + escapeText(s, 0, s.length(), letterAdjust, font, parentArea); + } + + /** + * Escapes text according to PDF rules. + * @param s Text to escape + * @param start the start position in the text + * @param end the end position in the text + * @param letterAdjust an array of widths for letter adjustment (may be null) + * @param font to font in use * @param parentArea the parent text area to retrieve certain traits from - * @param useMultiByte Indicates the use of multi byte convention - * @param pdf target buffer for the escaped text */ - public void escapeText(String s, int[] letterAdjust, - Font fs, AbstractTextArea parentArea, - boolean useMultiByte, StringBuffer pdf) { - String startText = useMultiByte ? "<" : "("; - String endText = useMultiByte ? "> " : ") "; - - /* - boolean kerningAvailable = false; - Map kerning = fs.getKerning(); - if (kerning != null && !kerning.isEmpty()) { - //kerningAvailable = true; - //TODO Reenable me when the layout engine supports kerning, too - log.warn("Kerning support is disabled until it is supported by the layout engine!"); - } - */ + protected void escapeText(String s, int start, int end, + int[] letterAdjust, + Font font, AbstractTextArea parentArea) { + String fontName = font.getFontName(); + float fontSize = font.getFontSize() / 1000f; + Typeface tf = getTypeface(fontName); + SingleByteFont singleByteFont = null; + if (tf instanceof SingleByteFont) { + singleByteFont = (SingleByteFont)tf; + } int l = s.length(); - - float fontSize = fs.getFontSize() / 1000f; - boolean startPending = true; - for (int i = 0; i < l; i++) { + + for (int i = start; i < end; i++) { char orgChar = s.charAt(i); char ch; float glyphAdjust = 0; - if (fs.hasChar(orgChar)) { - ch = fs.mapChar(orgChar); + if (font.hasChar(orgChar)) { + ch = font.mapChar(orgChar); + if (singleByteFont != null && singleByteFont.hasAdditionalEncodings()) { + int encoding = ch / 256; + if (encoding == 0) { + textutil.updateTf(fontName, fontSize, tf.isMultiByte()); + } else { + textutil.updateTf(fontName + "_" + Integer.toString(encoding), + fontSize, tf.isMultiByte()); + ch = (char)(ch % 256); + } + } int tls = (i < l - 1 ? parentArea.getTextLetterSpaceAdjust() : 0); glyphAdjust -= tls; } else { if (CharUtilities.isFixedWidthSpace(orgChar)) { //Fixed width space are rendered as spaces so copy/paste works in a reader - ch = fs.mapChar(CharUtilities.SPACE); - glyphAdjust = fs.getCharWidth(ch) - fs.getCharWidth(orgChar); + ch = font.mapChar(CharUtilities.SPACE); + glyphAdjust = font.getCharWidth(ch) - font.getCharWidth(orgChar); } else { - ch = fs.mapChar(orgChar); + ch = font.mapChar(orgChar); } } if (letterAdjust != null && i < l - 1) { glyphAdjust -= letterAdjust[i + 1]; } - if (startPending) { - pdf.append(startText); - startPending = false; - } - if (!useMultiByte) { - if (ch < 32 || ch > 127) { - pdf.append("\\"); - pdf.append(Integer.toOctalString((int) ch)); - } else { - switch (ch) { - case '(': - case ')': - case '\\': - pdf.append("\\"); - break; - default: - } - pdf.append(ch); - } - } else { - pdf.append(PDFText.toUnicodeHex(ch)); - } + textutil.writeTJMappedChar(ch); float adjust = glyphAdjust / fontSize; if (adjust != 0) { - pdf.append(endText).append(format(adjust)).append(' '); - startPending = true; + textutil.adjustGlyphTJ(adjust); } } - if (!startPending) { - pdf.append(endText); - } - } - - /** - * Checks to see if we have some text rendering commands open - * still and writes out the TJ command to the stream if we do - */ - protected void closeText() { - /* - if (textOpen) { - currentStream.add("] TJ\n"); - textOpen = false; - prevWordX = 0; - prevWordY = 0; - currentFontName = ""; - }*/ } /** @@ -1623,8 +1566,6 @@ public class PDFRenderer extends AbstractPathOrientedRenderer { protected void setColor(Color col, boolean fill, StringBuffer pdf) { PDFColor color = new PDFColor(this.pdfDoc, col); - closeText(); - if (pdf != null) { pdf.append(color.getColorSpaceOut(fill)); } else { @@ -1656,22 +1597,10 @@ public class PDFRenderer extends AbstractPathOrientedRenderer { } /** {@inheritDoc} */ - protected void updateColor(Color col, boolean fill) { + protected void updateColor(Color col, boolean fill) { updateColor(col, fill, null); } - private void updateFont(String name, int size, StringBuffer pdf) { - if ((!name.equals(this.currentFontName)) - || (size != this.currentFontSize)) { - closeText(); - - this.currentFontName = name; - this.currentFontSize = size; - pdf = pdf.append("/" + name + " " + format((float) size / 1000f) - + " Tf\n"); - } - } - /** {@inheritDoc} */ public void renderImage(Image image, Rectangle2D pos) { endTextObject(); @@ -1727,8 +1656,6 @@ public class PDFRenderer extends AbstractPathOrientedRenderer { ImageSessionContext sessionContext = getUserAgent().getImageSessionContext(); info = manager.getImageInfo(uri, sessionContext); - - Map hints = ImageUtil.getDefaultHints(sessionContext); org.apache.xmlgraphics.image.loader.Image img = manager.getImage( info, imageHandlerRegistry.getSupportedFlavors(), hints, sessionContext); @@ -1803,9 +1730,8 @@ public class PDFRenderer extends AbstractPathOrientedRenderer { context.setProperty(PDFRendererContextConstants.PDF_CONTEXT, currentContext); context.setProperty(PDFRendererContextConstants.PDF_STREAM, currentStream); context.setProperty(PDFRendererContextConstants.PDF_FONT_INFO, fontInfo); - context.setProperty(PDFRendererContextConstants.PDF_FONT_NAME, currentFontName); - context.setProperty(PDFRendererContextConstants.PDF_FONT_SIZE, - new Integer(currentFontSize)); + context.setProperty(PDFRendererContextConstants.PDF_FONT_NAME, ""); + context.setProperty(PDFRendererContextConstants.PDF_FONT_SIZE, new Integer(0)); return context; } diff --git a/src/java/org/apache/fop/render/ps/PSFontUtils.java b/src/java/org/apache/fop/render/ps/PSFontUtils.java index bbc811b4e..1566ef799 100644 --- a/src/java/org/apache/fop/render/ps/PSFontUtils.java +++ b/src/java/org/apache/fop/render/ps/PSFontUtils.java @@ -32,16 +32,20 @@ import javax.xml.transform.stream.StreamSource; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.apache.xmlgraphics.fonts.Glyphs; import org.apache.xmlgraphics.ps.DSCConstants; import org.apache.xmlgraphics.ps.PSGenerator; import org.apache.xmlgraphics.ps.PSResource; import org.apache.xmlgraphics.ps.dsc.ResourceTracker; +import org.apache.fop.fonts.Base14Font; import org.apache.fop.fonts.CustomFont; import org.apache.fop.fonts.Font; import org.apache.fop.fonts.FontInfo; import org.apache.fop.fonts.FontType; import org.apache.fop.fonts.LazyFont; +import org.apache.fop.fonts.SingleByteEncoding; +import org.apache.fop.fonts.SingleByteFont; import org.apache.fop.fonts.Typeface; /** @@ -81,9 +85,21 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils { while (iter.hasNext()) { String key = (String)iter.next(); Typeface tf = getTypeFace(fontInfo, fonts, key); - PSResource fontRes = new PSResource("font", tf.getFontName()); + PSResource fontRes = new PSResource(PSResource.TYPE_FONT, tf.getFontName()); fontResources.put(key, fontRes); embedFont(gen, tf, fontRes); + + if (tf instanceof SingleByteFont) { + SingleByteFont sbf = (SingleByteFont)tf; + for (int i = 0, c = sbf.getAdditionalEncodingCount(); i < c; i++) { + SingleByteEncoding encoding = sbf.getAdditionalEncoding(i); + defineEncoding(gen, encoding); + String postFix = "_" + (i + 1); + PSResource derivedFontRes = defineDerivedFont(gen, tf.getFontName(), + tf.getFontName() + postFix, encoding.getName()); + fontResources.put(key + postFix, derivedFontRes); + } + } } gen.commentln("%FOPEndFontDict"); reencodeFonts(gen, fonts); @@ -91,29 +107,35 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils { } private static void reencodeFonts(PSGenerator gen, Map fonts) throws IOException { + ResourceTracker tracker = gen.getResourceTracker(); + + if (!tracker.isResourceSupplied(WINANSI_ENCODING_RESOURCE)) { + defineWinAnsiEncoding(gen); + } gen.commentln("%FOPBeginFontReencode"); - defineWinAnsiEncoding(gen); //Rewrite font encodings Iterator iter = fonts.keySet().iterator(); while (iter.hasNext()) { String key = (String)iter.next(); - Typeface fm = (Typeface)fonts.get(key); - if (fm instanceof LazyFont && ((LazyFont)fm).getRealFont() == null) { - continue; - } else if (null == fm.getEncoding()) { + Typeface tf = (Typeface)fonts.get(key); + if (tf instanceof LazyFont) { + tf = ((LazyFont)tf).getRealFont(); + if (tf == null) { + continue; + } + } + if (null == tf.getEncodingName()) { //ignore (ZapfDingbats and Symbol used to run through here, kept for safety reasons) - } else if ("SymbolEncoding".equals(fm.getEncoding())) { + } else if ("SymbolEncoding".equals(tf.getEncodingName())) { //ignore (no encoding redefinition) - } else if ("ZapfDingbatsEncoding".equals(fm.getEncoding())) { + } else if ("ZapfDingbatsEncoding".equals(tf.getEncodingName())) { //ignore (no encoding redefinition) - } else if ("WinAnsiEncoding".equals(fm.getEncoding())) { - redefineFontEncoding(gen, fm.getFontName(), fm.getEncoding()); } else { - /* Don't complain anymore, just use the font's default encoding. - gen.commentln("%WARNING: Only WinAnsiEncoding is supported. Font '" - + fm.getFontName() + "' asks for: " + fm.getEncoding()); - */ + if (tf instanceof Base14Font) { + //Our Base 14 fonts don't use the default encoding + redefineFontEncoding(gen, tf.getFontName(), tf.getEncodingName()); + } } } gen.commentln("%FOPEndFontReencode"); @@ -233,10 +255,88 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils { if (isEmbeddable(cf)) { resTracker.registerSuppliedResource(fontRes); } + if (tf instanceof SingleByteFont) { + SingleByteFont sbf = (SingleByteFont)tf; + for (int i = 0, c = sbf.getAdditionalEncodingCount(); i < c; i++) { + SingleByteEncoding encoding = sbf.getAdditionalEncoding(i); + PSResource encodingRes = new PSResource( + PSResource.TYPE_ENCODING, encoding.getName()); + resTracker.registerSuppliedResource(encodingRes); + PSResource derivedFontRes = new PSResource( + PSResource.TYPE_FONT, tf.getFontName() + "_" + (i + 1)); + resTracker.registerSuppliedResource(derivedFontRes); + } + } } } } return fontResources; } + /** + * Defines the single-byte encoding for use in PostScript files. + * @param gen the PostScript generator + * @param encoding the single-byte encoding + * @return the PSResource instance that represents the encoding + * @throws IOException In case of an I/O problem + */ + public static PSResource defineEncoding(PSGenerator gen, SingleByteEncoding encoding) + throws IOException { + PSResource res = new PSResource(PSResource.TYPE_ENCODING, encoding.getName()); + gen.writeDSCComment(DSCConstants.BEGIN_RESOURCE, res); + gen.writeln("/" + encoding.getName() + " ["); + String[] charNames = encoding.getCharNameMap(); + for (int i = 0; i < 256; i++) { + if (i > 0) { + if ((i % 5) == 0) { + gen.newLine(); + } else { + gen.write(" "); + } + } + String glyphname = null; + if (i < charNames.length) { + glyphname = charNames[i]; + } + if (glyphname == null || "".equals(glyphname)) { + glyphname = Glyphs.NOTDEF; + } + gen.write("/"); + gen.write(glyphname); + } + gen.newLine(); + gen.writeln("] def"); + gen.writeDSCComment(DSCConstants.END_RESOURCE); + gen.getResourceTracker().registerSuppliedResource(res); + return res; + } + + /** + * Derives a new font based on an existing font with a given encoding. The encoding must + * have been registered before. + * @param gen the PostScript generator + * @param baseFontName the font name of the font to derive from + * @param fontName the font name of the new font to be define + * @param encoding the new encoding (must be predefined in the PS file) + * @return the PSResource representing the derived font + * @throws IOException In case of an I/O problem + */ + public static PSResource defineDerivedFont(PSGenerator gen, String baseFontName, String fontName, + String encoding) throws IOException { + PSResource res = new PSResource(PSResource.TYPE_FONT, fontName); + gen.writeDSCComment(DSCConstants.BEGIN_RESOURCE, res); + gen.commentln("%XGCDependencies: font " + baseFontName); + gen.commentln("%XGC+ encoding " + encoding); + gen.writeln("/" + baseFontName + " findfont"); + gen.writeln("dup length dict begin"); + gen.writeln(" {1 index /FID ne {def} {pop pop} ifelse} forall"); + gen.writeln(" /Encoding " + encoding + " def"); + gen.writeln(" currentdict"); + gen.writeln("end"); + gen.writeln("/" + fontName + " exch definefont pop"); + gen.writeDSCComment(DSCConstants.END_RESOURCE); + gen.getResourceTracker().registerSuppliedResource(res); + return res; + } + } diff --git a/src/java/org/apache/fop/render/ps/PSImageUtils.java b/src/java/org/apache/fop/render/ps/PSImageUtils.java deleted file mode 100644 index 004d5a22a..000000000 --- a/src/java/org/apache/fop/render/ps/PSImageUtils.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * 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.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.apache.xmlgraphics.ps.PSGenerator; - -import org.apache.fop.image.EPSImage; -import org.apache.fop.image.FopImage; - -/** - * Utility code for rendering images in PostScript. - */ -public class PSImageUtils extends org.apache.xmlgraphics.ps.PSImageUtils { - - /** logging instance */ - protected static Log log = LogFactory.getLog(PSImageUtils.class); - - /** - * Renders an EPS image to PostScript. - * @param img EPS image to render - * @param x x position - * @param y y position - * @param w width - * @param h height - * @param gen PS generator - * @deprecated Use {@link #renderEPS(java.io.InputStream, String, java.awt.geom.Rectangle2D, - * java.awt.geom.Rectangle2D, PSGenerator)} instead - */ - public static void renderEPS(EPSImage img, - float x, float y, float w, float h, - PSGenerator gen) { - try { - if (!img.load(FopImage.ORIGINAL_DATA)) { - gen.commentln("%EPS image could not be processed: " + img); - return; - } - int[] bbox = img.getBBox(); - int bboxw = bbox[2] - bbox[0]; - int bboxh = bbox[3] - bbox[1]; - String name = img.getDocName(); - if (name == null || name.length() == 0) { - name = img.getOriginalURI(); - } - renderEPS(img.getEPSImage(), name, - x, y, w, h, - bbox[0], bbox[1], bboxw, bboxh, gen); - - } catch (Exception e) { - log.error("PSRenderer.renderImageArea(): Error rendering bitmap (" - + e.getMessage() + ")", e); - } - } - -} diff --git a/src/java/org/apache/fop/render/ps/PSRenderer.java b/src/java/org/apache/fop/render/ps/PSRenderer.java index ecd403749..7e32977e6 100644 --- a/src/java/org/apache/fop/render/ps/PSRenderer.java +++ b/src/java/org/apache/fop/render/ps/PSRenderer.java @@ -89,6 +89,7 @@ import org.apache.fop.fo.Constants; import org.apache.fop.fo.extensions.ExtensionAttachment; import org.apache.fop.fonts.Font; import org.apache.fop.fonts.LazyFont; +import org.apache.fop.fonts.SingleByteFont; import org.apache.fop.fonts.Typeface; import org.apache.fop.render.AbstractPathOrientedRenderer; import org.apache.fop.render.Graphics2DAdapter; @@ -660,6 +661,12 @@ public class PSRenderer extends AbstractPathOrientedRenderer } 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) { @@ -668,7 +675,11 @@ public class PSRenderer extends AbstractPathOrientedRenderer if (tf == null) { throw new IllegalStateException("Font not available: " + key); } - return tf.getFontName(); + if (postFix == null) { + return tf.getFontName(); + } else { + return tf.getFontName() + postFix; + } } /** @@ -698,7 +709,6 @@ public class PSRenderer extends AbstractPathOrientedRenderer protected void useFont(String key, int size) { try { PSResource res = getPSResourceForFontKey(key); - //gen.useFont(key, size / 1000f); gen.useFont("/" + res.getName(), size / 1000f); gen.getResourceTracker().notifyResourceUsageOnPage(res); } catch (IOException ioe) { @@ -951,7 +961,7 @@ public class PSRenderer extends AbstractPathOrientedRenderer if (!isOptimizeResources()) { this.fontResources = PSFontUtils.writeFontDict(gen, fontInfo); } else { - gen.commentln("%FOPFontSetup"); + gen.commentln("%FOPFontSetup"); //Place-holder, will be replaced in the second pass } gen.writeDSCComment(DSCConstants.END_SETUP); } @@ -1292,17 +1302,16 @@ public class PSRenderer extends AbstractPathOrientedRenderer */ public void renderText(TextArea area) { renderInlineAreaBackAndBorders(area); - String fontname = getInternalFontNameForArea(area); + String fontkey = getInternalFontNameForArea(area); int fontsize = area.getTraitAsInteger(Trait.FONT_SIZE); // This assumes that *all* CIDFonts use a /ToUnicode mapping - Typeface tf = (Typeface) fontInfo.getFonts().get(fontname); + Typeface tf = (Typeface) fontInfo.getFonts().get(fontkey); //Determine position int rx = currentIPPosition + area.getBorderAndPaddingWidthStart(); int bl = currentBPPosition + area.getOffset() + area.getBaselineOffset(); - useFont(fontname, fontsize); Color ct = (Color)area.getTrait(Trait.COLOR); if (ct != null) { try { @@ -1347,30 +1356,75 @@ public class PSRenderer extends AbstractPathOrientedRenderer super.renderSpace(space); } + private Typeface getTypeface(String fontName) { + Typeface tf = (Typeface)fontInfo.getFonts().get(fontName); + if (tf instanceof LazyFont) { + tf = ((LazyFont)tf).getRealFont(); + } + return tf; + } + private void renderText(AbstractTextArea area, String text, int[] letterAdjust) { + String fontkey = getInternalFontNameForArea(area); + int fontSize = area.getTraitAsInteger(Trait.FONT_SIZE); Font font = getFontFromArea(area); - Typeface tf = (Typeface) fontInfo.getFonts().get(font.getFontName()); + Typeface tf = getTypeface(font.getFontName()); + SingleByteFont singleByteFont = null; + if (tf instanceof SingleByteFont) { + singleByteFont = (SingleByteFont)tf; + } + int textLen = text.length(); + if (singleByteFont != null && singleByteFont.hasAdditionalEncodings()) { + 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(area, text, start, i - start, letterAdjust, fontSize, tf); + } + if (encoding == 0) { + useFont(fontkey, fontSize); + } else { + useFont(fontkey + "_" + Integer.toString(encoding), fontSize); + } + currentEncoding = encoding; + start = i; + } + } + writeText(area, text, start, textLen - start, letterAdjust, fontSize, tf); + } else { + useFont(fontkey, fontSize); + writeText(area, text, 0, textLen, letterAdjust, fontSize, tf); + } + } + + private void writeText(AbstractTextArea area, String text, int start, int len, + int[] letterAdjust, int fontsize, Typeface tf) { + int end = start + len; int initialSize = text.length(); initialSize += initialSize / 2; StringBuffer sb = new StringBuffer(initialSize); - int textLen = text.length(); if (letterAdjust == null && area.getTextLetterSpaceAdjust() == 0 && area.getTextWordSpaceAdjust() == 0) { sb.append("("); - for (int i = 0; i < textLen; i++) { + for (int i = start; i < end; i++) { final char c = text.charAt(i); - final char mapped = tf.mapChar(c); + final char mapped = (char)(tf.mapChar(c) % 256); PSGenerator.escapeChar(mapped, sb); } sb.append(") t"); } else { sb.append("("); - int[] offsets = new int[textLen]; - for (int i = 0; i < textLen; i++) { + int[] offsets = new int[len]; + for (int i = start; i < end; i++) { final char c = text.charAt(i); final char mapped = tf.mapChar(c); + char codepoint = (char)(mapped % 256); int wordSpace; if (CharUtilities.isAdjustableSpace(mapped)) { @@ -1378,14 +1432,14 @@ public class PSRenderer extends AbstractPathOrientedRenderer } else { wordSpace = 0; } - int cw = tf.getWidth(mapped, font.getFontSize()) / 1000; - int ladj = (letterAdjust != null && i < textLen - 1 ? letterAdjust[i + 1] : 0); - int tls = (i < textLen - 1 ? area.getTextLetterSpaceAdjust() : 0); - offsets[i] = cw + ladj + tls + wordSpace; - PSGenerator.escapeChar(mapped, sb); + int cw = tf.getWidth(mapped, fontsize) / 1000; + int ladj = (letterAdjust != null && i < end - 1 ? letterAdjust[i + 1] : 0); + int tls = (i < end - 1 ? area.getTextLetterSpaceAdjust() : 0); + offsets[i - start] = cw + ladj + tls + wordSpace; + PSGenerator.escapeChar(codepoint, sb); } sb.append(")" + PSGenerator.LF + "["); - for (int i = 0; i < textLen; i++) { + for (int i = 0; i < len; i++) { if (i > 0) { if (i % 8 == 0) { sb.append(PSGenerator.LF); @@ -1398,7 +1452,6 @@ public class PSRenderer extends AbstractPathOrientedRenderer sb.append("]" + PSGenerator.LF + "xshow"); } writeln(sb.toString()); - } /** {@inheritDoc} */ diff --git a/src/java/org/apache/fop/render/rtf/SVGConverter.java b/src/java/org/apache/fop/render/rtf/SVGConverter.java deleted file mode 100644 index 11ba8dc82..000000000 --- a/src/java/org/apache/fop/render/rtf/SVGConverter.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * 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.rtf; - -import org.apache.batik.transcoder.TranscoderException; -import org.apache.batik.transcoder.TranscoderInput; -import org.apache.batik.transcoder.TranscoderOutput; -import org.apache.batik.transcoder.image.JPEGTranscoder; -import org.apache.commons.io.output.ByteArrayOutputStream; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.apache.fop.image.XMLImage; - -/** - * Helper class for converting SVG to bitmap images. - */ -public final class SVGConverter { - - /** logger instance */ - private static Log log = LogFactory.getLog(SVGConverter.class); - - /** - * Constructor is private, because it's just a utility class. - */ - private SVGConverter() { - } - - /** - * Converts a SVG image to a JPEG bitmap. - * @param image the SVG image - * @return a byte array containing the JPEG image - */ - public static byte[] convertToJPEG(XMLImage image) { - JPEGTranscoder transcoder = new JPEGTranscoder(); - /* TODO Disabled to avoid side-effect due to the mixing of source and target resolutions - * This should be reenabled when it has been determined how exactly to handle this - transcoder.addTranscodingHint(ImageTranscoder.KEY_PIXEL_UNIT_TO_MILLIMETER, - new Float(25.4f / 300)); //300dpi should be enough for now. - */ - transcoder.addTranscodingHint(JPEGTranscoder.KEY_QUALITY, new Float(0.9f)); - TranscoderInput input = new TranscoderInput(image.getDocument()); - ByteArrayOutputStream baout = new ByteArrayOutputStream(16384); - TranscoderOutput output = new TranscoderOutput(baout); - try { - transcoder.transcode(input, output); - return baout.toByteArray(); - } catch (TranscoderException e) { - log.error(e); - return null; - } - } - -} diff --git a/src/java/org/apache/fop/image/analyser/SVGZReader.java b/src/java/org/apache/fop/svg/GraphicsConfiguration.java index 14fde1cb6..a92baed27 100644 --- a/src/java/org/apache/fop/image/analyser/SVGZReader.java +++ b/src/java/org/apache/fop/svg/GraphicsConfiguration.java @@ -16,38 +16,36 @@ */ /* $Id$ */ + -package org.apache.fop.image.analyser; +package org.apache.fop.svg; -import java.io.IOException; -import java.io.InputStream; -import java.util.zip.GZIPInputStream; - -import org.apache.fop.apps.FOUserAgent; -import org.apache.fop.image.FopImage; +import java.awt.image.VolatileImage; /** - * Implements a reader for gzipped XMLFiles. + * Adapter to allow subclassing java.awt.GraphicsConfiguration without + * compilation errors. + * The version for JDK 1.4 needs to add an override for the abstract + * createCompatibleVolatileImage() method. It can't be overidden + * for JDK 1.3 because there is no VolatileImage there. * - * <p> - * The current implementation is limited to SVG files only. */ -public class SVGZReader extends XMLReader { +abstract public class GraphicsConfiguration extends java.awt.GraphicsConfiguration { + /** - * Default constructor. + * @see java.awt.GraphicsConfiguration#createCompatibleVolatileImage(int, int) + * @since JDK 1.4 */ - public SVGZReader() { + public VolatileImage createCompatibleVolatileImage(int width, int height) { + return null; } - /** {@inheritDoc} */ - protected FopImage.ImageInfo loadImage(final String uri, - final InputStream bis, final FOUserAgent ua) { - try { - return new SVGReader().verifySignature(uri, - new GZIPInputStream(bis), ua); - } catch (final IOException e) { - // ignore - } + /** + * @see java.awt.GraphicsConfiguration#createCompatibleVolatileImage(int, int, int) + * @since JDK 1.5 + */ + public VolatileImage createCompatibleVolatileImage(int width, int height, int transparency) { return null; } + } diff --git a/src/java/org/apache/fop/svg/PDFGraphics2D.java b/src/java/org/apache/fop/svg/PDFGraphics2D.java index 864809ebe..f6a7cbc3b 100644 --- a/src/java/org/apache/fop/svg/PDFGraphics2D.java +++ b/src/java/org/apache/fop/svg/PDFGraphics2D.java @@ -70,12 +70,10 @@ import org.apache.xmlgraphics.image.loader.impl.ImageRendered; import org.apache.xmlgraphics.java2d.AbstractGraphics2D; import org.apache.xmlgraphics.java2d.GraphicContext; -import org.apache.fop.fonts.CIDFont; import org.apache.fop.fonts.Font; import org.apache.fop.fonts.FontInfo; import org.apache.fop.fonts.FontSetup; import org.apache.fop.fonts.FontTriplet; -import org.apache.fop.fonts.LazyFont; import org.apache.fop.pdf.BitmapImage; import org.apache.fop.pdf.PDFAnnotList; import org.apache.fop.pdf.PDFColor; @@ -1473,14 +1471,7 @@ public class PDFGraphics2D extends AbstractGraphics2D { // This assumes that *all* CIDFonts use a /ToUnicode mapping org.apache.fop.fonts.Typeface f = (org.apache.fop.fonts.Typeface)fontInfo.getFonts().get(name); - if (f instanceof LazyFont) { - if (((LazyFont) f).getRealFont() instanceof CIDFont) { - return true; - } - } else if (f instanceof CIDFont) { - return true; - } - return false; + return f.isMultiByte(); } private void addKerning(StringWriter buf, Integer ch1, Integer ch2, diff --git a/src/java/org/apache/fop/svg/PDFTextPainter.java b/src/java/org/apache/fop/svg/PDFTextPainter.java index 7154c68a9..754b0794b 100644 --- a/src/java/org/apache/fop/svg/PDFTextPainter.java +++ b/src/java/org/apache/fop/svg/PDFTextPainter.java @@ -43,6 +43,7 @@ import org.apache.batik.gvt.renderer.StrokingTextPainter; import org.apache.batik.gvt.text.GVTAttributedCharacterIterator; import org.apache.batik.gvt.text.TextPaintInfo; import org.apache.batik.gvt.text.TextSpanLayout; + import org.apache.fop.fonts.Font; import org.apache.fop.fonts.FontInfo; import org.apache.fop.fonts.FontTriplet; @@ -83,8 +84,12 @@ public class PDFTextPainter extends StrokingTextPainter { super.paintTextRuns(textRuns, g2d); return; } - PDFGraphics2D pdf = (PDFGraphics2D)g2d; - PDFTextUtil textUtil = new PDFTextUtil(pdf); + final PDFGraphics2D pdf = (PDFGraphics2D)g2d; + PDFTextUtil textUtil = new PDFTextUtil(pdf.fontInfo) { + protected void write(String code) { + pdf.currentStream.write(code); + } + }; for (int i = 0; i < textRuns.size(); i++) { TextRun textRun = (TextRun)textRuns.get(i); AttributedCharacterIterator runaci = textRun.getACI(); @@ -134,7 +139,7 @@ public class PDFTextPainter extends StrokingTextPainter { } textUtil.saveGraphicsState(); - textUtil.concatMatrixCurrentTransform(); + textUtil.concatMatrix(g2d.getTransform()); Shape imclip = g2d.getClip(); pdf.writeClip(imclip); diff --git a/src/java/org/apache/fop/svg/PDFTextUtil.java b/src/java/org/apache/fop/svg/PDFTextUtil.java index 0fb552026..f3c7f31a2 100644 --- a/src/java/org/apache/fop/svg/PDFTextUtil.java +++ b/src/java/org/apache/fop/svg/PDFTextUtil.java @@ -19,145 +19,33 @@ package org.apache.fop.svg; -import java.awt.geom.AffineTransform; - import org.apache.fop.fonts.Font; -import org.apache.fop.pdf.PDFNumber; -import org.apache.fop.pdf.PDFText; +import org.apache.fop.fonts.FontInfo; +import org.apache.fop.fonts.Typeface; /** - * Utility class for generating PDF text objects. + * Utility class for generating PDF text objects. It needs to be subclassed to add writing + * functionality (see {@link #write(String)}). */ -public class PDFTextUtil { +public abstract class PDFTextUtil extends org.apache.fop.pdf.PDFTextUtil { - /** The number of decimal places. */ - private static final int DEC = 8; - - /** PDF text rendering mode: Fill text */ - public static final int TR_FILL = 0; - /** PDF text rendering mode: Stroke text */ - public static final int TR_STROKE = 1; - /** PDF text rendering mode: Fill, then stroke text */ - public static final int TR_FILL_STROKE = 2; - /** PDF text rendering mode: Neither fill nor stroke text (invisible) */ - public static final int TR_INVISIBLE = 3; - /** PDF text rendering mode: Fill text and add to path for clipping */ - public static final int TR_FILL_CLIP = 4; - /** PDF text rendering mode: Stroke text and add to path for clipping */ - public static final int TR_STROKE_CLIP = 5; - /** PDF text rendering mode: Fill, then stroke text and add to path for clipping */ - public static final int TR_FILL_STROKE_CLIP = 6; - /** PDF text rendering mode: Add text to path for clipping */ - public static final int TR_CLIP = 7; - - - private PDFGraphics2D g2d; - private boolean inTextObject = false; + private FontInfo fontInfo; private Font[] fonts; private Font font; - private String startText; - private String endText; - private boolean useMultiByte; - private StringBuffer bufTJ; - private int textRenderingMode = 0; /** * Main constructor. - * @param g2d the PDFGraphics2D instance to work with + * @param fontInfo the font catalog */ - public PDFTextUtil(PDFGraphics2D g2d) { - this.g2d = g2d; + public PDFTextUtil(FontInfo fontInfo) { + super(); + this.fontInfo = fontInfo; } - private void writeAffineTransform(AffineTransform at, StringBuffer sb) { - double[] lt = new double[6]; - at.getMatrix(lt); - sb.append(PDFNumber.doubleOut(lt[0], DEC)).append(" "); - sb.append(PDFNumber.doubleOut(lt[1], DEC)).append(" "); - sb.append(PDFNumber.doubleOut(lt[2], DEC)).append(" "); - sb.append(PDFNumber.doubleOut(lt[3], DEC)).append(" "); - sb.append(PDFNumber.doubleOut(lt[4], DEC)).append(" "); - sb.append(PDFNumber.doubleOut(lt[5], DEC)); - } - - private void writeChar(char ch, StringBuffer sb) { - if (!useMultiByte) { - if (ch > 127) { - sb.append("\\").append(Integer.toOctalString((int)ch)); - } else { - switch (ch) { - case '(': - case ')': - case '\\': - sb.append("\\"); - break; - default: - } - sb.append(ch); - } - } else { - sb.append(PDFText.toUnicodeHex(ch)); - } - } - - private void checkInTextObject() { - if (!inTextObject) { - throw new IllegalStateException("Not in text object"); - } - } - - /** - * Called when a new text object should be started. Be sure to call setFont() before - * issuing any text painting commands. - */ - public void beginTextObject() { - if (inTextObject) { - throw new IllegalStateException("Already in text object"); - } - g2d.currentStream.write("BT\n"); - this.inTextObject = true; - } - - /** - * Called when a text object should be ended. - */ - public void endTextObject() { - checkInTextObject(); - g2d.currentStream.write("ET\n"); - this.inTextObject = false; - initValues(); - } - - private void initValues() { + /** {@inheritDoc} */ + protected void initValues() { + super.initValues(); this.font = null; - this.textRenderingMode = TR_FILL; - } - - /** - * Creates a "q" command, pushing a copy of the entire graphics state onto the stack. - */ - public void saveGraphicsState() { - g2d.currentStream.write("q\n"); - } - - /** - * Creates a "Q" command, restoring the entire graphics state to its former value by popping - * it from the stack. - */ - public void restoreGraphicsState() { - g2d.currentStream.write("Q\n"); - } - - /** - * Creates a "cm" command using the current transformation as the matrix. - */ - public void concatMatrixCurrentTransform() { - StringBuffer sb = new StringBuffer(); - if (!g2d.getTransform().isIdentity()) { - writeAffineTransform(g2d.getTransform(), sb); - sb.append(" cm\n"); - } - g2d.currentStream.write(sb.toString()); } /** @@ -194,63 +82,23 @@ public class PDFTextUtil { } /** + * Determines whether the font with the given name is a multi-byte font. + * @param name the name of the font + * @return true if it's a multi-byte font + */ + protected boolean isMultiByteFont(String name) { + Typeface f = (Typeface)fontInfo.getFonts().get(name); + return f.isMultiByte(); + } + + /** * Writes a "Tf" command, setting a new current font. * @param f the font to select */ public void writeTf(Font f) { - checkInTextObject(); String fontName = f.getFontName(); float fontSize = (float)f.getFontSize() / 1000f; - g2d.currentStream.write("/" + fontName + " " + PDFNumber.doubleOut(fontSize) + " Tf\n"); - - this.useMultiByte = g2d.isMultiByteFont(fontName); - this.startText = useMultiByte ? "<" : "("; - this.endText = useMultiByte ? ">" : ")"; - } - - /** - * Sets the text rendering mode. - * @param mode the rendering mode (value 0 to 7, see PDF Spec, constants: TR_*) - */ - public void setTextRenderingMode(int mode) { - if (mode < 0 || mode > 7) { - throw new IllegalArgumentException( - "Illegal value for text rendering mode. Expected: 0-7"); - } - if (mode != this.textRenderingMode) { - this.textRenderingMode = mode; - g2d.currentStream.write(this.textRenderingMode + " Tr\n"); - } - } - - /** - * Sets the text rendering mode. - * @param fill true if the text should be filled - * @param stroke true if the text should be stroked - * @param addToClip true if the path should be added for clipping - */ - public void setTextRenderingMode(boolean fill, boolean stroke, boolean addToClip) { - int mode; - if (fill) { - mode = (stroke ? 2 : 0); - } else { - mode = (stroke ? 1 : 3); - } - if (addToClip) { - mode += 4; - } - setTextRenderingMode(mode); - } - - /** - * Writes a "Tm" command, setting a new text transformation matrix. - * @param localTransform the new text transformation matrix - */ - public void writeTextMatrix(AffineTransform localTransform) { - StringBuffer sb = new StringBuffer(); - writeAffineTransform(localTransform, sb); - sb.append(" Tm\n"); - g2d.currentStream.write(sb.toString()); + updateTf(fontName, fontSize, isMultiByteFont(fontName)); } /** @@ -272,37 +120,8 @@ public class PDFTextUtil { * @param ch the unmapped character */ public void writeTJChar(char ch) { - if (bufTJ == null) { - bufTJ = new StringBuffer(); - } - if (bufTJ.length() == 0) { - bufTJ.append("[").append(startText); - } char mappedChar = font.mapChar(ch); - writeChar(mappedChar, bufTJ); - } - - /** - * Writes a glyph adjust value to the "TJ-Buffer". - * @param adjust the glyph adjust value in thousands of text unit space. - */ - public void adjustGlyphTJ(double adjust) { - bufTJ.append(endText).append(" "); - bufTJ.append(PDFNumber.doubleOut(adjust, DEC - 4)); - bufTJ.append(" "); - bufTJ.append(startText); - } - - /** - * Writes a "TJ" command, writing out the accumulated buffer with the characters and glyph - * positioning values. The buffer is reset afterwards. - */ - public void writeTJ() { - if (bufTJ != null && bufTJ.length() > 0) { - bufTJ.append(endText).append("] TJ\n"); - g2d.currentStream.write(bufTJ.toString()); - bufTJ.setLength(0); - } + writeTJMappedChar(mappedChar); } } |