From f8e822efe1de8bd8192dbb8ff035b9a79f876614 Mon Sep 17 00:00:00 2001 From: Vincent Hennebert Date: Mon, 29 Jul 2013 21:45:20 +0000 Subject: Directly use FOP fonts to lay out SVG images for PDF, PS and AFP outputs. The metrics are now taken from FOP configured fonts and no longer from AWT equivalents. That avoids discrepancies in case AWT and FOP use slightly different fonts, or if the font is not installed on the system. That actually also avoids having to install the font on the system. FOP is also used for the primary layout of text (prior to SVG-specific transforms like translation or rotation) for consistency between SVG and XSL-FO. This is a joint work from Peter Hancock and myself. git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_FopFontsForSVG@1508208 13f79535-47bb-0310-9956-ffa450edef68 --- src/java/org/apache/fop/afp/AFPEventProducer.java | 8 + src/java/org/apache/fop/afp/fonts/AFPFont.java | 32 +- .../apache/fop/afp/fonts/AbstractOutlineFont.java | 60 +- .../org/apache/fop/afp/fonts/CharacterSet.java | 72 +-- .../apache/fop/afp/fonts/CharacterSetBuilder.java | 95 ++- .../fop/afp/fonts/CharacterSetOrientation.java | 124 ++-- .../org/apache/fop/afp/fonts/DoubleByteFont.java | 34 +- .../org/apache/fop/afp/fonts/FopCharacterSet.java | 50 +- .../org/apache/fop/afp/fonts/IntegerKeyStore.java | 71 ++ src/java/org/apache/fop/afp/fonts/OutlineFont.java | 16 + src/java/org/apache/fop/afp/fonts/RasterFont.java | 88 +-- .../fop/afp/goca/GraphicsCharacterString.java | 6 +- .../org/apache/fop/afp/svg/AFPBridgeContext.java | 42 +- .../apache/fop/afp/svg/AFPFontFamilyResolver.java | 84 +++ .../org/apache/fop/afp/svg/AFPTextHandler.java | 31 +- .../org/apache/fop/afp/svg/AFPTextPainter.java | 21 +- src/java/org/apache/fop/fo/FOText.java | 94 +-- src/java/org/apache/fop/fonts/Base14Font.java | 11 + src/java/org/apache/fop/fonts/CIDFont.java | 1 + src/java/org/apache/fop/fonts/CustomFont.java | 47 ++ src/java/org/apache/fop/fonts/FontInfo.java | 37 +- src/java/org/apache/fop/fonts/FontMetrics.java | 44 ++ src/java/org/apache/fop/fonts/GlyphMapping.java | 327 ++++++++++ src/java/org/apache/fop/fonts/LazyFont.java | 26 + src/java/org/apache/fop/fonts/MultiByteFont.java | 18 + src/java/org/apache/fop/fonts/SingleByteFont.java | 40 +- src/java/org/apache/fop/fonts/TextFragment.java | 31 + .../org/apache/fop/fonts/truetype/TTFFile.java | 38 +- .../apache/fop/fonts/truetype/TTFFontLoader.java | 20 +- .../org/apache/fop/fonts/type1/AFMCharMetrics.java | 8 +- .../apache/fop/fonts/type1/Type1FontLoader.java | 17 +- .../image/loader/batik/ImageConverterSVG2G2D.java | 7 +- .../fop/image/loader/batik/PreloaderSVG.java | 3 +- .../fop/layoutmgr/inline/TextLayoutManager.java | 719 +++++++-------------- .../fop/render/AbstractGenericSVGHandler.java | 4 +- .../org/apache/fop/render/afp/AFPSVGHandler.java | 15 +- .../fop/render/java2d/CustomFontMetricsMapper.java | 21 + .../fop/render/java2d/Java2DFontMetrics.java | 20 + .../apache/fop/render/java2d/Java2DSVGHandler.java | 5 +- .../fop/render/java2d/SystemFontMetricsMapper.java | 21 + .../apache/fop/render/pdf/PDFImageHandlerSVG.java | 4 +- .../apache/fop/render/ps/AbstractPSTranscoder.java | 3 + .../apache/fop/render/ps/PSImageHandlerSVG.java | 5 +- .../org/apache/fop/render/ps/PSSVGHandler.java | 3 +- .../org/apache/fop/render/ps/PSTextPainter.java | 361 ++++------- src/java/org/apache/fop/svg/ACIUtils.java | 68 +- .../org/apache/fop/svg/AbstractFOPTextPainter.java | 331 +++------- .../org/apache/fop/svg/AbstractFOPTranscoder.java | 22 +- src/java/org/apache/fop/svg/NativeTextPainter.java | 147 ++++- src/java/org/apache/fop/svg/PDFTextPainter.java | 229 +++---- src/java/org/apache/fop/svg/PDFTextUtil.java | 41 -- src/java/org/apache/fop/svg/PDFTranscoder.java | 6 +- src/java/org/apache/fop/svg/SVGUserAgent.java | 11 +- .../org/apache/fop/svg/SimpleSVGUserAgent.java | 12 +- .../svg/font/AggregatingFontFamilyResolver.java | 70 ++ .../apache/fop/svg/font/FOPFontFamilyResolver.java | 31 + .../fop/svg/font/FOPFontFamilyResolverImpl.java | 67 ++ src/java/org/apache/fop/svg/font/FOPGVTFont.java | 159 +++++ .../org/apache/fop/svg/font/FOPGVTFontFamily.java | 73 +++ .../org/apache/fop/svg/font/FOPGVTGlyphVector.java | 339 ++++++++++ .../fop/svg/font/FilteringFontFamilyResolver.java | 47 ++ 61 files changed, 2720 insertions(+), 1717 deletions(-) create mode 100644 src/java/org/apache/fop/afp/fonts/IntegerKeyStore.java create mode 100644 src/java/org/apache/fop/afp/svg/AFPFontFamilyResolver.java create mode 100644 src/java/org/apache/fop/fonts/GlyphMapping.java create mode 100644 src/java/org/apache/fop/fonts/TextFragment.java create mode 100644 src/java/org/apache/fop/svg/font/AggregatingFontFamilyResolver.java create mode 100644 src/java/org/apache/fop/svg/font/FOPFontFamilyResolver.java create mode 100644 src/java/org/apache/fop/svg/font/FOPFontFamilyResolverImpl.java create mode 100644 src/java/org/apache/fop/svg/font/FOPGVTFont.java create mode 100644 src/java/org/apache/fop/svg/font/FOPGVTFontFamily.java create mode 100644 src/java/org/apache/fop/svg/font/FOPGVTGlyphVector.java create mode 100644 src/java/org/apache/fop/svg/font/FilteringFontFamilyResolver.java (limited to 'src/java/org/apache') diff --git a/src/java/org/apache/fop/afp/AFPEventProducer.java b/src/java/org/apache/fop/afp/AFPEventProducer.java index 01d5c4ad7..1b43400c5 100644 --- a/src/java/org/apache/fop/afp/AFPEventProducer.java +++ b/src/java/org/apache/fop/afp/AFPEventProducer.java @@ -122,4 +122,12 @@ public interface AFPEventProducer extends EventProducer { * @event.severity WARN */ void charactersetMissingMetrics(Object source, char character, String charSet); + + /** + * Double-byte fonts are not currently supported in SVG. + * @param source the event source + * @param fontFamily name of DB font + * @event.severity WARN + */ + void invalidDBFontInSVG(Object source, String fontFamily); } diff --git a/src/java/org/apache/fop/afp/fonts/AFPFont.java b/src/java/org/apache/fop/afp/fonts/AFPFont.java index 06f484f37..99e15a46b 100644 --- a/src/java/org/apache/fop/afp/fonts/AFPFont.java +++ b/src/java/org/apache/fop/afp/fonts/AFPFont.java @@ -19,6 +19,7 @@ package org.apache.fop.afp.fonts; +import java.awt.Rectangle; import java.util.HashSet; import java.util.Map; import java.util.Set; @@ -34,6 +35,8 @@ import org.apache.fop.fonts.Typeface; */ public abstract class AFPFont extends Typeface { + private static final double STRIKEOUT_POSITION_FACTOR = 0.45; + /** The font name */ protected final String name; @@ -117,7 +120,34 @@ public abstract class AFPFont extends Typeface { */ protected static final char toUnicodeCodepoint(int character) { //AFP fonts use Unicode directly as their mapped code points, so we can simply cast to char - return (char)character; + return (char) character; + } + + /** {@inheritDoc} */ + public int getUnderlineThickness(int size) { + // This is the FOCA recommendation in the absence of the Underline Thickness parameter + return getBoundingBox('-', size).height; + } + + /** {@inheritDoc} */ + public int getStrikeoutPosition(int size) { + //TODO This conflicts with the FOCA recommendation of 0 in the absence of the Throughscore Position + // parameter + return (int) (STRIKEOUT_POSITION_FACTOR * getCapHeight(size)); + } + + /** {@inheritDoc} */ + public int getStrikeoutThickness(int size) { + // This is the FOCA recommendation in the absence of the Throughscore Thickness parameter + return getBoundingBox('-', size).height; + } + + /** {@inheritDoc} */ + public abstract Rectangle getBoundingBox(int glyphIndex, int size); + + /** {@inheritDoc} */ + public int[] getWidths() { + throw new UnsupportedOperationException(); } /** {@inheritDoc} */ diff --git a/src/java/org/apache/fop/afp/fonts/AbstractOutlineFont.java b/src/java/org/apache/fop/afp/fonts/AbstractOutlineFont.java index 7b57a2b8c..edbdf5e95 100644 --- a/src/java/org/apache/fop/afp/fonts/AbstractOutlineFont.java +++ b/src/java/org/apache/fop/afp/fonts/AbstractOutlineFont.java @@ -70,22 +70,6 @@ public abstract class AbstractOutlineFont extends AFPFont { return charSet; } - /** - * Get the first character in this font. - * @return the first character in this font - */ - public int getFirstChar() { - return charSet.getFirstChar(); - } - - /** - * Get the last character in this font. - * @return the last character in this font - */ - public int getLastChar() { - return charSet.getLastChar(); - } - /** * The ascender is the part of a lowercase letter that extends above the * "x-height" (the height of the letter "x"), such as "d", "t", or "h". Also @@ -98,6 +82,17 @@ public abstract class AbstractOutlineFont extends AFPFont { return charSet.getAscender() * size; } + /** {@inheritDoc} */ + public int getUnderlinePosition(int size) { + return charSet.getUnderscorePosition() * size; + } + + @Override + public int getUnderlineThickness(int size) { + int underscoreWidth = charSet.getUnderscoreWidth(); + return underscoreWidth == 0 ? super.getUnderlineThickness(size) : underscoreWidth * size; + } + /** * Obtains the height of capital letters for the specified point size. * @@ -130,40 +125,7 @@ public abstract class AbstractOutlineFont extends AFPFont { return charSet.getXHeight() * size; } - /** - * Obtain the width of the character for the specified point size. - * @param character the character - * @param size the font size (in mpt) - * @return the width of the character for the specified point size - */ - public int getWidth(int character, int size) { - return charSet.getWidth(toUnicodeCodepoint(character)) * size; - } - /** - * Get the getWidth (in 1/1000ths of a point size) of all characters in this - * character set. - * - * @param size the font size (in mpt) - * @return the widths of all characters - */ - public int[] getWidths(int size) { - int[] widths = charSet.getWidths(); - for (int i = 0; i < widths.length; i++) { - widths[i] = widths[i] * size; - } - return widths; - } - - /** - * Get the getWidth (in 1/1000ths of a point size) of all characters in this - * character set. - * - * @return the widths of all characters - */ - public int[] getWidths() { - return getWidths(1000); - } /** {@inheritDoc} */ public boolean hasChar(char c) { diff --git a/src/java/org/apache/fop/afp/fonts/CharacterSet.java b/src/java/org/apache/fop/afp/fonts/CharacterSet.java index e0c3b9c9a..3df8ba4c4 100644 --- a/src/java/org/apache/fop/afp/fonts/CharacterSet.java +++ b/src/java/org/apache/fop/afp/fonts/CharacterSet.java @@ -19,10 +19,9 @@ package org.apache.fop.afp.fonts; +import java.awt.Rectangle; import java.io.UnsupportedEncodingException; import java.nio.charset.CharacterCodingException; -import java.util.HashMap; -import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -63,6 +62,8 @@ public class CharacterSet { private static final int MAX_NAME_LEN = 8; + /** The current orientation (currently only 0 is supported by FOP) */ + public static final int SUPPORTED_ORIENTATION = 0; /** The code page to which the character set relates */ protected final String codePage; @@ -79,11 +80,8 @@ public class CharacterSet { /** The path to the installed fonts */ private final AFPResourceAccessor accessor; - /** The current orientation (currently only 0 is supported by FOP) */ - private final String currentOrientation = "0"; - /** The collection of objects for each orientation */ - private final Map characterSetOrientations; + private CharacterSetOrientation characterSetOrientation; /** The nominal vertical size (in millipoints) for bitmap fonts. 0 for outline fonts. */ private int nominalVerticalSize; @@ -116,8 +114,6 @@ public class CharacterSet { this.encoding = encoding; this.encoder = charsetType.getEncoder(encoding); this.accessor = accessor; - - this.characterSetOrientations = new HashMap(4); } // right pad short names with space @@ -131,7 +127,9 @@ public class CharacterSet { * @param cso the metrics for the orientation */ public void addCharacterSetOrientation(CharacterSetOrientation cso) { - characterSetOrientations.put(String.valueOf(cso.getOrientation()), cso); + if (cso.getOrientation() == SUPPORTED_ORIENTATION) { + characterSetOrientation = cso; + } } /** @@ -165,10 +163,23 @@ public class CharacterSet { * @return the ascender value in millipoints */ public int getAscender() { - return getCharacterSetOrientation().getAscender(); } + /** + * TODO + */ + public int getUnderscoreWidth() { + return getCharacterSetOrientation().getUnderscoreWidth(); + } + + /** + * TODO + */ + public int getUnderscorePosition() { + return getCharacterSetOrientation().getUnderscorePosition(); + } + /** * Cap height is the average height of the uppercase characters in * a font. This value is specified by the designer of a font and is @@ -177,7 +188,6 @@ public class CharacterSet { * @return the cap height value in millipoints */ public int getCapHeight() { - return getCharacterSetOrientation().getCapHeight(); } @@ -193,24 +203,6 @@ public class CharacterSet { return getCharacterSetOrientation().getDescender(); } - /** - * Returns the first character in the character set - * - * @return the first character in the character set (Unicode codepoint) - */ - public char getFirstChar() { - return getCharacterSetOrientation().getFirstChar(); - } - - /** - * Returns the last character in the character set - * - * @return the last character in the character set (Unicode codepoint) - */ - public char getLastChar() { - return getCharacterSetOrientation().getLastChar(); - } - /** * Returns the resource accessor to load the font resources with. * @return the resource accessor to load the font resources with @@ -219,16 +211,6 @@ public class CharacterSet { return this.accessor; } - /** - * Get the width (in 1/1000ths of a point size) of all characters - * - * @return the widths of all characters - */ - public int[] getWidths() { - - return getCharacterSetOrientation().getWidths(); - } - /** * XHeight refers to the height of the lower case letters above the baseline. * @@ -246,11 +228,13 @@ public class CharacterSet { * @param character the Unicode character from which the width will be calculated * @return the width of the character */ - public int getWidth(char character) { - return getCharacterSetOrientation().getWidth(character); + public int getWidth(char character, int size) { + return getCharacterSetOrientation().getWidth(character, size); } - + public Rectangle getCharacterBox(char character, int size) { + return getCharacterSetOrientation().getCharacterBox(character, size); + } /** * Returns the AFP character set identifier @@ -309,9 +293,7 @@ public class CharacterSet { * @return characterSetOrentation The current orientation metrics. */ private CharacterSetOrientation getCharacterSetOrientation() { - CharacterSetOrientation c - = characterSetOrientations.get(currentOrientation); - return c; + return characterSetOrientation; } /** diff --git a/src/java/org/apache/fop/afp/fonts/CharacterSetBuilder.java b/src/java/org/apache/fop/afp/fonts/CharacterSetBuilder.java index 2565942b5..a3b2ab8ec 100644 --- a/src/java/org/apache/fop/afp/fonts/CharacterSetBuilder.java +++ b/src/java/org/apache/fop/afp/fonts/CharacterSetBuilder.java @@ -19,6 +19,7 @@ package org.apache.fop.afp.fonts; +import java.awt.Rectangle; import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; @@ -292,16 +293,14 @@ public abstract class CharacterSetBuilder { metricNormalizationFactor = 1000.0d * 72000.0d / fontDescriptor.getNominalFontSizeInMillipoints() / dpi; } - + ValueNormalizer normalizer = new ValueNormalizer(metricNormalizationFactor); //process D3AC89 Font Position - processFontPosition(structuredFieldReader, characterSetOrientations, - metricNormalizationFactor); - + processFontPosition(structuredFieldReader, characterSetOrientations, normalizer); //process D38C89 Font Index (per orientation) for (int i = 0; i < characterSetOrientations.length; i++) { - processFontIndex(structuredFieldReader, - characterSetOrientations[i], codePage, metricNormalizationFactor); - characterSet.addCharacterSetOrientation(characterSetOrientations[i]); + CharacterSetOrientation characterSetOrientation = characterSetOrientations[i]; + processFontIndex(structuredFieldReader, characterSetOrientation, codePage, normalizer); + characterSet.addCharacterSetOrientation(characterSetOrientation); } } else { throw new IOException("Missing D3AE89 Font Control structured field."); @@ -314,6 +313,19 @@ public abstract class CharacterSetBuilder { return characterSet; } + private static class ValueNormalizer { + + private final double factor; + + public ValueNormalizer(double factor) { + this.factor = factor; + } + + public int normalize(int value) { + return (int) Math.round(value * factor); + } + } + /** * Load the code page information from the appropriate file. The file name * to load is determined by the code page name and the file extension 'CDP'. @@ -475,7 +487,7 @@ public abstract class CharacterSetBuilder { * @throws IOException if an I/O exception of some sort has occurred. */ private void processFontPosition(StructuredFieldReader structuredFieldReader, - CharacterSetOrientation[] characterSetOrientations, double metricNormalizationFactor) + CharacterSetOrientation[] characterSetOrientations, ValueNormalizer normalizer) throws IOException { byte[] data = structuredFieldReader.getNext(FONT_POSITION_SF); @@ -493,48 +505,34 @@ public abstract class CharacterSetBuilder { if (position == 9) { CharacterSetOrientation characterSetOrientation = characterSetOrientations[characterSetOrientationIndex]; - int xHeight = getSBIN(fpData, 2); int capHeight = getSBIN(fpData, 4); int ascHeight = getSBIN(fpData, 6); int dscHeight = getSBIN(fpData, 8); - dscHeight = dscHeight * -1; - - characterSetOrientation.setXHeight( - (int)Math.round(xHeight * metricNormalizationFactor)); - characterSetOrientation.setCapHeight( - (int)Math.round(capHeight * metricNormalizationFactor)); - characterSetOrientation.setAscender( - (int)Math.round(ascHeight * metricNormalizationFactor)); - characterSetOrientation.setDescender( - (int)Math.round(dscHeight * metricNormalizationFactor)); + int underscoreWidth = getUBIN(fpData, 17); + int underscorePosition = getSBIN(fpData, 20); + characterSetOrientation.setXHeight(normalizer.normalize(xHeight)); + characterSetOrientation.setCapHeight(normalizer.normalize(capHeight)); + characterSetOrientation.setAscender(normalizer.normalize(ascHeight)); + characterSetOrientation.setDescender(normalizer.normalize(dscHeight)); + characterSetOrientation.setUnderscoreWidth(normalizer.normalize(underscoreWidth)); + characterSetOrientation.setUnderscorePosition(normalizer.normalize(underscorePosition)); } } else if (position == 22) { position = 0; characterSetOrientationIndex++; fpData[position] = data[index]; } - position++; } } - /** - * Process the font index details for the character set orientation. - * - * @param structuredFieldReader the structured field reader - * @param cso the CharacterSetOrientation object to populate - * @param codepage the map of code pages - * @param metricNormalizationFactor factor to apply to the metrics to get normalized - * font metric values - * @throws IOException if an I/O exception of some sort has occurred. - */ - private void processFontIndex(StructuredFieldReader structuredFieldReader, - CharacterSetOrientation cso, Map codepage, - double metricNormalizationFactor) - throws IOException { + + private void processFontIndex(StructuredFieldReader structuredFieldReader, CharacterSetOrientation cso, + Map codepage, ValueNormalizer normalizer) + throws IOException { byte[] data = structuredFieldReader.getNext(FONT_INDEX_SF); @@ -543,8 +541,6 @@ public abstract class CharacterSetBuilder { byte[] gcgid = new byte[8]; byte[] fiData = new byte[20]; - char lowest = 255; - char highest = 0; String firstABCMismatch = null; // Read data, ignoring bytes 0 - 2 @@ -569,13 +565,15 @@ public abstract class CharacterSetBuilder { char cidx = idx.charAt(0); int width = getUBIN(fiData, 0); + int ascendHt = getSBIN(fiData, 2); + int descendDp = getSBIN(fiData, 4); int a = getSBIN(fiData, 10); int b = getUBIN(fiData, 12); int c = getSBIN(fiData, 14); int abc = a + b + c; int diff = Math.abs(abc - width); if (diff != 0 && width != 0) { - double diffPercent = 100 * diff / (double)width; + double diffPercent = 100 * diff / (double) width; if (diffPercent > 2) { if (LOG.isTraceEnabled()) { LOG.trace(gcgiString + ": " @@ -587,27 +585,16 @@ public abstract class CharacterSetBuilder { } } } - - if (cidx < lowest) { - lowest = cidx; - } - - if (cidx > highest) { - highest = cidx; - } - - int normalizedWidth = (int)Math.round(width * metricNormalizationFactor); - - cso.setWidth(cidx, normalizedWidth); - + int normalizedWidth = normalizer.normalize(width); + int x0 = normalizer.normalize(a); + int y0 = normalizer.normalize(-descendDp); + int dx = normalizer.normalize(b); + int dy = normalizer.normalize(ascendHt + descendDp); + cso.setCharacterMetrics(cidx, normalizedWidth, new Rectangle(x0, y0, dx, dy)); } - } } - cso.setFirstChar(lowest); - cso.setLastChar(highest); - if (LOG.isDebugEnabled() && firstABCMismatch != null) { //Debug level because it usually is no problem. LOG.debug("Font has metrics inconsitencies where A+B+C doesn't equal the" diff --git a/src/java/org/apache/fop/afp/fonts/CharacterSetOrientation.java b/src/java/org/apache/fop/afp/fonts/CharacterSetOrientation.java index a730525d2..5fe524536 100644 --- a/src/java/org/apache/fop/afp/fonts/CharacterSetOrientation.java +++ b/src/java/org/apache/fop/afp/fonts/CharacterSetOrientation.java @@ -19,7 +19,7 @@ package org.apache.fop.afp.fonts; -import java.util.Arrays; +import java.awt.Rectangle; /** * The IBM Font Object Content Architecture (FOCA) supports presentation @@ -60,23 +60,13 @@ public class CharacterSetOrientation { /** * The character widths in the character set (indexed using Unicode codepoints) */ - private int[] charsWidths; + private IntegerKeyStore characterMetrics; /** * The height of lowercase letters */ private int xHeight; - /** - * The first character (Unicode codepoint) - */ - private char firstChar; - - /** - * The last character (Unicode codepoint) - */ - private char lastChar; - /** The character set orientation */ private final int orientation; /** space increment */ @@ -86,6 +76,10 @@ public class CharacterSetOrientation { /** Nominal Character Increment */ private final int nomCharIncrement; + private int underscoreWidth; + + private int underscorePosition; + /** * Constructor for the CharacterSetOrientation, the orientation is * expressed as the degrees rotation (i.e 0, 90, 180, 270) @@ -97,8 +91,7 @@ public class CharacterSetOrientation { this.spaceIncrement = spaceIncrement; this.emSpaceIncrement = emSpaceIncrement; this.nomCharIncrement = nomCharIncrement; - charsWidths = new int[256]; - Arrays.fill(charsWidths, -1); + this.characterMetrics = new IntegerKeyStore(); } /** @@ -138,19 +131,17 @@ public class CharacterSetOrientation { } /** - * The first character in the character set - * @return the first character (Unicode codepoint) + * TODO */ - public char getFirstChar() { - return firstChar; + public int getUnderscoreWidth() { + return underscoreWidth; } /** - * The last character in the character set - * @return the last character (Unicode codepoint) + * TODO */ - public char getLastChar() { - return lastChar; + public int getUnderscorePosition() { + return underscorePosition; } /** @@ -161,17 +152,6 @@ public class CharacterSetOrientation { return orientation; } - /** - * Get the width (in 1/1000ths of a point size) of all characters - * in this character set. - * @return the widths of all characters - */ - public int[] getWidths() { - int[] arr = new int[(getLastChar() - getFirstChar()) + 1]; - System.arraycopy(charsWidths, getFirstChar(), arr, 0, (getLastChar() - getFirstChar()) + 1); - return arr; - } - /** * XHeight refers to the height of the lower case letters above * the baseline. @@ -187,13 +167,38 @@ public class CharacterSetOrientation { * @param character the Unicode character to evaluate * @return the widths of the character */ - public int getWidth(char character) { - if (character >= charsWidths.length) { - throw new IllegalArgumentException("Invalid character: " - + character + " (" + Integer.toString(character) - + "), maximum is " + (charsWidths.length - 1)); + public int getWidth(char character, int size) { + CharacterMetrics cm = getCharacterMetrics(character); + return cm == null ? -1 : size * cm.width; + } + + private CharacterMetrics getCharacterMetrics(char character) { + return characterMetrics.get((int) character); + } + + /** + * Get the character box (rectangle with dimensions in 1/1000ths of a point size) of the character + * identified by the parameter passed. + * @param character the Unicode character to evaluate + * @return the character box + */ + public Rectangle getCharacterBox(char character, int size) { + CharacterMetrics cm = getCharacterMetrics(character); + return scale(cm == null ? getFallbackCharacterBox() : cm.characterBox, size); + } + + private static Rectangle scale(Rectangle rectangle, int size) { + if (rectangle == null) { + return null; + } else { + return new Rectangle((int) (size * rectangle.getX()), (int) (size * rectangle.getY()), + (int) (size * rectangle.getWidth()), (int) (size * rectangle.getHeight())); } - return charsWidths[character]; + } + + private Rectangle getFallbackCharacterBox() { + // TODO replace with something sensible + return new Rectangle(0, 0, 0, 0); } /** @@ -233,19 +238,19 @@ public class CharacterSetOrientation { } /** - * The first character in the character set - * @param firstChar the first character + * TODO + * @param underscoreWidth the underscore width value in millipoints */ - public void setFirstChar(char firstChar) { - this.firstChar = firstChar; + public void setUnderscoreWidth(int underscoreWidth) { + this.underscoreWidth = underscoreWidth; } /** - * The last character in the character set - * @param lastChar the last character + * TODO + * @param underscorePosition the underscore position value in millipoints */ - public void setLastChar(char lastChar) { - this.lastChar = lastChar; + public void setUnderscorePosition(int underscorePosition) { + this.underscorePosition = underscorePosition; } /** @@ -254,17 +259,8 @@ public class CharacterSetOrientation { * @param character the Unicode character for which the width is being set * @param width the widths of the character */ - public void setWidth(char character, int width) { - if (character >= charsWidths.length) { - // Increase the size of the array if necessary - // TODO Can we remove firstChar? surely firstChar==0 at this stage? - int[] arr = new int[(character - firstChar) + 1]; - System.arraycopy(charsWidths, 0, arr, 0, charsWidths.length); - Arrays.fill(arr, charsWidths.length, character - firstChar, -1); - charsWidths = arr; - } - charsWidths[character] = width; - + public void setCharacterMetrics(char character, int width, Rectangle characterBox) { + characterMetrics.put((int) character, new CharacterMetrics(width, characterBox)); } /** @@ -299,4 +295,16 @@ public class CharacterSetOrientation { public int getNominalCharIncrement() { return this.nomCharIncrement; } + + private static class CharacterMetrics { + + public final int width; + + public final Rectangle characterBox; + + public CharacterMetrics(int width, Rectangle characterBox) { + this.width = width; + this.characterBox = characterBox; + } + } } diff --git a/src/java/org/apache/fop/afp/fonts/DoubleByteFont.java b/src/java/org/apache/fop/afp/fonts/DoubleByteFont.java index 78da6ea82..5b9bf6101 100644 --- a/src/java/org/apache/fop/afp/fonts/DoubleByteFont.java +++ b/src/java/org/apache/fop/afp/fonts/DoubleByteFont.java @@ -19,6 +19,7 @@ package org.apache.fop.afp.fonts; +import java.awt.Rectangle; import java.lang.Character.UnicodeBlock; import java.util.HashSet; import java.util.Set; @@ -68,7 +69,7 @@ public class DoubleByteFont extends AbstractOutlineFont { public int getWidth(int character, int size) { int charWidth; try { - charWidth = charSet.getWidth(toUnicodeCodepoint(character)); + charWidth = charSet.getWidth(toUnicodeCodepoint(character), size); } catch (IllegalArgumentException e) { if (!charsProcessed.contains(character)) { charsProcessed.add(character); @@ -80,9 +81,9 @@ public class DoubleByteFont extends AbstractOutlineFont { } if (charWidth == -1) { - charWidth = getDefaultCharacterWidth(character); + charWidth = getDefaultCharacterWidth(character) * size; } - return charWidth * size; + return charWidth; } private int getDefaultCharacterWidth(int character) { @@ -94,6 +95,33 @@ public class DoubleByteFont extends AbstractOutlineFont { } } + @Override + public Rectangle getBoundingBox(int character, int size) { + Rectangle characterBox = getBoundingBoxOrNull(character, size); + if (characterBox == null) { + characterBox = getDefaultCharacterBox(character, size); + } + return characterBox; + } + + private Rectangle getBoundingBoxOrNull(int character, int size) { + Rectangle characterBox = null; + try { + characterBox = charSet.getCharacterBox(toUnicodeCodepoint(character), size); + } catch (IllegalArgumentException e) { + if (!charsProcessed.contains(character)) { + charsProcessed.add(character); + getAFPEventProducer().charactersetMissingMetrics(this, (char) character, + charSet.getName().trim()); + } + } + return characterBox; + } + + private Rectangle getDefaultCharacterBox(int character, int size) { + return getBoundingBoxOrNull('-', size); + } + private int inferCharWidth(int character) { //Is this character an ideograph? diff --git a/src/java/org/apache/fop/afp/fonts/FopCharacterSet.java b/src/java/org/apache/fop/afp/fonts/FopCharacterSet.java index 7c2b68506..b729a8995 100644 --- a/src/java/org/apache/fop/afp/fonts/FopCharacterSet.java +++ b/src/java/org/apache/fop/afp/fonts/FopCharacterSet.java @@ -19,6 +19,8 @@ package org.apache.fop.afp.fonts; +import java.awt.Rectangle; + import org.apache.fop.afp.AFPEventProducer; import org.apache.fop.afp.util.AFPResourceAccessor; import org.apache.fop.fonts.Typeface; @@ -84,45 +86,31 @@ public class FopCharacterSet extends CharacterSet { } /** - * The first character in the character set - * @return the first character + * XHeight refers to the height of the lower case letters above the baseline. + * @return the typical height of characters */ - public char getFirstChar() { - return 0; + public int getXHeight() { + return charSet.getXHeight(1); } - /** - * The last character in the character set - * @return the last character - */ - public char getLastChar() { - return 0; + @Override + public int getWidth(char character, int size) { + return charSet.getWidth(character, size); } - /** - * Get the width (in 1/1000ths of a point size) of all characters - * @return the widths of all characters - */ - public int[] getWidths() { - return charSet.getWidths(); - } + @Override + public Rectangle getCharacterBox(char character, int size) { + return charSet.getBoundingBox(character, size); + }; - /** - * XHeight refers to the height of the lower case letters above the baseline. - * @return the typical height of characters - */ - public int getXHeight() { - return charSet.getXHeight(1); + @Override + public int getUnderscoreWidth() { + return charSet.getUnderlineThickness(1); } - /** - * Get the width (in 1/1000ths of a point size) of the character - * identified by the parameter passed. - * @param character the character from which the width will be calculated - * @return the width of the character - */ - public int getWidth(char character) { - return charSet.getWidth(character, 1); + @Override + public int getUnderscorePosition() { + return charSet.getUnderlinePosition(1); } /** diff --git a/src/java/org/apache/fop/afp/fonts/IntegerKeyStore.java b/src/java/org/apache/fop/afp/fonts/IntegerKeyStore.java new file mode 100644 index 000000000..7e73b5b9f --- /dev/null +++ b/src/java/org/apache/fop/afp/fonts/IntegerKeyStore.java @@ -0,0 +1,71 @@ +/* + * 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.afp.fonts; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + * A simple compact data structure to model a sparse array + */ +class IntegerKeyStore { + + private static final int RANGE_BIT_SIZE = 8; + + private static final int RANGE_SIZE = 1 << RANGE_BIT_SIZE; + + private final Map> arrays = new HashMap>(); + + /** + * + * @param index a positive integer + * @param value value to store + */ + public void put(Integer index, T value) { + if (index < 0) { + throw new IndexOutOfBoundsException(); + } + int rangeKey = index >> RANGE_BIT_SIZE; + int rangeIndex = index % RANGE_SIZE; + ArrayList range = arrays.get(rangeKey); + if (range == null) { + range = new ArrayList(Collections.nCopies(RANGE_SIZE, null)); + arrays.put(rangeKey, range); + } + range.set(rangeIndex, value); + } + + /** + * + * @param index a positive integer + * @return value the value associated with the index or null + */ + public T get(Integer index) { + if (index < 0) { + throw new IndexOutOfBoundsException(); + } + int rangeKey = index >> RANGE_BIT_SIZE; + int rangeIndex = index % RANGE_SIZE; + ArrayList range = arrays.get(rangeKey); + return range == null ? null : range.get(rangeIndex); + } +} diff --git a/src/java/org/apache/fop/afp/fonts/OutlineFont.java b/src/java/org/apache/fop/afp/fonts/OutlineFont.java index e9cdf5ba4..fc2332ce9 100644 --- a/src/java/org/apache/fop/afp/fonts/OutlineFont.java +++ b/src/java/org/apache/fop/afp/fonts/OutlineFont.java @@ -19,6 +19,8 @@ package org.apache.fop.afp.fonts; +import java.awt.Rectangle; + import org.apache.fop.afp.AFPEventProducer; /** @@ -38,4 +40,18 @@ public class OutlineFont extends AbstractOutlineFont { super(name, embeddable, charSet, eventProducer); } + /** + * Obtain the width of the character for the specified point size. + * @param character the character + * @param size the font size (in mpt) + * @return the width of the character for the specified point size + */ + public int getWidth(int character, int size) { + return charSet.getWidth(toUnicodeCodepoint(character), size); + } + + @Override + public Rectangle getBoundingBox(int character, int size) { + return charSet.getCharacterBox(toUnicodeCodepoint(character), size); + } } diff --git a/src/java/org/apache/fop/afp/fonts/RasterFont.java b/src/java/org/apache/fop/afp/fonts/RasterFont.java index 5c4c38dc5..1fd30e0ba 100644 --- a/src/java/org/apache/fop/afp/fonts/RasterFont.java +++ b/src/java/org/apache/fop/afp/fonts/RasterFont.java @@ -19,8 +19,8 @@ package org.apache.fop.afp.fonts; +import java.awt.Rectangle; import java.util.HashMap; -import java.util.Iterator; import java.util.Map; import java.util.SortedMap; import java.util.TreeMap; @@ -135,46 +135,21 @@ public class RasterFont extends AFPFont { } - /** - * Get the first character in this font. - * @return the first character in this font. - */ - public int getFirstChar() { - Iterator it = charSets.values().iterator(); - if (it.hasNext()) { - CharacterSet csm = it.next(); - return csm.getFirstChar(); - } else { - String msg = "getFirstChar() - No character set found for font:" + getFontName(); - LOG.error(msg); - throw new FontRuntimeException(msg); - } - } - - /** - * Get the last character in this font. - * @return the last character in this font. - */ - public int getLastChar() { - - Iterator it = charSets.values().iterator(); - if (it.hasNext()) { - CharacterSet csm = it.next(); - return csm.getLastChar(); + private int metricsToAbsoluteSize(CharacterSet cs, int value, int givenSize) { + int nominalVerticalSize = cs.getNominalVerticalSize(); + if (nominalVerticalSize != 0) { + return value * nominalVerticalSize; } else { - String msg = "getLastChar() - No character set found for font:" + getFontName(); - LOG.error(msg); - throw new FontRuntimeException(msg); + return value * givenSize; } - } - private int metricsToAbsoluteSize(CharacterSet cs, int value, int givenSize) { + private int metricsToAbsoluteSize(CharacterSet cs, double value, int givenSize) { int nominalVerticalSize = cs.getNominalVerticalSize(); if (nominalVerticalSize != 0) { - return value * nominalVerticalSize; + return (int) (value * nominalVerticalSize); } else { - return value * givenSize; + return (int) (value * givenSize); } } @@ -191,6 +166,20 @@ public class RasterFont extends AFPFont { return metricsToAbsoluteSize(cs, cs.getAscender(), size); } + /** {@inheritDoc} */ + public int getUnderlinePosition(int size) { + CharacterSet cs = getCharacterSet(size); + return metricsToAbsoluteSize(cs, cs.getUnderscorePosition(), size); + } + + @Override + public int getUnderlineThickness(int size) { + CharacterSet cs = getCharacterSet(size); + int underscoreWidth = cs.getUnderscoreWidth(); + return underscoreWidth == 0 ? super.getUnderlineThickness(size) + : metricsToAbsoluteSize(cs, underscoreWidth, size); + } + /** * Obtains the height of capital letters for the specified point size. * @@ -234,33 +223,20 @@ public class RasterFont extends AFPFont { */ public int getWidth(int character, int size) { CharacterSet cs = getCharacterSet(size); - return metricsToAbsoluteSize(cs, cs.getWidth(toUnicodeCodepoint(character)), size); + return metricsToAbsoluteSize(cs, cs.getWidth(toUnicodeCodepoint(character), 1), size); } /** - * Get the getWidth (in 1/1000ths of a point size) of all characters in this - * character set. - * - * @param size the font size (in mpt) - * @return the widths of all characters + * TODO */ - public int[] getWidths(int size) { + public Rectangle getBoundingBox(int character, int size) { CharacterSet cs = getCharacterSet(size); - int[] widths = cs.getWidths(); - for (int i = 0, c = widths.length; i < c; i++) { - widths[i] = metricsToAbsoluteSize(cs, widths[i], size); - } - return widths; - } - - /** - * Get the getWidth (in 1/1000ths of a point size) of all characters in this - * character set. - * - * @return the widths of all characters - */ - public int[] getWidths() { - return getWidths(1000); + Rectangle characterBox = cs.getCharacterBox(toUnicodeCodepoint(character), 1); + int x = metricsToAbsoluteSize(cs, characterBox.getX(), size); + int y = metricsToAbsoluteSize(cs, characterBox.getY(), size); + int w = metricsToAbsoluteSize(cs, characterBox.getWidth(), size); + int h = metricsToAbsoluteSize(cs, characterBox.getHeight(), size); + return new Rectangle(x, y, w, h); } /** {@inheritDoc} */ diff --git a/src/java/org/apache/fop/afp/goca/GraphicsCharacterString.java b/src/java/org/apache/fop/afp/goca/GraphicsCharacterString.java index 77ad7e806..d41adf867 100644 --- a/src/java/org/apache/fop/afp/goca/GraphicsCharacterString.java +++ b/src/java/org/apache/fop/afp/goca/GraphicsCharacterString.java @@ -67,7 +67,11 @@ public class GraphicsCharacterString extends AbstractGraphicsCoord { /** {@inheritDoc} */ public int getDataLength() { - return super.getDataLength() + str.length(); + try { + return super.getDataLength() + getStringAsBytes().length; + } catch (IOException ioe) { + throw new RuntimeException(ioe); + } } /** {@inheritDoc} */ diff --git a/src/java/org/apache/fop/afp/svg/AFPBridgeContext.java b/src/java/org/apache/fop/afp/svg/AFPBridgeContext.java index 2c6668454..5c13b9d92 100644 --- a/src/java/org/apache/fop/afp/svg/AFPBridgeContext.java +++ b/src/java/org/apache/fop/afp/svg/AFPBridgeContext.java @@ -25,13 +25,17 @@ import org.apache.batik.bridge.BridgeContext; import org.apache.batik.bridge.DocumentLoader; import org.apache.batik.bridge.UserAgent; import org.apache.batik.gvt.TextPainter; +import org.apache.batik.gvt.font.DefaultFontFamilyResolver; +import org.apache.batik.gvt.font.FontFamilyResolver; import org.apache.xmlgraphics.image.loader.ImageManager; import org.apache.xmlgraphics.image.loader.ImageSessionContext; import org.apache.fop.afp.AFPGraphics2D; +import org.apache.fop.events.EventBroadcaster; import org.apache.fop.fonts.FontInfo; import org.apache.fop.svg.AbstractFOPBridgeContext; +import org.apache.fop.svg.font.AggregatingFontFamilyResolver; /** * An AFP specific implementation of a Batik BridgeContext @@ -40,6 +44,8 @@ public class AFPBridgeContext extends AbstractFOPBridgeContext { private final AFPGraphics2D g2d; + private final EventBroadcaster eventBroadCaster; + /** * Constructs a new bridge context. * @@ -54,47 +60,35 @@ public class AFPBridgeContext extends AbstractFOPBridgeContext { */ public AFPBridgeContext(UserAgent userAgent, FontInfo fontInfo, ImageManager imageManager, ImageSessionContext imageSessionContext, - AffineTransform linkTransform, AFPGraphics2D g2d) { + AffineTransform linkTransform, AFPGraphics2D g2d, EventBroadcaster eventBroadCaster) { super(userAgent, fontInfo, imageManager, imageSessionContext, linkTransform); this.g2d = g2d; + this.eventBroadCaster = eventBroadCaster; } - /** - * Constructs a new bridge context. - * @param userAgent the user agent - * @param documentLoader the Document Loader to use for referenced documents. - * @param fontInfo the font list for the text painter, may be null - * in which case text is painted as shapes - * @param imageManager an image manager - * @param imageSessionContext an image session context - * @param linkTransform AffineTransform to properly place links, - * may be null - * @param g2d an AFPGraphics 2D implementation - */ - public AFPBridgeContext(UserAgent userAgent, DocumentLoader documentLoader, + private AFPBridgeContext(UserAgent userAgent, DocumentLoader documentLoader, FontInfo fontInfo, ImageManager imageManager, ImageSessionContext imageSessionContext, - AffineTransform linkTransform, AFPGraphics2D g2d) { - super(userAgent, documentLoader, fontInfo, imageManager, - imageSessionContext, linkTransform); + AffineTransform linkTransform, AFPGraphics2D g2d, EventBroadcaster eventBroadCaster) { + super(userAgent, documentLoader, fontInfo, imageManager, imageSessionContext, linkTransform); this.g2d = g2d; + this.eventBroadCaster = eventBroadCaster; } /** {@inheritDoc} */ @Override public void registerSVGBridges() { super.registerSVGBridges(); - if (fontInfo != null) { AFPTextHandler textHandler = new AFPTextHandler(fontInfo, g2d.getResourceManager()); g2d.setCustomTextHandler(textHandler); - - TextPainter textPainter = new AFPTextPainter(textHandler); - setTextPainter(textPainter); - + //TODO + FontFamilyResolver fontFamilyResolver = new AggregatingFontFamilyResolver( + new AFPFontFamilyResolver(fontInfo, eventBroadCaster), DefaultFontFamilyResolver.SINGLETON); + TextPainter textPainter = new AFPTextPainter(textHandler, fontFamilyResolver); + setTextPainter(new AFPTextPainter(textHandler, fontFamilyResolver)); putBridge(new AFPTextElementBridge(textPainter)); } - putBridge(new AFPImageElementBridge()); } @@ -105,7 +99,7 @@ public class AFPBridgeContext extends AbstractFOPBridgeContext { fontInfo, getImageManager(), getImageSessionContext(), - linkTransform, g2d); + linkTransform, g2d, eventBroadCaster); } } diff --git a/src/java/org/apache/fop/afp/svg/AFPFontFamilyResolver.java b/src/java/org/apache/fop/afp/svg/AFPFontFamilyResolver.java new file mode 100644 index 000000000..27026f4f3 --- /dev/null +++ b/src/java/org/apache/fop/afp/svg/AFPFontFamilyResolver.java @@ -0,0 +1,84 @@ +/* + * 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.afp.svg; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.fop.afp.AFPEventProducer; +import org.apache.fop.afp.fonts.DoubleByteFont; +import org.apache.fop.events.EventBroadcaster; +import org.apache.fop.fonts.Font; +import org.apache.fop.fonts.FontInfo; +import org.apache.fop.fonts.FontTriplet; +import org.apache.fop.fonts.Typeface; +import org.apache.fop.svg.font.FOPFontFamilyResolverImpl; +import org.apache.fop.svg.font.FOPGVTFontFamily; +import org.apache.fop.svg.font.FilteringFontFamilyResolver; + +public class AFPFontFamilyResolver extends FilteringFontFamilyResolver { + + private final FontInfo fontInfo; + + private final AFPEventProducer eventProducer; + + + public AFPFontFamilyResolver(FontInfo fontInfo, EventBroadcaster eventBroadCaster) { + super(new FOPFontFamilyResolverImpl(fontInfo)); + this.fontInfo = fontInfo; + this.eventProducer = AFPEventProducer.Provider.get(eventBroadCaster); + } + + @Override + public FOPGVTFontFamily resolve(String familyName) { + FOPGVTFontFamily fopGVTFontFamily = super.resolve(familyName); + // TODO why don't DB fonts work with GOCA?!? + if (fopGVTFontFamily != null && fopGVTFontFamily.deriveFont(1, new HashMap()) + .getFont().getFontMetrics() instanceof DoubleByteFont) { + notifyDBFontRejection(fopGVTFontFamily.getFamilyName()); + fopGVTFontFamily = null; + } + return fopGVTFontFamily; + } + + @Override + public FOPGVTFontFamily getFamilyThatCanDisplay(char c) { + Map fonts = fontInfo.getFonts(); + for (Typeface font : fonts.values()) { + // TODO why don't DB fonts work with GOCA?!? + if (font.hasChar(c) && !(font instanceof DoubleByteFont)) { + String fontFamily = font.getFamilyNames().iterator().next(); + if (font instanceof DoubleByteFont) { + notifyDBFontRejection(font.getFontName()); + } else { + return new FOPGVTFontFamily(fontInfo, fontFamily, + new FontTriplet(fontFamily, Font.STYLE_NORMAL, Font.WEIGHT_NORMAL)); + } + + } + } + return null; + } + + private void notifyDBFontRejection(String fontFamily) { + eventProducer.invalidDBFontInSVG(this, fontFamily); + } + +} diff --git a/src/java/org/apache/fop/afp/svg/AFPTextHandler.java b/src/java/org/apache/fop/afp/svg/AFPTextHandler.java index 2bb4cb60e..3e987648e 100644 --- a/src/java/org/apache/fop/afp/svg/AFPTextHandler.java +++ b/src/java/org/apache/fop/afp/svg/AFPTextHandler.java @@ -135,27 +135,18 @@ public class AFPTextHandler extends FOPTextHandlerAdapter { if (log.isDebugEnabled()) { log.debug(" with overriding font: " + internalFontName + ", " + fontSize); } - } else { - java.awt.Font awtFont = g2d.getFont(); - Font fopFont = fontInfo.getFontInstanceForAWTFont(awtFont); - if (log.isDebugEnabled()) { - log.debug(" with font: " + fopFont); - } - internalFontName = fopFont.getFontName(); - fontSize = fopFont.getFontSize(); + fontSize = (int) Math.round(g2d.convertToAbsoluteLength(fontSize)); + fontReference = registerPageFont(pageFonts, internalFontName, fontSize); + // TODO: re-think above registerPageFont code... + AFPFont afpFont = (AFPFont) fontInfo.getFonts().get(internalFontName); + final CharacterSet charSet = afpFont.getCharacterSet(fontSize); + // Work-around for InfoPrint's AFP which loses character set state + // over Graphics Data + // boundaries. + graphicsObj.setCharacterSet(fontReference); + // add the character string + graphicsObj.addString(str, Math.round(x), Math.round(y), charSet); } - fontSize = (int)Math.round( - g2d.convertToAbsoluteLength(fontSize)); - fontReference = registerPageFont(pageFonts, internalFontName, fontSize); - // TODO: re-think above registerPageFont code... - AFPFont afpFont = (AFPFont) fontInfo.getFonts().get(internalFontName); - final CharacterSet charSet = afpFont.getCharacterSet(fontSize); - // Work-around for InfoPrint's AFP which loses character set state - // over Graphics Data - // boundaries. - graphicsObj.setCharacterSet(fontReference); - // add the character string - graphicsObj.addString(str, Math.round(x), Math.round(y), charSet); } else { //Inside Batik's SVG filter operations, you won't get an AFPGraphics2D g.drawString(str, x, y); diff --git a/src/java/org/apache/fop/afp/svg/AFPTextPainter.java b/src/java/org/apache/fop/afp/svg/AFPTextPainter.java index 3815c9eae..996ae8691 100644 --- a/src/java/org/apache/fop/afp/svg/AFPTextPainter.java +++ b/src/java/org/apache/fop/afp/svg/AFPTextPainter.java @@ -21,6 +21,9 @@ package org.apache.fop.afp.svg; import java.awt.Graphics2D; +import org.apache.batik.gvt.font.FontFamilyResolver; +import org.apache.batik.gvt.renderer.StrokingTextPainter; + import org.apache.fop.afp.AFPGraphics2D; import org.apache.fop.svg.AbstractFOPTextPainter; import org.apache.fop.svg.FOPTextHandler; @@ -39,8 +42,8 @@ public class AFPTextPainter extends AbstractFOPTextPainter { * Create a new text painter with the given font information. * @param nativeTextHandler the NativeTextHandler instance used for text painting */ - public AFPTextPainter(FOPTextHandler nativeTextHandler) { - super(nativeTextHandler); + public AFPTextPainter(FOPTextHandler nativeTextHandler, FontFamilyResolver fopFontFamilyResolver) { + super(nativeTextHandler, new FOPStrokingTextPainter(fopFontFamilyResolver)); } /** {@inheritDoc} */ @@ -48,4 +51,18 @@ public class AFPTextPainter extends AbstractFOPTextPainter { return g2d instanceof AFPGraphics2D; } + private static class FOPStrokingTextPainter extends StrokingTextPainter { + + private final FontFamilyResolver fopFontFontFamily; + + FOPStrokingTextPainter(FontFamilyResolver fopFontFontFamily) { + this.fopFontFontFamily = fopFontFontFamily; + } + + @Override + protected FontFamilyResolver getFontFamilyResolver() { + return fopFontFontFamily; + } + } + } diff --git a/src/java/org/apache/fop/fo/FOText.java b/src/java/org/apache/fop/fo/FOText.java index 2fc998c63..0b7dde212 100644 --- a/src/java/org/apache/fop/fo/FOText.java +++ b/src/java/org/apache/fop/fo/FOText.java @@ -21,7 +21,6 @@ package org.apache.fop.fo; import java.awt.Color; import java.nio.CharBuffer; -import java.util.Map; import java.util.NoSuchElementException; import java.util.Stack; @@ -38,12 +37,13 @@ import org.apache.fop.fo.properties.CommonTextDecoration; import org.apache.fop.fo.properties.KeepProperty; import org.apache.fop.fo.properties.Property; import org.apache.fop.fo.properties.SpaceProperty; +import org.apache.fop.fonts.TextFragment; import org.apache.fop.util.CharUtilities; /** * A text node (PCDATA) in the formatting object tree. */ -public class FOText extends FONode implements CharSequence { +public class FOText extends FONode implements CharSequence, TextFragment { /** the CharBuffer containing the text */ private CharBuffer charBuffer; @@ -93,9 +93,6 @@ public class FOText extends FONode implements CharSequence { /* bidi levels */ private int[] bidiLevels; - /* advanced script processing state */ - private Map/**/ mappings; - private static final int IS_WORD_CHAR_FALSE = 0; private static final int IS_WORD_CHAR_TRUE = 1; private static final int IS_WORD_CHAR_MAYBE = 2; @@ -804,93 +801,6 @@ public class FOText extends FONode implements CharSequence { } } - /** - * Add characters mapped by script substitution processing. - * @param start index in character buffer - * @param end index in character buffer - * @param mappedChars sequence of character codes denoting substituted characters - */ - public void addMapping(int start, int end, CharSequence mappedChars) { - if (mappings == null) { - mappings = new java.util.HashMap(); - } - mappings.put(new MapRange(start, end), mappedChars.toString()); - } - - /** - * Determine if characters over specific interval have a mapping. - * @param start index in character buffer - * @param end index in character buffer - * @return true if a mapping exist such that the mapping's interval is coincident to - * [start,end) - */ - public boolean hasMapping(int start, int end) { - return (mappings != null) && (mappings.containsKey(new MapRange(start, end))); - } - - /** - * Obtain mapping of characters over specific interval. - * @param start index in character buffer - * @param end index in character buffer - * @return a string of characters representing the mapping over the interval - * [start,end) - */ - public String getMapping(int start, int end) { - if (mappings != null) { - return (String) mappings.get(new MapRange(start, end)); - } else { - return null; - } - } - - /** - * Obtain length of mapping of characters over specific interval. - * @param start index in character buffer - * @param end index in character buffer - * @return the length of the mapping (if present) or zero - */ - public int getMappingLength(int start, int end) { - if (mappings != null) { - return ((String) mappings.get(new MapRange(start, end))) .length(); - } else { - return 0; - } - } - - /** - * Obtain bidirectional levels of mapping of characters over specific interval. - * @param start index in character buffer - * @param end index in character buffer - * @return a (possibly empty) array of bidi levels or null - * in case no bidi levels have been assigned - */ - public int[] getMappingBidiLevels(int start, int end) { - if (hasMapping(start, end)) { - int nc = end - start; - int nm = getMappingLength(start, end); - int[] la = getBidiLevels(start, end); - if (la == null) { - return null; - } else if (nm == nc) { // mapping is same length as mapped range - return la; - } else if (nm > nc) { // mapping is longer than mapped range - int[] ma = new int [ nm ]; - System.arraycopy(la, 0, ma, 0, la.length); - for (int i = la.length, - n = ma.length, l = (i > 0) ? la [ i - 1 ] : 0; i < n; i++) { - ma [ i ] = l; - } - return ma; - } else { // mapping is shorter than mapped range - int[] ma = new int [ nm ]; - System.arraycopy(la, 0, ma, 0, ma.length); - return ma; - } - } else { - return getBidiLevels(start, end); - } - } - @Override protected Stack collectDelimitedTextRanges(Stack ranges, DelimitedTextRange currentRange) { if (currentRange != null) { diff --git a/src/java/org/apache/fop/fonts/Base14Font.java b/src/java/org/apache/fop/fonts/Base14Font.java index 9b2e95bc7..fdefd0cdd 100644 --- a/src/java/org/apache/fop/fonts/Base14Font.java +++ b/src/java/org/apache/fop/fonts/Base14Font.java @@ -25,4 +25,15 @@ package org.apache.fop.fonts; */ public abstract class Base14Font extends Typeface { + /** Thickness for underline and strikeout. */ + private static final int LINE_THICKNESS = 50; + + public int getStrikeoutPosition(int size) { + return getXHeight(size) / 2; + } + + public int getStrikeoutThickness(int size) { + return size * LINE_THICKNESS; + } + } diff --git a/src/java/org/apache/fop/fonts/CIDFont.java b/src/java/org/apache/fop/fonts/CIDFont.java index dc398263e..26212ea56 100644 --- a/src/java/org/apache/fop/fonts/CIDFont.java +++ b/src/java/org/apache/fop/fonts/CIDFont.java @@ -19,6 +19,7 @@ package org.apache.fop.fonts; + import org.apache.fop.apps.io.InternalResourceResolver; //Java diff --git a/src/java/org/apache/fop/fonts/CustomFont.java b/src/java/org/apache/fop/fonts/CustomFont.java index 70961a55c..96b70feea 100644 --- a/src/java/org/apache/fop/fonts/CustomFont.java +++ b/src/java/org/apache/fop/fonts/CustomFont.java @@ -37,6 +37,9 @@ import org.apache.fop.apps.io.InternalResourceResolver; public abstract class CustomFont extends Typeface implements FontDescriptor, MutableFont { + /** Fallback thickness for underline and strikeout when not provided by the font. */ + private static final int DEFAULT_LINE_THICKNESS = 50; + private String fontName; private String fullName; private Set familyNames; @@ -60,6 +63,14 @@ public abstract class CustomFont extends Typeface private int firstChar; private int lastChar = 255; + private int underlinePosition; + + private int underlineThickness; + + private int strikeoutPosition; + + private int strikeoutThickness; + private Map> kerning; private boolean useKerning = true; @@ -507,4 +518,40 @@ public abstract class CustomFont extends Typeface return copy; } + public int getUnderlinePosition(int size) { + return (underlinePosition == 0) + ? getDescender(size) / 2 + : size * underlinePosition; + } + + public void setUnderlinePosition(int underlinePosition) { + this.underlinePosition = underlinePosition; + } + + public int getUnderlineThickness(int size) { + return size * ((underlineThickness == 0) ? DEFAULT_LINE_THICKNESS : underlineThickness); + } + + public void setUnderlineThickness(int underlineThickness) { + this.underlineThickness = underlineThickness; + } + + public int getStrikeoutPosition(int size) { + return (strikeoutPosition == 0) + ? getXHeight(size) / 2 + : size * strikeoutPosition; + } + + public void setStrikeoutPosition(int strikeoutPosition) { + this.strikeoutPosition = strikeoutPosition; + } + + public int getStrikeoutThickness(int size) { + return (strikeoutThickness == 0) ? getUnderlineThickness(size) : size * strikeoutThickness; + } + + public void setStrikeoutThickness(int strikeoutThickness) { + this.strikeoutThickness = strikeoutThickness; + } + } diff --git a/src/java/org/apache/fop/fonts/FontInfo.java b/src/java/org/apache/fop/fonts/FontInfo.java index 472567eec..720531205 100644 --- a/src/java/org/apache/fop/fonts/FontInfo.java +++ b/src/java/org/apache/fop/fonts/FontInfo.java @@ -30,6 +30,7 @@ import java.util.TreeSet; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + /** * The FontInfo holds font information for the layout and rendering of a fo document. * This stores the list of available fonts that are setup by @@ -135,12 +136,10 @@ public class FontInfo { if (oldName != null) { int oldPriority = tripletPriorities.get(triplet).intValue(); if (oldPriority < newPriority) { - logDuplicateFont(triplet, false, oldName, oldPriority, - internalFontKey, newPriority); + logDuplicateFont(triplet, false, oldName, oldPriority, internalFontKey, newPriority); return; } else { - logDuplicateFont(triplet, true, oldName, oldPriority, - internalFontKey, newPriority); + logDuplicateFont(triplet, true, oldName, oldPriority, internalFontKey, newPriority); } } this.triplets.put(triplet, internalFontKey); @@ -157,9 +156,8 @@ public class FontInfo { * @param newKey the new internal font name * @param newPriority the priority of the duplicate font mapping */ - private void logDuplicateFont(FontTriplet triplet, boolean replacing, - String oldKey, int oldPriority, - String newKey, int newPriority) { + private void logDuplicateFont(FontTriplet triplet, boolean replacing, String oldKey, int oldPriority, + String newKey, int newPriority) { if (log.isDebugEnabled()) { log.debug(triplet + (replacing ? ": Replacing " : ": Not replacing ") @@ -198,8 +196,7 @@ public class FontInfo { * default font if not found * @return internal font triplet key */ - private FontTriplet fontLookup(String family, String style, - int weight, boolean substitutable) { + private FontTriplet fontLookup(String family, String style, int weight, boolean substitutable) { if (log.isTraceEnabled()) { log.trace("Font lookup: " + family + " " + style + " " + weight + (substitutable ? " substitutable" : "")); @@ -302,8 +299,7 @@ public class FontInfo { * @return the requested Font instance */ public Font getFontInstance(FontTriplet triplet, int fontSize) { - Map sizes - = getFontInstanceCache().get(triplet); + Map sizes = getFontInstanceCache().get(triplet); if (sizes == null) { sizes = new HashMap(); getFontInstanceCache().put(triplet, sizes); @@ -379,13 +375,11 @@ public class FontInfo { * @param weight font weight * @return the font triplet of the font chosen */ - public FontTriplet fontLookup(String family, String style, - int weight) { + public FontTriplet fontLookup(String family, String style, int weight) { return fontLookup(family, style, weight, true); } - private List fontLookup(String[] families, String style, - int weight, boolean substitutable) { + private List fontLookup(String[] families, String style, int weight, boolean substitutable) { List matchingTriplets = new ArrayList(); FontTriplet triplet = null; for (int i = 0; i < families.length; i++) { @@ -410,8 +404,7 @@ public class FontInfo { * @return the set of font triplets of all supported and chosen font-families * in the specified style and weight. */ - public FontTriplet[] fontLookup(String[] families, String style, - int weight) { + public FontTriplet[] fontLookup(String[] families, String style, int weight) { if (families.length == 0) { throw new IllegalArgumentException("Specify at least one font family"); } @@ -434,8 +427,8 @@ public class FontInfo { sb.append(families[i]); } throw new IllegalStateException( - "fontLookup must return an array with at least one " - + "FontTriplet on the last call. Lookup: " + sb.toString()); + "fontLookup must return an array with at least one " + + "FontTriplet on the last call. Lookup: " + sb.toString()); } FontTriplet[] fontTriplets = new FontTriplet[matchedTriplets.size()]; @@ -469,8 +462,7 @@ public class FontInfo { * @param weight font weight * @return internal key */ - public FontTriplet findAdjustWeight(String family, String style, - int weight) { + public FontTriplet findAdjustWeight(String family, String style, int weight) { FontTriplet key = null; String f = null; int newWeight = weight; @@ -542,8 +534,7 @@ public class FontInfo { * @param weight font weight * @return internal key */ - public static FontTriplet createFontKey(String family, String style, - int weight) { + public static FontTriplet createFontKey(String family, String style, int weight) { return new FontTriplet(family, style, weight); } diff --git a/src/java/org/apache/fop/fonts/FontMetrics.java b/src/java/org/apache/fop/fonts/FontMetrics.java index ff32d7305..159d321f7 100644 --- a/src/java/org/apache/fop/fonts/FontMetrics.java +++ b/src/java/org/apache/fop/fonts/FontMetrics.java @@ -19,6 +19,7 @@ package org.apache.fop.fonts; +import java.awt.Rectangle; import java.util.Map; import java.util.Set; @@ -119,6 +120,15 @@ public interface FontMetrics { */ int[] getWidths(); + /** + * Returns the bounding box of the glyph at the given index, for the given font size. + * + * @param glyphIndex glyph index + * @param size font size + * @return the scaled bounding box scaled in 1/1000ths of the given size + */ + Rectangle getBoundingBox(int glyphIndex, int size); + /** * Indicates if the font has kering information. * @return True, if kerning is available. @@ -131,4 +141,38 @@ public interface FontMetrics { */ Map> getKerningInfo(); + /** + * Returns the distance from the baseline to the center of the underline (negative + * value indicates below baseline). + * + * @param size font size + * @return the position in 1/1000ths of the font size + */ + int getUnderlinePosition(int size); + + /** + * Returns the thickness of the underline. + * + * @param size font size + * @return the thickness in 1/1000ths of the font size + */ + int getUnderlineThickness(int size); + + /** + * Returns the distance from the baseline to the center of the strikeout line + * (negative value indicates below baseline). + * + * @param size font size + * @return the position in 1/1000ths of the font size + */ + int getStrikeoutPosition(int size); + + /** + * Returns the thickness of the strikeout line. + * + * @param size font size + * @return the thickness in 1/1000ths of the font size + */ + int getStrikeoutThickness(int size); + } diff --git a/src/java/org/apache/fop/fonts/GlyphMapping.java b/src/java/org/apache/fop/fonts/GlyphMapping.java new file mode 100644 index 000000000..a8a82085e --- /dev/null +++ b/src/java/org/apache/fop/fonts/GlyphMapping.java @@ -0,0 +1,327 @@ +/* + * 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.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.apache.fop.complexscripts.fonts.GlyphPositioningTable; +import org.apache.fop.complexscripts.util.CharScript; +import org.apache.fop.traits.MinOptMax; +import org.apache.fop.util.CharUtilities; + +/** + * Stores the mapping of a text fragment to glyphs, along with various information. + */ +public class GlyphMapping { + + private static final Log LOG = LogFactory.getLog(GlyphMapping.class); + /** Inclusive. */ + public final int startIndex; + /** Exclusive. */ + public final int endIndex; + private int wordCharLength; + public final int wordSpaceCount; + public int letterSpaceCount; + public MinOptMax areaIPD; + public final boolean isHyphenated; + public final boolean isSpace; + public boolean breakOppAfter; + public final Font font; + public final int level; + public final int[][] gposAdjustments; + public final String mapping; + + public GlyphMapping(int startIndex, int endIndex, int wordSpaceCount, int letterSpaceCount, + MinOptMax areaIPD, boolean isHyphenated, boolean isSpace, boolean breakOppAfter, + Font font, int level, int[][] gposAdjustments) { + this(startIndex, endIndex, wordSpaceCount, letterSpaceCount, areaIPD, isHyphenated, + isSpace, breakOppAfter, font, level, gposAdjustments, null); + } + + public GlyphMapping(int startIndex, int endIndex, int wordSpaceCount, int letterSpaceCount, + MinOptMax areaIPD, boolean isHyphenated, boolean isSpace, boolean breakOppAfter, + Font font, int level, int[][] gposAdjustments, String mapping) { + assert startIndex <= endIndex; + this.startIndex = startIndex; + this.endIndex = endIndex; + this.wordCharLength = -1; + this.wordSpaceCount = wordSpaceCount; + this.letterSpaceCount = letterSpaceCount; + this.areaIPD = areaIPD; + this.isHyphenated = isHyphenated; + this.isSpace = isSpace; + this.breakOppAfter = breakOppAfter; + this.font = font; + this.level = level; + this.gposAdjustments = gposAdjustments; + this.mapping = mapping; + } + + public static GlyphMapping doGlyphMapping(TextFragment text, int startIndex, int endIndex, + Font font, MinOptMax letterSpaceIPD, MinOptMax[] letterSpaceAdjustArray, + char precedingChar, char breakOpportunityChar, final boolean endsWithHyphen, int level) { + GlyphMapping mapping; + if (font.performsSubstitution() || font.performsPositioning()) { + mapping = processWordMapping(text, startIndex, endIndex, font, + breakOpportunityChar, endsWithHyphen, level); + } else { + mapping = processWordNoMapping(text, startIndex, endIndex, font, + letterSpaceIPD, letterSpaceAdjustArray, precedingChar, breakOpportunityChar, endsWithHyphen, + level); + } + return mapping; + } + + private static GlyphMapping processWordMapping(TextFragment text, int startIndex, + int endIndex, final Font font, final char breakOpportunityChar, + final boolean endsWithHyphen, int level) { + int e = endIndex; // end index of word in FOText character buffer + int nLS = 0; // # of letter spaces + String script = text.getScript(); + String language = text.getLanguage(); + + if (LOG.isDebugEnabled()) { + LOG.debug("PW: [" + startIndex + "," + endIndex + "]: {" + + " +M" + + ", level = " + level + + " }"); + } + + // 1. extract unmapped character sequence + CharSequence ics = text.subSequence(startIndex, e); + + // 2. if script is not specified (by FO property) or it is specified as 'auto', + // then compute dominant script + if ((script == null) || "auto".equals(script)) { + script = CharScript.scriptTagFromCode(CharScript.dominantScript(ics)); + } + if ((language == null) || "none".equals(language)) { + language = "dflt"; + } + + // 3. perform mapping of chars to glyphs ... to glyphs ... to chars + CharSequence mcs = font.performSubstitution(ics, script, language); + + // 4. compute glyph position adjustments on (substituted) characters + int[][] gpa; + if (font.performsPositioning()) { + // handle GPOS adjustments + gpa = font.performPositioning(mcs, script, language); + } else if (font.hasKerning()) { + // handle standard (non-GPOS) kerning adjustments + gpa = getKerningAdjustments(mcs, font); + } else { + gpa = null; + } + + // 5. reorder combining marks so that they precede (within the mapped char sequence) the + // base to which they are applied; N.B. position adjustments (gpa) are reordered in place + mcs = font.reorderCombiningMarks(mcs, gpa, script, language); + + // 6. compute word ipd based on final position adjustments + MinOptMax ipd = MinOptMax.ZERO; + for (int i = 0, n = mcs.length(); i < n; i++) { + int c = mcs.charAt(i); + // TODO !BMP + int w = font.getCharWidth(c); + if (w < 0) { + w = 0; + } + if (gpa != null) { + w += gpa[i][GlyphPositioningTable.Value.IDX_X_ADVANCE]; + } + ipd = ipd.plus(w); + } + + // [TBD] - handle letter spacing + + return new GlyphMapping(startIndex, e, 0, nLS, ipd, endsWithHyphen, false, + breakOpportunityChar != 0, font, level, gpa, + CharUtilities.isSameSequence(mcs, ics) ? null : mcs.toString()); + } + + /** + * Given a mapped character sequence MCS, obtain glyph position adjustments from the + * font's kerning data. + * + * @param mcs mapped character sequence + * @param font applicable font + * @return glyph position adjustments (or null if no kerning) + */ + private static int[][] getKerningAdjustments(CharSequence mcs, final Font font) { + int nc = mcs.length(); + // extract kerning array + int[] ka = new int[nc]; // kerning array + for (int i = 0, n = nc, cPrev = -1; i < n; i++) { + int c = mcs.charAt(i); + // TODO !BMP + if (cPrev >= 0) { + ka[i] = font.getKernValue(cPrev, c); + } + cPrev = c; + } + // was there a non-zero kerning? + boolean hasKerning = false; + for (int i = 0, n = nc; i < n; i++) { + if (ka[i] != 0) { + hasKerning = true; + break; + } + } + // if non-zero kerning, then create and return glyph position adjustment array + if (hasKerning) { + int[][] gpa = new int[nc][4]; + for (int i = 0, n = nc; i < n; i++) { + if (i > 0) { + gpa [i - 1][GlyphPositioningTable.Value.IDX_X_ADVANCE] = ka[i]; + } + } + return gpa; + } else { + return null; + } + } + + private static GlyphMapping processWordNoMapping(TextFragment text, int startIndex, int endIndex, + final Font font, MinOptMax letterSpaceIPD, MinOptMax[] letterSpaceAdjustArray, + char precedingChar, final char breakOpportunityChar, final boolean endsWithHyphen, int level) { + boolean kerning = font.hasKerning(); + MinOptMax wordIPD = MinOptMax.ZERO; + + if (LOG.isDebugEnabled()) { + LOG.debug("PW: [" + startIndex + "," + endIndex + "]: {" + + " -M" + + ", level = " + level + + " }"); + } + + for (int i = startIndex; i < endIndex; i++) { + char currentChar = text.charAt(i); + + // character width + int charWidth = font.getCharWidth(currentChar); + wordIPD = wordIPD.plus(charWidth); + + // kerning + if (kerning) { + int kern = 0; + if (i > startIndex) { + char previousChar = text.charAt(i - 1); + kern = font.getKernValue(previousChar, currentChar); + } else if (precedingChar != 0) { + kern = font.getKernValue(precedingChar, currentChar); + } + if (kern != 0) { + addToLetterAdjust(letterSpaceAdjustArray, i, kern); + wordIPD = wordIPD.plus(kern); + } + } + } + if (kerning + && (breakOpportunityChar != 0) + && !isSpace(breakOpportunityChar) + && endIndex > 0 + && endsWithHyphen) { + int kern = font.getKernValue(text.charAt(endIndex - 1), breakOpportunityChar); + if (kern != 0) { + addToLetterAdjust(letterSpaceAdjustArray, endIndex, kern); + // TODO: add kern to wordIPD? + } + } + // shy+chars at start of word: wordLength == 0 && breakOpportunity + // shy only characters in word: wordLength == 0 && !breakOpportunity + int wordLength = endIndex - startIndex; + int letterSpaces = 0; + if (wordLength != 0) { + letterSpaces = wordLength - 1; + // if there is a break opportunity and the next one (break character) + // is not a space, it could be used as a line end; + // add one more letter space, in case other text follows + if ((breakOpportunityChar != 0) && !isSpace(breakOpportunityChar)) { + letterSpaces++; + } + } + assert letterSpaces >= 0; + wordIPD = wordIPD.plus(letterSpaceIPD.mult(letterSpaces)); + + // create and return the AreaInfo object + return new GlyphMapping(startIndex, endIndex, 0, + letterSpaces, wordIPD, + endsWithHyphen, + false, breakOpportunityChar != 0, font, level, null); + } + + private static void addToLetterAdjust(MinOptMax[] letterSpaceAdjustArray, int index, int width) { + if (letterSpaceAdjustArray[index] == null) { + letterSpaceAdjustArray[index] = MinOptMax.getInstance(width); + } else { + letterSpaceAdjustArray[index] = letterSpaceAdjustArray[index].plus(width); + } + } + + /** + * Indicates whether a character is a space in terms of this layout manager. + * + * @param ch the character + * @return true if it's a space + */ + public static boolean isSpace(final char ch) { + return ch == CharUtilities.SPACE + || CharUtilities.isNonBreakableSpace(ch) + || CharUtilities.isFixedWidthSpace(ch); + } + + /** + * Obtain number of 'characters' contained in word. If word is mapped, then this + * number may be less than or greater than the original length (breakIndex - + * startIndex). We compute and memoize thius length upon first invocation of this + * method. + */ + public int getWordLength() { + if (wordCharLength == -1) { + if (mapping != null) { + wordCharLength = mapping.length(); + } else { + assert endIndex >= startIndex; + wordCharLength = endIndex - startIndex; + } + } + return wordCharLength; + } + + public void addToAreaIPD(MinOptMax idp) { + areaIPD = areaIPD.plus(idp); + } + + public String toString() { + return super.toString() + "{" + + "interval = [" + startIndex + "," + endIndex + "]" + + ", isSpace = " + isSpace + + ", level = " + level + + ", areaIPD = " + areaIPD + + ", letterSpaceCount = " + letterSpaceCount + + ", wordSpaceCount = " + wordSpaceCount + + ", isHyphenated = " + isHyphenated + + ", font = " + font + + "}"; + } + +} diff --git a/src/java/org/apache/fop/fonts/LazyFont.java b/src/java/org/apache/fop/fonts/LazyFont.java index 49b2e0457..1877a895e 100644 --- a/src/java/org/apache/fop/fonts/LazyFont.java +++ b/src/java/org/apache/fop/fonts/LazyFont.java @@ -18,6 +18,7 @@ /* $Id$ */ package org.apache.fop.fonts; +import java.awt.Rectangle; import java.io.IOException; import java.io.InputStream; import java.net.URI; @@ -250,6 +251,26 @@ public class LazyFont extends Typeface implements FontDescriptor, Substitutable, return realFont.getXHeight(size); } + public int getUnderlinePosition(int size) { + load(true); + return realFont.getUnderlinePosition(size); + } + + public int getUnderlineThickness(int size) { + load(true); + return realFont.getUnderlineThickness(size); + } + + public int getStrikeoutPosition(int size) { + load(true); + return realFont.getStrikeoutPosition(size); + } + + public int getStrikeoutThickness(int size) { + load(true); + return realFont.getStrikeoutThickness(size); + } + /** * {@inheritDoc} */ @@ -268,6 +289,11 @@ public class LazyFont extends Typeface implements FontDescriptor, Substitutable, return realFont.getWidths(); } + public Rectangle getBoundingBox(int glyphIndex, int size) { + load(true); + return realFont.getBoundingBox(glyphIndex, size); + } + /** * {@inheritDoc} */ diff --git a/src/java/org/apache/fop/fonts/MultiByteFont.java b/src/java/org/apache/fop/fonts/MultiByteFont.java index 83f6491f3..1d1186dcb 100644 --- a/src/java/org/apache/fop/fonts/MultiByteFont.java +++ b/src/java/org/apache/fop/fonts/MultiByteFont.java @@ -19,6 +19,7 @@ package org.apache.fop.fonts; +import java.awt.Rectangle; import java.nio.CharBuffer; import java.nio.IntBuffer; import java.util.BitSet; @@ -67,6 +68,9 @@ public class MultiByteFont extends CIDFont implements Substitutable, Positionabl private int firstUnmapped; private int lastUnmapped; + /** Contains the character bounding boxes for all characters in the font */ + protected Rectangle[] boundingBoxes; + /** * Default constructor */ @@ -170,6 +174,12 @@ public class MultiByteFont extends CIDFont implements Substitutable, Positionabl return arr; } + public Rectangle getBoundingBox(int glyphIndex, int size) { + int index = isEmbeddable() ? cidSet.getOriginalGlyphIndex(glyphIndex) : glyphIndex; + Rectangle bbox = boundingBoxes[index]; + return new Rectangle(bbox.x * size, bbox.y * size, bbox.width * size, bbox.height * size); + } + /** * Returns the glyph index for a Unicode character. The method returns 0 if there's no * such glyph in the character map. @@ -365,6 +375,14 @@ public class MultiByteFont extends CIDFont implements Substitutable, Positionabl this.width = wds; } + /** + * Sets the bounding boxes array. + * @param boundingBoxes array of bounding boxes. + */ + public void setBBoxArray(Rectangle[] boundingBoxes) { + this.boundingBoxes = boundingBoxes; + } + /** * Returns a Map of used Glyphs. * @return Map Map of used Glyphs diff --git a/src/java/org/apache/fop/fonts/SingleByteFont.java b/src/java/org/apache/fop/fonts/SingleByteFont.java index cd11c7849..d412609d5 100644 --- a/src/java/org/apache/fop/fonts/SingleByteFont.java +++ b/src/java/org/apache/fop/fonts/SingleByteFont.java @@ -19,6 +19,7 @@ package org.apache.fop.fonts; +import java.awt.Rectangle; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -47,6 +48,8 @@ public class SingleByteFont extends CustomFont { private int[] width = null; + private Rectangle[] boundingBoxes; + private Map unencodedCharacters; private List additionalEncodings; private Map alternativeCodes; @@ -111,6 +114,24 @@ public class SingleByteFont extends CustomFont { return arr; } + public Rectangle getBoundingBox(int glyphIndex, int size) { + Rectangle bbox = null; + if (glyphIndex < 256) { + int idx = glyphIndex - getFirstChar(); + if (idx >= 0 && idx < boundingBoxes.length) { + bbox = boundingBoxes[idx]; + } + } else if (this.additionalEncodings != null) { + int encodingIndex = (glyphIndex / 256) - 1; + SimpleSingleByteEncoding encoding = getAdditionalEncoding(encodingIndex); + int codePoint = glyphIndex % 256; + NamedCharacter nc = encoding.getCharacterForIndex(codePoint); + UnencodedCharacter uc = this.unencodedCharacters.get(Character.valueOf(nc.getSingleUnicodeValue())); + bbox = uc.getBBox(); + } + return bbox == null ? null : new Rectangle(bbox.x * size, bbox.y * size, bbox.width * size, bbox.height * size); + } + /** * Lookup a character using its alternative names. If found, cache it so we * can speed up lookups. @@ -292,17 +313,24 @@ public class SingleByteFont extends CustomFont { this.width[index - getFirstChar()] = w; } + public void setBoundingBox(int index, Rectangle bbox) { + if (this.boundingBoxes == null) { + this.boundingBoxes = new Rectangle[getLastChar() - getFirstChar() + 1]; + } + this.boundingBoxes[index - getFirstChar()] = bbox; + } + /** * 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) { + public void addUnencodedCharacter(NamedCharacter ch, int width, Rectangle bbox) { if (this.unencodedCharacters == null) { this.unencodedCharacters = new HashMap(); } if (ch.hasSingleUnicodeValue()) { - UnencodedCharacter uc = new UnencodedCharacter(ch, width); + UnencodedCharacter uc = new UnencodedCharacter(ch, width, bbox); this.unencodedCharacters.put(Character.valueOf(ch.getSingleUnicodeValue()), uc); } else { //Cannot deal with unicode sequences, so ignore this character @@ -381,10 +409,12 @@ public class SingleByteFont extends CustomFont { private final NamedCharacter character; private final int width; + private final Rectangle bbox; - public UnencodedCharacter(NamedCharacter character, int width) { + public UnencodedCharacter(NamedCharacter character, int width, Rectangle bbox) { this.character = character; this.width = width; + this.bbox = bbox; } public NamedCharacter getCharacter() { @@ -395,6 +425,10 @@ public class SingleByteFont extends CustomFont { return this.width; } + public Rectangle getBBox() { + return bbox; + } + /** {@inheritDoc} */ @Override public String toString() { diff --git a/src/java/org/apache/fop/fonts/TextFragment.java b/src/java/org/apache/fop/fonts/TextFragment.java new file mode 100644 index 000000000..ad72db8e0 --- /dev/null +++ b/src/java/org/apache/fop/fonts/TextFragment.java @@ -0,0 +1,31 @@ +/* + * 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; + +public interface TextFragment { + + String getScript(); + + String getLanguage(); + + char charAt(int index); + + CharSequence subSequence(int startIndex, int endIndex); +} diff --git a/src/java/org/apache/fop/fonts/truetype/TTFFile.java b/src/java/org/apache/fop/fonts/truetype/TTFFile.java index 2e78ef7f0..62686bbd4 100644 --- a/src/java/org/apache/fop/fonts/truetype/TTFFile.java +++ b/src/java/org/apache/fop/fonts/truetype/TTFFile.java @@ -19,6 +19,7 @@ package org.apache.fop.fonts.truetype; +import java.awt.Rectangle; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; @@ -199,6 +200,8 @@ public class TTFFile { private int os2CapHeight = 0; private int underlinePosition = 0; private int underlineThickness = 0; + private int strikeoutPosition; + private int strikeoutThickness; private int xHeight = 0; private int os2xHeight = 0; //Effective ascender/descender @@ -976,10 +979,22 @@ public class TTFFile { for (int i = 0; i < wx.length; i++) { wx[i] = convertTTFUnit2PDFUnit(mtxTab[i].getWx()); } - return wx; } + public Rectangle[] getBoundingBoxes() { + Rectangle[] boundingBoxes = new Rectangle[mtxTab.length]; + for (int i = 0; i < boundingBoxes.length; i++) { + int[] boundingBox = mtxTab[i].getBoundingBox(); + boundingBoxes[i] = new Rectangle( + convertTTFUnit2PDFUnit(boundingBox[0]), + convertTTFUnit2PDFUnit(boundingBox[1]), + convertTTFUnit2PDFUnit(boundingBox[2] - boundingBox[0]), + convertTTFUnit2PDFUnit(boundingBox[3] - boundingBox[1])); + } + return boundingBoxes; + } + /** * Returns an array (xMin, yMin, xMax, yMax) for a glyph. * @@ -1020,6 +1035,22 @@ public class TTFFile { return ansiKerningTab; } + public int getUnderlinePosition() { + return convertTTFUnit2PDFUnit(underlinePosition); + } + + public int getUnderlineThickness() { + return convertTTFUnit2PDFUnit(underlineThickness); + } + + public int getStrikeoutPosition() { + return convertTTFUnit2PDFUnit(strikeoutPosition); + } + + public int getStrikeoutThickness() { + return convertTTFUnit2PDFUnit(strikeoutThickness); + } + /** * Indicates if the font may be embedded. * @return boolean True if it may be embedded @@ -1301,7 +1332,10 @@ public class TTFFile { } else { isEmbeddable = true; } - fontFile.skip(11 * 2); + fontFile.skip(8 * 2); + strikeoutThickness = fontFile.readTTFShort(); + strikeoutPosition = fontFile.readTTFShort(); + fontFile.skip(2); fontFile.skip(10); //panose array fontFile.skip(4 * 4); //unicode ranges fontFile.skip(4); diff --git a/src/java/org/apache/fop/fonts/truetype/TTFFontLoader.java b/src/java/org/apache/fop/fonts/truetype/TTFFontLoader.java index b7adbd4c9..98f81ab3e 100644 --- a/src/java/org/apache/fop/fonts/truetype/TTFFontLoader.java +++ b/src/java/org/apache/fop/fonts/truetype/TTFFontLoader.java @@ -19,6 +19,7 @@ package org.apache.fop.fonts.truetype; +import java.awt.Rectangle; import java.io.IOException; import java.io.InputStream; import java.net.URI; @@ -144,6 +145,10 @@ public class TTFFontLoader extends FontLoader { returnFont.setAscender(ttf.getLowerCaseAscent()); returnFont.setDescender(ttf.getLowerCaseDescent()); returnFont.setFontBBox(ttf.getFontBBox()); + returnFont.setUnderlinePosition(ttf.getUnderlinePosition() - ttf.getUnderlineThickness() / 2); + returnFont.setUnderlineThickness(ttf.getUnderlineThickness()); + returnFont.setStrikeoutPosition(ttf.getStrikeoutPosition() - ttf.getStrikeoutThickness() / 2); + returnFont.setStrikeoutThickness(ttf.getStrikeoutThickness()); returnFont.setFlags(ttf.getFlags()); returnFont.setStemV(Integer.parseInt(ttf.getStemV())); //not used for TTF returnFont.setItalicAngle(Integer.parseInt(ttf.getItalicAngle())); @@ -152,15 +157,15 @@ public class TTFFontLoader extends FontLoader { returnFont.setEmbeddingMode(this.embeddingMode); if (isCid) { multiFont.setCIDType(CIDFontType.CIDTYPE2); - int[] wx = ttf.getWidths(); - multiFont.setWidthArray(wx); + multiFont.setWidthArray(ttf.getWidths()); + multiFont.setBBoxArray(ttf.getBoundingBoxes()); } else { singleFont.setFontType(FontType.TRUETYPE); singleFont.setEncoding(ttf.getCharSetName()); returnFont.setFirstChar(ttf.getFirstChar()); returnFont.setLastChar(ttf.getLastChar()); singleFont.setTrueTypePostScriptVersion(ttf.getPostScriptVersion()); - copyWidthsSingleByte(ttf); + copyGlyphMetricsSingleByte(ttf); } returnFont.setCMap(getCMap(ttf)); @@ -186,12 +191,15 @@ public class TTFFontLoader extends FontLoader { return ttf.getCMaps().toArray(array); } - private void copyWidthsSingleByte(TTFFile ttf) { + private void copyGlyphMetricsSingleByte(TTFFile ttf) { int[] wx = ttf.getWidths(); + Rectangle[] bboxes = ttf.getBoundingBoxes(); for (int i = singleFont.getFirstChar(); i <= singleFont.getLastChar(); i++) { singleFont.setWidth(i, ttf.getCharWidth(i)); + int[] bbox = ttf.getBBox(i); + singleFont.setBoundingBox(i, + new Rectangle(bbox[0], bbox[1], bbox[2] - bbox[0], bbox[3] - bbox[1])); } - for (CMapSegment segment : ttf.getCMaps()) { if (segment.getUnicodeStart() < 0xFFFE) { for (char u = (char)segment.getUnicodeStart(); u <= segment.getUnicodeEnd(); u++) { @@ -205,7 +213,7 @@ public class TTFFontLoader extends FontLoader { if (glyphName.length() > 0) { String unicode = Character.toString(u); NamedCharacter nc = new NamedCharacter(glyphName, unicode); - singleFont.addUnencodedCharacter(nc, wx[glyphIndex]); + singleFont.addUnencodedCharacter(nc, wx[glyphIndex], bboxes[glyphIndex]); } } } diff --git a/src/java/org/apache/fop/fonts/type1/AFMCharMetrics.java b/src/java/org/apache/fop/fonts/type1/AFMCharMetrics.java index 4906f2c31..8d9f4238b 100644 --- a/src/java/org/apache/fop/fonts/type1/AFMCharMetrics.java +++ b/src/java/org/apache/fop/fonts/type1/AFMCharMetrics.java @@ -19,7 +19,7 @@ package org.apache.fop.fonts.type1; -import java.awt.geom.RectangularShape; +import java.awt.Rectangle; import org.apache.fop.fonts.NamedCharacter; @@ -33,7 +33,7 @@ public class AFMCharMetrics { private NamedCharacter character; private double widthX; private double widthY; - private RectangularShape bBox; + private Rectangle bBox; /** * Returns the character code. @@ -137,7 +137,7 @@ public class AFMCharMetrics { * Returns the character's bounding box. * @return the bounding box (or null if it isn't available) */ - public RectangularShape getBBox() { + public Rectangle getBBox() { return bBox; } @@ -145,7 +145,7 @@ public class AFMCharMetrics { * Sets the character's bounding box. * @param box the bounding box */ - public void setBBox(RectangularShape box) { + public void setBBox(Rectangle box) { bBox = box; } diff --git a/src/java/org/apache/fop/fonts/type1/Type1FontLoader.java b/src/java/org/apache/fop/fonts/type1/Type1FontLoader.java index 853e23eb5..7922ff6fd 100644 --- a/src/java/org/apache/fop/fonts/type1/Type1FontLoader.java +++ b/src/java/org/apache/fop/fonts/type1/Type1FontLoader.java @@ -203,12 +203,16 @@ public class Type1FontLoader extends FontLoader { for (AFMCharMetrics metrics : charMetrics) { String charName = metrics.getCharName(); if (charName != null && !glyphNames.contains(charName)) { - singleFont.addUnencodedCharacter(metrics.getCharacter(), - (int)Math.round(metrics.getWidthX())); + addUnencodedCharacter(singleFont, metrics); } } } + private static void addUnencodedCharacter(SingleByteFont font, AFMCharMetrics metrics) { + font.addUnencodedCharacter(metrics.getCharacter(), + (int) Math.round(metrics.getWidthX()), metrics.getBBox()); + } + /** * Adds characters not encoded in the font's primary encoding. This method is used when * the primary encoding is built based on the character codes in the AFM rather than @@ -220,8 +224,7 @@ public class Type1FontLoader extends FontLoader { 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())); + addUnencodedCharacter(singleFont, metrics); } } } @@ -267,7 +270,10 @@ public class Type1FontLoader extends FontLoader { } else { returnFont.setStemV(80); // Arbitrary value } - returnFont.setItalicAngle((int) afm.getWritingDirectionMetrics(0).getItalicAngle()); + AFMWritingDirectionMetrics metrics = afm.getWritingDirectionMetrics(0); + returnFont.setItalicAngle((int) metrics.getItalicAngle()); + returnFont.setUnderlinePosition(metrics.getUnderlinePosition().intValue()); + returnFont.setUnderlineThickness(metrics.getUnderlineThickness().intValue()); } else { returnFont.setFontBBox(pfm.getFontBBox()); returnFont.setStemV(pfm.getStemV()); @@ -369,6 +375,7 @@ public class Type1FontLoader extends FontLoader { for (AFMCharMetrics chm : afm.getCharMetrics()) { if (chm.hasCharCode()) { singleFont.setWidth(chm.getCharCode(), (int) Math.round(chm.getWidthX())); + singleFont.setBoundingBox(chm.getCharCode(), chm.getBBox()); } } if (useKerning) { diff --git a/src/java/org/apache/fop/image/loader/batik/ImageConverterSVG2G2D.java b/src/java/org/apache/fop/image/loader/batik/ImageConverterSVG2G2D.java index 79592efd1..ec187b8d0 100644 --- a/src/java/org/apache/fop/image/loader/batik/ImageConverterSVG2G2D.java +++ b/src/java/org/apache/fop/image/loader/batik/ImageConverterSVG2G2D.java @@ -33,6 +33,7 @@ import org.apache.batik.bridge.GVTBuilder; import org.apache.batik.bridge.UserAgent; import org.apache.batik.dom.svg.SVGDOMImplementation; import org.apache.batik.gvt.GraphicsNode; +import org.apache.batik.gvt.font.DefaultFontFamilyResolver; import org.apache.xmlgraphics.image.GraphicsConstants; import org.apache.xmlgraphics.image.loader.Image; @@ -123,10 +124,8 @@ public class ImageConverterSVG2G2D extends AbstractImageConverter { * @return the newly created user agent */ protected SimpleSVGUserAgent createBatikUserAgent(float pxToMillimeter) { - return new SimpleSVGUserAgent( - pxToMillimeter, - new AffineTransform()) { - + return new SimpleSVGUserAgent(pxToMillimeter, new AffineTransform(), + DefaultFontFamilyResolver.SINGLETON) { /** {@inheritDoc} */ public void displayMessage(String message) { //TODO Refine and pipe through to caller diff --git a/src/java/org/apache/fop/image/loader/batik/PreloaderSVG.java b/src/java/org/apache/fop/image/loader/batik/PreloaderSVG.java index 3aa340a4a..58daadc52 100644 --- a/src/java/org/apache/fop/image/loader/batik/PreloaderSVG.java +++ b/src/java/org/apache/fop/image/loader/batik/PreloaderSVG.java @@ -38,6 +38,7 @@ import org.apache.batik.bridge.UnitProcessor; import org.apache.batik.bridge.UserAgent; import org.apache.batik.dom.svg.SAXSVGDocumentFactory; import org.apache.batik.dom.svg.SVGOMDocument; +import org.apache.batik.gvt.font.DefaultFontFamilyResolver; import org.apache.xmlgraphics.image.loader.ImageContext; import org.apache.xmlgraphics.image.loader.ImageInfo; @@ -162,7 +163,7 @@ public class PreloaderSVG extends AbstractImagePreloader { Element e = doc.getRootElement(); float pxUnitToMillimeter = UnitConv.IN2MM / context.getSourceResolution(); UserAgent userAg = new SimpleSVGUserAgent(pxUnitToMillimeter, - new AffineTransform()) { + new AffineTransform(), DefaultFontFamilyResolver.SINGLETON) { /** {@inheritDoc} */ public void displayMessage(String message) { diff --git a/src/java/org/apache/fop/layoutmgr/inline/TextLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/TextLayoutManager.java index 53f51cd32..625b43ee5 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/TextLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/TextLayoutManager.java @@ -30,12 +30,11 @@ import org.apache.commons.logging.LogFactory; import org.apache.fop.area.Trait; import org.apache.fop.area.inline.TextArea; -import org.apache.fop.complexscripts.fonts.GlyphPositioningTable; -import org.apache.fop.complexscripts.util.CharScript; import org.apache.fop.fo.Constants; import org.apache.fop.fo.FOText; import org.apache.fop.fonts.Font; import org.apache.fop.fonts.FontSelector; +import org.apache.fop.fonts.GlyphMapping; import org.apache.fop.layoutmgr.InlineKnuthSequence; import org.apache.fop.layoutmgr.KnuthBox; import org.apache.fop.layoutmgr.KnuthElement; @@ -64,92 +63,16 @@ public class TextLayoutManager extends LeafNodeLayoutManager { // static final int SOFT_HYPHEN_PENALTY = KnuthPenalty.FLAGGED_PENALTY / 10; private static final int SOFT_HYPHEN_PENALTY = 1; - /** - * Store information about each potential text area. - * Index of character which ends the area, IPD of area, including - * any word-space and letter-space. - * Number of word-spaces? - */ - private class AreaInfo { - - private final int startIndex; - private final int breakIndex; - private int wordCharLength; - private final int wordSpaceCount; - private int letterSpaceCount; - private MinOptMax areaIPD; - private final boolean isHyphenated; - private final boolean isSpace; - private boolean breakOppAfter; - private final Font font; - private final int level; - private final int[][] gposAdjustments; - - AreaInfo( - int startIndex, int breakIndex, int wordSpaceCount, int letterSpaceCount, - MinOptMax areaIPD, boolean isHyphenated, boolean isSpace, boolean breakOppAfter, - Font font, int level, int[][] gposAdjustments) { - assert startIndex <= breakIndex; - this.startIndex = startIndex; - this.breakIndex = breakIndex; - this.wordCharLength = -1; - this.wordSpaceCount = wordSpaceCount; - this.letterSpaceCount = letterSpaceCount; - this.areaIPD = areaIPD; - this.isHyphenated = isHyphenated; - this.isSpace = isSpace; - this.breakOppAfter = breakOppAfter; - this.font = font; - this.level = level; - this.gposAdjustments = gposAdjustments; - } - - /** - * Obtain number of 'characters' contained in word. If word - * is mapped, then this number may be less than or greater than the - * original length (breakIndex - startIndex). We compute and - * memoize thius length upon first invocation of this method. - */ - private int getWordLength() { - if (wordCharLength == -1) { - if (foText.hasMapping(startIndex, breakIndex)) { - wordCharLength = foText.getMapping(startIndex, breakIndex).length(); - } else { - assert breakIndex >= startIndex; - wordCharLength = breakIndex - startIndex; - } - } - return wordCharLength; - } - - private void addToAreaIPD(MinOptMax idp) { - areaIPD = areaIPD.plus(idp); - } - - public String toString() { - return super.toString() + "{" - + "interval = [" + startIndex + "," + breakIndex + "]" - + ", isSpace = " + isSpace - + ", level = " + level - + ", areaIPD = " + areaIPD - + ", letterSpaceCount = " + letterSpaceCount - + ", wordSpaceCount = " + wordSpaceCount - + ", isHyphenated = " + isHyphenated - + ", font = " + font - + "}"; - } - } - /** * this class stores information about changes in vecAreaInfo which are not yet applied */ private final class PendingChange { - private final AreaInfo areaInfo; + private final GlyphMapping mapping; private final int index; - private PendingChange(final AreaInfo areaInfo, final int index) { - this.areaInfo = areaInfo; + private PendingChange(final GlyphMapping mapping, final int index) { + this.mapping = mapping; this.index = index; } } @@ -160,7 +83,7 @@ public class TextLayoutManager extends LeafNodeLayoutManager { private static final Log LOG = LogFactory.getLog(TextLayoutManager.class); // Hold all possible breaks for the text in this LM's FO. - private final List areaInfos; + private final List mappings; /** Non-space characters on which we can end a line. */ private static final String BREAK_CHARS = "-/"; @@ -216,7 +139,7 @@ public class TextLayoutManager extends LeafNodeLayoutManager { public TextLayoutManager(FOText node) { foText = node; letterSpaceAdjustArray = new MinOptMax[node.length() + 1]; - areaInfos = new ArrayList(); + mappings = new ArrayList(); } private KnuthPenalty makeZeroWidthPenalty(int penaltyValue) { @@ -274,61 +197,61 @@ public class TextLayoutManager extends LeafNodeLayoutManager { public void addAreas(final PositionIterator posIter, final LayoutContext context) { // Add word areas - AreaInfo areaInfo; + GlyphMapping mapping; int wordSpaceCount = 0; int letterSpaceCount = 0; - int firstAreaInfoIndex = -1; - int lastAreaInfoIndex = 0; + int firstMappingIndex = -1; + int lastMappingIndex = 0; MinOptMax realWidth = MinOptMax.ZERO; /* On first area created, add any leading space. * Calculate word-space stretch value. */ - AreaInfo lastAreaInfo = null; + GlyphMapping lastMapping = null; while (posIter.hasNext()) { final LeafPosition tbpNext = (LeafPosition) posIter.next(); if (tbpNext == null) { continue; //Ignore elements without Positions } if (tbpNext.getLeafPos() != -1) { - areaInfo = (AreaInfo) areaInfos.get(tbpNext.getLeafPos()); - if (lastAreaInfo == null - || (areaInfo.font != lastAreaInfo.font) - || (areaInfo.level != lastAreaInfo.level)) { - if (lastAreaInfo != null) { - addAreaInfoAreas(lastAreaInfo, wordSpaceCount, - letterSpaceCount, firstAreaInfoIndex, - lastAreaInfoIndex, realWidth, context); + mapping = mappings.get(tbpNext.getLeafPos()); + if (lastMapping == null + || (mapping.font != lastMapping.font) + || (mapping.level != lastMapping.level)) { + if (lastMapping != null) { + addMappingAreas(lastMapping, wordSpaceCount, + letterSpaceCount, firstMappingIndex, + lastMappingIndex, realWidth, context); } - firstAreaInfoIndex = tbpNext.getLeafPos(); + firstMappingIndex = tbpNext.getLeafPos(); wordSpaceCount = 0; letterSpaceCount = 0; realWidth = MinOptMax.ZERO; } - wordSpaceCount += areaInfo.wordSpaceCount; - letterSpaceCount += areaInfo.letterSpaceCount; - realWidth = realWidth.plus(areaInfo.areaIPD); - lastAreaInfoIndex = tbpNext.getLeafPos(); - lastAreaInfo = areaInfo; + wordSpaceCount += mapping.wordSpaceCount; + letterSpaceCount += mapping.letterSpaceCount; + realWidth = realWidth.plus(mapping.areaIPD); + lastMappingIndex = tbpNext.getLeafPos(); + lastMapping = mapping; } } - if (lastAreaInfo != null) { - addAreaInfoAreas(lastAreaInfo, wordSpaceCount, letterSpaceCount, firstAreaInfoIndex, - lastAreaInfoIndex, realWidth, context); + if (lastMapping != null) { + addMappingAreas(lastMapping, wordSpaceCount, letterSpaceCount, firstMappingIndex, + lastMappingIndex, realWidth, context); } } - private void addAreaInfoAreas(AreaInfo areaInfo, int wordSpaceCount, int letterSpaceCount, - int firstAreaInfoIndex, int lastAreaInfoIndex, + private void addMappingAreas(GlyphMapping mapping, int wordSpaceCount, int letterSpaceCount, + int firstMappingIndex, int lastMappingIndex, MinOptMax realWidth, LayoutContext context) { // TODO: These two statements (if, for) were like this before my recent - // changes. However, it seems as if they should use the AreaInfo from - // firstAreaInfoIndex.. lastAreaInfoIndex rather than just the last areaInfo. + // changes. However, it seems as if they should use the GlyphMapping from + // firstMappingIndex.. lastMappingIndex rather than just the last mapping. // This needs to be checked. - int textLength = areaInfo.getWordLength(); - if (areaInfo.letterSpaceCount == textLength && !areaInfo.isHyphenated + int textLength = mapping.getWordLength(); + if (mapping.letterSpaceCount == textLength && !mapping.isHyphenated && context.isLastArea()) { // the line ends at a character like "/" or "-"; // remove the letter space after the last character @@ -336,7 +259,7 @@ public class TextLayoutManager extends LeafNodeLayoutManager { letterSpaceCount--; } - for (int i = areaInfo.startIndex; i < areaInfo.breakIndex; i++) { + for (int i = mapping.startIndex; i < mapping.endIndex; i++) { MinOptMax letterSpaceAdjustment = letterSpaceAdjustArray[i + 1]; if (letterSpaceAdjustment != null && letterSpaceAdjustment.isElastic()) { letterSpaceCount++; @@ -344,7 +267,7 @@ public class TextLayoutManager extends LeafNodeLayoutManager { } // add hyphenation character if the last word is hyphenated - if (context.isLastArea() && areaInfo.isHyphenated) { + if (context.isLastArea() && mapping.isHyphenated) { realWidth = realWidth.plus(hyphIPD); } @@ -385,8 +308,8 @@ public class TextLayoutManager extends LeafNodeLayoutManager { totalAdjust = difference; } - TextArea textArea = new TextAreaBuilder(realWidth, totalAdjust, context, firstAreaInfoIndex, - lastAreaInfoIndex, context.isLastArea(), areaInfo.font).build(); + TextArea textArea = new TextAreaBuilder(realWidth, totalAdjust, context, firstMappingIndex, + lastMappingIndex, context.isLastArea(), mapping.font).build(); // wordSpaceDim is computed in relation to wordSpaceIPD.opt // but the renderer needs to know the adjustment in relation @@ -417,15 +340,15 @@ public class TextLayoutManager extends LeafNodeLayoutManager { private final MinOptMax width; // content ipd private final int adjust; // content ipd adjustment private final LayoutContext context; // layout context - private final int firstIndex; // index of first AreaInfo - private final int lastIndex; // index of last AreaInfo + private final int firstIndex; // index of first GlyphMapping + private final int lastIndex; // index of last GlyphMapping private final boolean isLastArea; // true if last inline area in line area private final Font font; // applicable font // other, non-constructor state private TextArea textArea; // text area being constructed private int blockProgressionDimension; // calculated bpd - private AreaInfo areaInfo; // current area info when iterating over words + private GlyphMapping mapping; // current mapping when iterating over words private StringBuffer wordChars; // current word's character buffer private int[] letterSpaceAdjust; // current word's letter space adjustments private int letterSpaceAdjustIndex; // last written letter space adjustment index @@ -442,8 +365,8 @@ public class TextLayoutManager extends LeafNodeLayoutManager { * @param width the MinOptMax width of the content * @param adjust the total ipd adjustment with respect to the optimal width * @param context the layout context - * @param firstIndex the index of the first AreaInfo used for the TextArea - * @param lastIndex the index of the last AreaInfo used for the TextArea + * @param firstIndex the index of the first GlyphMapping used for the TextArea + * @param lastIndex the index of the last GlyphMapping used for the TextArea * @param isLastArea is this TextArea the last in a line? * @param font Font to be used in this particular TextArea */ @@ -516,30 +439,30 @@ public class TextLayoutManager extends LeafNodeLayoutManager { * Sets the text of the TextArea, split into words and spaces. */ private void setText() { - int areaInfoIndex = -1; + int mappingIndex = -1; int wordCharLength = 0; for (int wordIndex = firstIndex; wordIndex <= lastIndex; wordIndex++) { - areaInfo = getAreaInfo(wordIndex); - if (areaInfo.isSpace) { + mapping = getGlyphMapping(wordIndex); + if (mapping.isSpace) { addSpaces(); } else { - // areaInfo stores information about a word fragment - if (areaInfoIndex == -1) { + // mapping stores information about a word fragment + if (mappingIndex == -1) { // here starts a new word - areaInfoIndex = wordIndex; + mappingIndex = wordIndex; wordCharLength = 0; } - wordCharLength += areaInfo.getWordLength(); + wordCharLength += mapping.getWordLength(); if (isWordEnd(wordIndex)) { - addWord(areaInfoIndex, wordIndex, wordCharLength); - areaInfoIndex = -1; + addWord(mappingIndex, wordIndex, wordCharLength); + mappingIndex = -1; } } } } - private boolean isWordEnd(int areaInfoIndex) { - return areaInfoIndex == lastIndex || getAreaInfo(areaInfoIndex + 1).isSpace; + private boolean isWordEnd(int mappingIndex) { + return mappingIndex == lastIndex || getGlyphMapping(mappingIndex + 1).isSpace; } /** @@ -564,10 +487,10 @@ public class TextLayoutManager extends LeafNodeLayoutManager { // iterate over word's fragments, adding word chars (with bidi // levels), letter space adjustments, and glyph position adjustments for (int i = startIndex; i <= endIndex; i++) { - AreaInfo wordAreaInfo = getAreaInfo(i); - addWordChars(wordAreaInfo); - addLetterAdjust(wordAreaInfo); - if (addGlyphPositionAdjustments(wordAreaInfo)) { + GlyphMapping wordMapping = getGlyphMapping(i); + addWordChars(wordMapping); + addLetterAdjust(wordMapping); + if (addGlyphPositionAdjustments(wordMapping)) { gposAdjusted = true; } } @@ -617,7 +540,7 @@ public class TextLayoutManager extends LeafNodeLayoutManager { } private boolean isHyphenated(int endIndex) { - return isLastArea && endIndex == lastIndex && areaInfo.isHyphenated; + return isLastArea && endIndex == lastIndex && mapping.isHyphenated; } private void addHyphenationChar() { @@ -632,21 +555,54 @@ public class TextLayoutManager extends LeafNodeLayoutManager { * (1) concatenate (possibly mapped) word characters to word character buffer; * (2) concatenante (possibly mapped) word bidi levels to levels buffer; * (3) update word's IPD with optimal IPD of fragment. - * @param wordAreaInfo fragment info + * @param wordMapping fragment info */ - private void addWordChars(AreaInfo wordAreaInfo) { - int s = wordAreaInfo.startIndex; - int e = wordAreaInfo.breakIndex; - if (foText.hasMapping(s, e)) { - wordChars.append(foText.getMapping(s, e)); - addWordLevels(foText.getMappingBidiLevels(s, e)); + private void addWordChars(GlyphMapping wordMapping) { + int s = wordMapping.startIndex; + int e = wordMapping.endIndex; + if (wordMapping.mapping != null) { + wordChars.append(wordMapping.mapping); + addWordLevels(getMappingBidiLevels(wordMapping)); } else { for (int i = s; i < e; i++) { wordChars.append(foText.charAt(i)); } addWordLevels(foText.getBidiLevels(s, e)); } - wordIPD += wordAreaInfo.areaIPD.getOpt(); + wordIPD += wordMapping.areaIPD.getOpt(); + } + + /** + * Obtain bidirectional levels of mapping of characters over specific interval. + * @param start index in character buffer + * @param end index in character buffer + * @return a (possibly empty) array of bidi levels or null + * in case no bidi levels have been assigned + */ + private int[] getMappingBidiLevels(GlyphMapping mapping) { + if (mapping.mapping != null) { + int nc = mapping.endIndex - mapping.startIndex; + int nm = mapping.mapping.length(); + int[] la = foText.getBidiLevels(mapping.startIndex, mapping.endIndex); + if (la == null) { + return null; + } else if (nm == nc) { // mapping is same length as mapped range + return la; + } else if (nm > nc) { // mapping is longer than mapped range + int[] ma = new int[nm]; + System.arraycopy(la, 0, ma, 0, la.length); + for (int i = la.length, n = ma.length, l = (i > 0) ? la[i - 1] : 0; i < n; i++) { + ma[i] = l; + } + return ma; + } else { // mapping is shorter than mapped range + int[] ma = new int[nm]; + System.arraycopy(la, 0, ma, 0, ma.length); + return ma; + } + } else { + return foText.getBidiLevels(mapping.startIndex, mapping.endIndex); + } } /** @@ -672,16 +628,16 @@ public class TextLayoutManager extends LeafNodeLayoutManager { /** * Given a word area info associated with a word fragment, * concatenate letter space adjustments for each (possibly mapped) character. - * @param wordAreaInfo fragment info + * @param wordMapping fragment info */ - private void addLetterAdjust(AreaInfo wordAreaInfo) { - int letterSpaceCount = wordAreaInfo.letterSpaceCount; - int wordLength = wordAreaInfo.getWordLength(); + private void addLetterAdjust(GlyphMapping wordMapping) { + int letterSpaceCount = wordMapping.letterSpaceCount; + int wordLength = wordMapping.getWordLength(); int taAdjust = textArea.getTextLetterSpaceAdjust(); for (int i = 0, n = wordLength; i < n; i++) { int j = letterSpaceAdjustIndex + i; if (j > 0) { - int k = wordAreaInfo.startIndex + i; + int k = wordMapping.startIndex + i; MinOptMax adj = (k < letterSpaceAdjustArray.length) ? letterSpaceAdjustArray [ k ] : null; letterSpaceAdjust [ j ] = (adj == null) ? 0 : adj.getOpt(); @@ -697,14 +653,14 @@ public class TextLayoutManager extends LeafNodeLayoutManager { /** * Given a word area info associated with a word fragment, * concatenate glyph position adjustments for each (possibly mapped) character. - * @param wordAreaInfo fragment info + * @param wordMapping fragment info * @return true if an adjustment was non-zero */ - private boolean addGlyphPositionAdjustments(AreaInfo wordAreaInfo) { + private boolean addGlyphPositionAdjustments(GlyphMapping wordMapping) { boolean adjusted = false; - int[][] gpa = wordAreaInfo.gposAdjustments; + int[][] gpa = wordMapping.gposAdjustments; int numAdjusts = (gpa != null) ? gpa.length : 0; - int wordLength = wordAreaInfo.getWordLength(); + int wordLength = wordMapping.getWordLength(); if (numAdjusts > 0) { int need = gposAdjustmentsIndex + numAdjusts; if (need <= gposAdjustments.length) { @@ -733,7 +689,7 @@ public class TextLayoutManager extends LeafNodeLayoutManager { } /** - * The AreaInfo stores information about spaces. + * The GlyphMapping stores information about spaces. *

* Add the spaces - except zero-width spaces - to the TextArea. */ @@ -743,16 +699,16 @@ public class TextLayoutManager extends LeafNodeLayoutManager { // divide the area info's allocated IPD evenly among the // non-zero-width space characters int numZeroWidthSpaces = 0; - for (int i = areaInfo.startIndex; i < areaInfo.breakIndex; i++) { + for (int i = mapping.startIndex; i < mapping.endIndex; i++) { char spaceChar = foText.charAt(i); if (CharUtilities.isZeroWidthSpace(spaceChar)) { numZeroWidthSpaces++; } } - int numSpaces = areaInfo.breakIndex - areaInfo.startIndex - numZeroWidthSpaces; - int spaceIPD = areaInfo.areaIPD.getOpt() / ((numSpaces > 0) ? numSpaces : 1); + int numSpaces = mapping.endIndex - mapping.startIndex - numZeroWidthSpaces; + int spaceIPD = mapping.areaIPD.getOpt() / ((numSpaces > 0) ? numSpaces : 1); // add space area children, one for each non-zero-width space character - for (int i = areaInfo.startIndex; i < areaInfo.breakIndex; i++) { + for (int i = mapping.startIndex; i < mapping.endIndex; i++) { char spaceChar = foText.charAt(i); int level = foText.bidiLevelAt(i); if (!CharUtilities.isZeroWidthSpace(spaceChar)) { @@ -766,39 +722,20 @@ public class TextLayoutManager extends LeafNodeLayoutManager { } - private void addAreaInfo(AreaInfo ai) { - addAreaInfo(areaInfos.size(), ai); + private void addGlyphMapping(GlyphMapping mapping) { + addGlyphMapping(mappings.size(), mapping); } - private void addAreaInfo(int index, AreaInfo ai) { - areaInfos.add(index, ai); + private void addGlyphMapping(int index, GlyphMapping mapping) { + mappings.add(index, mapping); } - private void removeAreaInfo(int index) { - areaInfos.remove(index); + private void removeGlyphMapping(int index) { + mappings.remove(index); } - private AreaInfo getAreaInfo(int index) { - return (AreaInfo) areaInfos.get(index); - } - - private void addToLetterAdjust(int index, int width) { - if (letterSpaceAdjustArray[index] == null) { - letterSpaceAdjustArray[index] = MinOptMax.getInstance(width); - } else { - letterSpaceAdjustArray[index] = letterSpaceAdjustArray[index].plus(width); - } - } - - /** - * Indicates whether a character is a space in terms of this layout manager. - * @param ch the character - * @return true if it's a space - */ - private static boolean isSpace(final char ch) { - return ch == CharUtilities.SPACE - || CharUtilities.isNonBreakableSpace(ch) - || CharUtilities.isFixedWidthSpace(ch); + private GlyphMapping getGlyphMapping(int index) { + return mappings.get(index); } /** {@inheritDoc} */ @@ -810,8 +747,8 @@ public class TextLayoutManager extends LeafNodeLayoutManager { final List returnList = new LinkedList(); KnuthSequence sequence = new InlineKnuthSequence(); - AreaInfo areaInfo = null; - AreaInfo prevAreaInfo = null; + GlyphMapping mapping = null; + GlyphMapping prevMapping = null; returnList.add(sequence); if (LOG.isDebugEnabled()) { @@ -857,24 +794,24 @@ public class TextLayoutManager extends LeafNodeLayoutManager { } if (inWord) { if (breakOpportunity - || TextLayoutManager.isSpace(ch) + || GlyphMapping.isSpace(ch) || CharUtilities.isExplicitBreak(ch) || ((prevLevel != -1) && (level != prevLevel))) { // this.foText.charAt(lastIndex) == CharUtilities.SOFT_HYPHEN - prevAreaInfo = processWord(alignment, sequence, prevAreaInfo, ch, + prevMapping = processWord(alignment, sequence, prevMapping, ch, breakOpportunity, true, prevLevel); } } else if (inWhitespace) { if (ch != CharUtilities.SPACE || breakOpportunity) { - prevAreaInfo = processWhitespace(alignment, sequence, + prevMapping = processWhitespace(alignment, sequence, breakOpportunity, prevLevel); } } else { - if (areaInfo != null) { - prevAreaInfo = areaInfo; - processLeftoverAreaInfo(alignment, sequence, areaInfo, + if (mapping != null) { + prevMapping = mapping; + processLeftoverGlyphMapping(alignment, sequence, mapping, ch == CharUtilities.SPACE || breakOpportunity); - areaInfo = null; + mapping = null; } if (breakAction == LineBreakStatus.EXPLICIT_BREAK) { sequence = processLinebreak(returnList, sequence); @@ -888,15 +825,15 @@ public class TextLayoutManager extends LeafNodeLayoutManager { this.foText, this); font.mapChar(ch); // preserved space or non-breaking space: - // create the AreaInfo object - areaInfo = new AreaInfo(nextStart, nextStart + 1, 1, 0, wordSpaceIPD, false, true, + // create the GlyphMapping object + mapping = new GlyphMapping(nextStart, nextStart + 1, 1, 0, wordSpaceIPD, false, true, breakOpportunity, spaceFont, level, null); thisStart = nextStart + 1; } else if (CharUtilities.isFixedWidthSpace(ch) || CharUtilities.isZeroWidthSpace(ch)) { - // create the AreaInfo object + // create the GlyphMapping object Font font = FontSelector.selectFontForCharacterInText(ch, foText, this); MinOptMax ipd = MinOptMax.getInstance(font.getCharWidth(ch)); - areaInfo = new AreaInfo(nextStart, nextStart + 1, 0, 0, ipd, false, true, + mapping = new GlyphMapping(nextStart, nextStart + 1, 0, 0, ipd, false, true, breakOpportunity, font, level, null); thisStart = nextStart + 1; } else if (CharUtilities.isExplicitBreak(ch)) { @@ -904,7 +841,7 @@ public class TextLayoutManager extends LeafNodeLayoutManager { thisStart = nextStart + 1; } - inWord = !TextLayoutManager.isSpace(ch) && !CharUtilities.isExplicitBreak(ch); + inWord = !GlyphMapping.isSpace(ch) && !CharUtilities.isExplicitBreak(ch); inWhitespace = ch == CharUtilities.SPACE && foText.getWhitespaceTreatment() != Constants.EN_PRESERVE; prevLevel = level; @@ -913,11 +850,11 @@ public class TextLayoutManager extends LeafNodeLayoutManager { // Process any last elements if (inWord) { - processWord(alignment, sequence, prevAreaInfo, ch, false, false, prevLevel); + processWord(alignment, sequence, prevMapping, ch, false, false, prevLevel); } else if (inWhitespace) { processWhitespace(alignment, sequence, !keepTogether, prevLevel); - } else if (areaInfo != null) { - processLeftoverAreaInfo(alignment, sequence, areaInfo, + } else if (mapping != null) { + processLeftoverGlyphMapping(alignment, sequence, mapping, ch == CharUtilities.ZERO_WIDTH_SPACE); } else if (CharUtilities.isExplicitBreak(ch)) { this.processLinebreak(returnList, sequence); @@ -948,15 +885,14 @@ public class TextLayoutManager extends LeafNodeLayoutManager { return sequence; } - private void processLeftoverAreaInfo(int alignment, - KnuthSequence sequence, AreaInfo areaInfo, - boolean breakOpportunityAfter) { - addAreaInfo(areaInfo); - areaInfo.breakOppAfter = breakOpportunityAfter; - addElementsForASpace(sequence, alignment, areaInfo, areaInfos.size() - 1); + private void processLeftoverGlyphMapping(int alignment, KnuthSequence sequence, + GlyphMapping mapping, boolean breakOpportunityAfter) { + addGlyphMapping(mapping); + mapping.breakOppAfter = breakOpportunityAfter; + addElementsForASpace(sequence, alignment, mapping, mappings.size() - 1); } - private AreaInfo processWhitespace(final int alignment, + private GlyphMapping processWhitespace(final int alignment, final KnuthSequence sequence, final boolean breakOpportunity, int level) { if (LOG.isDebugEnabled()) { @@ -964,209 +900,24 @@ public class TextLayoutManager extends LeafNodeLayoutManager { } // End of whitespace - // create the AreaInfo object + // create the GlyphMapping object assert nextStart >= thisStart; - AreaInfo areaInfo = new AreaInfo( - thisStart, nextStart, nextStart - thisStart, 0, + GlyphMapping mapping = new GlyphMapping( + thisStart, nextStart, nextStart - thisStart, 0, wordSpaceIPD.mult(nextStart - thisStart), false, true, breakOpportunity, spaceFont, level, null); - addAreaInfo(areaInfo); + addGlyphMapping(mapping); // create the elements - addElementsForASpace(sequence, alignment, areaInfo, areaInfos.size() - 1); + addElementsForASpace(sequence, alignment, mapping, mappings.size() - 1); thisStart = nextStart; - return areaInfo; - } - - private AreaInfo processWordMapping( - int lastIndex, final Font font, AreaInfo prevAreaInfo, final char breakOpportunityChar, - final boolean endsWithHyphen, int level) { - int s = this.thisStart; // start index of word in FOText character buffer - int e = lastIndex; // end index of word in FOText character buffer - int nLS = 0; // # of letter spaces - String script = foText.getScript(); - String language = foText.getLanguage(); - - if (LOG.isDebugEnabled()) { - LOG.debug("PW: [" + thisStart + "," + lastIndex + "]: {" - + " +M" - + ", level = " + level - + " }"); - } - - // 1. extract unmapped character sequence - CharSequence ics = foText.subSequence(s, e); - - // 2. if script is not specified (by FO property) or it is specified as 'auto', - // then compute dominant script - if ((script == null) || "auto".equals(script)) { - script = CharScript.scriptTagFromCode(CharScript.dominantScript(ics)); - } - if ((language == null) || "none".equals(language)) { - language = "dflt"; - } - - // 3. perform mapping of chars to glyphs ... to glyphs ... to chars - CharSequence mcs = font.performSubstitution(ics, script, language); - - // 4. compute glyph position adjustments on (substituted) characters - int[][] gpa; - if (font.performsPositioning()) { - // handle GPOS adjustments - gpa = font.performPositioning(mcs, script, language); - } else if (font.hasKerning()) { - // handle standard (non-GPOS) kerning adjustments - gpa = getKerningAdjustments(mcs, font); - } else { - gpa = null; - } - - // 5. reorder combining marks so that they precede (within the mapped char sequence) the - // base to which they are applied; N.B. position adjustments (gpa) are reordered in place - mcs = font.reorderCombiningMarks(mcs, gpa, script, language); - - // 6. if mapped sequence differs from input sequence, then memoize mapped sequence - if (!CharUtilities.isSameSequence(mcs, ics)) { - foText.addMapping(s, e, mcs); - } - - // 7. compute word ipd based on final position adjustments - MinOptMax ipd = MinOptMax.ZERO; - for (int i = 0, n = mcs.length(); i < n; i++) { - int c = mcs.charAt(i); - // TODO !BMP - int w = font.getCharWidth(c); - if (w < 0) { - w = 0; - } - if (gpa != null) { - w += gpa [ i ] [ GlyphPositioningTable.Value.IDX_X_ADVANCE ]; - } - ipd = ipd.plus(w); - } - - // [TBD] - handle letter spacing - - return new AreaInfo( - s, e, 0, nLS, ipd, endsWithHyphen, false, - breakOpportunityChar != 0, font, level, gpa); - } - - /** - * Given a mapped character sequence MCS, obtain glyph position adjustments - * from the font's kerning data. - * @param mcs mapped character sequence - * @param font applicable font - * @return glyph position adjustments (or null if no kerning) - */ - private int[][] getKerningAdjustments(CharSequence mcs, final Font font) { - int nc = mcs.length(); - // extract kerning array - int[] ka = new int [ nc ]; // kerning array - for (int i = 0, n = nc, cPrev = -1; i < n; i++) { - int c = mcs.charAt(i); - // TODO !BMP - if (cPrev >= 0) { - ka[i] = font.getKernValue(cPrev, c); - } - cPrev = c; - } - // was there a non-zero kerning? - boolean hasKerning = false; - for (int i = 0, n = nc; i < n; i++) { - if (ka[i] != 0) { - hasKerning = true; - break; - } - } - // if non-zero kerning, then create and return glyph position adjustment array - if (hasKerning) { - int[][] gpa = new int [ nc ] [ 4 ]; - for (int i = 0, n = nc; i < n; i++) { - if (i > 0) { - gpa [ i - 1 ] [ GlyphPositioningTable.Value.IDX_X_ADVANCE ] = ka [ i ]; - } - } - return gpa; - } else { - return null; - } + return mapping; } - private AreaInfo processWordNoMapping(int lastIndex, final Font font, AreaInfo prevAreaInfo, - final char breakOpportunityChar, final boolean endsWithHyphen, int level) { - boolean kerning = font.hasKerning(); - MinOptMax wordIPD = MinOptMax.ZERO; - - if (LOG.isDebugEnabled()) { - LOG.debug("PW: [" + thisStart + "," + lastIndex + "]: {" - + " -M" - + ", level = " + level - + " }"); - } - - for (int i = thisStart; i < lastIndex; i++) { - char currentChar = foText.charAt(i); - - //character width - int charWidth = font.getCharWidth(currentChar); - wordIPD = wordIPD.plus(charWidth); - - //kerning - if (kerning) { - int kern = 0; - if (i > thisStart) { - char previousChar = foText.charAt(i - 1); - kern = font.getKernValue(previousChar, currentChar); - } else if (prevAreaInfo != null - && !prevAreaInfo.isSpace && prevAreaInfo.breakIndex > 0) { - char previousChar = foText.charAt(prevAreaInfo.breakIndex - 1); - kern = font.getKernValue(previousChar, currentChar); - } - if (kern != 0) { - addToLetterAdjust(i, kern); - wordIPD = wordIPD.plus(kern); - } - } - } - if (kerning - && (breakOpportunityChar != 0) - && !TextLayoutManager.isSpace(breakOpportunityChar) - && lastIndex > 0 - && endsWithHyphen) { - int kern = font.getKernValue(foText.charAt(lastIndex - 1), breakOpportunityChar); - if (kern != 0) { - addToLetterAdjust(lastIndex, kern); - //TODO: add kern to wordIPD? - } - } - // shy+chars at start of word: wordLength == 0 && breakOpportunity - // shy only characters in word: wordLength == 0 && !breakOpportunity - int wordLength = lastIndex - thisStart; - int letterSpaces = 0; - if (wordLength != 0) { - letterSpaces = wordLength - 1; - // if there is a break opportunity and the next one (break character) - // is not a space, it could be used as a line end; - // add one more letter space, in case other text follows - if ((breakOpportunityChar != 0) && !TextLayoutManager.isSpace(breakOpportunityChar)) { - letterSpaces++; - } - } - assert letterSpaces >= 0; - wordIPD = wordIPD.plus(letterSpaceIPD.mult(letterSpaces)); - - // create and return the AreaInfo object - return new AreaInfo(thisStart, lastIndex, 0, - letterSpaces, wordIPD, - endsWithHyphen, - false, breakOpportunityChar != 0, font, level, null); - } - - private AreaInfo processWord(final int alignment, final KnuthSequence sequence, - AreaInfo prevAreaInfo, final char ch, final boolean breakOpportunity, + private GlyphMapping processWord(final int alignment, final KnuthSequence sequence, + GlyphMapping prevMapping, final char ch, final boolean breakOpportunity, final boolean checkEndsWithHyphen, int level) { //Word boundary found, process widths and kerning @@ -1178,23 +929,20 @@ public class TextLayoutManager extends LeafNodeLayoutManager { && foText.charAt(lastIndex) == CharUtilities.SOFT_HYPHEN; Font font = FontSelector.selectFontForCharactersInText( foText, thisStart, lastIndex, foText, this); - AreaInfo areaInfo; - if (font.performsSubstitution() || font.performsPositioning()) { - areaInfo = processWordMapping( - lastIndex, font, prevAreaInfo, breakOpportunity ? ch : 0, endsWithHyphen, level); - } else { - areaInfo = processWordNoMapping( - lastIndex, font, prevAreaInfo, breakOpportunity ? ch : 0, endsWithHyphen, level); - } - prevAreaInfo = areaInfo; - addAreaInfo(areaInfo); + char breakOpportunityChar = breakOpportunity ? ch : 0; + char precedingChar = prevMapping != null && !prevMapping.isSpace + && prevMapping.endIndex > 0 ? foText.charAt(prevMapping.endIndex - 1) : 0; + GlyphMapping mapping = GlyphMapping.doGlyphMapping(foText, thisStart, lastIndex, font, + letterSpaceIPD, letterSpaceAdjustArray, precedingChar, breakOpportunityChar, endsWithHyphen, level); + prevMapping = mapping; + addGlyphMapping(mapping); tempStart = nextStart; //add the elements - addElementsForAWordFragment(sequence, alignment, areaInfo, areaInfos.size() - 1); + addElementsForAWordFragment(sequence, alignment, mapping, mappings.size() - 1); thisStart = nextStart; - return prevAreaInfo; + return prevMapping; } /** {@inheritDoc} */ @@ -1214,9 +962,9 @@ public class TextLayoutManager extends LeafNodeLayoutManager { int index = leafPos.getLeafPos(); //element could refer to '-1' position, for non-collapsed spaces (?) if (index > -1) { - AreaInfo areaInfo = getAreaInfo(index); - areaInfo.letterSpaceCount++; - areaInfo.addToAreaIPD(letterSpaceIPD); + GlyphMapping mapping = getGlyphMapping(index); + mapping.letterSpaceCount++; + mapping.addToAreaIPD(letterSpaceIPD); if (TextLayoutManager.BREAK_CHARS.indexOf(foText.charAt(tempStart - 1)) >= 0) { // the last character could be used as a line break // append new elements to oldList @@ -1227,13 +975,13 @@ public class TextLayoutManager extends LeafNodeLayoutManager { } else if (letterSpaceIPD.isStiff()) { // constant letter space: replace the box // give it the unwrapped position of the replaced element - oldListIterator.set(new KnuthInlineBox(areaInfo.areaIPD.getOpt(), + oldListIterator.set(new KnuthInlineBox(mapping.areaIPD.getOpt(), alignmentContext, pos, false)); } else { // adjustable letter space: replace the glue oldListIterator.next(); // this would return the penalty element oldListIterator.next(); // this would return the glue element - oldListIterator.set(new KnuthGlue(letterSpaceIPD.mult(areaInfo.letterSpaceCount), + oldListIterator.set(new KnuthGlue(letterSpaceIPD.mult(mapping.letterSpaceCount), auxiliaryPosition, true)); } } @@ -1242,26 +990,26 @@ public class TextLayoutManager extends LeafNodeLayoutManager { /** {@inheritDoc} */ public void hyphenate(Position pos, HyphContext hyphContext) { - AreaInfo areaInfo = getAreaInfo(((LeafPosition) pos).getLeafPos() + changeOffset); - int startIndex = areaInfo.startIndex; + GlyphMapping mapping = getGlyphMapping(((LeafPosition) pos).getLeafPos() + changeOffset); + int startIndex = mapping.startIndex; int stopIndex; boolean nothingChanged = true; - Font font = areaInfo.font; + Font font = mapping.font; - while (startIndex < areaInfo.breakIndex) { + while (startIndex < mapping.endIndex) { MinOptMax newIPD = MinOptMax.ZERO; boolean hyphenFollows; stopIndex = startIndex + hyphContext.getNextHyphPoint(); - if (hyphContext.hasMoreHyphPoints() && stopIndex <= areaInfo.breakIndex) { + if (hyphContext.hasMoreHyphPoints() && stopIndex <= mapping.endIndex) { // stopIndex is the index of the first character // after a hyphenation point hyphenFollows = true; } else { // there are no more hyphenation points, - // or the next one is after areaInfo.breakIndex + // or the next one is after mapping.breakIndex hyphenFollows = false; - stopIndex = areaInfo.breakIndex; + stopIndex = mapping.endIndex; } hyphContext.updateOffset(stopIndex - startIndex); @@ -1286,18 +1034,18 @@ public class TextLayoutManager extends LeafNodeLayoutManager { // add letter spaces boolean isWordEnd - = (stopIndex == areaInfo.breakIndex) - && (areaInfo.letterSpaceCount < areaInfo.getWordLength()); + = (stopIndex == mapping.endIndex) + && (mapping.letterSpaceCount < mapping.getWordLength()); int letterSpaceCount = isWordEnd ? stopIndex - startIndex - 1 : stopIndex - startIndex; assert letterSpaceCount >= 0; newIPD = newIPD.plus(letterSpaceIPD.mult(letterSpaceCount)); - if (!(nothingChanged && stopIndex == areaInfo.breakIndex && !hyphenFollows)) { - // the new AreaInfo object is not equal to the old one + if (!(nothingChanged && stopIndex == mapping.endIndex && !hyphenFollows)) { + // the new GlyphMapping object is not equal to the old one changeList.add( new PendingChange( - new AreaInfo(startIndex, stopIndex, 0, + new GlyphMapping(startIndex, stopIndex, 0, letterSpaceCount, newIPD, hyphenFollows, false, false, font, -1, null), ((LeafPosition) pos).getLeafPos() + changeOffset)); @@ -1324,7 +1072,7 @@ public class TextLayoutManager extends LeafNodeLayoutManager { return false; } - // Find the first and last positions in oldList that point to an AreaInfo + // Find the first and last positions in oldList that point to a GlyphMapping // (i.e. getLeafPos() != -1) LeafPosition startPos = null; LeafPosition endPos = null; @@ -1349,8 +1097,8 @@ public class TextLayoutManager extends LeafNodeLayoutManager { returnedIndices[0] = (startPos != null ? startPos.getLeafPos() : -1) + changeOffset; returnedIndices[1] = (endPos != null ? endPos.getLeafPos() : -1) + changeOffset; - int areaInfosAdded = 0; - int areaInfosRemoved = 0; + int mappingsAdded = 0; + int mappingsRemoved = 0; if (!changeList.isEmpty()) { int oldIndex = -1; @@ -1360,24 +1108,24 @@ public class TextLayoutManager extends LeafNodeLayoutManager { while (changeListIterator.hasNext()) { currChange = (PendingChange) changeListIterator.next(); if (currChange.index == oldIndex) { - areaInfosAdded++; - changeIndex = currChange.index + areaInfosAdded - areaInfosRemoved; + mappingsAdded++; + changeIndex = currChange.index + mappingsAdded - mappingsRemoved; } else { - areaInfosRemoved++; - areaInfosAdded++; + mappingsRemoved++; + mappingsAdded++; oldIndex = currChange.index; - changeIndex = currChange.index + areaInfosAdded - areaInfosRemoved; - removeAreaInfo(changeIndex); + changeIndex = currChange.index + mappingsAdded - mappingsRemoved; + removeGlyphMapping(changeIndex); } - addAreaInfo(changeIndex, currChange.areaInfo); + addGlyphMapping(changeIndex, currChange.mapping); } changeList.clear(); } // increase the end index for getChangedKnuthElements() - returnedIndices[1] += (areaInfosAdded - areaInfosRemoved); + returnedIndices[1] += (mappingsAdded - mappingsRemoved); // increase offset to use for subsequent paragraphs - changeOffset += (areaInfosAdded - areaInfosRemoved); + changeOffset += (mappingsAdded - mappingsRemoved); return hasChanged; } @@ -1391,16 +1139,16 @@ public class TextLayoutManager extends LeafNodeLayoutManager { final LinkedList returnList = new LinkedList(); for (; returnedIndices[0] <= returnedIndices[1]; returnedIndices[0]++) { - AreaInfo areaInfo = getAreaInfo(returnedIndices[0]); - if (areaInfo.wordSpaceCount == 0) { - // areaInfo refers either to a word or a word fragment - addElementsForAWordFragment(returnList, alignment, areaInfo, returnedIndices[0]); + GlyphMapping mapping = getGlyphMapping(returnedIndices[0]); + if (mapping.wordSpaceCount == 0) { + // mapping refers either to a word or a word fragment + addElementsForAWordFragment(returnList, alignment, mapping, returnedIndices[0]); } else { - // areaInfo refers to a space - addElementsForASpace(returnList, alignment, areaInfo, returnedIndices[0]); + // mapping refers to a space + addElementsForASpace(returnList, alignment, mapping, returnedIndices[0]); } } - setFinished(returnedIndices[0] == areaInfos.size() - 1); + setFinished(returnedIndices[0] == mappings.size() - 1); //ElementListObserver.observe(returnList, "text-changed", null); return returnList; } @@ -1409,9 +1157,9 @@ public class TextLayoutManager extends LeafNodeLayoutManager { public String getWordChars(Position pos) { int leafValue = ((LeafPosition) pos).getLeafPos() + changeOffset; if (leafValue != -1) { - AreaInfo areaInfo = getAreaInfo(leafValue); - StringBuffer buffer = new StringBuffer(areaInfo.getWordLength()); - for (int i = areaInfo.startIndex; i < areaInfo.breakIndex; i++) { + GlyphMapping mapping = getGlyphMapping(leafValue); + StringBuffer buffer = new StringBuffer(mapping.getWordLength()); + for (int i = mapping.startIndex; i < mapping.endIndex; i++) { buffer.append(foText.charAt(i)); } return buffer.toString(); @@ -1420,41 +1168,39 @@ public class TextLayoutManager extends LeafNodeLayoutManager { } } - private void addElementsForASpace(List baseList, int alignment, AreaInfo areaInfo, + private void addElementsForASpace(List baseList, int alignment, GlyphMapping mapping, int leafValue) { LeafPosition mainPosition = new LeafPosition(this, leafValue); - if (!areaInfo.breakOppAfter) { + if (!mapping.breakOppAfter) { // a non-breaking space if (alignment == Constants.EN_JUSTIFY) { // the space can stretch and shrink, and must be preserved // when starting a line baseList.add(makeAuxiliaryZeroWidthBox()); baseList.add(makeZeroWidthPenalty(KnuthElement.INFINITE)); - baseList.add(new KnuthGlue(areaInfo.areaIPD, mainPosition, false)); + baseList.add(new KnuthGlue(mapping.areaIPD, mainPosition, false)); } else { // the space does not need to stretch or shrink, and must be // preserved when starting a line - baseList.add(new KnuthInlineBox(areaInfo.areaIPD.getOpt(), null, mainPosition, + baseList.add(new KnuthInlineBox(mapping.areaIPD.getOpt(), null, mainPosition, true)); } } else { - if (foText.charAt(areaInfo.startIndex) != CharUtilities.SPACE + if (foText.charAt(mapping.startIndex) != CharUtilities.SPACE || foText.getWhitespaceTreatment() == Constants.EN_PRESERVE) { // a breaking space that needs to be preserved - baseList - .addAll(getElementsForBreakingSpace(alignment, areaInfo, auxiliaryPosition, 0, - mainPosition, areaInfo.areaIPD.getOpt(), true)); + baseList.addAll(getElementsForBreakingSpace(alignment, mapping, auxiliaryPosition, 0, + mainPosition, mapping.areaIPD.getOpt(), true)); } else { // a (possible block) of breaking spaces - baseList - .addAll(getElementsForBreakingSpace(alignment, areaInfo, mainPosition, - areaInfo.areaIPD.getOpt(), auxiliaryPosition, 0, false)); + baseList.addAll(getElementsForBreakingSpace(alignment, mapping, mainPosition, + mapping.areaIPD.getOpt(), auxiliaryPosition, 0, false)); } } } - private List getElementsForBreakingSpace(int alignment, AreaInfo areaInfo, Position pos2, + private List getElementsForBreakingSpace(int alignment, GlyphMapping mapping, Position pos2, int p2WidthOffset, Position pos3, int p3WidthOffset, boolean skipZeroCheck) { List elements = new ArrayList(); @@ -1504,7 +1250,7 @@ public class TextLayoutManager extends LeafNodeLayoutManager { elements.add(g); elements.add(makeZeroWidthPenalty(0)); g = new KnuthGlue( - areaInfo.areaIPD.getOpt(), + mapping.areaIPD.getOpt(), -3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0, pos2, false); elements.add(g); } @@ -1513,25 +1259,24 @@ public class TextLayoutManager extends LeafNodeLayoutManager { case EN_JUSTIFY: // justified text: // the stretch and shrink depends on the space width - elements.addAll(getElementsForJustifiedText(areaInfo, pos2, p2WidthOffset, pos3, - p3WidthOffset, skipZeroCheck, areaInfo.areaIPD.getShrink())); + elements.addAll(getElementsForJustifiedText(mapping, pos2, p2WidthOffset, pos3, + p3WidthOffset, skipZeroCheck, mapping.areaIPD.getShrink())); break; default: // last line justified, the other lines unjustified: // use only the space stretch - elements.addAll(getElementsForJustifiedText(areaInfo, pos2, p2WidthOffset, pos3, + elements.addAll(getElementsForJustifiedText(mapping, pos2, p2WidthOffset, pos3, p3WidthOffset, skipZeroCheck, 0)); } return elements; } - private List getElementsForJustifiedText( - AreaInfo areaInfo, Position pos2, int p2WidthOffset, - Position pos3, int p3WidthOffset, boolean skipZeroCheck, - int shrinkability) { + private List getElementsForJustifiedText(GlyphMapping mapping, Position pos2, int p2WidthOffset, + Position pos3, int p3WidthOffset, boolean skipZeroCheck, + int shrinkability) { - int stretchability = areaInfo.areaIPD.getStretch(); + int stretchability = mapping.areaIPD.getStretch(); List elements = new ArrayList(); if (skipZeroCheck || lineStartBAP != 0 || lineEndBAP != 0) { @@ -1543,34 +1288,34 @@ public class TextLayoutManager extends LeafNodeLayoutManager { elements.add(makeZeroWidthPenalty(KnuthElement.INFINITE)); elements.add(new KnuthGlue(lineStartBAP + p3WidthOffset, 0, 0, pos3, false)); } else { - elements.add(new KnuthGlue(areaInfo.areaIPD.getOpt(), stretchability, shrinkability, + elements.add(new KnuthGlue(mapping.areaIPD.getOpt(), stretchability, shrinkability, pos2, false)); } return elements; } - private void addElementsForAWordFragment(List baseList, int alignment, AreaInfo areaInfo, + private void addElementsForAWordFragment(List baseList, int alignment, GlyphMapping mapping, int leafValue) { LeafPosition mainPosition = new LeafPosition(this, leafValue); // if the last character of the word fragment is '-' or '/', // the fragment could end a line; in this case, it loses one // of its letter spaces; - boolean suppressibleLetterSpace = areaInfo.breakOppAfter && !areaInfo.isHyphenated; + boolean suppressibleLetterSpace = mapping.breakOppAfter && !mapping.isHyphenated; if (letterSpaceIPD.isStiff()) { // constant letter spacing baseList.add(new KnuthInlineBox(suppressibleLetterSpace - ? areaInfo.areaIPD.getOpt() - letterSpaceIPD.getOpt() - : areaInfo.areaIPD.getOpt(), + ? mapping.areaIPD.getOpt() - letterSpaceIPD.getOpt() + : mapping.areaIPD.getOpt(), alignmentContext, notifyPos(mainPosition), false)); } else { // adjustable letter spacing int unsuppressibleLetterSpaces = suppressibleLetterSpace - ? areaInfo.letterSpaceCount - 1 - : areaInfo.letterSpaceCount; - baseList.add(new KnuthInlineBox(areaInfo.areaIPD.getOpt() - - areaInfo.letterSpaceCount * letterSpaceIPD.getOpt(), + ? mapping.letterSpaceCount - 1 + : mapping.letterSpaceCount; + baseList.add(new KnuthInlineBox(mapping.areaIPD.getOpt() + - mapping.letterSpaceCount * letterSpaceIPD.getOpt(), alignmentContext, notifyPos(mainPosition), false)); baseList.add(makeZeroWidthPenalty(KnuthElement.INFINITE)); baseList.add(new KnuthGlue(letterSpaceIPD.mult(unsuppressibleLetterSpaces), @@ -1580,19 +1325,19 @@ public class TextLayoutManager extends LeafNodeLayoutManager { // extra-elements if the word fragment is the end of a syllable, // or it ends with a character that can be used as a line break - if (areaInfo.isHyphenated) { + if (mapping.isHyphenated) { MinOptMax widthIfNoBreakOccurs = null; - if (areaInfo.breakIndex < foText.length()) { + if (mapping.endIndex < foText.length()) { //Add in kerning in no-break condition - widthIfNoBreakOccurs = letterSpaceAdjustArray[areaInfo.breakIndex]; + widthIfNoBreakOccurs = letterSpaceAdjustArray[mapping.endIndex]; } - //if (areaInfo.breakIndex) + //if (mapping.breakIndex) // the word fragment ends at the end of a syllable: // if a break occurs the content width increases, // otherwise nothing happens addElementsForAHyphen(baseList, alignment, hyphIPD, widthIfNoBreakOccurs, - areaInfo.breakOppAfter && areaInfo.isHyphenated); + mapping.breakOppAfter && mapping.isHyphenated); } else if (suppressibleLetterSpace) { // the word fragment ends with a character that acts as a hyphen // if a break occurs the width does not increase, diff --git a/src/java/org/apache/fop/render/AbstractGenericSVGHandler.java b/src/java/org/apache/fop/render/AbstractGenericSVGHandler.java index 392eb875e..983a5ad90 100644 --- a/src/java/org/apache/fop/render/AbstractGenericSVGHandler.java +++ b/src/java/org/apache/fop/render/AbstractGenericSVGHandler.java @@ -31,6 +31,7 @@ import org.apache.batik.bridge.GVTBuilder; import org.apache.batik.dom.AbstractDocument; import org.apache.batik.dom.svg.SVGDOMImplementation; import org.apache.batik.gvt.GraphicsNode; +import org.apache.batik.gvt.font.DefaultFontFamilyResolver; import org.apache.xmlgraphics.java2d.Graphics2DImagePainter; @@ -122,7 +123,8 @@ public abstract class AbstractGenericSVGHandler implements XMLHandler, RendererC //Prepare FOUserAgent userAgent = rendererContext.getUserAgent(); - SVGUserAgent svgUserAgent = new SVGUserAgent(userAgent, new AffineTransform()); + SVGUserAgent svgUserAgent = new SVGUserAgent(userAgent, DefaultFontFamilyResolver.SINGLETON, + new AffineTransform()); //Create Batik BridgeContext final BridgeContext bridgeContext = new BridgeContext(svgUserAgent); diff --git a/src/java/org/apache/fop/render/afp/AFPSVGHandler.java b/src/java/org/apache/fop/render/afp/AFPSVGHandler.java index 4549a26af..ccb4cc678 100644 --- a/src/java/org/apache/fop/render/afp/AFPSVGHandler.java +++ b/src/java/org/apache/fop/render/afp/AFPSVGHandler.java @@ -29,6 +29,7 @@ import org.w3c.dom.Document; import org.apache.batik.bridge.BridgeContext; import org.apache.batik.dom.svg.SVGDOMImplementation; import org.apache.batik.gvt.GraphicsNode; +import org.apache.batik.gvt.font.DefaultFontFamilyResolver; import org.apache.xmlgraphics.image.loader.ImageManager; import org.apache.xmlgraphics.image.loader.ImageSessionContext; @@ -43,6 +44,7 @@ import org.apache.fop.afp.AFPResourceInfo; import org.apache.fop.afp.AFPResourceManager; import org.apache.fop.afp.AFPUnitConverter; import org.apache.fop.afp.svg.AFPBridgeContext; +import org.apache.fop.afp.svg.AFPFontFamilyResolver; import org.apache.fop.apps.FOUserAgent; import org.apache.fop.fonts.FontInfo; import org.apache.fop.image.loader.batik.BatikUtil; @@ -53,6 +55,7 @@ import org.apache.fop.render.RendererContext; import org.apache.fop.render.RendererContext.RendererContextWrapper; import org.apache.fop.svg.SVGEventProducer; import org.apache.fop.svg.SVGUserAgent; +import org.apache.fop.svg.font.AggregatingFontFamilyResolver; /** * AFP XML handler for SVG. Uses Apache Batik for SVG processing. @@ -196,15 +199,13 @@ public class AFPSVGHandler extends AbstractGenericSVGHandler { */ public static BridgeContext createBridgeContext(FOUserAgent userAgent, AFPGraphics2D g2d) { ImageManager imageManager = userAgent.getImageManager(); - - SVGUserAgent svgUserAgent - = new SVGUserAgent(userAgent, new AffineTransform()); - - ImageSessionContext imageSessionContext = userAgent.getImageSessionContext(); - FontInfo fontInfo = g2d.getFontInfo(); + SVGUserAgent svgUserAgent = new SVGUserAgent(userAgent, new AggregatingFontFamilyResolver( + new AFPFontFamilyResolver(fontInfo, userAgent.getEventBroadcaster()), DefaultFontFamilyResolver.SINGLETON), + new AffineTransform()); + ImageSessionContext imageSessionContext = userAgent.getImageSessionContext(); return new AFPBridgeContext(svgUserAgent, fontInfo, imageManager, imageSessionContext, - new AffineTransform(), g2d); + new AffineTransform(), g2d, userAgent.getEventBroadcaster()); } /** {@inheritDoc} */ diff --git a/src/java/org/apache/fop/render/java2d/CustomFontMetricsMapper.java b/src/java/org/apache/fop/render/java2d/CustomFontMetricsMapper.java index 2f8865b14..5e9372a75 100644 --- a/src/java/org/apache/fop/render/java2d/CustomFontMetricsMapper.java +++ b/src/java/org/apache/fop/render/java2d/CustomFontMetricsMapper.java @@ -21,6 +21,7 @@ package org.apache.fop.render.java2d; import java.awt.Font; import java.awt.FontFormatException; +import java.awt.Rectangle; import java.io.IOException; import java.io.InputStream; import java.util.Map; @@ -183,11 +184,31 @@ public class CustomFontMetricsMapper extends Typeface implements FontMetricsMapp return typeface.getWidths(); } + public Rectangle getBoundingBox(int glyphIndex, int size) { + return typeface.getBoundingBox(glyphIndex, size); + } + /** {@inheritDoc} */ public final int getXHeight(final int size) { return typeface.getXHeight(size); } + public int getUnderlinePosition(int size) { + return typeface.getUnderlinePosition(size); + } + + public int getUnderlineThickness(int size) { + return typeface.getUnderlineThickness(size); + } + + public int getStrikeoutPosition(int size) { + return typeface.getStrikeoutPosition(size); + } + + public int getStrikeoutThickness(int size) { + return typeface.getStrikeoutThickness(size); + } + /** {@inheritDoc} */ public final boolean hasKerningInfo() { return typeface.hasKerningInfo(); diff --git a/src/java/org/apache/fop/render/java2d/Java2DFontMetrics.java b/src/java/org/apache/fop/render/java2d/Java2DFontMetrics.java index fc4af3574..5e681e9e2 100644 --- a/src/java/org/apache/fop/render/java2d/Java2DFontMetrics.java +++ b/src/java/org/apache/fop/render/java2d/Java2DFontMetrics.java @@ -223,6 +223,26 @@ public class Java2DFontMetrics { return xHeight * 1000; } + public int getUnderlinePosition(String family, int style, int size) { + setFont(family, style, size); + return -Math.round(lineMetrics.getUnderlineOffset()); + } + + public int getUnderlineThickness(String family, int style, int size) { + setFont(family, style, size); + return Math.round(lineMetrics.getUnderlineThickness()); + } + + public int getStrikeoutPosition(String family, int style, int size) { + setFont(family, style, size); + return -Math.round(lineMetrics.getStrikethroughOffset()); + } + + public int getStrikeoutThickness(String family, int style, int size) { + setFont(family, style, size); + return Math.round(lineMetrics.getStrikethroughThickness()); + } + /** * Returns width (in 1/1000ths of point size) of character at * code point i diff --git a/src/java/org/apache/fop/render/java2d/Java2DSVGHandler.java b/src/java/org/apache/fop/render/java2d/Java2DSVGHandler.java index 110581f27..f3128c600 100644 --- a/src/java/org/apache/fop/render/java2d/Java2DSVGHandler.java +++ b/src/java/org/apache/fop/render/java2d/Java2DSVGHandler.java @@ -31,6 +31,7 @@ import org.apache.commons.logging.LogFactory; import org.apache.batik.bridge.BridgeContext; import org.apache.batik.bridge.GVTBuilder; import org.apache.batik.gvt.GraphicsNode; +import org.apache.batik.gvt.font.DefaultFontFamilyResolver; import org.apache.fop.image.loader.batik.BatikUtil; import org.apache.fop.render.AbstractGenericSVGHandler; @@ -128,8 +129,8 @@ public class Java2DSVGHandler extends AbstractGenericSVGHandler int x = info.currentXPosition; int y = info.currentYPosition; - - SVGUserAgent ua = new SVGUserAgent(context.getUserAgent(), new AffineTransform()); + SVGUserAgent ua = new SVGUserAgent(context.getUserAgent(), DefaultFontFamilyResolver.SINGLETON, + new AffineTransform()); BridgeContext ctx = new BridgeContext(ua); diff --git a/src/java/org/apache/fop/render/java2d/SystemFontMetricsMapper.java b/src/java/org/apache/fop/render/java2d/SystemFontMetricsMapper.java index 06c975b86..f922e3f05 100644 --- a/src/java/org/apache/fop/render/java2d/SystemFontMetricsMapper.java +++ b/src/java/org/apache/fop/render/java2d/SystemFontMetricsMapper.java @@ -20,6 +20,7 @@ package org.apache.fop.render.java2d; // Java +import java.awt.Rectangle; import java.util.Map; import java.util.Set; @@ -133,6 +134,22 @@ public class SystemFontMetricsMapper extends Typeface implements FontMetricsMapp return java2DFontMetrics.getXHeight(family, style, size); } + public int getUnderlinePosition(int size) { + return java2DFontMetrics.getUnderlinePosition(family, style, size); + } + + public int getUnderlineThickness(int size) { + return java2DFontMetrics.getUnderlineThickness(family, style, size); + } + + public int getStrikeoutPosition(int size) { + return java2DFontMetrics.getStrikeoutPosition(family, style, size); + } + + public int getStrikeoutThickness(int size) { + return java2DFontMetrics.getStrikeoutThickness(family, style, size); + } + /** * {@inheritDoc} */ @@ -148,6 +165,10 @@ public class SystemFontMetricsMapper extends Typeface implements FontMetricsMapp return java2DFontMetrics.getWidths(family, style, Java2DFontMetrics.FONT_SIZE); } + public Rectangle getBoundingBox(int glyphIndex, int size) { + throw new UnsupportedOperationException("Not implemented"); + } + /** * {@inheritDoc} */ diff --git a/src/java/org/apache/fop/render/pdf/PDFImageHandlerSVG.java b/src/java/org/apache/fop/render/pdf/PDFImageHandlerSVG.java index 45d8dff2d..3b7084a65 100644 --- a/src/java/org/apache/fop/render/pdf/PDFImageHandlerSVG.java +++ b/src/java/org/apache/fop/render/pdf/PDFImageHandlerSVG.java @@ -52,6 +52,7 @@ import org.apache.fop.svg.PDFBridgeContext; import org.apache.fop.svg.PDFGraphics2D; import org.apache.fop.svg.SVGEventProducer; import org.apache.fop.svg.SVGUserAgent; +import org.apache.fop.svg.font.FOPFontFamilyResolverImpl; /** * Image Handler implementation which handles SVG images. @@ -76,7 +77,8 @@ public class PDFImageHandlerSVG implements ImageHandler { } final float uaResolution = userAgent.getSourceResolution(); - SVGUserAgent ua = new SVGUserAgent(userAgent, new AffineTransform()); + SVGUserAgent ua = new SVGUserAgent(userAgent, new FOPFontFamilyResolverImpl(pdfContext.getFontInfo()), + new AffineTransform()); GVTBuilder builder = new GVTBuilder(); diff --git a/src/java/org/apache/fop/render/ps/AbstractPSTranscoder.java b/src/java/org/apache/fop/render/ps/AbstractPSTranscoder.java index 1d464cae6..96886910e 100644 --- a/src/java/org/apache/fop/render/ps/AbstractPSTranscoder.java +++ b/src/java/org/apache/fop/render/ps/AbstractPSTranscoder.java @@ -40,6 +40,7 @@ import org.apache.fop.apps.FOPException; import org.apache.fop.fonts.FontInfo; import org.apache.fop.svg.AbstractFOPTranscoder; import org.apache.fop.svg.PDFDocumentGraphics2DConfigurator; +import org.apache.fop.svg.font.FOPFontFamilyResolverImpl; /** *

This class enables to transcode an input to a PostScript document.

@@ -115,6 +116,8 @@ public abstract class AbstractPSTranscoder extends AbstractFOPTranscoder { this.fontInfo = PDFDocumentGraphics2DConfigurator.createFontInfo( getEffectiveConfiguration(), useComplexScriptFeatures); graphics.setCustomTextHandler(new NativeTextHandler(graphics, fontInfo)); + ((FOPTranscoderUserAgent) userAgent).setFontFamilyResolver( + new FOPFontFamilyResolverImpl(fontInfo)); } catch (FOPException fe) { throw new TranscoderException(fe); } diff --git a/src/java/org/apache/fop/render/ps/PSImageHandlerSVG.java b/src/java/org/apache/fop/render/ps/PSImageHandlerSVG.java index 3ade34522..bd8e97063 100644 --- a/src/java/org/apache/fop/render/ps/PSImageHandlerSVG.java +++ b/src/java/org/apache/fop/render/ps/PSImageHandlerSVG.java @@ -41,6 +41,7 @@ import org.apache.fop.render.ImageHandler; import org.apache.fop.render.RenderingContext; import org.apache.fop.svg.SVGEventProducer; import org.apache.fop.svg.SVGUserAgent; +import org.apache.fop.svg.font.FOPFontFamilyResolverImpl; /** * Image handler implementation which handles SVG images for PostScript output. @@ -62,8 +63,8 @@ public class PSImageHandlerSVG implements ImageHandler { boolean strokeText = false; //TODO Configure text stroking - SVGUserAgent ua - = new SVGUserAgent(context.getUserAgent(), new AffineTransform()); + SVGUserAgent ua = new SVGUserAgent(context.getUserAgent(), + new FOPFontFamilyResolverImpl(psContext.getFontInfo()), new AffineTransform()); PSGraphics2D graphics = new PSGraphics2D(strokeText, gen); graphics.setGraphicContext(new org.apache.xmlgraphics.java2d.GraphicContext()); diff --git a/src/java/org/apache/fop/render/ps/PSSVGHandler.java b/src/java/org/apache/fop/render/ps/PSSVGHandler.java index e30f6391b..dfbaf60b7 100644 --- a/src/java/org/apache/fop/render/ps/PSSVGHandler.java +++ b/src/java/org/apache/fop/render/ps/PSSVGHandler.java @@ -249,8 +249,7 @@ public class PSSVGHandler extends AbstractGenericSVGHandler strokeText = cfg.getChild("stroke-text", true).getValueAsBoolean(strokeText); } - SVGUserAgent ua - = new SVGUserAgent(context.getUserAgent(), new AffineTransform()); + SVGUserAgent ua = new SVGUserAgent(context.getUserAgent(), null /* TODO */, new AffineTransform()); PSGraphics2D graphics = new PSGraphics2D(strokeText, gen); graphics.setGraphicContext(new org.apache.xmlgraphics.java2d.GraphicContext()); diff --git a/src/java/org/apache/fop/render/ps/PSTextPainter.java b/src/java/org/apache/fop/render/ps/PSTextPainter.java index eb2188026..44b7b08a3 100644 --- a/src/java/org/apache/fop/render/ps/PSTextPainter.java +++ b/src/java/org/apache/fop/render/ps/PSTextPainter.java @@ -19,25 +19,21 @@ package org.apache.fop.render.ps; -import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Shape; import java.awt.Stroke; import java.awt.geom.AffineTransform; -import java.awt.geom.Ellipse2D; -import java.awt.geom.GeneralPath; import java.awt.geom.PathIterator; import java.awt.geom.Point2D; +import java.awt.geom.Point2D.Double; import java.io.IOException; -import java.text.AttributedCharacterIterator; import java.util.Iterator; +import java.util.LinkedList; import java.util.List; -import org.apache.batik.gvt.font.GVTGlyphVector; import org.apache.batik.gvt.text.TextPaintInfo; -import org.apache.batik.gvt.text.TextSpanLayout; import org.apache.xmlgraphics.java2d.ps.PSGraphics2D; import org.apache.xmlgraphics.ps.PSGenerator; @@ -48,7 +44,6 @@ import org.apache.fop.fonts.FontMetrics; import org.apache.fop.fonts.LazyFont; import org.apache.fop.fonts.MultiByteFont; import org.apache.fop.svg.NativeTextPainter; -import org.apache.fop.util.CharUtilities; import org.apache.fop.util.HexEncoder; /** @@ -62,10 +57,20 @@ import org.apache.fop.util.HexEncoder; */ public class PSTextPainter extends NativeTextPainter { - private static final boolean DEBUG = false; - private FontResourceCache fontResources; + private PSGraphics2D ps; + + private PSGenerator gen; + + private TextUtil textUtil; + + private boolean flushCurrentRun; + + private PSTextRun psRun; + + private Double relPos; + private static final AffineTransform IDENTITY_TRANSFORM = new AffineTransform(); /** @@ -82,165 +87,26 @@ public class PSTextPainter extends NativeTextPainter { return g2d instanceof PSGraphics2D; } - /** {@inheritDoc} */ - protected void paintTextRun(TextRun textRun, Graphics2D g2d) throws IOException { - AttributedCharacterIterator runaci = textRun.getACI(); - runaci.first(); - - TextPaintInfo tpi = (TextPaintInfo)runaci.getAttribute(PAINT_INFO); - if (tpi == null || !tpi.visible) { - return; - } - if ((tpi != null) && (tpi.composite != null)) { - g2d.setComposite(tpi.composite); - } - - //------------------------------------ - TextSpanLayout layout = textRun.getLayout(); - logTextRun(runaci, layout); - CharSequence chars = collectCharacters(runaci); - runaci.first(); //Reset ACI - - final PSGraphics2D ps = (PSGraphics2D)g2d; - final PSGenerator gen = ps.getPSGenerator(); + @Override + protected void preparePainting(Graphics2D g2d) { + ps = (PSGraphics2D) g2d; + gen = ps.getPSGenerator(); ps.preparePainting(); + } - if (DEBUG) { - log.debug("Text: " + chars); - gen.commentln("%Text: " + chars); - } - - GeneralPath debugShapes = null; - if (DEBUG) { - debugShapes = new GeneralPath(); - } - - TextUtil textUtil = new TextUtil(gen); - textUtil.setupFonts(runaci); - if (!textUtil.hasFonts()) { - //Draw using Java2D when no native fonts are available - textRun.getLayout().draw(g2d); - return; - } - + @Override + protected void saveGraphicsState() throws IOException { gen.saveGraphicsState(); - gen.concatMatrix(g2d.getTransform()); - Shape imclip = g2d.getClip(); - clip(ps, imclip); - - gen.writeln("BT"); //beginTextObject() - - AffineTransform localTransform = new AffineTransform(); - Point2D prevPos = null; - GVTGlyphVector gv = layout.getGlyphVector(); - PSTextRun psRun = new PSTextRun(); //Used to split a text run into smaller runs - for (int index = 0, c = gv.getNumGlyphs(); index < c; index++) { - char ch = chars.charAt(index); - boolean visibleChar = gv.isGlyphVisible(index) - || (CharUtilities.isAnySpace(ch) && !CharUtilities.isZeroWidthSpace(ch)); - logCharacter(ch, layout, index, visibleChar); - if (!visibleChar) { - continue; - } - Point2D glyphPos = gv.getGlyphPosition(index); - - AffineTransform glyphTransform = gv.getGlyphTransform(index); - if (log.isTraceEnabled()) { - log.trace("pos " + glyphPos + ", transform " + glyphTransform); - } - if (DEBUG) { - Shape sh = gv.getGlyphLogicalBounds(index); - if (sh == null) { - sh = new Ellipse2D.Double(glyphPos.getX(), glyphPos.getY(), 2, 2); - } - debugShapes.append(sh, false); - } - - //Exact position of the glyph - localTransform.setToIdentity(); - localTransform.translate(glyphPos.getX(), glyphPos.getY()); - if (glyphTransform != null) { - localTransform.concatenate(glyphTransform); - } - localTransform.scale(1, -1); - - boolean flushCurrentRun = false; - //Try to optimize by combining characters using the same font and on the same line. - if (glyphTransform != null) { - //Happens for text-on-a-path - flushCurrentRun = true; - } - if (psRun.getRunLength() >= 128) { - //Don't let a run get too long - flushCurrentRun = true; - } - - //Note the position of the glyph relative to the previous one - Point2D relPos; - if (prevPos == null) { - relPos = new Point2D.Double(0, 0); - } else { - relPos = new Point2D.Double( - glyphPos.getX() - prevPos.getX(), - glyphPos.getY() - prevPos.getY()); - } - if (psRun.vertChanges == 0 - && psRun.getHorizRunLength() > 2 - && relPos.getY() != 0) { - //new line - flushCurrentRun = true; - } - - //Select the actual character to paint - char paintChar = (CharUtilities.isAnySpace(ch) ? ' ' : ch); - - //Select (sub)font for character - Font f = textUtil.selectFontForChar(paintChar); - char mapped = f.mapChar(ch); - boolean fontChanging = textUtil.isFontChanging(f, mapped); - if (fontChanging) { - flushCurrentRun = true; - } - - if (flushCurrentRun) { - //Paint the current run and reset for the next run - psRun.paint(ps, textUtil, tpi); - psRun.reset(); - } - - //Track current run - psRun.addCharacter(paintChar, relPos); - psRun.noteStartingTransformation(localTransform); - - //Change font if necessary - if (fontChanging) { - textUtil.setCurrentFont(f, mapped); - } + } - //Update last position - prevPos = glyphPos; - } - psRun.paint(ps, textUtil, tpi); - gen.writeln("ET"); //endTextObject() + @Override + protected void restoreGraphicsState() throws IOException { gen.restoreGraphicsState(); - - if (DEBUG) { - //Paint debug shapes - g2d.setStroke(new BasicStroke(0)); - g2d.setColor(Color.LIGHT_GRAY); - g2d.draw(debugShapes); - } } - private void applyColor(Paint paint, final PSGenerator gen) throws IOException { - if (paint == null) { - return; - } else if (paint instanceof Color) { - Color col = (Color)paint; - gen.useColor(col); - } else { - log.warn("Paint not supported: " + paint.toString()); - } + @Override + protected void setInitialTransform(AffineTransform transform) throws IOException { + gen.concatMatrix(transform); } private PSFontResource getResourceForFont(Font f, String postfix) { @@ -248,7 +114,8 @@ public class PSTextPainter extends NativeTextPainter { return this.fontResources.getFontResourceForFontKey(key); } - private void clip(PSGraphics2D ps, Shape shape) throws IOException { + @Override + protected void clip(Shape shape) throws IOException { if (shape == null) { return; } @@ -258,17 +125,76 @@ public class PSTextPainter extends NativeTextPainter { ps.getPSGenerator().writeln("clip"); } + @Override + protected void beginTextObject() throws IOException { + gen.writeln("BT"); + textUtil = new TextUtil(); + psRun = new PSTextRun(); //Used to split a text run into smaller runs + } + + @Override + protected void endTextObject() throws IOException { + psRun.paint(ps, textUtil, tpi); + gen.writeln("ET"); + } + + @Override + protected void positionGlyph(Point2D prevPos, Point2D glyphPos, boolean reposition) { + flushCurrentRun = false; + //Try to optimize by combining characters using the same font and on the same line. + if (reposition) { + //Happens for text-on-a-path + flushCurrentRun = true; + } + if (psRun.getRunLength() >= 128) { + //Don't let a run get too long + flushCurrentRun = true; + } + + //Note the position of the glyph relative to the previous one + if (prevPos == null) { + relPos = new Point2D.Double(0, 0); + } else { + relPos = new Point2D.Double( + glyphPos.getX() - prevPos.getX(), + glyphPos.getY() - prevPos.getY()); + } + if (psRun.vertChanges == 0 + && psRun.getHorizRunLength() > 2 + && relPos.getY() != 0) { + //new line + flushCurrentRun = true; + } + } + + @Override + protected void writeGlyph(char glyph, AffineTransform localTransform) throws IOException { + boolean fontChanging = textUtil.isFontChanging(font, glyph); + if (fontChanging) { + flushCurrentRun = true; + } + + if (flushCurrentRun) { + //Paint the current run and reset for the next run + psRun.paint(ps, textUtil, tpi); + psRun.reset(); + } + + //Track current run + psRun.addGlyph(glyph, relPos); + psRun.noteStartingTransformation(localTransform); + + //Change font if necessary + if (fontChanging) { + textUtil.setCurrentFont(font, glyph); + } + } + private class TextUtil { - private PSGenerator gen; - private Font[] fonts; private Font currentFont; private int currentEncoding = -1; - public TextUtil(PSGenerator gen) { - this.gen = gen; - } - public boolean isMultiByte(Font f) { FontMetrics metrics = f.getFontMetrics(); boolean multiByte = metrics instanceof MultiByteFont || metrics instanceof LazyFont @@ -276,15 +202,6 @@ public class PSTextPainter extends NativeTextPainter { return multiByte; } - public Font selectFontForChar(char ch) { - for (int i = 0, c = fonts.length; i < c; i++) { - if (fonts[i].hasChar(ch)) { - return fonts[i]; - } - } - return fonts[0]; //TODO Maybe fall back to painting with shapes - } - public void writeTextMatrix(AffineTransform transform) throws IOException { double[] matrix = new double[6]; transform.getMatrix(matrix); @@ -335,27 +252,19 @@ public class PSTextPainter extends NativeTextPainter { setCurrentFont(font, encoding); } - public void setupFonts(AttributedCharacterIterator runaci) { - this.fonts = findFonts(runaci); - } - - public boolean hasFonts() { - return (fonts != null) && (fonts.length > 0); - } - } private class PSTextRun { private AffineTransform textTransform; - private List relativePositions = new java.util.LinkedList(); - private StringBuffer currentChars = new StringBuffer(); + private List relativePositions = new LinkedList(); + private StringBuffer currentGlyphs = new StringBuffer(); private int horizChanges = 0; private int vertChanges = 0; public void reset() { textTransform = null; - currentChars.setLength(0); + currentGlyphs.setLength(0); horizChanges = 0; vertChanges = 0; relativePositions.clear(); @@ -369,9 +278,9 @@ public class PSTextPainter extends NativeTextPainter { return 0; } - public void addCharacter(char paintChar, Point2D relPos) { + public void addGlyph(char glyph, Point2D relPos) { addRelativePosition(relPos); - currentChars.append(paintChar); + currentGlyphs.append(glyph); } private void addRelativePosition(Point2D relPos) { @@ -393,7 +302,7 @@ public class PSTextPainter extends NativeTextPainter { } public int getRunLength() { - return currentChars.length(); + return currentGlyphs.length(); } private boolean isXShow() { @@ -407,9 +316,6 @@ public class PSTextPainter extends NativeTextPainter { public void paint(PSGraphics2D g2d, TextUtil textUtil, TextPaintInfo tpi) throws IOException { if (getRunLength() > 0) { - if (log.isDebugEnabled()) { - log.debug("Text run: " + currentChars); - } textUtil.writeTextMatrix(this.textTransform); if (isXShow()) { log.debug("Horizontal text: xshow"); @@ -431,25 +337,20 @@ public class PSTextPainter extends NativeTextPainter { private void paintXYShow(PSGraphics2D g2d, TextUtil textUtil, Paint paint, boolean x, boolean y) throws IOException { - PSGenerator gen = textUtil.gen; - char firstChar = this.currentChars.charAt(0); - //Font only has to be setup up before the first character - Font f = textUtil.selectFontForChar(firstChar); - char mapped = f.mapChar(firstChar); - textUtil.selectFont(f, mapped); - textUtil.setCurrentFont(f, mapped); - applyColor(paint, gen); - - boolean multiByte = textUtil.isMultiByte(f); + char glyph = currentGlyphs.charAt(0); + textUtil.selectFont(font, glyph); + textUtil.setCurrentFont(font, glyph); + applyColor(paint); + + boolean multiByte = textUtil.isMultiByte(font); StringBuffer sb = new StringBuffer(); sb.append(multiByte ? '<' : '('); - for (int i = 0, c = this.currentChars.length(); i < c; i++) { - char ch = this.currentChars.charAt(i); - mapped = f.mapChar(ch); + for (int i = 0, c = this.currentGlyphs.length(); i < c; i++) { + glyph = this.currentGlyphs.charAt(i); if (multiByte) { - sb.append(HexEncoder.encode(mapped)); + sb.append(HexEncoder.encode(glyph)); } else { - char codepoint = (char) (mapped % 256); + char codepoint = (char) (glyph % 256); PSGenerator.escapeChar(codepoint, sb); } } @@ -457,9 +358,9 @@ public class PSTextPainter extends NativeTextPainter { if (x || y) { sb.append("\n["); int idx = 0; - Iterator iter = this.relativePositions.iterator(); + Iterator iter = this.relativePositions.iterator(); while (iter.hasNext()) { - Point2D pt = (Point2D)iter.next(); + Point2D pt = iter.next(); if (idx > 0) { if (x) { sb.append(format(gen, pt.getX())); @@ -500,6 +401,17 @@ public class PSTextPainter extends NativeTextPainter { gen.writeln(sb.toString()); } + private void applyColor(Paint paint) throws IOException { + if (paint == null) { + return; + } else if (paint instanceof Color) { + Color col = (Color) paint; + gen.useColor(col); + } else { + log.warn("Paint not supported: " + paint.toString()); + } + } + private String format(PSGenerator gen, double coord) { if (Math.abs(coord) < 0.00001) { return "0"; @@ -510,30 +422,21 @@ public class PSTextPainter extends NativeTextPainter { private void paintStrokedGlyphs(PSGraphics2D g2d, TextUtil textUtil, Paint strokePaint, Stroke stroke) throws IOException { - PSGenerator gen = textUtil.gen; - - applyColor(strokePaint, gen); + applyColor(strokePaint); PSGraphics2D.applyStroke(stroke, gen); - Font f = null; - Iterator iter = this.relativePositions.iterator(); + Iterator iter = this.relativePositions.iterator(); iter.next(); Point2D pos = new Point2D.Double(0, 0); gen.writeln("0 0 M"); - for (int i = 0, c = this.currentChars.length(); i < c; i++) { - char ch = this.currentChars.charAt(0); + for (int i = 0, c = this.currentGlyphs.length(); i < c; i++) { + char mapped = this.currentGlyphs.charAt(i); if (i == 0) { - //Font only has to be setup up before the first character - f = textUtil.selectFontForChar(ch); - } - char mapped = f.mapChar(ch); - if (i == 0) { - textUtil.selectFont(f, mapped); - textUtil.setCurrentFont(f, mapped); + textUtil.selectFont(font, mapped); + textUtil.setCurrentFont(font, mapped); } //add glyph outlines to current path - mapped = f.mapChar(this.currentChars.charAt(i)); - FontMetrics metrics = f.getFontMetrics(); + FontMetrics metrics = font.getFontMetrics(); boolean multiByte = metrics instanceof MultiByteFont || metrics instanceof LazyFont && ((LazyFont) metrics).getRealFont() instanceof MultiByteFont; @@ -542,14 +445,14 @@ public class PSTextPainter extends NativeTextPainter { gen.write(HexEncoder.encode(mapped)); gen.write(">"); } else { - char codepoint = (char)(mapped % 256); + char codepoint = (char) (mapped % 256); gen.write("(" + codepoint + ")"); } gen.writeln(" false charpath"); if (iter.hasNext()) { //Position for the next character - Point2D pt = (Point2D)iter.next(); + Point2D pt = iter.next(); pos.setLocation(pos.getX() + pt.getX(), pos.getY() - pt.getY()); gen.writeln(gen.formatDouble5(pos.getX()) + " " + gen.formatDouble5(pos.getY()) + " M"); diff --git a/src/java/org/apache/fop/svg/ACIUtils.java b/src/java/org/apache/fop/svg/ACIUtils.java index f54a026e1..deba98f21 100644 --- a/src/java/org/apache/fop/svg/ACIUtils.java +++ b/src/java/org/apache/fop/svg/ACIUtils.java @@ -38,7 +38,8 @@ import org.apache.batik.gvt.text.GVTAttributedCharacterIterator; import org.apache.fop.fonts.Font; import org.apache.fop.fonts.FontInfo; -import org.apache.fop.fonts.FontTriplet; +import org.apache.fop.svg.font.FOPGVTFont; +import org.apache.fop.svg.font.FOPGVTFontFamily; /** * Utilities for java.text.AttributedCharacterIterator. @@ -64,57 +65,43 @@ public final class ACIUtils { @SuppressWarnings("unchecked") List gvtFonts = (List) aci.getAttribute( GVTAttributedCharacterIterator.TextAttribute.GVT_FONT_FAMILIES); - Float posture = (Float) aci.getAttribute(TextAttribute.POSTURE); - Float taWeight = (Float) aci.getAttribute(TextAttribute.WEIGHT); - Float fontSize = (Float) aci.getAttribute(TextAttribute.SIZE); - - String style = toStyle(posture); - int weight = toCSSWeight(taWeight); - int fsize = (int)(fontSize.floatValue() * 1000); + String style = toStyle((Float) aci.getAttribute(TextAttribute.POSTURE)); + int weight = toCSSWeight((Float) aci.getAttribute(TextAttribute.WEIGHT)); + float fontSize = ((Float) aci.getAttribute(TextAttribute.SIZE)).floatValue(); String firstFontFamily = null; - //GVT_FONT can sometimes be different from the fonts in GVT_FONT_FAMILIES //or GVT_FONT_FAMILIES can even be empty and only GVT_FONT is set - /* The following code section is not available until Batik 1.7 is released. */ - GVTFont gvtFont = (GVTFont)aci.getAttribute( - GVTAttributedCharacterIterator.TextAttribute.GVT_FONT); + GVTFont gvtFont = (GVTFont) aci.getAttribute(GVTAttributedCharacterIterator.TextAttribute.GVT_FONT); if (gvtFont != null) { String gvtFontFamily = gvtFont.getFamilyName(); - if (fontInfo.hasFont(gvtFontFamily, style, weight)) { - FontTriplet triplet = fontInfo.fontLookup(gvtFontFamily, style, - weight); - Font f = fontInfo.getFontInstance(triplet, fsize); + if (gvtFont instanceof FOPGVTFont) { + Font font = ((FOPGVTFont) gvtFont).getFont(); if (LOG.isDebugEnabled()) { LOG.debug("Found a font that matches the GVT font: " + gvtFontFamily + ", " + weight + ", " + style - + " -> " + f); + + " -> " + font); } - fonts.add(f); + fonts.add(font); } firstFontFamily = gvtFontFamily; } if (gvtFonts != null) { boolean haveInstanceOfSVGFontFamily = false; - for (GVTFontFamily fam : gvtFonts) { - if (fam instanceof SVGFontFamily) { + for (GVTFontFamily fontFamily : gvtFonts) { + if (fontFamily instanceof SVGFontFamily) { haveInstanceOfSVGFontFamily = true; - } - String fontFamily = fam.getFamilyName(); - if (fontInfo.hasFont(fontFamily, style, weight)) { - FontTriplet triplet = fontInfo.fontLookup(fontFamily, style, - weight); - Font f = fontInfo.getFontInstance(triplet, fsize); + } else if (fontFamily instanceof FOPGVTFontFamily) { + Font font = ((FOPGVTFontFamily) fontFamily).deriveFont(fontSize, aci).getFont(); if (LOG.isDebugEnabled()) { LOG.debug("Found a font that matches the GVT font family: " - + fontFamily + ", " + weight + ", " + style - + " -> " + f); + + fontFamily.getFamilyName() + ", " + weight + ", " + style + " -> " + font); } - fonts.add(f); + fonts.add(font); } if (firstFontFamily == null) { - firstFontFamily = fontFamily; + firstFontFamily = fontFamily.getFamilyName(); } } // SVG fonts are embedded fonts in the SVG document and are rarely used; however if they @@ -126,25 +113,10 @@ public final class ACIUtils { return null; // Let Batik paint this text! } } - if (fonts.isEmpty()) { - if (firstFontFamily == null) { - //This will probably never happen. Just to be on the safe side. - firstFontFamily = "any"; - } - //lookup with fallback possibility (incl. substitution notification) - FontTriplet triplet = fontInfo.fontLookup(firstFontFamily, style, weight); - Font f = fontInfo.getFontInstance(triplet, fsize); - if (LOG.isDebugEnabled()) { - LOG.debug("Falling back to adjustable font lookup up for: " - + firstFontFamily + ", " + weight + ", " + style - + " -> " + f); - } - fonts.add(f); - } - return fonts.toArray(new Font[fonts.size()]); + return fonts.isEmpty() ? null : fonts.toArray(new Font[fonts.size()]); } - private static int toCSSWeight(Float weight) { + public static int toCSSWeight(Float weight) { if (weight == null) { return 400; } else if (weight <= TextAttribute.WEIGHT_EXTRA_LIGHT.floatValue()) { @@ -170,7 +142,7 @@ public final class ACIUtils { } } - private static String toStyle(Float posture) { + public static String toStyle(Float posture) { return ((posture != null) && (posture.floatValue() > 0.0)) ? Font.STYLE_ITALIC : Font.STYLE_NORMAL; diff --git a/src/java/org/apache/fop/svg/AbstractFOPTextPainter.java b/src/java/org/apache/fop/svg/AbstractFOPTextPainter.java index 3e16799f4..7df127d3a 100644 --- a/src/java/org/apache/fop/svg/AbstractFOPTextPainter.java +++ b/src/java/org/apache/fop/svg/AbstractFOPTextPainter.java @@ -23,20 +23,15 @@ import java.awt.Color; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Shape; -import java.awt.font.TextAttribute; -import java.awt.geom.AffineTransform; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.text.AttributedCharacterIterator; import java.text.CharacterIterator; -import java.util.Iterator; -import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.apache.batik.dom.svg.SVGOMTextElement; import org.apache.batik.gvt.TextNode; import org.apache.batik.gvt.TextPainter; import org.apache.batik.gvt.renderer.StrokingTextPainter; @@ -66,15 +61,15 @@ public abstract class AbstractFOPTextPainter implements TextPainter { * Use the stroking text painter to get the bounds and shape. * Also used as a fallback to draw the string with strokes. */ - protected static final TextPainter - PROXY_PAINTER = StrokingTextPainter.getInstance(); + private final TextPainter proxyTextPainter; /** * Create a new PS text painter with the given font information. * @param nativeTextHandler the NativeTextHandler instance used for text painting */ - public AbstractFOPTextPainter(FOPTextHandler nativeTextHandler) { + public AbstractFOPTextPainter(FOPTextHandler nativeTextHandler, TextPainter proxyTextPainter) { this.nativeTextHandler = nativeTextHandler; + this.proxyTextPainter = proxyTextPainter; } /** @@ -85,19 +80,10 @@ public abstract class AbstractFOPTextPainter implements TextPainter { * @param g2d the Graphics2D to use */ public void paint(TextNode node, Graphics2D g2d) { - Point2D loc = node.getLocation(); - if (!isSupportedGraphics2D(g2d) || hasUnsupportedAttributes(node)) { - if (log.isDebugEnabled()) { - log.debug("painting text node " + node - + " by stroking due to unsupported attributes or an incompatible Graphics2D"); - } - PROXY_PAINTER.paint(node, g2d); - } else { - if (log.isDebugEnabled()) { - log.debug("painting text node " + node + " normally."); - } - paintTextRuns(node.getTextRuns(), g2d, loc); + if (isSupportedGraphics2D(g2d)) { + new TextRunPainter().paintTextRuns(node.getTextRuns(), g2d, node.getLocation()); } + proxyTextPainter.paint(node, g2d); } /** @@ -109,190 +95,99 @@ public abstract class AbstractFOPTextPainter implements TextPainter { */ protected abstract boolean isSupportedGraphics2D(Graphics2D g2d); - private boolean hasUnsupportedAttributes(TextNode node) { - Iterator iter = node.getTextRuns().iterator(); - while (iter.hasNext()) { - StrokingTextPainter.TextRun - run = (StrokingTextPainter.TextRun)iter.next(); - AttributedCharacterIterator aci = run.getACI(); - boolean hasUnsupported = hasUnsupportedAttributes(aci); - if (hasUnsupported) { - return true; - } - } - return false; - } - - private boolean hasUnsupportedAttributes(AttributedCharacterIterator aci) { - boolean hasUnsupported = false; - - Font font = getFont(aci); - String text = getText(aci); - if (hasUnsupportedGlyphs(text, font)) { - log.trace("-> Unsupported glyphs found"); - hasUnsupported = true; - } + private class TextRunPainter { - TextPaintInfo tpi = (TextPaintInfo) aci.getAttribute( - GVTAttributedCharacterIterator.TextAttribute.PAINT_INFO); - if ((tpi != null) - && ((tpi.strokeStroke != null && tpi.strokePaint != null) - || (tpi.strikethroughStroke != null) - || (tpi.underlineStroke != null) - || (tpi.overlineStroke != null))) { - log.trace("-> under/overlines etc. found"); - hasUnsupported = true; - } + private Point2D currentLocation; - //Alpha is not supported - Paint foreground = (Paint) aci.getAttribute(TextAttribute.FOREGROUND); - if (foreground instanceof Color) { - Color col = (Color)foreground; - if (col.getAlpha() != 255) { - log.trace("-> transparency found"); - hasUnsupported = true; + public void paintTextRuns(Iterable textRuns, Graphics2D g2d, + Point2D nodeLocation) { + currentLocation = new Point2D.Double(nodeLocation.getX(), nodeLocation.getY()); + for (StrokingTextPainter.TextRun run : textRuns) { + paintTextRun(run, g2d); } } - Object letSpace = aci.getAttribute( - GVTAttributedCharacterIterator.TextAttribute.LETTER_SPACING); - if (letSpace != null) { - log.trace("-> letter spacing found"); - hasUnsupported = true; - } - - Object wordSpace = aci.getAttribute( - GVTAttributedCharacterIterator.TextAttribute.WORD_SPACING); - if (wordSpace != null) { - log.trace("-> word spacing found"); - hasUnsupported = true; - } - - Object lengthAdjust = aci.getAttribute( - GVTAttributedCharacterIterator.TextAttribute.LENGTH_ADJUST); - if (lengthAdjust != null) { - log.trace("-> length adjustments found"); - hasUnsupported = true; - } - - Object writeMod = aci.getAttribute( - GVTAttributedCharacterIterator.TextAttribute.WRITING_MODE); - if (writeMod != null - && !GVTAttributedCharacterIterator.TextAttribute.WRITING_MODE_LTR.equals( - writeMod)) { - log.trace("-> Unsupported writing modes found"); - hasUnsupported = true; - } - - Object vertOr = aci.getAttribute( - GVTAttributedCharacterIterator.TextAttribute.VERTICAL_ORIENTATION); - if (GVTAttributedCharacterIterator.TextAttribute.ORIENTATION_ANGLE.equals( - vertOr)) { - log.trace("-> vertical orientation found"); - hasUnsupported = true; - } - - Object rcDel = aci.getAttribute( - GVTAttributedCharacterIterator.TextAttribute.TEXT_COMPOUND_DELIMITER); - //Batik 1.6 returns null here which makes it impossible to determine whether this can - //be painted or not, i.e. fall back to stroking. :-( - if (rcDel != null && !(rcDel instanceof SVGOMTextElement)) { - log.trace("-> spans found"); - hasUnsupported = true; //Filter spans - } - - if (hasUnsupported) { - log.trace("Unsupported attributes found in ACI, using StrokingTextPainter"); - } - return hasUnsupported; - } - - /** - * Paint a list of text runs on the Graphics2D at a given location. - * @param textRuns the list of text runs - * @param g2d the Graphics2D to paint to - * @param loc the current location of the "cursor" - */ - protected void paintTextRuns(List textRuns, Graphics2D g2d, Point2D loc) { - Point2D currentloc = loc; - Iterator i = textRuns.iterator(); - while (i.hasNext()) { - StrokingTextPainter.TextRun - run = (StrokingTextPainter.TextRun)i.next(); - currentloc = paintTextRun(run, g2d, currentloc); - } - } - - /** - * Paint a single text run on the Graphics2D at a given location. - * @param run the text run to paint - * @param g2d the Graphics2D to paint to - * @param loc the current location of the "cursor" - * @return the new location of the "cursor" after painting the text run - */ - protected Point2D paintTextRun(StrokingTextPainter.TextRun run, Graphics2D g2d, Point2D loc) { - AttributedCharacterIterator aci = run.getACI(); - aci.first(); - - updateLocationFromACI(aci, loc); - AffineTransform at = g2d.getTransform(); - loc = at.transform(loc, null); - - // font - Font font = getFont(aci); - if (font != null) { - nativeTextHandler.setOverrideFont(font); - } - - // color - TextPaintInfo tpi = (TextPaintInfo) aci.getAttribute( - GVTAttributedCharacterIterator.TextAttribute.PAINT_INFO); - if (tpi == null) { - return loc; - } - Paint foreground = tpi.fillPaint; - if (foreground instanceof Color) { - Color col = (Color)foreground; - g2d.setColor(col); - } - g2d.setPaint(foreground); - - // text anchor - TextNode.Anchor anchor = (TextNode.Anchor)aci.getAttribute( - GVTAttributedCharacterIterator.TextAttribute.ANCHOR_TYPE); - - // text - String txt = getText(aci); - float advance = getStringWidth(txt, font); - float tx = 0; - if (anchor != null) { - switch (anchor.getType()) { - case TextNode.Anchor.ANCHOR_MIDDLE: - tx = -advance / 2; - break; - case TextNode.Anchor.ANCHOR_END: - tx = -advance; - break; - default: //nop + private void paintTextRun(StrokingTextPainter.TextRun run, Graphics2D g2d) { + AttributedCharacterIterator aci = run.getACI(); + aci.first(); + updateLocationFromACI(aci, currentLocation); + // font + Font font = getFont(aci); + if (font != null) { + nativeTextHandler.setOverrideFont(font); } - } - - // draw string - double x = loc.getX(); - double y = loc.getY(); - try { + // color + TextPaintInfo tpi = (TextPaintInfo) aci.getAttribute( + GVTAttributedCharacterIterator.TextAttribute.PAINT_INFO); + if (tpi == null) { + return; + } + Paint foreground = tpi.fillPaint; + if (foreground instanceof Color) { + Color col = (Color) foreground; + g2d.setColor(col); + } + g2d.setPaint(foreground); + // text anchor + TextNode.Anchor anchor = (TextNode.Anchor) aci.getAttribute( + GVTAttributedCharacterIterator.TextAttribute.ANCHOR_TYPE); + // text + String txt = getText(aci); + double advance = font == null ? run.getLayout().getAdvance2D().getX() : getStringWidth(txt, font); + double tx = 0; + if (anchor != null) { + switch (anchor.getType()) { + case TextNode.Anchor.ANCHOR_MIDDLE: + tx = -advance / 2; + break; + case TextNode.Anchor.ANCHOR_END: + tx = -advance; + break; + default: //nop + } + } + // draw string + Point2D outputLocation = g2d.getTransform().transform(currentLocation, null); + double x = outputLocation.getX(); + double y = outputLocation.getY(); try { - nativeTextHandler.drawString(g2d, txt, (float)x + tx, (float)y); - } catch (IOException ioe) { - if (g2d instanceof AFPGraphics2D) { - ((AFPGraphics2D)g2d).handleIOException(ioe); + try { + //TODO draw underline and overline if set + nativeTextHandler.drawString(g2d, txt, (float) (x + tx), (float) y); + //TODO draw strikethrough if set + } catch (IOException ioe) { + if (g2d instanceof AFPGraphics2D) { + ((AFPGraphics2D) g2d).handleIOException(ioe); + } } + } finally { + nativeTextHandler.setOverrideFont(null); + } + currentLocation.setLocation(currentLocation.getX() + advance, currentLocation.getY()); + } + private void updateLocationFromACI(AttributedCharacterIterator aci, Point2D loc) { + //Adjust position of span + Float xpos = (Float) aci.getAttribute( + GVTAttributedCharacterIterator.TextAttribute.X); + Float ypos = (Float) aci.getAttribute( + GVTAttributedCharacterIterator.TextAttribute.Y); + Float dxpos = (Float) aci.getAttribute( + GVTAttributedCharacterIterator.TextAttribute.DX); + Float dypos = (Float) aci.getAttribute( + GVTAttributedCharacterIterator.TextAttribute.DY); + if (xpos != null) { + loc.setLocation(xpos.doubleValue(), loc.getY()); + } + if (ypos != null) { + loc.setLocation(loc.getX(), ypos.doubleValue()); + } + if (dxpos != null) { + loc.setLocation(loc.getX() + dxpos.doubleValue(), loc.getY()); + } + if (dypos != null) { + loc.setLocation(loc.getX(), loc.getY() + dypos.doubleValue()); } - } finally { - nativeTextHandler.setOverrideFont(null); } - loc.setLocation(loc.getX() + advance, loc.getY()); - return loc; } /** @@ -305,36 +200,9 @@ public abstract class AbstractFOPTextPainter implements TextPainter { for (char c = aci.first(); c != CharacterIterator.DONE; c = aci.next()) { sb.append(c); } - aci.first(); return sb.toString(); } - private void updateLocationFromACI( - AttributedCharacterIterator aci, - Point2D loc) { - //Adjust position of span - Float xpos = (Float)aci.getAttribute( - GVTAttributedCharacterIterator.TextAttribute.X); - Float ypos = (Float)aci.getAttribute( - GVTAttributedCharacterIterator.TextAttribute.Y); - Float dxpos = (Float)aci.getAttribute( - GVTAttributedCharacterIterator.TextAttribute.DX); - Float dypos = (Float)aci.getAttribute( - GVTAttributedCharacterIterator.TextAttribute.DY); - if (xpos != null) { - loc.setLocation(xpos.doubleValue(), loc.getY()); - } - if (ypos != null) { - loc.setLocation(loc.getX(), ypos.doubleValue()); - } - if (dxpos != null) { - loc.setLocation(loc.getX() + dxpos.doubleValue(), loc.getY()); - } - if (dypos != null) { - loc.setLocation(loc.getX(), loc.getY() + dypos.doubleValue()); - } - } - private Font getFont(AttributedCharacterIterator aci) { Font[] fonts = ACIUtils.findFontsForBatikACI(aci, nativeTextHandler.getFontInfo()); return fonts == null ? null : fonts[0]; @@ -360,21 +228,6 @@ public abstract class AbstractFOPTextPainter implements TextPainter { return wordWidth / 1000f; } - private boolean hasUnsupportedGlyphs(String str, Font font) { - if (font == null) { - return true; - } - for (int i = 0; i < str.length(); i++) { - char c = str.charAt(i); - if (!((c == ' ') || (c == '\n') || (c == '\r') || (c == '\t'))) { - if (!font.hasChar(c)) { - return true; - } - } - } - return false; - } - /** * Get the outline shape of the text characters. * This uses the StrokingTextPainter to get the outline @@ -384,7 +237,7 @@ public abstract class AbstractFOPTextPainter implements TextPainter { * @return the outline shape of the text characters */ public Shape getOutline(TextNode node) { - return PROXY_PAINTER.getOutline(node); + return proxyTextPainter.getOutline(node); } /** @@ -399,7 +252,7 @@ public abstract class AbstractFOPTextPainter implements TextPainter { /* (todo) getBounds2D() is too slow * because it uses the StrokingTextPainter. We should implement this * method ourselves. */ - return PROXY_PAINTER.getBounds2D(node); + return proxyTextPainter.getBounds2D(node); } /** @@ -411,7 +264,7 @@ public abstract class AbstractFOPTextPainter implements TextPainter { * @return the bounds of the text */ public Rectangle2D getGeometryBounds(TextNode node) { - return PROXY_PAINTER.getGeometryBounds(node); + return proxyTextPainter.getGeometryBounds(node); } // Methods that have no purpose for PS diff --git a/src/java/org/apache/fop/svg/AbstractFOPTranscoder.java b/src/java/org/apache/fop/svg/AbstractFOPTranscoder.java index e04bf0d35..9b1606953 100644 --- a/src/java/org/apache/fop/svg/AbstractFOPTranscoder.java +++ b/src/java/org/apache/fop/svg/AbstractFOPTranscoder.java @@ -36,9 +36,9 @@ import org.apache.avalon.framework.configuration.DefaultConfiguration; import org.apache.commons.logging.Log; import org.apache.commons.logging.impl.SimpleLog; -import org.apache.batik.bridge.UserAgent; import org.apache.batik.dom.svg.SVGDOMImplementation; import org.apache.batik.dom.util.DocumentFactory; +import org.apache.batik.gvt.font.FontFamilyResolver; import org.apache.batik.transcoder.ErrorHandler; import org.apache.batik.transcoder.SVGAbstractTranscoder; import org.apache.batik.transcoder.TranscoderException; @@ -56,6 +56,8 @@ import org.apache.xmlgraphics.image.loader.ImageSessionContext; import org.apache.xmlgraphics.image.loader.impl.AbstractImageSessionContext; import org.apache.xmlgraphics.util.UnitConv; +import org.apache.fop.svg.font.FOPFontFamilyResolver; + /** * This is the common base class of all of FOP's transcoders. */ @@ -86,11 +88,6 @@ public abstract class AbstractFOPTranscoder extends SVGAbstractTranscoder implem /** The value to turn off text stroking. */ public static final Boolean VALUE_FORMAT_OFF = Boolean.FALSE; - /** - * The user agent dedicated to this Transcoder. - */ - protected UserAgent userAgent = createUserAgent(); - private Log logger; private EntityResolver resolver; private Configuration cfg = null; @@ -113,7 +110,7 @@ public abstract class AbstractFOPTranscoder extends SVGAbstractTranscoder implem * this method if you need non-default behaviour. * @return UserAgent the newly created user agent */ - protected UserAgent createUserAgent() { + protected FOPTranscoderUserAgent createUserAgent() { return new FOPTranscoderUserAgent(); } @@ -331,6 +328,8 @@ public abstract class AbstractFOPTranscoder extends SVGAbstractTranscoder implem */ protected class FOPTranscoderUserAgent extends SVGAbstractTranscoderUserAgent { + private FOPFontFamilyResolver fontFamilyResolver; + /** * Displays the specified error message using the {@link ErrorHandler}. * @param message the message to display @@ -386,6 +385,15 @@ public abstract class AbstractFOPTranscoder extends SVGAbstractTranscoder implem return "print"; } + public void setFontFamilyResolver(FOPFontFamilyResolver resolver) { + fontFamilyResolver = resolver; + } + + @Override + public FontFamilyResolver getFontFamilyResolver() { + return fontFamilyResolver; + } + } } diff --git a/src/java/org/apache/fop/svg/NativeTextPainter.java b/src/java/org/apache/fop/svg/NativeTextPainter.java index 4513e0101..200f6558b 100644 --- a/src/java/org/apache/fop/svg/NativeTextPainter.java +++ b/src/java/org/apache/fop/svg/NativeTextPainter.java @@ -19,7 +19,14 @@ package org.apache.fop.svg; +import java.awt.BasicStroke; +import java.awt.Color; import java.awt.Graphics2D; +import java.awt.Shape; +import java.awt.geom.AffineTransform; +import java.awt.geom.Ellipse2D; +import java.awt.geom.GeneralPath; +import java.awt.geom.Point2D; import java.io.IOException; import java.text.AttributedCharacterIterator; import java.util.List; @@ -27,11 +34,17 @@ import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.apache.batik.bridge.SVGGVTFont; +import org.apache.batik.gvt.font.FontFamilyResolver; +import org.apache.batik.gvt.font.GVTGlyphVector; import org.apache.batik.gvt.renderer.StrokingTextPainter; +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.svg.font.FOPFontFamilyResolverImpl; +import org.apache.fop.svg.font.FOPGVTFont; import org.apache.fop.util.CharUtilities; /** @@ -41,17 +54,26 @@ import org.apache.fop.util.CharUtilities; public abstract class NativeTextPainter extends StrokingTextPainter { /** the logger for this class */ - protected Log log = LogFactory.getLog(NativeTextPainter.class); + protected static final Log log = LogFactory.getLog(NativeTextPainter.class); + + private static final boolean DEBUG = false; /** the font collection */ protected final FontInfo fontInfo; + protected final FontFamilyResolver fontFamilyResolver; + + protected Font font; + + protected TextPaintInfo tpi; + /** * Creates a new instance. * @param fontInfo the font collection */ public NativeTextPainter(FontInfo fontInfo) { this.fontInfo = fontInfo; + this.fontFamilyResolver = new FOPFontFamilyResolverImpl(fontInfo); } /** @@ -68,11 +90,93 @@ public abstract class NativeTextPainter extends StrokingTextPainter { * @param g2d the target Graphics2D instance * @throws IOException if an I/O error occurs while rendering the text */ - protected abstract void paintTextRun(TextRun textRun, Graphics2D g2d) throws IOException; + protected final void paintTextRun(TextRun textRun, Graphics2D g2d) throws IOException { + AttributedCharacterIterator runaci = textRun.getACI(); + runaci.first(); + + tpi = (TextPaintInfo) runaci.getAttribute(PAINT_INFO); + if (tpi == null || !tpi.visible) { + return; + } + if (tpi.composite != null) { + g2d.setComposite(tpi.composite); + } + + //------------------------------------ + TextSpanLayout layout = textRun.getLayout(); + logTextRun(runaci, layout); + runaci.first(); //Reset ACI + + GeneralPath debugShapes = null; + if (DEBUG) { + debugShapes = new GeneralPath(); + } + + preparePainting(g2d); + + GVTGlyphVector gv = layout.getGlyphVector(); + if (!(gv.getFont() instanceof FOPGVTFont)) { + assert gv.getFont() == null || gv.getFont() instanceof SVGGVTFont; + //Draw using Java2D when no native fonts are available + textRun.getLayout().draw(g2d); + return; + } + font = ((FOPGVTFont) gv.getFont()).getFont(); + + saveGraphicsState(); + setInitialTransform(g2d.getTransform()); + clip(g2d.getClip()); + beginTextObject(); + + AffineTransform localTransform = new AffineTransform(); + Point2D prevPos = null; + AffineTransform prevGlyphTransform = null; + for (int index = 0, c = gv.getNumGlyphs(); index < c; index++) { + if (!gv.isGlyphVisible(index)) { + continue; + } + Point2D glyphPos = gv.getGlyphPosition(index); + + AffineTransform glyphTransform = gv.getGlyphTransform(index); + if (log.isTraceEnabled()) { + log.trace("pos " + glyphPos + ", transform " + glyphTransform); + } + if (DEBUG) { + Shape sh = gv.getGlyphLogicalBounds(index); + if (sh == null) { + sh = new Ellipse2D.Double(glyphPos.getX(), glyphPos.getY(), 2, 2); + } + debugShapes.append(sh, false); + } + + //Exact position of the glyph + localTransform.setToIdentity(); + localTransform.translate(glyphPos.getX(), glyphPos.getY()); + if (glyphTransform != null) { + localTransform.concatenate(glyphTransform); + } + localTransform.scale(1, -1); + + positionGlyph(prevPos, glyphPos, glyphTransform != null || prevGlyphTransform != null); + char glyph = (char) gv.getGlyphCode(index); + //Update last position + prevPos = glyphPos; + prevGlyphTransform = glyphTransform; + + writeGlyph(glyph, localTransform); + } + endTextObject(); + restoreGraphicsState(); + if (DEBUG) { + //Paint debug shapes + g2d.setStroke(new BasicStroke(0)); + g2d.setColor(Color.LIGHT_GRAY); + g2d.draw(debugShapes); + } + } - /** {@inheritDoc} */ @Override - protected void paintTextRuns(List textRuns, Graphics2D g2d) { + protected void paintTextRuns(@SuppressWarnings("rawtypes") List textRuns, Graphics2D g2d) { if (log.isTraceEnabled()) { log.trace("paintTextRuns: count = " + textRuns.size()); } @@ -81,7 +185,7 @@ public abstract class NativeTextPainter extends StrokingTextPainter { return; } for (int i = 0; i < textRuns.size(); i++) { - TextRun textRun = (TextRun)textRuns.get(i); + TextRun textRun = (TextRun) textRuns.get(i); try { paintTextRun(textRun, g2d); } catch (IOException ioe) { @@ -91,16 +195,6 @@ public abstract class NativeTextPainter extends StrokingTextPainter { } } - /** - * Finds an array of suitable fonts for a given AttributedCharacterIterator. - * @param aci the character iterator - * @return the array of fonts - */ - protected Font[] findFonts(AttributedCharacterIterator aci) { - Font[] fonts = ACIUtils.findFontsForBatikACI(aci, fontInfo); - return fonts; - } - /** * Collects all characters from an {@link AttributedCharacterIterator}. * @param runaci the character iterator @@ -115,6 +209,25 @@ public abstract class NativeTextPainter extends StrokingTextPainter { return chars; } + protected abstract void preparePainting(Graphics2D g2d); + + protected abstract void saveGraphicsState() throws IOException; + + protected abstract void restoreGraphicsState() throws IOException; + + protected abstract void setInitialTransform(AffineTransform transform) throws IOException; + + protected abstract void clip(Shape clip) throws IOException; + + protected abstract void beginTextObject() throws IOException; + + protected abstract void endTextObject() throws IOException; + + protected abstract void positionGlyph(Point2D prevPos, Point2D glyphPos, boolean reposition); + + protected abstract void writeGlyph(char glyph, AffineTransform transform) throws IOException; + + /** * @param runaci an attributed character iterator * @param layout a text span layout @@ -155,5 +268,9 @@ public abstract class NativeTextPainter extends StrokingTextPainter { } } + @Override + protected FontFamilyResolver getFontFamilyResolver() { + return this.fontFamilyResolver; + } } diff --git a/src/java/org/apache/fop/svg/PDFTextPainter.java b/src/java/org/apache/fop/svg/PDFTextPainter.java index ef376663f..c5fa9f04e 100644 --- a/src/java/org/apache/fop/svg/PDFTextPainter.java +++ b/src/java/org/apache/fop/svg/PDFTextPainter.java @@ -19,25 +19,17 @@ package org.apache.fop.svg; -import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Shape; import java.awt.Stroke; import java.awt.geom.AffineTransform; -import java.awt.geom.Ellipse2D; -import java.awt.geom.GeneralPath; import java.awt.geom.Point2D; -import java.text.AttributedCharacterIterator; -import org.apache.batik.gvt.font.GVTGlyphVector; 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.util.CharUtilities; /** * Renders the attributed character iterator of a {@link org.apache.batik.gvt.TextNode}. @@ -51,11 +43,19 @@ import org.apache.fop.util.CharUtilities; */ class PDFTextPainter extends NativeTextPainter { - private static final boolean DEBUG = false; + private PDFGraphics2D pdf; + + private PDFTextUtil textUtil; + + private double prevVisibleGlyphWidth; + + private boolean repositionNextGlyph; /** * Create a new PDF text painter with the given font information. + * * @param fi the font info + * @param fontFamilyResolver the Font Family Resolver */ public PDFTextPainter(FontInfo fi) { super(fi); @@ -67,29 +67,29 @@ class PDFTextPainter extends NativeTextPainter { return g2d instanceof PDFGraphics2D; } - /** {@inheritDoc} */ @Override - protected void paintTextRun(TextRun textRun, Graphics2D g2d) { - AttributedCharacterIterator runaci = textRun.getACI(); - runaci.first(); + protected void preparePainting(Graphics2D g2d) { + pdf = (PDFGraphics2D) g2d; + } - TextPaintInfo tpi = (TextPaintInfo)runaci.getAttribute(PAINT_INFO); - if (tpi == null || !tpi.visible) { - return; - } - if ((tpi != null) && (tpi.composite != null)) { - g2d.setComposite(tpi.composite); - } + @Override + protected void saveGraphicsState() { + pdf.saveGraphicsState(); + } - //------------------------------------ - TextSpanLayout layout = textRun.getLayout(); - logTextRun(runaci, layout); - CharSequence chars = collectCharacters(runaci); - runaci.first(); //Reset ACI + @Override + protected void restoreGraphicsState() { + pdf.restoreGraphicsState(); + } - final PDFGraphics2D pdf = (PDFGraphics2D)g2d; + @Override + protected void setInitialTransform(AffineTransform transform) { + createTextUtil(); + textUtil.concatMatrix(transform); + } - PDFTextUtil textUtil = new PDFTextUtil(pdf.fontInfo) { + private void createTextUtil() { + textUtil = new PDFTextUtil(pdf.fontInfo) { protected void write(String code) { pdf.currentStream.write(code); } @@ -97,142 +97,39 @@ class PDFTextPainter extends NativeTextPainter { pdf.currentStream.append(code); } }; + } - if (DEBUG) { - log.debug("Text: " + chars); - pdf.currentStream.write("%Text: " + chars + "\n"); - } - - GeneralPath debugShapes = null; - if (DEBUG) { - debugShapes = new GeneralPath(); - } - - Font[] fonts = findFonts(runaci); - if (fonts == null || fonts.length == 0) { - //Draw using Java2D when no native fonts are available - textRun.getLayout().draw(g2d); - return; - } - - pdf.saveGraphicsState(); - textUtil.concatMatrix(g2d.getTransform()); - Shape imclip = g2d.getClip(); - pdf.writeClip(imclip); - - applyColorAndPaint(tpi, pdf); + @Override + protected void clip(Shape clip) { + pdf.writeClip(clip); + } + @Override + protected void beginTextObject() { + applyColorAndPaint(tpi); textUtil.beginTextObject(); - textUtil.setFonts(fonts); - boolean stroke = (tpi.strokePaint != null) - && (tpi.strokeStroke != null); + boolean stroke = (tpi.strokePaint != null) && (tpi.strokeStroke != null); textUtil.setTextRenderingMode(tpi.fillPaint != null, stroke, false); + } - AffineTransform localTransform = new AffineTransform(); - Point2D prevPos = null; - double prevVisibleCharWidth = 0.0; - GVTGlyphVector gv = layout.getGlyphVector(); - for (int index = 0, c = gv.getNumGlyphs(); index < c; index++) { - char ch = chars.charAt(index); - boolean visibleChar = gv.isGlyphVisible(index) - || (CharUtilities.isAnySpace(ch) && !CharUtilities.isZeroWidthSpace(ch)); - logCharacter(ch, layout, index, visibleChar); - if (!visibleChar) { - continue; - } - Point2D glyphPos = gv.getGlyphPosition(index); - - AffineTransform glyphTransform = gv.getGlyphTransform(index); - //TODO Glyph transforms could be refined so not every char has to be painted - //with its own TJ command (stretch/squeeze case could be optimized) - if (log.isTraceEnabled()) { - log.trace("pos " + glyphPos + ", transform " + glyphTransform); - } - if (DEBUG) { - Shape sh = gv.getGlyphLogicalBounds(index); - if (sh == null) { - sh = new Ellipse2D.Double(glyphPos.getX(), glyphPos.getY(), 2, 2); - } - debugShapes.append(sh, false); - } - - //Exact position of the glyph - localTransform.setToIdentity(); - localTransform.translate(glyphPos.getX(), glyphPos.getY()); - if (glyphTransform != null) { - localTransform.concatenate(glyphTransform); - } - localTransform.scale(1, -1); - - boolean yPosChanged = (prevPos == null - || prevPos.getY() != glyphPos.getY() - || glyphTransform != null); - if (yPosChanged) { - if (index > 0) { - textUtil.writeTJ(); - textUtil.writeTextMatrix(localTransform); - } - } else { - double xdiff = glyphPos.getX() - prevPos.getX(); - //Width of previous character - Font font = textUtil.getCurrentFont(); - double cw = prevVisibleCharWidth; - double effxdiff = (1000 * xdiff) - cw; - if (effxdiff != 0) { - double adjust = (-effxdiff / font.getFontSize()); - textUtil.adjustGlyphTJ(adjust * 1000); - } - if (log.isTraceEnabled()) { - log.trace("==> x diff: " + xdiff + ", " + effxdiff - + ", charWidth: " + cw); - } - } - Font f = textUtil.selectFontForChar(ch); - char paintChar = (CharUtilities.isAnySpace(ch) ? ' ' : ch); - char mappedChar = f.mapChar(paintChar); - boolean encodingChanging = false; // used for single byte - if (!textUtil.isMultiByteFont(f.getFontName())) { - int encoding = mappedChar / 256; - mappedChar = (char) (mappedChar % 256); - if (textUtil.getCurrentEncoding() != encoding) { - textUtil.setCurrentEncoding(encoding); - encodingChanging = true; - } - } - if (f != textUtil.getCurrentFont() || encodingChanging) { - textUtil.writeTJ(); - textUtil.setCurrentFont(f); - textUtil.writeTf(f); - textUtil.writeTextMatrix(localTransform); - } - textUtil.writeTJMappedChar(mappedChar); - - //Update last position - prevPos = glyphPos; - prevVisibleCharWidth = textUtil.getCurrentFont().getCharWidth(chars.charAt(index)); - } + @Override + protected void endTextObject() { textUtil.writeTJ(); textUtil.endTextObject(); - pdf.restoreGraphicsState(); - if (DEBUG) { - g2d.setStroke(new BasicStroke(0)); - g2d.setColor(Color.LIGHT_GRAY); - g2d.draw(debugShapes); - } } - private void applyColorAndPaint(TextPaintInfo tpi, PDFGraphics2D pdf) { + private void applyColorAndPaint(TextPaintInfo tpi) { Paint fillPaint = tpi.fillPaint; Paint strokePaint = tpi.strokePaint; Stroke stroke = tpi.strokeStroke; int fillAlpha = PDFGraphics2D.OPAQUE; if (fillPaint instanceof Color) { - Color col = (Color)fillPaint; + Color col = (Color) fillPaint; pdf.applyColor(col, true); fillAlpha = col.getAlpha(); } if (strokePaint instanceof Color) { - Color col = (Color)strokePaint; + Color col = (Color) strokePaint; pdf.applyColor(col, false); } pdf.applyPaint(fillPaint, true); @@ -243,4 +140,46 @@ class PDFTextPainter extends NativeTextPainter { pdf.applyAlpha(fillAlpha, PDFGraphics2D.OPAQUE); } + @Override + protected void positionGlyph(Point2D prevPos, Point2D glyphPos, boolean reposition) { + // TODO Glyph transforms could be refined so not every char has to be painted + // with its own TJ command (stretch/squeeze case could be optimized) + repositionNextGlyph = (prevPos == null + || prevPos.getY() != glyphPos.getY() + || reposition); + if (!repositionNextGlyph) { + double xdiff = glyphPos.getX() - prevPos.getX(); + //Width of previous character + double cw = prevVisibleGlyphWidth; + double effxdiff = (1000 * xdiff) - cw; + if (effxdiff != 0) { + double adjust = (-effxdiff / font.getFontSize()); + textUtil.adjustGlyphTJ(adjust * 1000); + } + } + } + + @Override + protected void writeGlyph(char glyph, AffineTransform transform) { + prevVisibleGlyphWidth = font.getWidth(glyph); + boolean encodingChanging = false; // used for single byte + if (!textUtil.isMultiByteFont(font.getFontName())) { + int encoding = glyph / 256; + glyph = (char) (glyph % 256); + if (textUtil.getCurrentEncoding() != encoding) { + textUtil.setCurrentEncoding(encoding); + encodingChanging = true; + } + } + if (repositionNextGlyph || encodingChanging) { + textUtil.writeTJ(); + if (font != textUtil.getCurrentFont() || encodingChanging) { + textUtil.setCurrentFont(font); + textUtil.writeTf(font); + } + textUtil.writeTextMatrix(transform); + } + textUtil.writeTJMappedChar(glyph); + } + } diff --git a/src/java/org/apache/fop/svg/PDFTextUtil.java b/src/java/org/apache/fop/svg/PDFTextUtil.java index 8325dce1f..d525ecefc 100644 --- a/src/java/org/apache/fop/svg/PDFTextUtil.java +++ b/src/java/org/apache/fop/svg/PDFTextUtil.java @@ -30,7 +30,6 @@ import org.apache.fop.fonts.Typeface; public abstract class PDFTextUtil extends org.apache.fop.pdf.PDFTextUtil { private FontInfo fontInfo; - private Font[] fonts; private Font font; private int encoding; @@ -49,23 +48,6 @@ public abstract class PDFTextUtil extends org.apache.fop.pdf.PDFTextUtil { this.font = null; } - /** - * Sets the current fonts for the text object. For every character, the suitable font will - * be selected. - * @param fonts the new fonts - */ - public void setFonts(Font[] fonts) { - this.fonts = fonts; - } - - /** - * Sets the current font for the text object. - * @param font the new font - */ - public void setFont(Font font) { - setFonts(new Font[] {font}); - } - /** * Returns the current font in use. * @return the current font or null if no font is currently active. @@ -123,27 +105,4 @@ public abstract class PDFTextUtil extends org.apache.fop.pdf.PDFTextUtil { } } - /** - * Selects a font from the font list suitable to display the given character. - * @param ch the character - * @return the recommended Font to use - */ - public Font selectFontForChar(char ch) { - for (int i = 0, c = fonts.length; i < c; i++) { - if (fonts[i].hasChar(ch)) { - return fonts[i]; - } - } - return fonts[0]; //TODO Maybe fall back to painting with shapes - } - - /** - * Writes a char to the "TJ-Buffer". - * @param ch the unmapped character - */ - public void writeTJChar(char ch) { - char mappedChar = font.mapChar(ch); - writeTJMappedChar(mappedChar); - } - } diff --git a/src/java/org/apache/fop/svg/PDFTranscoder.java b/src/java/org/apache/fop/svg/PDFTranscoder.java index 9f0345657..1a3f154cb 100644 --- a/src/java/org/apache/fop/svg/PDFTranscoder.java +++ b/src/java/org/apache/fop/svg/PDFTranscoder.java @@ -31,7 +31,6 @@ import org.apache.avalon.framework.configuration.Configuration; import org.apache.batik.bridge.BridgeContext; import org.apache.batik.bridge.UnitProcessor; -import org.apache.batik.bridge.UserAgent; import org.apache.batik.ext.awt.RenderingHintsKeyExt; import org.apache.batik.transcoder.TranscoderException; import org.apache.batik.transcoder.TranscoderOutput; @@ -39,6 +38,7 @@ import org.apache.batik.transcoder.image.ImageTranscoder; import org.apache.fop.Version; import org.apache.fop.fonts.FontInfo; +import org.apache.fop.svg.font.FOPFontFamilyResolverImpl; /** *

This class enables to transcode an input to a PDF document.

@@ -88,7 +88,7 @@ public class PDFTranscoder extends AbstractFOPTranscoder { /** * {@inheritDoc} */ - protected UserAgent createUserAgent() { + protected FOPTranscoderUserAgent createUserAgent() { return new AbstractFOPTranscoder.FOPTranscoderUserAgent() { // The PDF stuff wants everything at 72dpi public float getPixelUnitToMillimeter() { @@ -131,6 +131,8 @@ public class PDFTranscoder extends AbstractFOPTranscoder { } else { graphics.setupDefaultFontInfo(); } + ((FOPTranscoderUserAgent) userAgent).setFontFamilyResolver( + new FOPFontFamilyResolverImpl(graphics.getFontInfo())); } catch (Exception e) { throw new TranscoderException( "Error while setting up PDFDocumentGraphics2D", e); diff --git a/src/java/org/apache/fop/svg/SVGUserAgent.java b/src/java/org/apache/fop/svg/SVGUserAgent.java index d43552289..a265b4fef 100644 --- a/src/java/org/apache/fop/svg/SVGUserAgent.java +++ b/src/java/org/apache/fop/svg/SVGUserAgent.java @@ -21,6 +21,8 @@ package org.apache.fop.svg; import java.awt.geom.AffineTransform; +import org.apache.batik.gvt.font.FontFamilyResolver; + import org.apache.fop.apps.FOUserAgent; /** @@ -34,10 +36,11 @@ public class SVGUserAgent extends SimpleSVGUserAgent { /** * Creates a new SVGUserAgent. * @param foUserAgent the FO user agent to associate with this SVG user agent + * @param fontFamilyResolver the font family resolver * @param at the current transform */ - public SVGUserAgent(FOUserAgent foUserAgent, AffineTransform at) { - super(foUserAgent.getSourcePixelUnitToMillimeter(), at); + public SVGUserAgent(FOUserAgent foUserAgent, FontFamilyResolver fontFamilyResolver, AffineTransform at) { + super(foUserAgent.getSourcePixelUnitToMillimeter(), at, fontFamilyResolver); this.eventProducer = SVGEventProducer.Provider.get(foUserAgent.getEventBroadcaster()); } @@ -45,8 +48,8 @@ public class SVGUserAgent extends SimpleSVGUserAgent { * Creates a new SVGUserAgent. * @param foUserAgent the FO user agent to associate with this SVG user agent */ - public SVGUserAgent(FOUserAgent foUserAgent) { - this(foUserAgent, new AffineTransform()); + public SVGUserAgent(FOUserAgent foUserAgent, FontFamilyResolver fontFamilyResolver) { + this(foUserAgent, fontFamilyResolver, new AffineTransform()); } /** diff --git a/src/java/org/apache/fop/svg/SimpleSVGUserAgent.java b/src/java/org/apache/fop/svg/SimpleSVGUserAgent.java index 2b27945a4..42a18b17c 100644 --- a/src/java/org/apache/fop/svg/SimpleSVGUserAgent.java +++ b/src/java/org/apache/fop/svg/SimpleSVGUserAgent.java @@ -26,6 +26,7 @@ import java.awt.geom.Dimension2D; import javax.xml.parsers.SAXParserFactory; import org.apache.batik.bridge.UserAgentAdapter; +import org.apache.batik.gvt.font.FontFamilyResolver; /** * A simple SVG user agent. @@ -35,14 +36,18 @@ import org.apache.batik.bridge.UserAgentAdapter; public class SimpleSVGUserAgent extends UserAgentAdapter { private AffineTransform currentTransform = null; + private float pixelUnitToMillimeter = 0.0f; + private final FontFamilyResolver fontFamilyResolver; + /** * Creates a new user agent. * @param pixelUnitToMM the pixel to millimeter conversion factor currently in effect * @param at the current transform */ - public SimpleSVGUserAgent(float pixelUnitToMM, AffineTransform at) { + public SimpleSVGUserAgent(float pixelUnitToMM, AffineTransform at, FontFamilyResolver fontFamilyResolver) { + this.fontFamilyResolver = fontFamilyResolver; pixelUnitToMillimeter = pixelUnitToMM; currentTransform = at; } @@ -122,5 +127,10 @@ public class SimpleSVGUserAgent extends UserAgentAdapter { return new Dimension(100, 100); } + @Override + public FontFamilyResolver getFontFamilyResolver() { + return fontFamilyResolver; + } + } diff --git a/src/java/org/apache/fop/svg/font/AggregatingFontFamilyResolver.java b/src/java/org/apache/fop/svg/font/AggregatingFontFamilyResolver.java new file mode 100644 index 000000000..313a6a74b --- /dev/null +++ b/src/java/org/apache/fop/svg/font/AggregatingFontFamilyResolver.java @@ -0,0 +1,70 @@ +/* + * 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.svg.font; + +import java.util.Arrays; +import java.util.List; + +import org.apache.batik.gvt.font.FontFamilyResolver; +import org.apache.batik.gvt.font.GVTFontFamily; + +public class AggregatingFontFamilyResolver implements FontFamilyResolver { + + private final List resolvers; + + public AggregatingFontFamilyResolver(FontFamilyResolver... resolvers) { + this.resolvers = Arrays.asList(resolvers); + } + + public String lookup(String familyName) { + for (FontFamilyResolver resolver : resolvers) { + String lookup = resolver.lookup(familyName); + if (lookup != null) { + return lookup; + } + } + return null; + } + + public GVTFontFamily resolve(String familyName) { + for (FontFamilyResolver resolver : resolvers) { + GVTFontFamily family = resolver.resolve(familyName); + if (family != null) { + return family; + } + } + return null; + } + + public GVTFontFamily getDefault() { + return resolve("any"); + } + + public GVTFontFamily getFamilyThatCanDisplay(char c) { + for (FontFamilyResolver resolver : resolvers) { + GVTFontFamily family = resolver.getFamilyThatCanDisplay(c); + if (family != null) { + return family; + } + } + return null; + } + +} diff --git a/src/java/org/apache/fop/svg/font/FOPFontFamilyResolver.java b/src/java/org/apache/fop/svg/font/FOPFontFamilyResolver.java new file mode 100644 index 000000000..7af5f0b4f --- /dev/null +++ b/src/java/org/apache/fop/svg/font/FOPFontFamilyResolver.java @@ -0,0 +1,31 @@ +/* + * 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.svg.font; + +import org.apache.batik.gvt.font.FontFamilyResolver; + +public interface FOPFontFamilyResolver extends FontFamilyResolver { + + FOPGVTFontFamily resolve(String familyName); + + FOPGVTFontFamily getDefault(); + + FOPGVTFontFamily getFamilyThatCanDisplay(char c); +} diff --git a/src/java/org/apache/fop/svg/font/FOPFontFamilyResolverImpl.java b/src/java/org/apache/fop/svg/font/FOPFontFamilyResolverImpl.java new file mode 100644 index 000000000..a9a631691 --- /dev/null +++ b/src/java/org/apache/fop/svg/font/FOPFontFamilyResolverImpl.java @@ -0,0 +1,67 @@ +/* + * 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.svg.font; + +import java.util.Map; + +import org.apache.fop.fonts.Font; +import org.apache.fop.fonts.FontInfo; +import org.apache.fop.fonts.FontTriplet; +import org.apache.fop.fonts.Typeface; + +public class FOPFontFamilyResolverImpl implements FOPFontFamilyResolver { + + private final FontInfo fontInfo; + + public FOPFontFamilyResolverImpl(FontInfo fontInfo) { + this.fontInfo = fontInfo; + } + + public String lookup(String familyName) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException(); + } + + public FOPGVTFontFamily resolve(String familyName) { + FOPGVTFontFamily gvtFontFamily = null; + FontTriplet triplet = fontInfo.fontLookup(familyName, Font.STYLE_NORMAL, Font.WEIGHT_NORMAL); + if (fontInfo.hasFont(familyName, Font.STYLE_NORMAL, Font.WEIGHT_NORMAL)) { + gvtFontFamily = new FOPGVTFontFamily(fontInfo, familyName, triplet); + } + return gvtFontFamily; + } + + public FOPGVTFontFamily getDefault() { + return resolve("any"); + } + + public FOPGVTFontFamily getFamilyThatCanDisplay(char c) { + Map fonts = fontInfo.getFonts(); + for (Typeface font : fonts.values()) { + if (font.hasChar(c)) { + String fontFamily = font.getFamilyNames().iterator().next(); + return new FOPGVTFontFamily(fontInfo, fontFamily, + new FontTriplet(fontFamily, Font.STYLE_NORMAL, Font.WEIGHT_NORMAL)); + } + } + return null; + } + +} diff --git a/src/java/org/apache/fop/svg/font/FOPGVTFont.java b/src/java/org/apache/fop/svg/font/FOPGVTFont.java new file mode 100644 index 000000000..c55e2fa8b --- /dev/null +++ b/src/java/org/apache/fop/svg/font/FOPGVTFont.java @@ -0,0 +1,159 @@ +/* + * 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.svg.font; + +import java.awt.font.FontRenderContext; +import java.text.CharacterIterator; +import java.text.StringCharacterIterator; + +import org.apache.batik.gvt.font.GVTFont; +import org.apache.batik.gvt.font.GVTFontFamily; +import org.apache.batik.gvt.font.GVTGlyphVector; +import org.apache.batik.gvt.font.GVTLineMetrics; + +import org.apache.fop.fonts.Font; +import org.apache.fop.fonts.FontMetrics; + +public class FOPGVTFont implements GVTFont { + + private final Font font; + + private final GVTFontFamily fontFamily; + + public FOPGVTFont(Font font, GVTFontFamily fontFamily) { + this.font = font; + this.fontFamily = fontFamily; + } + + public Font getFont() { + return font; + } + + public boolean canDisplay(char c) { + return font.hasChar(c); + } + + public int canDisplayUpTo(char[] text, int start, int limit) { + for (int i = start; i < limit; i++) { + if (!canDisplay(text[i])) { + return i; + } + } + return -1; + } + + + public int canDisplayUpTo(CharacterIterator iter, int start, int limit) { + for (char c = iter.setIndex(start); iter.getIndex() < limit; c = iter.next()) { + if (!canDisplay(c)) { + return iter.getIndex(); + } + } + return -1; + } + + public int canDisplayUpTo(String str) { + for (int i = 0; i < str.length(); i++) { + if (!canDisplay(str.charAt(i))) { + return i; + } + } + return -1; + } + + public GVTGlyphVector createGlyphVector(FontRenderContext frc, char[] chars) { + return createGlyphVector(frc, new String(chars)); + } + + public GVTGlyphVector createGlyphVector(FontRenderContext frc, CharacterIterator ci) { + // TODO Batik does manual glyph shaping for Arabic. Replace with complex scripts implementation + return new FOPGVTGlyphVector(this, ci, frc); + } + + public GVTGlyphVector createGlyphVector(FontRenderContext frc, + int[] glyphCodes, + CharacterIterator ci) { + throw new UnsupportedOperationException("Not implemented"); + } + + public GVTGlyphVector createGlyphVector(FontRenderContext frc, String str) { + StringCharacterIterator sci = new StringCharacterIterator(str); + return createGlyphVector(frc, sci); + } + + public FOPGVTFont deriveFont(float size) { + throw new UnsupportedOperationException("Not implemented"); + } + + public String getFamilyName() { + return fontFamily.getFamilyName(); + } + + public GVTLineMetrics getLineMetrics(char[] chars, int beginIndex, int limit, FontRenderContext frc) { + return getLineMetrics(limit - beginIndex); + } + + GVTLineMetrics getLineMetrics(int numChars) { + numChars = numChars < 0 ? 0 : numChars; + FontMetrics metrics = font.getFontMetrics(); + int size = font.getFontSize(); + return new GVTLineMetrics( + metrics.getCapHeight(size) / 1000000f, + java.awt.Font.ROMAN_BASELINE, // Not actually used by Batik + null, // Not actually used by Batik + -metrics.getDescender(size) / 1000000f, + 0, // Not actually used by Batik + 0, // Not actually used by Batik + numChars, + -metrics.getStrikeoutPosition(size) / 1000000f, + metrics.getStrikeoutThickness(size) / 1000000f, + -metrics.getUnderlinePosition(size) / 1000000f, + metrics.getUnderlineThickness(size) / 1000000f, + -metrics.getCapHeight(size) / 1000000f, // Because this is what Batik does in GVTLineMetrics + metrics.getUnderlineThickness(size) / 1000000f); + } + + public GVTLineMetrics getLineMetrics(CharacterIterator ci, int beginIndex, int limit, + FontRenderContext frc) { + return getLineMetrics(limit - beginIndex); + } + + public GVTLineMetrics getLineMetrics(String str, FontRenderContext frc) { + return getLineMetrics(str.length()); + } + + public GVTLineMetrics getLineMetrics(String str, int beginIndex, int limit, FontRenderContext frc) { + return getLineMetrics(limit - beginIndex); + } + + public float getSize() { + return font.getFontSize() / 1000f; + } + + public float getVKern(int glyphCode1, int glyphCode2) { + return 0; + } + + public float getHKern(int glyphCode1, int glyphCode2) { + // TODO Cannot be implemented until getKernValue takes glyph indices instead of character codes + return 0; + } + +} diff --git a/src/java/org/apache/fop/svg/font/FOPGVTFontFamily.java b/src/java/org/apache/fop/svg/font/FOPGVTFontFamily.java new file mode 100644 index 000000000..036b560a0 --- /dev/null +++ b/src/java/org/apache/fop/svg/font/FOPGVTFontFamily.java @@ -0,0 +1,73 @@ +/* + * 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.svg.font; + +import java.awt.font.TextAttribute; +import java.text.AttributedCharacterIterator; +import java.util.Map; + +import org.apache.batik.gvt.font.GVTFontFace; +import org.apache.batik.gvt.font.GVTFontFamily; + +import org.apache.fop.fonts.FontInfo; +import org.apache.fop.fonts.FontTriplet; +import org.apache.fop.svg.ACIUtils; + +public class FOPGVTFontFamily implements GVTFontFamily { + + private final FontInfo fontInfo; + + private final FontTriplet fontTriplet; + + private final String familyName; + + public FOPGVTFontFamily(FontInfo fontInfo, String familyName, FontTriplet triplet) { + this.fontInfo = fontInfo; + this.fontTriplet = triplet; + this.familyName = familyName; + } + + public String getFamilyName() { + return familyName; + } + + public GVTFontFace getFontFace() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException(); + } + + public FOPGVTFont deriveFont(float size, AttributedCharacterIterator aci) { + return deriveFont(size, aci.getAttributes()); + } + + public FOPGVTFont deriveFont(float size, @SuppressWarnings("rawtypes") Map attrs) { + Float fontWeight = (Float) attrs.get(TextAttribute.WEIGHT); + int weight = fontWeight == null ? fontTriplet.getWeight() : ACIUtils.toCSSWeight(fontWeight); + Float fontStyle = (Float) attrs.get(TextAttribute.POSTURE); + String style = fontStyle == null ? fontTriplet.getStyle() : ACIUtils.toStyle(fontStyle); + FontTriplet triplet = fontInfo.fontLookup(fontTriplet.getName(), style, weight); + return new FOPGVTFont(fontInfo.getFontInstance(triplet, (int) (size * 1000)), this); + } + + public boolean isComplex() { + return false; + } + +} diff --git a/src/java/org/apache/fop/svg/font/FOPGVTGlyphVector.java b/src/java/org/apache/fop/svg/font/FOPGVTGlyphVector.java new file mode 100644 index 000000000..3567bb508 --- /dev/null +++ b/src/java/org/apache/fop/svg/font/FOPGVTGlyphVector.java @@ -0,0 +1,339 @@ +/* + * 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.svg.font; + +import java.awt.Graphics2D; +import java.awt.Rectangle; +import java.awt.Shape; +import java.awt.font.FontRenderContext; +import java.awt.font.GlyphJustificationInfo; +import java.awt.font.GlyphMetrics; +import java.awt.geom.AffineTransform; +import java.awt.geom.GeneralPath; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.text.AttributedCharacterIterator; +import java.text.CharacterIterator; +import java.text.StringCharacterIterator; +import java.util.Arrays; + +import org.apache.batik.gvt.font.GVTFont; +import org.apache.batik.gvt.font.GVTGlyphMetrics; +import org.apache.batik.gvt.font.GVTGlyphVector; +import org.apache.batik.gvt.font.GVTLineMetrics; + +import org.apache.fop.fonts.Font; +import org.apache.fop.fonts.FontMetrics; +import org.apache.fop.fonts.GlyphMapping; +import org.apache.fop.fonts.TextFragment; +import org.apache.fop.traits.MinOptMax; + +class FOPGVTGlyphVector implements GVTGlyphVector { + + private final CharacterIterator charIter; + + private final FOPGVTFont font; + + private final int fontSize; + + private final FontMetrics fontMetrics; + + private final FontRenderContext frc; + + private int[] glyphs; + + private float[] positions; + + private Rectangle2D[] boundingBoxes; + + private GeneralPath outline; + + private AffineTransform[] glyphTransforms; + + private boolean[] glyphVisibilities; + + private Rectangle2D logicalBounds; + + FOPGVTGlyphVector(FOPGVTFont font, final CharacterIterator iter, FontRenderContext frc) { + this.charIter = iter; + this.font = font; + Font f = font.getFont(); + this.fontSize = f.getFontSize(); + this.fontMetrics = f.getFontMetrics(); + this.frc = frc; + } + + public void performDefaultLayout() { + Font f = font.getFont(); + TextFragment text = new SVGTextFragment(charIter); + MinOptMax letterSpaceIPD = MinOptMax.ZERO; + MinOptMax[] letterSpaceAdjustments = new MinOptMax[charIter.getEndIndex() - charIter.getBeginIndex()]; + GlyphMapping mapping = GlyphMapping.doGlyphMapping(text, charIter.getBeginIndex(), charIter.getEndIndex(), + f, letterSpaceIPD, letterSpaceAdjustments, '\0', '\0', false, 0 /* TODO */); + glyphs = buildGlyphs(f, mapping.mapping != null ? new StringCharacterIterator(mapping.mapping) : charIter); + buildGlyphPositions(mapping, letterSpaceAdjustments); + this.glyphVisibilities = new boolean[this.glyphs.length]; + Arrays.fill(glyphVisibilities, true); + this.glyphTransforms = new AffineTransform[this.glyphs.length]; + } + + private static class SVGTextFragment implements TextFragment { + + private final CharacterIterator charIter; + + SVGTextFragment(CharacterIterator charIter) { + this.charIter = charIter; + } + + public CharSequence subSequence(int startIndex, int endIndex) { + StringBuilder sb = new StringBuilder(); + for (char c = charIter.first(); c != CharacterIterator.DONE; c = charIter.next()) { + sb.append(c); + } + return sb.toString(); + } + + public String getScript() { + return "DFLT"; // TODO pass on script value from SVG + } + + public String getLanguage() { + return "dflt"; // TODO pass on language value from SVG + } + + public char charAt(int index) { + return charIter.setIndex(index - charIter.getBeginIndex()); + } + } + + private int[] buildGlyphs(Font font, final CharacterIterator charIter) { + int[] glyphs = new int[charIter.getEndIndex() - charIter.getBeginIndex()]; + int index = 0; + for (char c = charIter.first(); c != CharacterIterator.DONE; c = charIter.next()) { + glyphs[index] = font.mapChar(c); + index++; + } + return glyphs; + } + + private void buildGlyphPositions(GlyphMapping ai, MinOptMax[] letterSpaceAdjustments) { + positions = new float[2 * glyphs.length + 2]; + if (ai.gposAdjustments != null) { + assert ai.gposAdjustments.length == glyphs.length; + for (int glyphIndex = 0; glyphIndex < glyphs.length; glyphIndex++) { + int n = 2 * glyphIndex; + if (ai.gposAdjustments[glyphIndex] != null) { + for (int p = 0; p < 4; p++) { + positions[n + p] += ai.gposAdjustments[glyphIndex][p] / 1000f; + } + } + positions[n + 2] += positions[n] + getGlyphWidth(glyphIndex); + } + } else { + for (int i = 0, n = 2; i < glyphs.length; i++, n += 2) { + int kern = i < glyphs.length - 1 && letterSpaceAdjustments[i + 1] != null + ? letterSpaceAdjustments[i + 1].getOpt() + : 0; + positions[n] = positions[n - 2] + getGlyphWidth(i) + kern / 1000f; + positions[n + 1] = 0; + } + } + } + + private float getGlyphWidth(int index) { + return fontMetrics.getWidth(glyphs[index], fontSize) / 1000000f; + } + + public GVTFont getFont() { + return font; + } + + public FontRenderContext getFontRenderContext() { + return frc; + } + + public int getGlyphCode(int glyphIndex) { + return glyphs[glyphIndex]; + } + + public int[] getGlyphCodes(int beginGlyphIndex, int numEntries, + int[] codeReturn) { + if (codeReturn == null) { + codeReturn = new int[numEntries]; + } + System.arraycopy(glyphs, beginGlyphIndex, codeReturn, 0, numEntries); + return codeReturn; + } + + public GlyphJustificationInfo getGlyphJustificationInfo(int glyphIndex) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException(); + } + + public Shape getGlyphLogicalBounds(int glyphIndex) { + GVTGlyphMetrics metrics = getGlyphMetrics(glyphIndex); + Point2D pos = getGlyphPosition(glyphIndex); + GVTLineMetrics fontMetrics = font.getLineMetrics(0); + Rectangle2D bounds = new Rectangle2D.Float(0, -fontMetrics.getDescent(), metrics.getHorizontalAdvance(), + fontMetrics.getAscent() + fontMetrics.getDescent()); + AffineTransform t = AffineTransform.getTranslateInstance(pos.getX(), pos.getY()); + AffineTransform transf = getGlyphTransform(glyphIndex); + if (transf != null) { + t.concatenate(transf); + } + t.scale(1, -1); // Translate from glyph coordinate system to user + return t.createTransformedShape(bounds); + } + + public GVTGlyphMetrics getGlyphMetrics(int glyphIndex) { + Rectangle2D bbox = getBoundingBoxes()[glyphIndex]; + return new GVTGlyphMetrics(positions[2 * (glyphIndex + 1)] - positions[2 * glyphIndex], + (fontMetrics.getAscender(fontSize) - fontMetrics.getDescender(fontSize)) / 1000000f, + bbox, GlyphMetrics.STANDARD); + } + + public Shape getGlyphOutline(int glyphIndex) { + Shape glyphBox = getBoundingBoxes()[glyphIndex]; + AffineTransform tr = AffineTransform.getTranslateInstance(positions[glyphIndex * 2], + positions[glyphIndex * 2 + 1]); + AffineTransform glyphTransform = getGlyphTransform(glyphIndex); + if (glyphTransform != null) { + tr.concatenate(glyphTransform); + } + return tr.createTransformedShape(glyphBox); + } + + public Rectangle2D getGlyphCellBounds(int glyphIndex) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException(); + } + + public Point2D getGlyphPosition(int glyphIndex) { + int positionIndex = glyphIndex * 2; + return new Point2D.Float(positions[positionIndex], positions[positionIndex + 1]); + } + + public float[] getGlyphPositions(int beginGlyphIndex, int numEntries, float[] positionReturn) { + if (positionReturn == null) { + positionReturn = new float[numEntries * 2]; + } + System.arraycopy(positions, beginGlyphIndex * 2, positionReturn, 0, numEntries * 2); + return positionReturn; + } + + public AffineTransform getGlyphTransform(int glyphIndex) { + return glyphTransforms[glyphIndex]; + } + + public Shape getGlyphVisualBounds(int glyphIndex) { + Rectangle2D bbox = getBoundingBoxes()[glyphIndex]; + Point2D pos = getGlyphPosition(glyphIndex); + AffineTransform t = AffineTransform.getTranslateInstance(pos.getX(), pos.getY()); + AffineTransform transf = getGlyphTransform(glyphIndex); + if (transf != null) { + t.concatenate(transf); + } + return t.createTransformedShape(bbox); + } + + public Rectangle2D getLogicalBounds() { + if (logicalBounds == null) { + GeneralPath logicalBoundsPath = new GeneralPath(); + for (int i = 0; i < getNumGlyphs(); i++) { + Shape glyphLogicalBounds = getGlyphLogicalBounds(i); + logicalBoundsPath.append(glyphLogicalBounds, false); + } + logicalBounds = logicalBoundsPath.getBounds2D(); + } + return logicalBounds; + } + + public int getNumGlyphs() { + return glyphs.length; + } + + public Shape getOutline() { + if (outline == null) { + outline = new GeneralPath(); + for (int i = 0; i < glyphs.length; i++) { + outline.append(getGlyphOutline(i), false); + } + } + return outline; + } + + public Shape getOutline(float x, float y) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException(); + } + + public Rectangle2D getGeometricBounds() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException(); + } + + public Rectangle2D getBounds2D(AttributedCharacterIterator aci) { + return getOutline().getBounds2D(); + } + + public void setGlyphPosition(int glyphIndex, Point2D newPos) { + int idx = glyphIndex * 2; + positions[idx] = (float) newPos.getX(); + positions[idx + 1] = (float) newPos.getY(); + } + + public void setGlyphTransform(int glyphIndex, AffineTransform newTX) { + glyphTransforms[glyphIndex] = newTX; + } + + public void setGlyphVisible(int glyphIndex, boolean visible) { + glyphVisibilities[glyphIndex] = visible; + } + + public boolean isGlyphVisible(int glyphIndex) { + return glyphVisibilities[glyphIndex]; + } + + public int getCharacterCount(int startGlyphIndex, int endGlyphIndex) { + // TODO Not that simple if complex scripts are involved + return endGlyphIndex - startGlyphIndex + 1; + } + + public void draw(Graphics2D graphics2d, AttributedCharacterIterator aci) { + // NOP + } + + private Rectangle2D[] getBoundingBoxes() { + if (boundingBoxes == null) { + buildBoundingBoxes(); + } + return boundingBoxes; + } + + private void buildBoundingBoxes() { + boundingBoxes = new Rectangle2D[glyphs.length]; + for (int i = 0; i < glyphs.length; i++) { + Rectangle bbox = fontMetrics.getBoundingBox(glyphs[i], fontSize); + boundingBoxes[i] = new Rectangle2D.Float(bbox.x / 1000000f, -(bbox.y + bbox.height) / 1000000f, + bbox.width / 1000000f, bbox.height / 1000000f); + } + } + +} diff --git a/src/java/org/apache/fop/svg/font/FilteringFontFamilyResolver.java b/src/java/org/apache/fop/svg/font/FilteringFontFamilyResolver.java new file mode 100644 index 000000000..319d6b2b8 --- /dev/null +++ b/src/java/org/apache/fop/svg/font/FilteringFontFamilyResolver.java @@ -0,0 +1,47 @@ +/* + * 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.svg.font; + + +public class FilteringFontFamilyResolver implements FOPFontFamilyResolver { + + private final FOPFontFamilyResolver delegate; + + public FilteringFontFamilyResolver(FOPFontFamilyResolver delegate) { + this.delegate = delegate; + } + + public String lookup(String familyName) { + return delegate.lookup(familyName); + } + + public FOPGVTFontFamily resolve(String familyName) { + return delegate.resolve(familyName); + } + + public FOPGVTFontFamily getDefault() { + return delegate.getDefault(); + } + + public FOPGVTFontFamily getFamilyThatCanDisplay(char c) { + return delegate.getFamilyThatCanDisplay(c); + } + +} -- cgit v1.2.3 From bd45787c63fcae138d6cdb28a1250396ccebbda0 Mon Sep 17 00:00:00 2001 From: Vincent Hennebert Date: Mon, 21 Oct 2013 13:24:55 +0000 Subject: First bits of support for fo:inline-container git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_InlineContainer@1534143 13f79535-47bb-0310-9956-ffa450edef68 --- .../org/apache/fop/fo/flow/InlineContainer.java | 164 ++++++-------- .../apache/fop/layoutmgr/LayoutManagerMapping.java | 12 +- .../fop/layoutmgr/inline/ICLayoutManager.java | 54 ----- .../inline/InlineContainerLayoutManager.java | 237 +++++++++++++++++++++ .../standard-testcases/inline-container_basic.xml | 56 +++++ 5 files changed, 361 insertions(+), 162 deletions(-) delete mode 100644 src/java/org/apache/fop/layoutmgr/inline/ICLayoutManager.java create mode 100644 src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java create mode 100644 test/layoutengine/standard-testcases/inline-container_basic.xml (limited to 'src/java/org/apache') diff --git a/src/java/org/apache/fop/fo/flow/InlineContainer.java b/src/java/org/apache/fop/fo/flow/InlineContainer.java index 748eb593a..d3a80eb3f 100644 --- a/src/java/org/apache/fop/fo/flow/InlineContainer.java +++ b/src/java/org/apache/fop/fo/flow/InlineContainer.java @@ -29,6 +29,7 @@ import org.apache.fop.fo.FObj; import org.apache.fop.fo.PropertyList; import org.apache.fop.fo.ValidationException; import org.apache.fop.fo.properties.CommonBorderPaddingBackground; +import org.apache.fop.fo.properties.CommonFont; import org.apache.fop.fo.properties.CommonMarginInline; import org.apache.fop.fo.properties.KeepProperty; import org.apache.fop.fo.properties.LengthRangeProperty; @@ -37,78 +38,72 @@ import org.apache.fop.traits.Direction; import org.apache.fop.traits.WritingMode; import org.apache.fop.traits.WritingModeTraits; -/** - * Class modelling the - * fo:inline-container object. - */ public class InlineContainer extends FObj { - // The value of FO traits (refined properties) that apply to fo:inline-container. - private Length alignmentAdjust; - private int alignmentBaseline; - private Length baselineShift; + private LengthRangeProperty inlineProgressionDimension; private LengthRangeProperty blockProgressionDimension; + private int overflow; private CommonBorderPaddingBackground commonBorderPaddingBackground; private CommonMarginInline commonMarginInline; - private int clip; - private int dominantBaseline; - private LengthRangeProperty inlineProgressionDimension; + private Numeric referenceOrientation; + private int displayAlign; private KeepProperty keepTogether; + private KeepProperty keepWithNext; + private KeepProperty keepWithPrevious; private SpaceProperty lineHeight; - private int overflow; - private Numeric referenceOrientation; + private Length alignmentAdjust; + private int alignmentBaseline; + private Length baselineShift; + private int dominantBaseline; private WritingModeTraits writingModeTraits; - // Unused but valid items, commented out for performance: - // private CommonRelativePosition commonRelativePosition; - // private int displayAlign; - // private Length height; - // private KeepProperty keepWithNext; - // private KeepProperty keepWithPrevious; - // private Length width; - // End of FO trait values /** used for FO validation */ - private boolean blockItemFound = false; + private boolean blockItemFound; + private CommonFont commonFont; /** - * Base constructor + * Creates a new instance. * - * @param parent {@link FONode} that is the parent of this object + * @param parent the parent of this inline-container */ public InlineContainer(FONode parent) { super(parent); } - /** {@inheritDoc} */ + @Override public void bind(PropertyList pList) throws FOPException { super.bind(pList); + commonFont = pList.getFontProps(); // TODO get directly from parent? alignmentAdjust = pList.get(PR_ALIGNMENT_ADJUST).getLength(); alignmentBaseline = pList.get(PR_ALIGNMENT_BASELINE).getEnum(); baselineShift = pList.get(PR_BASELINE_SHIFT).getLength(); blockProgressionDimension = pList.get(PR_BLOCK_PROGRESSION_DIMENSION).getLengthRange(); commonBorderPaddingBackground = pList.getBorderPaddingBackgroundProps(); commonMarginInline = pList.getMarginInlineProps(); - clip = pList.get(PR_CLIP).getEnum(); + displayAlign = pList.get(PR_DISPLAY_ALIGN).getEnum(); dominantBaseline = pList.get(PR_DOMINANT_BASELINE).getEnum(); inlineProgressionDimension = pList.get(PR_INLINE_PROGRESSION_DIMENSION).getLengthRange(); keepTogether = pList.get(PR_KEEP_TOGETHER).getKeep(); + keepWithNext = pList.get(PR_KEEP_WITH_NEXT).getKeep(); + keepWithPrevious = pList.get(PR_KEEP_WITH_PREVIOUS).getKeep(); lineHeight = pList.get(PR_LINE_HEIGHT).getSpace(); overflow = pList.get(PR_OVERFLOW).getEnum(); referenceOrientation = pList.get(PR_REFERENCE_ORIENTATION).getNumeric(); writingModeTraits = new WritingModeTraits( - WritingMode.valueOf(pList.get(PR_WRITING_MODE).getEnum())); + WritingMode.valueOf(pList.get(PR_WRITING_MODE).getEnum())); } /** * {@inheritDoc} *
XSL Content Model: marker* (%block;)+ */ + @Override protected void validateChildNode(Locator loc, String nsURI, String localName) throws ValidationException { if (FO_URI.equals(nsURI)) { if (localName.equals("marker")) { if (blockItemFound) { - nodesOutOfOrderError(loc, "fo:marker", "(%block;)"); + nodesOutOfOrderError(loc, "fo:marker", "(%block;)+"); } } else if (!isBlockItem(nsURI, localName)) { invalidChildError(loc, nsURI, localName); @@ -118,142 +113,109 @@ public class InlineContainer extends FObj { } } - /** {@inheritDoc} */ + @Override public void endOfNode() throws FOPException { if (!blockItemFound) { missingChildElementError("marker* (%block;)+"); } } - /** @return the "alignment-adjust" FO trait */ - public Length getAlignmentAdjust() { - return alignmentAdjust; + /** {@inheritDoc} */ + public String getLocalName() { + return "inline-container"; } - /** @return the "alignment-baseline" FO trait */ - public int getAlignmentBaseline() { - return alignmentBaseline; + /** + * {@inheritDoc} + * @return {@link org.apache.fop.fo.Constants#FO_INLINE_CONTAINER} + */ + public int getNameId() { + return FO_INLINE_CONTAINER; } - /** @return the "baseline-shift" FO trait */ - public Length getBaselineShift() { - return baselineShift; + public LengthRangeProperty getInlineProgressionDimension() { + return inlineProgressionDimension; } - /** @return the "block-progression-dimension" FO trait */ public LengthRangeProperty getBlockProgressionDimension() { return blockProgressionDimension; } - /** @return the "clip" FO trait */ - public int getClip() { - return clip; + public int getOverflow() { + return overflow; } - /**@return Returns the {@link CommonBorderPaddingBackground} */ public CommonBorderPaddingBackground getCommonBorderPaddingBackground() { return this.commonBorderPaddingBackground; } - /** @return Returns the {@link CommonMarginInline} */ public CommonMarginInline getCommonMarginInline() { return this.commonMarginInline; } - /** @return the "dominant-baseline" FO trait */ - public int getDominantBaseline() { - return dominantBaseline; + public int getReferenceOrientation() { + return referenceOrientation.getValue(); } - /** @return the "keep-together" FO trait */ - public KeepProperty getKeepTogether() { - return keepTogether; + public int getDisplayAlign() { + return this.displayAlign; } - /** @return the "inline-progression-dimension" FO trait */ - public LengthRangeProperty getInlineProgressionDimension() { - return inlineProgressionDimension; + public KeepProperty getKeepTogether() { + return keepTogether; } - /** @return the "line-height" FO trait */ public SpaceProperty getLineHeight() { return lineHeight; } - /** @return the "overflow" FO trait */ - public int getOverflow() { - return overflow; + public Length getAlignmentAdjust() { + return alignmentAdjust; } - /** @return the "reference-orientation" FO trait */ - public int getReferenceOrientation() { - return referenceOrientation.getValue(); + public int getAlignmentBaseline() { + return alignmentBaseline; + } + + public Length getBaselineShift() { + return baselineShift; + } + + public int getDominantBaseline() { + return dominantBaseline; + } + + public WritingMode getWritingMode() { + return writingModeTraits.getWritingMode(); } - /** - * Obtain inline progression direction. - * @return the inline progression direction - */ public Direction getInlineProgressionDirection() { return writingModeTraits.getInlineProgressionDirection(); } - /** - * Obtain block progression direction. - * @return the block progression direction - */ public Direction getBlockProgressionDirection() { return writingModeTraits.getBlockProgressionDirection(); } - /** - * Obtain column progression direction. - * @return the column progression direction - */ public Direction getColumnProgressionDirection() { return writingModeTraits.getColumnProgressionDirection(); } - /** - * Obtain row progression direction. - * @return the row progression direction - */ public Direction getRowProgressionDirection() { return writingModeTraits.getRowProgressionDirection(); } - /** - * Obtain (baseline) shift direction. - * @return the (baseline) shift direction - */ public Direction getShiftDirection() { return writingModeTraits.getShiftDirection(); } - /** - * Obtain writing mode. - * @return the writing mode - */ - public WritingMode getWritingMode() { - return writingModeTraits.getWritingMode(); - } - - /** {@inheritDoc} */ - public String getLocalName() { - return "inline-container"; - } - - /** - * {@inheritDoc} - * @return {@link org.apache.fop.fo.Constants#FO_INLINE_CONTAINER} - */ - public int getNameId() { - return FO_INLINE_CONTAINER; - } - @Override public boolean isDelimitedTextRangeBoundary(int boundary) { return false; } + public CommonFont getCommonFont() { + return commonFont; + } + } diff --git a/src/java/org/apache/fop/layoutmgr/LayoutManagerMapping.java b/src/java/org/apache/fop/layoutmgr/LayoutManagerMapping.java index 292251a84..0e333d219 100644 --- a/src/java/org/apache/fop/layoutmgr/LayoutManagerMapping.java +++ b/src/java/org/apache/fop/layoutmgr/LayoutManagerMapping.java @@ -73,7 +73,7 @@ import org.apache.fop.layoutmgr.inline.CharacterLayoutManager; import org.apache.fop.layoutmgr.inline.ContentLayoutManager; import org.apache.fop.layoutmgr.inline.ExternalGraphicLayoutManager; import org.apache.fop.layoutmgr.inline.FootnoteLayoutManager; -import org.apache.fop.layoutmgr.inline.ICLayoutManager; +import org.apache.fop.layoutmgr.inline.InlineContainerLayoutManager; import org.apache.fop.layoutmgr.inline.InlineLayoutManager; import org.apache.fop.layoutmgr.inline.InstreamForeignObjectLM; import org.apache.fop.layoutmgr.inline.LeaderLayoutManager; @@ -257,9 +257,9 @@ public class LayoutManagerMapping implements LayoutManagerMaker { /** a layout manager maker */ public static class InlineLayoutManagerMaker extends Maker { /** {@inheritDoc} */ - public void make(FONode node, List lms) { - lms.add(new InlineLayoutManager((InlineLevel) node)); - } + public void make(FONode node, List lms) { + lms.add(new InlineLayoutManager((InlineLevel) node)); + } } /** a layout manager maker */ @@ -274,9 +274,7 @@ public class LayoutManagerMapping implements LayoutManagerMaker { public static class InlineContainerLayoutManagerMaker extends Maker { /** {@inheritDoc} */ public void make(FONode node, List lms) { - ArrayList childList = new ArrayList(); - super.make(node, childList); - lms.add(new ICLayoutManager((InlineContainer) node, childList)); + lms.add(new InlineContainerLayoutManager((InlineContainer) node)); } } diff --git a/src/java/org/apache/fop/layoutmgr/inline/ICLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/ICLayoutManager.java deleted file mode 100644 index 7fe90f63c..000000000 --- a/src/java/org/apache/fop/layoutmgr/inline/ICLayoutManager.java +++ /dev/null @@ -1,54 +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.layoutmgr.inline; - -// Java -import java.util.List; - -// FOP -import org.apache.fop.area.inline.InlineArea; -import org.apache.fop.fo.flow.InlineContainer; -/** - * This creates a single inline container area after - * laying out the child block areas. All footnotes, floats - * and id areas are maintained for later retrieval. - */ -public class ICLayoutManager extends LeafNodeLayoutManager { - private List childrenLM; - - /** - * Construct inline container layout manager. - * @param node inline container FO node - * @param childLM child layout manager - */ - public ICLayoutManager(InlineContainer node, List childLM) { - super(node); - childrenLM = childLM; - } - - /** - * @param index an integer - * @return an inline area or null - */ - public InlineArea get(int index) { - return null; - } - -} diff --git a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java new file mode 100644 index 000000000..548225ad7 --- /dev/null +++ b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java @@ -0,0 +1,237 @@ +/* + * 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.layoutmgr.inline; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; + +import org.apache.fop.area.Area; +import org.apache.fop.area.Block; +import org.apache.fop.area.Trait; +import org.apache.fop.area.inline.Container; +import org.apache.fop.area.inline.InlineViewport; +import org.apache.fop.fo.flow.InlineContainer; +import org.apache.fop.fo.properties.CommonBorderPaddingBackground; +import org.apache.fop.fo.properties.LengthRangeProperty; +import org.apache.fop.fo.properties.Property; +import org.apache.fop.fonts.Font; +import org.apache.fop.fonts.FontInfo; +import org.apache.fop.fonts.FontTriplet; +import org.apache.fop.layoutmgr.AbstractLayoutManager; +import org.apache.fop.layoutmgr.InlineKnuthSequence; +import org.apache.fop.layoutmgr.KnuthPossPosIter; +import org.apache.fop.layoutmgr.KnuthSequence; +import org.apache.fop.layoutmgr.LayoutContext; +import org.apache.fop.layoutmgr.LayoutManager; +import org.apache.fop.layoutmgr.ListElement; +import org.apache.fop.layoutmgr.Position; +import org.apache.fop.layoutmgr.PositionIterator; +import org.apache.fop.layoutmgr.SpaceResolver; +import org.apache.fop.layoutmgr.TraitSetter; + +/** + * This creates a single inline container area after + * laying out the child block areas. All footnotes, floats + * and id areas are maintained for later retrieval. + */ +public class InlineContainerLayoutManager extends AbstractLayoutManager implements InlineLevelLayoutManager { + + private CommonBorderPaddingBackground borderProps; + private int alignmentBaseline = EN_BASELINE; + private int contentAreaIPD; + private int contentAreaBPD; + + private List childElements; + private InlineViewport currentViewport; + private Container referenceArea; + + public InlineContainerLayoutManager(InlineContainer node) { + super(node); + } + + @Override + public void initialize() { + InlineContainer node = (InlineContainer) fobj; + borderProps = node.getCommonBorderPaddingBackground(); + } + + @Override + public List getNextKnuthElements(LayoutContext context, int alignment) { + InlineContainer ic = (InlineContainer) fobj; + contentAreaIPD = getLength(ic.getInlineProgressionDimension()); + contentAreaBPD = getLength(ic.getBlockProgressionDimension()); + LayoutContext childLC = LayoutContext.offspringOf(context); // TODO copyOf? + childLC.setRefIPD(contentAreaIPD); + childElements = getChildKnuthElements(childLC, alignment); // TODO which alignment? + AlignmentContext alignmentContext = makeAlignmentContext(context); // TODO correct? + Position position = new Position(this, 0); + KnuthSequence knuthSequence = new InlineKnuthSequence(); + knuthSequence.add(new KnuthInlineBox(contentAreaIPD, alignmentContext, position, false)); + List knuthElements = new ArrayList(1); + knuthElements.add(knuthSequence); + setFinished(true); + return knuthElements; + } + + private int getLength(LengthRangeProperty property) { + Property optimum = property.getOptimum(this); // TODO percent base context + if (optimum.isAuto()) { + throw new UnsupportedOperationException("auto dimension not supported"); + } + return optimum.getLength().getValue(this); // TODO percent base context + } + + private List getChildKnuthElements(LayoutContext layoutContext, int alignment) { + List allChildElements = new LinkedList(); + LayoutManager childLM; + while ((childLM = getChildLM()) != null) { + LayoutContext childLC = LayoutContext.offspringOf(layoutContext); // TODO copyOf? newInstance? + childLC.setRefIPD(layoutContext.getRefIPD()); + @SuppressWarnings("unchecked") + List childElements = childLM.getNextKnuthElements(childLC, alignmentBaseline); + allChildElements.addAll(childElements); + // TODO breaks, keeps, empty content + } + SpaceResolver.resolveElementList(allChildElements); + // TODO break-before, break-after + return allChildElements; + } + + @Override + public void addAreas(PositionIterator posIter, LayoutContext context) { + Position inlineContainerPosition = null; + while (posIter.hasNext()) { + Position pos = posIter.next(); + if (pos.getLM() == this) { + inlineContainerPosition = pos; + } + } + addId(); +// addMarkersToPage( +// true, +// true, +// lastPos == null || isLast(lastPos)); + + if (inlineContainerPosition != null) { + LayoutManager childLM; + KnuthPossPosIter childPosIter = new KnuthPossPosIter(childElements); + while ((childLM = childPosIter.getNextChildLM()) != null) { + LayoutContext childLC = LayoutContext.copyOf(context); // TODO correct? + childLM.addAreas(childPosIter, childLC); + } + } + +// addMarkersToPage( +// false, +// true, +// lastPos == null || isLast(lastPos)); + +// boolean isLast = (context.isLastArea() && prevLM == lastChildLM); +// context.setFlags(LayoutContext.LAST_AREA, isLast); + } + + @Override + public Area getParentArea(Area childArea) { + if (referenceArea == null) { + referenceArea = new Container(); + referenceArea.addTrait(Trait.IS_REFERENCE_AREA, Boolean.TRUE); + TraitSetter.setProducerID(referenceArea, fobj.getId()); + referenceArea.setIPD(contentAreaIPD); + currentViewport = new InlineViewport(referenceArea); + currentViewport.addTrait(Trait.IS_VIEWPORT_AREA, Boolean.TRUE); + currentViewport.setIPD(getContentAreaIPD()); + currentViewport.setBPD(getContentAreaBPD()); + TraitSetter.setProducerID(currentViewport, fobj.getId()); + TraitSetter.addBorders(currentViewport, + borderProps, + false, false, false, false, this); + TraitSetter.addPadding(currentViewport, + borderProps, + false, false, false, false, this); + TraitSetter.addBackground(currentViewport, + borderProps, + this); + currentViewport.setClip(needClip()); + currentViewport.setContentPosition( + new java.awt.geom.Rectangle2D.Float(0, 0, getContentAreaIPD(), getContentAreaBPD())); + getParent().addChildArea(currentViewport); + } + return referenceArea; + } + + @Override + public int getContentAreaIPD() { + return contentAreaIPD; + } + + @Override + public int getContentAreaBPD() { + return contentAreaBPD; + } + + @Override + public void addChildArea(Area childArea) { + referenceArea.addBlock((Block) childArea); + } + + private boolean needClip() { + int overflow = ((InlineContainer) fobj).getOverflow(); + return (overflow == EN_HIDDEN || overflow == EN_ERROR_IF_OVERFLOW); + } + + protected AlignmentContext makeAlignmentContext(LayoutContext context) { + InlineContainer ic = (InlineContainer) fobj; + FontInfo fi = fobj.getFOEventHandler().getFontInfo(); + FontTriplet[] fontkeys = ic.getCommonFont().getFontState(fi); + Font fs = fi.getFontInstance(fontkeys[0], ic.getCommonFont().fontSize.getValue(this)); + return new AlignmentContext(fs, ic.getLineHeight().getOptimum(this).getLength().getValue(this), // TODO + context.getWritingMode()); + } + + public List addALetterSpaceTo(List oldList) { + throw new UnsupportedOperationException("Not implemented"); + } + + public List addALetterSpaceTo(List oldList, int depth) { + throw new UnsupportedOperationException("Not implemented"); + } + + public String getWordChars(Position pos) { + throw new UnsupportedOperationException("Not implemented"); + } + + public void hyphenate(Position pos, HyphContext hyphContext) { + throw new UnsupportedOperationException("Not implemented"); + } + + public boolean applyChanges(List oldList) { + throw new UnsupportedOperationException("Not implemented"); + } + + public boolean applyChanges(List oldList, int depth) { + throw new UnsupportedOperationException("Not implemented"); + } + + public List getChangedKnuthElements(List oldList, int alignment, int depth) { + throw new UnsupportedOperationException("Not implemented"); + } + +} diff --git a/test/layoutengine/standard-testcases/inline-container_basic.xml b/test/layoutengine/standard-testcases/inline-container_basic.xml new file mode 100644 index 000000000..b2dfcc6e5 --- /dev/null +++ b/test/layoutengine/standard-testcases/inline-container_basic.xml @@ -0,0 +1,56 @@ + + + + + +

+ Test for a basic implementation of fo:inline-container. +

+
+ + + + + + + + + + Before: + Text inside inline-container. + After. + + + + + + + + + + + + + + + + + + +
-- cgit v1.2.3 From 31037a0f70a013e5be7c7467590ec9e4f202342b Mon Sep 17 00:00:00 2001 From: Vincent Hennebert Date: Thu, 24 Oct 2013 14:00:23 +0000 Subject: Parse back the container area from the area tree. Fixes AreaTreeParserTestCase. git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_InlineContainer@1535384 13f79535-47bb-0310-9956-ffa450edef68 --- src/java/org/apache/fop/area/AreaTreeParser.java | 17 +++++++++++++++++ src/java/org/apache/fop/area/inline/Container.java | 15 +++++++-------- .../layoutmgr/inline/InlineContainerLayoutManager.java | 3 +-- 3 files changed, 25 insertions(+), 10 deletions(-) (limited to 'src/java/org/apache') diff --git a/src/java/org/apache/fop/area/AreaTreeParser.java b/src/java/org/apache/fop/area/AreaTreeParser.java index c79f975c1..e9f0ffb8c 100644 --- a/src/java/org/apache/fop/area/AreaTreeParser.java +++ b/src/java/org/apache/fop/area/AreaTreeParser.java @@ -62,6 +62,7 @@ import org.apache.fop.apps.FOUserAgent; import org.apache.fop.area.Trait.Background; import org.apache.fop.area.Trait.InternalLink; import org.apache.fop.area.inline.AbstractTextArea; +import org.apache.fop.area.inline.Container; import org.apache.fop.area.inline.ForeignObject; import org.apache.fop.area.inline.Image; import org.apache.fop.area.inline.InlineArea; @@ -195,6 +196,7 @@ public class AreaTreeParser { makers.put("space", new SpaceMaker()); makers.put("leader", new LeaderMaker()); makers.put("viewport", new InlineViewportMaker()); + makers.put("container", new ContainerMaker()); makers.put("image", new ImageMaker()); makers.put("foreignObject", new ForeignObjectMaker()); makers.put("bookmarkTree", new BookmarkTreeMaker()); @@ -863,6 +865,21 @@ public class AreaTreeParser { } } + private class ContainerMaker extends AbstractMaker { + + public void startElement(Attributes attributes) { + Container container = new Container(); + transferForeignObjects(attributes, container); + InlineViewport parent = (InlineViewport) areaStack.peek(); + parent.setContent(container); + areaStack.push(container); + } + + public void endElement() { + assertObjectOfClass(areaStack.pop(), Container.class); + } + } + private class InlineViewportMaker extends AbstractMaker { public void startElement(Attributes attributes) { diff --git a/src/java/org/apache/fop/area/inline/Container.java b/src/java/org/apache/fop/area/inline/Container.java index bc2acaa28..3d0060007 100644 --- a/src/java/org/apache/fop/area/inline/Container.java +++ b/src/java/org/apache/fop/area/inline/Container.java @@ -51,13 +51,12 @@ public class Container extends Area { public Container() { } - /** - * Add the block to this area. - * - * @param block the block area to add - */ - public void addBlock(Block block) { - blocks.add(block); + @Override + public void addChildArea(Area child) { + if (!(child instanceof Block)) { + throw new IllegalArgumentException("Container only accepts block areas"); + } + blocks.add((Block) child); } /** @@ -65,7 +64,7 @@ public class Container extends Area { * * @return the list of block areas */ - public List getBlocks() { + public List getBlocks() { return blocks; } diff --git a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java index 548225ad7..3e044ad38 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java @@ -24,7 +24,6 @@ import java.util.LinkedList; import java.util.List; import org.apache.fop.area.Area; -import org.apache.fop.area.Block; import org.apache.fop.area.Trait; import org.apache.fop.area.inline.Container; import org.apache.fop.area.inline.InlineViewport; @@ -189,7 +188,7 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen @Override public void addChildArea(Area childArea) { - referenceArea.addBlock((Block) childArea); + referenceArea.addChildArea(childArea); } private boolean needClip() { -- cgit v1.2.3 From 69ff3a9270810958bbbcb414b60c7e2d460d3221 Mon Sep 17 00:00:00 2001 From: Vincent Hennebert Date: Thu, 24 Oct 2013 15:17:36 +0000 Subject: When inline-progression-dimension has been left to auto on fo:inline-container, fall back to the IPD of the nearest ancestor reference-area. git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_InlineContainer@1535410 13f79535-47bb-0310-9956-ffa450edef68 --- .../inline/InlineContainerLayoutManager.java | 15 +++++- .../layoutmgr/inline/InlineLevelEventProducer.java | 9 ++++ .../layoutmgr/inline/InlineLevelEventProducer.xml | 1 + .../inline-container_auto-ipd.xml | 61 ++++++++++++++++++++++ 4 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 test/layoutengine/standard-testcases/inline-container_auto-ipd.xml (limited to 'src/java/org/apache') diff --git a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java index 3e044ad38..fb342bd8e 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java @@ -75,7 +75,7 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen @Override public List getNextKnuthElements(LayoutContext context, int alignment) { InlineContainer ic = (InlineContainer) fobj; - contentAreaIPD = getLength(ic.getInlineProgressionDimension()); + determineIPD(context); contentAreaBPD = getLength(ic.getBlockProgressionDimension()); LayoutContext childLC = LayoutContext.offspringOf(context); // TODO copyOf? childLC.setRefIPD(contentAreaIPD); @@ -90,6 +90,19 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen return knuthElements; } + private void determineIPD(LayoutContext layoutContext) { + LengthRangeProperty ipd = ((InlineContainer) fobj).getInlineProgressionDimension(); + Property optimum = ipd.getOptimum(this); // TODO percent base context + if (optimum.isAuto()) { + contentAreaIPD = layoutContext.getRefIPD(); + InlineLevelEventProducer eventProducer = InlineLevelEventProducer.Provider.get( + fobj.getUserAgent().getEventBroadcaster()); + eventProducer.inlineContainerAutoIPDNotSupported(this, contentAreaIPD / 1000f); + } else { + contentAreaIPD = optimum.getLength().getValue(this); // TODO percent base context + } + } + private int getLength(LengthRangeProperty property) { Property optimum = property.getOptimum(this); // TODO percent base context if (optimum.isAuto()) { diff --git a/src/java/org/apache/fop/layoutmgr/inline/InlineLevelEventProducer.java b/src/java/org/apache/fop/layoutmgr/inline/InlineLevelEventProducer.java index 15284ae0a..332e14935 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/InlineLevelEventProducer.java +++ b/src/java/org/apache/fop/layoutmgr/inline/InlineLevelEventProducer.java @@ -67,4 +67,13 @@ public interface InlineLevelEventProducer extends EventProducer { */ void lineOverflows(Object source, String elementName, int line, int overflowLength, Locator loc); + /** + * Auto IPD on inline-container is not supported. + * + * @param source the event source + * @param fallback the value in points that will be used as a fallback + * @event.severity WARN + */ + void inlineContainerAutoIPDNotSupported(Object source, float fallback); + } diff --git a/src/java/org/apache/fop/layoutmgr/inline/InlineLevelEventProducer.xml b/src/java/org/apache/fop/layoutmgr/inline/InlineLevelEventProducer.xml index 66d352eb7..8d699f6bc 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/InlineLevelEventProducer.xml +++ b/src/java/org/apache/fop/layoutmgr/inline/InlineLevelEventProducer.xml @@ -20,4 +20,5 @@ [ (See position {loc})| (See {#gatherContextInfo})| (No context info available)] fo:leader is set to "use-content" but has no content.{{locator}} The contents of {elementName} line {line} exceed the available area in the inline-progression direction by {overflowLength,choice,50000#{overflowLength} millipoints|50000<more than 50 points}.{{locator}} + A value of "auto" for the inline-progression-dimension property on fo:inline-container is not supported. Falling back to {fallback}pt.{{locator}} diff --git a/test/layoutengine/standard-testcases/inline-container_auto-ipd.xml b/test/layoutengine/standard-testcases/inline-container_auto-ipd.xml new file mode 100644 index 000000000..48f5cd1cf --- /dev/null +++ b/test/layoutengine/standard-testcases/inline-container_auto-ipd.xml @@ -0,0 +1,61 @@ + + + + + +

+ When inline-progression-dimension has been left to auto on fo:inline-container, fall back to + the IPD of the nearest ancestor reference-area. +

+
+ + + + + + + + + + Before: + Text inside inline-container. + After. + + + + + + + Before: + Inside the inline-container. + After. + + + + + + + + + + + + + +
-- cgit v1.2.3 From c925990c7c290876d6857a4cc02e362b0ed6ca72 Mon Sep 17 00:00:00 2001 From: Vincent Hennebert Date: Thu, 24 Oct 2013 15:36:12 +0000 Subject: Added support for block-progression-dimension="auto" git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_InlineContainer@1535417 13f79535-47bb-0310-9956-ffa450edef68 --- .../inline/InlineContainerLayoutManager.java | 14 ++++--- .../standard-testcases/inline-container_bpd.xml | 46 ++++++++++++++++++++++ 2 files changed, 54 insertions(+), 6 deletions(-) create mode 100644 test/layoutengine/standard-testcases/inline-container_bpd.xml (limited to 'src/java/org/apache') diff --git a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java index fb342bd8e..107461c0a 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java @@ -35,6 +35,7 @@ import org.apache.fop.fonts.Font; import org.apache.fop.fonts.FontInfo; import org.apache.fop.fonts.FontTriplet; import org.apache.fop.layoutmgr.AbstractLayoutManager; +import org.apache.fop.layoutmgr.ElementListUtils; import org.apache.fop.layoutmgr.InlineKnuthSequence; import org.apache.fop.layoutmgr.KnuthPossPosIter; import org.apache.fop.layoutmgr.KnuthSequence; @@ -74,12 +75,11 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen @Override public List getNextKnuthElements(LayoutContext context, int alignment) { - InlineContainer ic = (InlineContainer) fobj; determineIPD(context); - contentAreaBPD = getLength(ic.getBlockProgressionDimension()); LayoutContext childLC = LayoutContext.offspringOf(context); // TODO copyOf? childLC.setRefIPD(contentAreaIPD); childElements = getChildKnuthElements(childLC, alignment); // TODO which alignment? + determineBPD(); AlignmentContext alignmentContext = makeAlignmentContext(context); // TODO correct? Position position = new Position(this, 0); KnuthSequence knuthSequence = new InlineKnuthSequence(); @@ -103,12 +103,14 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen } } - private int getLength(LengthRangeProperty property) { - Property optimum = property.getOptimum(this); // TODO percent base context + private void determineBPD() { + LengthRangeProperty bpd = ((InlineContainer) fobj).getBlockProgressionDimension(); + Property optimum = bpd.getOptimum(this); // TODO percent base context if (optimum.isAuto()) { - throw new UnsupportedOperationException("auto dimension not supported"); + contentAreaBPD = ElementListUtils.calcContentLength(childElements); + } else { + contentAreaBPD = optimum.getLength().getValue(this); // TODO percent base context } - return optimum.getLength().getValue(this); // TODO percent base context } private List getChildKnuthElements(LayoutContext layoutContext, int alignment) { diff --git a/test/layoutengine/standard-testcases/inline-container_bpd.xml b/test/layoutengine/standard-testcases/inline-container_bpd.xml new file mode 100644 index 000000000..0a9d87442 --- /dev/null +++ b/test/layoutengine/standard-testcases/inline-container_bpd.xml @@ -0,0 +1,46 @@ + + + + + +

+ Checks that inline-progression-dimension on fo:inline-container is properly handled. +

+
+ + + + + + + + + + Before: + This text inside inline-container should fit on four lines. + After. + + + + + + + + +
-- cgit v1.2.3 From 5530937d762b0a15b48940c544a157769b1137a0 Mon Sep 17 00:00:00 2001 From: Vincent Hennebert Date: Thu, 24 Oct 2013 16:26:26 +0000 Subject: Issue a warning if the content of an inline-container overflows it in the block-progression-direction git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_InlineContainer@1535430 13f79535-47bb-0310-9956-ffa450edef68 --- .../fop/layoutmgr/inline/InlineContainerLayoutManager.java | 12 +++++++++++- .../standard-testcases/inline-container_auto-ipd.xml | 2 +- .../layoutengine/standard-testcases/inline-container_bpd.xml | 12 +++++++++++- 3 files changed, 23 insertions(+), 3 deletions(-) (limited to 'src/java/org/apache') diff --git a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java index 107461c0a..152d16a3d 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java @@ -35,6 +35,7 @@ import org.apache.fop.fonts.Font; import org.apache.fop.fonts.FontInfo; import org.apache.fop.fonts.FontTriplet; import org.apache.fop.layoutmgr.AbstractLayoutManager; +import org.apache.fop.layoutmgr.BlockLevelEventProducer; import org.apache.fop.layoutmgr.ElementListUtils; import org.apache.fop.layoutmgr.InlineKnuthSequence; import org.apache.fop.layoutmgr.KnuthPossPosIter; @@ -106,10 +107,19 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen private void determineBPD() { LengthRangeProperty bpd = ((InlineContainer) fobj).getBlockProgressionDimension(); Property optimum = bpd.getOptimum(this); // TODO percent base context + int actualBPD = ElementListUtils.calcContentLength(childElements); if (optimum.isAuto()) { - contentAreaBPD = ElementListUtils.calcContentLength(childElements); + contentAreaBPD = actualBPD; } else { contentAreaBPD = optimum.getLength().getValue(this); // TODO percent base context + if (contentAreaBPD < actualBPD) { + BlockLevelEventProducer eventProducer = BlockLevelEventProducer.Provider.get( + fobj.getUserAgent().getEventBroadcaster()); + boolean canRecover = (((InlineContainer) fobj).getOverflow() != EN_ERROR_IF_OVERFLOW); + eventProducer.viewportBPDOverflow(this, fobj.getName(), + actualBPD - contentAreaBPD, needClip(), canRecover, + fobj.getLocator()); + } } } diff --git a/test/layoutengine/standard-testcases/inline-container_auto-ipd.xml b/test/layoutengine/standard-testcases/inline-container_auto-ipd.xml index 48f5cd1cf..0833d7f0d 100644 --- a/test/layoutengine/standard-testcases/inline-container_auto-ipd.xml +++ b/test/layoutengine/standard-testcases/inline-container_auto-ipd.xml @@ -42,7 +42,7 @@ - Before: + Before: Inside the inline-container. After. diff --git a/test/layoutengine/standard-testcases/inline-container_bpd.xml b/test/layoutengine/standard-testcases/inline-container_bpd.xml index 0a9d87442..bd2dfdf9e 100644 --- a/test/layoutengine/standard-testcases/inline-container_bpd.xml +++ b/test/layoutengine/standard-testcases/inline-container_bpd.xml @@ -37,10 +37,20 @@ After. + + + Before: + This text overflows the inline-container in the + block-progression-direction. + After. + + - + + + -- cgit v1.2.3 From dc25857bb531326a321c930b7734626aaef3b909 Mon Sep 17 00:00:00 2001 From: Vincent Hennebert Date: Mon, 28 Oct 2013 17:09:21 +0000 Subject: Do space resolution on the children of the inline-container git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_InlineContainer@1536418 13f79535-47bb-0310-9956-ffa450edef68 --- .../org/apache/fop/layoutmgr/AreaAdditionUtil.java | 24 +++++++++--------- .../inline/InlineContainerLayoutManager.java | 29 ++++++++++------------ .../standard-testcases/inline-container_bpd.xml | 12 +++++++++ 3 files changed, 37 insertions(+), 28 deletions(-) (limited to 'src/java/org/apache') diff --git a/src/java/org/apache/fop/layoutmgr/AreaAdditionUtil.java b/src/java/org/apache/fop/layoutmgr/AreaAdditionUtil.java index 0ed6cb69b..d731ab62a 100644 --- a/src/java/org/apache/fop/layoutmgr/AreaAdditionUtil.java +++ b/src/java/org/apache/fop/layoutmgr/AreaAdditionUtil.java @@ -33,11 +33,11 @@ public final class AreaAdditionUtil { /** * Creates the child areas for the given layout manager. - * @param bslm the BlockStackingLayoutManager instance for which "addAreas" is performed. + * @param parentLM the parent layout manager * @param parentIter the position iterator * @param layoutContext the layout context */ - public static void addAreas(BlockStackingLayoutManager bslm, + public static void addAreas(AbstractLayoutManager parentLM, PositionIterator parentIter, LayoutContext layoutContext) { LayoutManager childLM; LayoutContext lc = LayoutContext.offspringOf(layoutContext); @@ -46,8 +46,8 @@ public final class AreaAdditionUtil { Position firstPos = null; Position lastPos = null; - if (bslm != null) { - bslm.addId(); + if (parentLM != null) { + parentLM.addId(); } // "unwrap" the NonLeafPositions stored in parentIter @@ -86,11 +86,11 @@ public final class AreaAdditionUtil { //doesn't give us that info. } - if (bslm != null) { - bslm.registerMarkers( + if (parentLM != null) { + parentLM.registerMarkers( true, - bslm.isFirst(firstPos), - bslm.isLast(lastPos)); + parentLM.isFirst(firstPos), + parentLM.isLast(lastPos)); } PositionIterator childPosIter = new PositionIterator(positionList.listIterator()); @@ -113,11 +113,11 @@ public final class AreaAdditionUtil { childLM.addAreas(childPosIter, lc); } - if (bslm != null) { - bslm.registerMarkers( + if (parentLM != null) { + parentLM.registerMarkers( false, - bslm.isFirst(firstPos), - bslm.isLast(lastPos)); + parentLM.isFirst(firstPos), + parentLM.isLast(lastPos)); } diff --git a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java index 152d16a3d..3688bcff0 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java @@ -35,6 +35,7 @@ import org.apache.fop.fonts.Font; import org.apache.fop.fonts.FontInfo; import org.apache.fop.fonts.FontTriplet; import org.apache.fop.layoutmgr.AbstractLayoutManager; +import org.apache.fop.layoutmgr.AreaAdditionUtil; import org.apache.fop.layoutmgr.BlockLevelEventProducer; import org.apache.fop.layoutmgr.ElementListUtils; import org.apache.fop.layoutmgr.InlineKnuthSequence; @@ -43,6 +44,7 @@ import org.apache.fop.layoutmgr.KnuthSequence; import org.apache.fop.layoutmgr.LayoutContext; import org.apache.fop.layoutmgr.LayoutManager; import org.apache.fop.layoutmgr.ListElement; +import org.apache.fop.layoutmgr.NonLeafPosition; import org.apache.fop.layoutmgr.Position; import org.apache.fop.layoutmgr.PositionIterator; import org.apache.fop.layoutmgr.SpaceResolver; @@ -134,11 +136,20 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen allChildElements.addAll(childElements); // TODO breaks, keeps, empty content } + wrapPositions(allChildElements); SpaceResolver.resolveElementList(allChildElements); // TODO break-before, break-after return allChildElements; } + private void wrapPositions(List elements) { + for (ListElement element : elements) { + Position position = new NonLeafPosition(this, element.getPosition()); + notifyPos(position); + element.setPosition(position); + } + } + @Override public void addAreas(PositionIterator posIter, LayoutContext context) { Position inlineContainerPosition = null; @@ -148,26 +159,12 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen inlineContainerPosition = pos; } } - addId(); -// addMarkersToPage( -// true, -// true, -// lastPos == null || isLast(lastPos)); - if (inlineContainerPosition != null) { - LayoutManager childLM; + SpaceResolver.performConditionalsNotification(childElements, 0, childElements.size() - 1, -1); KnuthPossPosIter childPosIter = new KnuthPossPosIter(childElements); - while ((childLM = childPosIter.getNextChildLM()) != null) { - LayoutContext childLC = LayoutContext.copyOf(context); // TODO correct? - childLM.addAreas(childPosIter, childLC); - } + AreaAdditionUtil.addAreas(this, childPosIter, context); } -// addMarkersToPage( -// false, -// true, -// lastPos == null || isLast(lastPos)); - // boolean isLast = (context.isLastArea() && prevLM == lastChildLM); // context.setFlags(LayoutContext.LAST_AREA, isLast); } diff --git a/test/layoutengine/standard-testcases/inline-container_bpd.xml b/test/layoutengine/standard-testcases/inline-container_bpd.xml index bd2dfdf9e..e63172b15 100644 --- a/test/layoutengine/standard-testcases/inline-container_bpd.xml +++ b/test/layoutengine/standard-testcases/inline-container_bpd.xml @@ -45,12 +45,24 @@ After. + + + Before: + Block 1 + Block 2 + After. + + + + + -- cgit v1.2.3 From 8c5aef924ae27549bac3d34057e5e36aaf0bca81 Mon Sep 17 00:00:00 2001 From: Vincent Hennebert Date: Mon, 28 Oct 2013 17:13:14 +0000 Subject: Slightly simplified looping through positions in addAreas git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_InlineContainer@1536424 13f79535-47bb-0310-9956-ffa450edef68 --- .../inline/InlineContainerLayoutManager.java | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) (limited to 'src/java/org/apache') diff --git a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java index 3688bcff0..a51d64c2c 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java @@ -154,16 +154,18 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen public void addAreas(PositionIterator posIter, LayoutContext context) { Position inlineContainerPosition = null; while (posIter.hasNext()) { - Position pos = posIter.next(); - if (pos.getLM() == this) { - inlineContainerPosition = pos; - } - } - if (inlineContainerPosition != null) { - SpaceResolver.performConditionalsNotification(childElements, 0, childElements.size() - 1, -1); - KnuthPossPosIter childPosIter = new KnuthPossPosIter(childElements); - AreaAdditionUtil.addAreas(this, childPosIter, context); + /* + * Should iterate only once, but hasNext must be called twice for its + * side-effects to apply and the iterator to switch to the next LM. + */ + assert inlineContainerPosition == null; + inlineContainerPosition = posIter.next(); + assert inlineContainerPosition.getLM() == this; } + assert inlineContainerPosition != null; + SpaceResolver.performConditionalsNotification(childElements, 0, childElements.size() - 1, -1); + KnuthPossPosIter childPosIter = new KnuthPossPosIter(childElements); + AreaAdditionUtil.addAreas(this, childPosIter, context); // boolean isLast = (context.isLastArea() && prevLM == lastChildLM); // context.setFlags(LayoutContext.LAST_AREA, isLast); -- cgit v1.2.3 From 0169e567a969752cf36139831318187a1e63c8e9 Mon Sep 17 00:00:00 2001 From: Vincent Hennebert Date: Mon, 28 Oct 2013 17:15:35 +0000 Subject: Added support for percentage dimensions git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_InlineContainer@1536425 13f79535-47bb-0310-9956-ffa450edef68 --- .../inline/InlineContainerLayoutManager.java | 27 ++++++---- .../inline-container_auto-ipd.xml | 61 ---------------------- .../inline-container_ipd-auto.xml | 61 ++++++++++++++++++++++ .../inline-container_ipd-percent.xml | 61 ++++++++++++++++++++++ 4 files changed, 138 insertions(+), 72 deletions(-) delete mode 100644 test/layoutengine/standard-testcases/inline-container_auto-ipd.xml create mode 100644 test/layoutengine/standard-testcases/inline-container_ipd-auto.xml create mode 100644 test/layoutengine/standard-testcases/inline-container_ipd-percent.xml (limited to 'src/java/org/apache') diff --git a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java index a51d64c2c..7e2b186be 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java @@ -95,32 +95,37 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen private void determineIPD(LayoutContext layoutContext) { LengthRangeProperty ipd = ((InlineContainer) fobj).getInlineProgressionDimension(); - Property optimum = ipd.getOptimum(this); // TODO percent base context + Property optimum = ipd.getOptimum(this); if (optimum.isAuto()) { contentAreaIPD = layoutContext.getRefIPD(); InlineLevelEventProducer eventProducer = InlineLevelEventProducer.Provider.get( fobj.getUserAgent().getEventBroadcaster()); eventProducer.inlineContainerAutoIPDNotSupported(this, contentAreaIPD / 1000f); } else { - contentAreaIPD = optimum.getLength().getValue(this); // TODO percent base context + contentAreaIPD = optimum.getLength().getValue(this); } } private void determineBPD() { LengthRangeProperty bpd = ((InlineContainer) fobj).getBlockProgressionDimension(); - Property optimum = bpd.getOptimum(this); // TODO percent base context + Property optimum = bpd.getOptimum(this); int actualBPD = ElementListUtils.calcContentLength(childElements); if (optimum.isAuto()) { contentAreaBPD = actualBPD; } else { - contentAreaBPD = optimum.getLength().getValue(this); // TODO percent base context - if (contentAreaBPD < actualBPD) { - BlockLevelEventProducer eventProducer = BlockLevelEventProducer.Provider.get( - fobj.getUserAgent().getEventBroadcaster()); - boolean canRecover = (((InlineContainer) fobj).getOverflow() != EN_ERROR_IF_OVERFLOW); - eventProducer.viewportBPDOverflow(this, fobj.getName(), - actualBPD - contentAreaBPD, needClip(), canRecover, - fobj.getLocator()); + double bpdValue = optimum.getLength().getNumericValue(this); + if (bpdValue < 0) { + contentAreaBPD = actualBPD; + } else { + contentAreaBPD = (int) Math.round(bpdValue); + if (contentAreaBPD < actualBPD) { + BlockLevelEventProducer eventProducer = BlockLevelEventProducer.Provider.get( + fobj.getUserAgent().getEventBroadcaster()); + boolean canRecover = (((InlineContainer) fobj).getOverflow() != EN_ERROR_IF_OVERFLOW); + eventProducer.viewportBPDOverflow(this, fobj.getName(), + actualBPD - contentAreaBPD, needClip(), canRecover, + fobj.getLocator()); + } } } } diff --git a/test/layoutengine/standard-testcases/inline-container_auto-ipd.xml b/test/layoutengine/standard-testcases/inline-container_auto-ipd.xml deleted file mode 100644 index 0833d7f0d..000000000 --- a/test/layoutengine/standard-testcases/inline-container_auto-ipd.xml +++ /dev/null @@ -1,61 +0,0 @@ - - - - - -

- When inline-progression-dimension has been left to auto on fo:inline-container, fall back to - the IPD of the nearest ancestor reference-area. -

-
- - - - - - - - - - Before: - Text inside inline-container. - After. - - - - - - - Before: - Inside the inline-container. - After. - - - - - - - - - - - - - -
diff --git a/test/layoutengine/standard-testcases/inline-container_ipd-auto.xml b/test/layoutengine/standard-testcases/inline-container_ipd-auto.xml new file mode 100644 index 000000000..0833d7f0d --- /dev/null +++ b/test/layoutengine/standard-testcases/inline-container_ipd-auto.xml @@ -0,0 +1,61 @@ + + + + + +

+ When inline-progression-dimension has been left to auto on fo:inline-container, fall back to + the IPD of the nearest ancestor reference-area. +

+
+ + + + + + + + + + Before: + Text inside inline-container. + After. + + + + + + + Before: + Inside the inline-container. + After. + + + + + + + + + + + + + +
diff --git a/test/layoutengine/standard-testcases/inline-container_ipd-percent.xml b/test/layoutengine/standard-testcases/inline-container_ipd-percent.xml new file mode 100644 index 000000000..aa681d134 --- /dev/null +++ b/test/layoutengine/standard-testcases/inline-container_ipd-percent.xml @@ -0,0 +1,61 @@ + + + + + +

+ Checks that percentage values for the dimensions of an fo:inline-container are resolved + properly. +

+
+ + + + + + + + + + Before: + Text inside inline-container. + After. + + + + + + + Before: + Inside the inline-container. + After. + + + + + + + + + + + + + +
-- cgit v1.2.3 From 19f04b9ac8fdaaea7436753ef272d19b658d0471 Mon Sep 17 00:00:00 2001 From: Vincent Hennebert Date: Mon, 28 Oct 2013 18:54:11 +0000 Subject: Properly handle "error-if-overflow" value of "overflow" property when overflow happens in inline-progression-direction git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_InlineContainer@1536486 13f79535-47bb-0310-9956-ffa450edef68 --- .../fop/layoutmgr/BlockStackingLayoutManager.java | 3 +++ .../inline/InlineContainerLayoutManager.java | 30 +++++++++++++++++++--- 2 files changed, 29 insertions(+), 4 deletions(-) (limited to 'src/java/org/apache') diff --git a/src/java/org/apache/fop/layoutmgr/BlockStackingLayoutManager.java b/src/java/org/apache/fop/layoutmgr/BlockStackingLayoutManager.java index d11f062cb..250e07727 100644 --- a/src/java/org/apache/fop/layoutmgr/BlockStackingLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/BlockStackingLayoutManager.java @@ -36,6 +36,7 @@ import org.apache.fop.fo.properties.BreakPropertySet; import org.apache.fop.fo.properties.CommonBorderPaddingBackground; import org.apache.fop.fo.properties.KeepProperty; import org.apache.fop.fo.properties.SpaceProperty; +import org.apache.fop.layoutmgr.inline.InlineContainerLayoutManager; import org.apache.fop.layoutmgr.inline.InlineLayoutManager; import org.apache.fop.traits.MinOptMax; import org.apache.fop.util.ListUtil; @@ -1246,6 +1247,8 @@ public abstract class BlockStackingLayoutManager extends AbstractLayoutManager public boolean handleOverflow(int milliPoints) { if (getParent() instanceof BlockStackingLayoutManager) { return ((BlockStackingLayoutManager) getParent()).handleOverflow(milliPoints); + } else if (getParent() instanceof InlineContainerLayoutManager) { + return ((InlineContainerLayoutManager) getParent()).handleOverflow(milliPoints); } return false; } diff --git a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java index 7e2b186be..85d21833b 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java @@ -63,6 +63,7 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen private int contentAreaBPD; private List childElements; + private int ipdOverflow; private InlineViewport currentViewport; private Container referenceArea; @@ -119,17 +120,23 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen } else { contentAreaBPD = (int) Math.round(bpdValue); if (contentAreaBPD < actualBPD) { - BlockLevelEventProducer eventProducer = BlockLevelEventProducer.Provider.get( - fobj.getUserAgent().getEventBroadcaster()); - boolean canRecover = (((InlineContainer) fobj).getOverflow() != EN_ERROR_IF_OVERFLOW); + BlockLevelEventProducer eventProducer = getBlockLevelEventProducer(); eventProducer.viewportBPDOverflow(this, fobj.getName(), - actualBPD - contentAreaBPD, needClip(), canRecover, + actualBPD - contentAreaBPD, needClip(), canRecoverFromOverflow(), fobj.getLocator()); } } } } + private BlockLevelEventProducer getBlockLevelEventProducer() { + return BlockLevelEventProducer.Provider.get(fobj.getUserAgent().getEventBroadcaster()); + } + + private boolean canRecoverFromOverflow() { + return ((InlineContainer) fobj).getOverflow() != EN_ERROR_IF_OVERFLOW; + } + private List getChildKnuthElements(LayoutContext layoutContext, int alignment) { List allChildElements = new LinkedList(); LayoutManager childLM; @@ -141,12 +148,22 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen allChildElements.addAll(childElements); // TODO breaks, keeps, empty content } + handleIPDOverflow(); wrapPositions(allChildElements); SpaceResolver.resolveElementList(allChildElements); // TODO break-before, break-after return allChildElements; } + private void handleIPDOverflow() { + if (ipdOverflow > 0) { + BlockLevelEventProducer eventProducer = getBlockLevelEventProducer(); + eventProducer.viewportIPDOverflow(this, fobj.getName(), + ipdOverflow, needClip(), canRecoverFromOverflow(), + fobj.getLocator()); + } + } + private void wrapPositions(List elements) { for (ListElement element : elements) { Position position = new NonLeafPosition(this, element.getPosition()); @@ -234,6 +251,11 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen context.getWritingMode()); } + public boolean handleOverflow(int milliPoints) { + ipdOverflow = Math.max(ipdOverflow, milliPoints); + return true; + } + public List addALetterSpaceTo(List oldList) { throw new UnsupportedOperationException("Not implemented"); } -- cgit v1.2.3 From 69f539c6f5fb7ef4b1ade45ad110901253772892 Mon Sep 17 00:00:00 2001 From: Vincent Hennebert Date: Thu, 31 Oct 2013 20:06:34 +0000 Subject: Take into account the inline-container's bpd in the alignment context git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_InlineContainer@1537612 13f79535-47bb-0310-9956-ffa450edef68 --- .../apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src/java/org/apache') diff --git a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java index 85d21833b..669b88838 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java @@ -247,8 +247,10 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen FontInfo fi = fobj.getFOEventHandler().getFontInfo(); FontTriplet[] fontkeys = ic.getCommonFont().getFontState(fi); Font fs = fi.getFontInstance(fontkeys[0], ic.getCommonFont().fontSize.getValue(this)); - return new AlignmentContext(fs, ic.getLineHeight().getOptimum(this).getLength().getValue(this), // TODO - context.getWritingMode()); + return new AlignmentContext(contentAreaBPD, + ic.getAlignmentAdjust(), ic.getAlignmentBaseline(), + ic.getBaselineShift(), ic.getDominantBaseline(), + context.getAlignmentContext()); } public boolean handleOverflow(int milliPoints) { -- cgit v1.2.3 From b0c8e3a541682a9a2becd5438e21d585d2f1656d Mon Sep 17 00:00:00 2001 From: Vincent Hennebert Date: Thu, 31 Oct 2013 20:07:19 +0000 Subject: Re-ordered methods from higher to lower level git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_InlineContainer@1537614 13f79535-47bb-0310-9956-ffa450edef68 --- .../inline/InlineContainerLayoutManager.java | 70 +++++++++++----------- 1 file changed, 35 insertions(+), 35 deletions(-) (limited to 'src/java/org/apache') diff --git a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java index 669b88838..75eb12d5b 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java @@ -107,6 +107,24 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen } } + private List getChildKnuthElements(LayoutContext layoutContext, int alignment) { + List allChildElements = new LinkedList(); + LayoutManager childLM; + while ((childLM = getChildLM()) != null) { + LayoutContext childLC = LayoutContext.offspringOf(layoutContext); // TODO copyOf? newInstance? + childLC.setRefIPD(layoutContext.getRefIPD()); + @SuppressWarnings("unchecked") + List childElements = childLM.getNextKnuthElements(childLC, alignmentBaseline); + allChildElements.addAll(childElements); + // TODO breaks, keeps, empty content + } + handleIPDOverflow(); + wrapPositions(allChildElements); + SpaceResolver.resolveElementList(allChildElements); + // TODO break-before, break-after + return allChildElements; + } + private void determineBPD() { LengthRangeProperty bpd = ((InlineContainer) fobj).getBlockProgressionDimension(); Property optimum = bpd.getOptimum(this); @@ -129,30 +147,15 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen } } - private BlockLevelEventProducer getBlockLevelEventProducer() { - return BlockLevelEventProducer.Provider.get(fobj.getUserAgent().getEventBroadcaster()); - } - - private boolean canRecoverFromOverflow() { - return ((InlineContainer) fobj).getOverflow() != EN_ERROR_IF_OVERFLOW; - } - - private List getChildKnuthElements(LayoutContext layoutContext, int alignment) { - List allChildElements = new LinkedList(); - LayoutManager childLM; - while ((childLM = getChildLM()) != null) { - LayoutContext childLC = LayoutContext.offspringOf(layoutContext); // TODO copyOf? newInstance? - childLC.setRefIPD(layoutContext.getRefIPD()); - @SuppressWarnings("unchecked") - List childElements = childLM.getNextKnuthElements(childLC, alignmentBaseline); - allChildElements.addAll(childElements); - // TODO breaks, keeps, empty content - } - handleIPDOverflow(); - wrapPositions(allChildElements); - SpaceResolver.resolveElementList(allChildElements); - // TODO break-before, break-after - return allChildElements; + protected AlignmentContext makeAlignmentContext(LayoutContext context) { + InlineContainer ic = (InlineContainer) fobj; + FontInfo fi = fobj.getFOEventHandler().getFontInfo(); + FontTriplet[] fontkeys = ic.getCommonFont().getFontState(fi); + Font fs = fi.getFontInstance(fontkeys[0], ic.getCommonFont().fontSize.getValue(this)); + return new AlignmentContext(contentAreaBPD, + ic.getAlignmentAdjust(), ic.getAlignmentBaseline(), + ic.getBaselineShift(), ic.getDominantBaseline(), + context.getAlignmentContext()); } private void handleIPDOverflow() { @@ -172,6 +175,14 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen } } + private BlockLevelEventProducer getBlockLevelEventProducer() { + return BlockLevelEventProducer.Provider.get(fobj.getUserAgent().getEventBroadcaster()); + } + + private boolean canRecoverFromOverflow() { + return ((InlineContainer) fobj).getOverflow() != EN_ERROR_IF_OVERFLOW; + } + @Override public void addAreas(PositionIterator posIter, LayoutContext context) { Position inlineContainerPosition = null; @@ -242,17 +253,6 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen return (overflow == EN_HIDDEN || overflow == EN_ERROR_IF_OVERFLOW); } - protected AlignmentContext makeAlignmentContext(LayoutContext context) { - InlineContainer ic = (InlineContainer) fobj; - FontInfo fi = fobj.getFOEventHandler().getFontInfo(); - FontTriplet[] fontkeys = ic.getCommonFont().getFontState(fi); - Font fs = fi.getFontInstance(fontkeys[0], ic.getCommonFont().fontSize.getValue(this)); - return new AlignmentContext(contentAreaBPD, - ic.getAlignmentAdjust(), ic.getAlignmentBaseline(), - ic.getBaselineShift(), ic.getDominantBaseline(), - context.getAlignmentContext()); - } - public boolean handleOverflow(int milliPoints) { ipdOverflow = Math.max(ipdOverflow, milliPoints); return true; -- cgit v1.2.3 From 3e04d9245827bc5bcbd320728fb14ec6f7613290 Mon Sep 17 00:00:00 2001 From: Vincent Hennebert Date: Wed, 6 Nov 2013 19:52:38 +0000 Subject: Added support for default alignment of inline-container areas with their parent areas git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_InlineContainer@1539441 13f79535-47bb-0310-9956-ffa450edef68 --- .../org/apache/fop/fo/flow/InlineContainer.java | 8 +- .../fop/layoutmgr/AbstractLayoutManager.java | 28 ++++ .../fop/layoutmgr/BlockContainerLayoutManager.java | 62 ++------- .../apache/fop/layoutmgr/BlockLayoutManager.java | 62 ++------- .../org/apache/fop/layoutmgr/LayoutManager.java | 17 +++ .../SpacedBorderedPaddedBlockLayoutManager.java | 112 +++++++++++++++ .../fop/layoutmgr/inline/ContentLayoutManager.java | 9 ++ .../inline/InlineContainerLayoutManager.java | 22 +-- .../fop/layoutmgr/inline/LineLayoutManager.java | 22 ++- .../fop/layoutmgr/list/ListBlockLayoutManager.java | 65 ++------- .../fop/layoutmgr/list/ListItemLayoutManager.java | 84 ++++------- .../fop/layoutmgr/table/TableLayoutManager.java | 68 ++------- .../inline-container_alignment.xml | 154 +++++++++++++++++++++ 13 files changed, 417 insertions(+), 296 deletions(-) create mode 100644 src/java/org/apache/fop/layoutmgr/SpacedBorderedPaddedBlockLayoutManager.java create mode 100644 test/layoutengine/standard-testcases/inline-container_alignment.xml (limited to 'src/java/org/apache') diff --git a/src/java/org/apache/fop/fo/flow/InlineContainer.java b/src/java/org/apache/fop/fo/flow/InlineContainer.java index d3a80eb3f..13ea9943a 100644 --- a/src/java/org/apache/fop/fo/flow/InlineContainer.java +++ b/src/java/org/apache/fop/fo/flow/InlineContainer.java @@ -29,7 +29,6 @@ import org.apache.fop.fo.FObj; import org.apache.fop.fo.PropertyList; import org.apache.fop.fo.ValidationException; import org.apache.fop.fo.properties.CommonBorderPaddingBackground; -import org.apache.fop.fo.properties.CommonFont; import org.apache.fop.fo.properties.CommonMarginInline; import org.apache.fop.fo.properties.KeepProperty; import org.apache.fop.fo.properties.LengthRangeProperty; @@ -59,7 +58,6 @@ public class InlineContainer extends FObj { /** used for FO validation */ private boolean blockItemFound; - private CommonFont commonFont; /** * Creates a new instance. @@ -73,7 +71,6 @@ public class InlineContainer extends FObj { @Override public void bind(PropertyList pList) throws FOPException { super.bind(pList); - commonFont = pList.getFontProps(); // TODO get directly from parent? alignmentAdjust = pList.get(PR_ALIGNMENT_ADJUST).getLength(); alignmentBaseline = pList.get(PR_ALIGNMENT_BASELINE).getEnum(); baselineShift = pList.get(PR_BASELINE_SHIFT).getLength(); @@ -214,8 +211,9 @@ public class InlineContainer extends FObj { return false; } - public CommonFont getCommonFont() { - return commonFont; + @Override + public boolean generatesReferenceAreas() { + return true; } } diff --git a/src/java/org/apache/fop/layoutmgr/AbstractLayoutManager.java b/src/java/org/apache/fop/layoutmgr/AbstractLayoutManager.java index 0285a41e7..2dd18e4ee 100644 --- a/src/java/org/apache/fop/layoutmgr/AbstractLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/AbstractLayoutManager.java @@ -342,6 +342,34 @@ public abstract class AbstractLayoutManager extends AbstractBaseLayoutManager im && isFinished()); } + public boolean hasLineAreaDescendant() { + if (childLMs == null || childLMs.isEmpty()) { + return false; + } else { + for (LayoutManager childLM : childLMs) { + if (childLM.hasLineAreaDescendant()) { + return true; + } + } + } + return false; + } + + public int getBaselineOffset() { + if (childLMs != null) { + for (LayoutManager childLM : childLMs) { + if (childLM.hasLineAreaDescendant()) { + return childLM.getBaselineOffset(); + } + } + } + throw newNoLineAreaDescendantException(); + } + + protected IllegalStateException newNoLineAreaDescendantException() { + return new IllegalStateException("getBaselineOffset called on an object that has no line-area descendant"); + } + /** * Transfers foreign attributes from the formatting object to the area. * @param targetArea the area to set the attributes on diff --git a/src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java b/src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java index a23cd28f1..83990797b 100644 --- a/src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java @@ -37,6 +37,7 @@ import org.apache.fop.datatypes.FODimension; import org.apache.fop.datatypes.Length; import org.apache.fop.fo.flow.BlockContainer; import org.apache.fop.fo.properties.CommonAbsolutePosition; +import org.apache.fop.fo.properties.CommonBorderPaddingBackground; import org.apache.fop.fo.properties.KeepProperty; import org.apache.fop.traits.MinOptMax; import org.apache.fop.traits.SpaceVal; @@ -44,8 +45,8 @@ import org.apache.fop.traits.SpaceVal; /** * LayoutManager for a block-container FO. */ -public class BlockContainerLayoutManager extends BlockStackingLayoutManager implements - ConditionalElementListener, BreakOpportunity { +public class BlockContainerLayoutManager extends SpacedBorderedPaddedBlockLayoutManager + implements BreakOpportunity { /** * logging instance @@ -79,13 +80,6 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager impl private MinOptMax foBlockSpaceBefore; private MinOptMax foBlockSpaceAfter; - private boolean discardBorderBefore; - private boolean discardBorderAfter; - private boolean discardPaddingBefore; - private boolean discardPaddingAfter; - private MinOptMax effSpaceBefore; - private MinOptMax effSpaceAfter; - private int horizontalOverflow; private double contentRectOffsetX = 0; private double contentRectOffsetY = 0; @@ -128,6 +122,11 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager impl .spaceAfter.getSpace().getOptimum(this).getLength().getValue(this); } + @Override + protected CommonBorderPaddingBackground getCommonBorderPaddingBackground() { + return getBlockContainerFO().getCommonBorderPaddingBackground(); + } + private void resetSpaces() { this.discardBorderBefore = false; this.discardBorderAfter = false; @@ -993,51 +992,6 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager impl return true; } - /** {@inheritDoc} */ - public void notifySpace(RelSide side, MinOptMax effectiveLength) { - if (RelSide.BEFORE == side) { - if (log.isDebugEnabled()) { - log.debug(this + ": Space " + side + ", " - + this.effSpaceBefore + "-> " + effectiveLength); - } - this.effSpaceBefore = effectiveLength; - } else { - if (log.isDebugEnabled()) { - log.debug(this + ": Space " + side + ", " - + this.effSpaceAfter + "-> " + effectiveLength); - } - this.effSpaceAfter = effectiveLength; - } - } - - /** {@inheritDoc} */ - public void notifyBorder(RelSide side, MinOptMax effectiveLength) { - if (effectiveLength == null) { - if (RelSide.BEFORE == side) { - this.discardBorderBefore = true; - } else { - this.discardBorderAfter = true; - } - } - if (log.isDebugEnabled()) { - log.debug(this + ": Border " + side + " -> " + effectiveLength); - } - } - - /** {@inheritDoc} */ - public void notifyPadding(RelSide side, MinOptMax effectiveLength) { - if (effectiveLength == null) { - if (RelSide.BEFORE == side) { - this.discardPaddingBefore = true; - } else { - this.discardPaddingAfter = true; - } - } - if (log.isDebugEnabled()) { - log.debug(this + ": Padding " + side + " -> " + effectiveLength); - } - } - /** {@inheritDoc} */ public boolean handleOverflow(int milliPoints) { if (milliPoints > this.horizontalOverflow) { diff --git a/src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java b/src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java index 0fb738aea..4129b65bd 100644 --- a/src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java @@ -32,6 +32,7 @@ import org.apache.fop.area.Block; import org.apache.fop.area.LineArea; import org.apache.fop.datatypes.Length; import org.apache.fop.fo.FONode; +import org.apache.fop.fo.properties.CommonBorderPaddingBackground; import org.apache.fop.fo.properties.KeepProperty; import org.apache.fop.fonts.Font; import org.apache.fop.fonts.FontInfo; @@ -44,8 +45,8 @@ import org.apache.fop.traits.SpaceVal; /** * LayoutManager for a block FO. */ -public class BlockLayoutManager extends BlockStackingLayoutManager implements ConditionalElementListener, - BreakOpportunity { +public class BlockLayoutManager extends SpacedBorderedPaddedBlockLayoutManager + implements BreakOpportunity { /** logging instance */ private static Log log = LogFactory.getLog(BlockLayoutManager.class); @@ -60,13 +61,6 @@ public class BlockLayoutManager extends BlockStackingLayoutManager implements Co private int follow = 2000; //private int middleShift = 0; - private boolean discardBorderBefore; - private boolean discardBorderAfter; - private boolean discardPaddingBefore; - private boolean discardPaddingAfter; - private MinOptMax effSpaceBefore; - private MinOptMax effSpaceAfter; - /** * Creates a new BlockLayoutManager. * @param inBlock the block FO object to create the layout manager for. @@ -100,6 +94,11 @@ public class BlockLayoutManager extends BlockStackingLayoutManager implements Co .getOptimum(this).getLength().getValue(this); } + @Override + protected CommonBorderPaddingBackground getCommonBorderPaddingBackground() { + return getBlockFO().getCommonBorderPaddingBackground(); + } + /** {@inheritDoc} */ @Override public List getNextKnuthElements(LayoutContext context, int alignment) { @@ -456,51 +455,6 @@ public class BlockLayoutManager extends BlockStackingLayoutManager implements Co return true; } - /** {@inheritDoc} */ - public void notifySpace(RelSide side, MinOptMax effectiveLength) { - if (RelSide.BEFORE == side) { - if (log.isDebugEnabled()) { - log.debug(this + ": Space " + side + ", " - + this.effSpaceBefore + "-> " + effectiveLength); - } - this.effSpaceBefore = effectiveLength; - } else { - if (log.isDebugEnabled()) { - log.debug(this + ": Space " + side + ", " - + this.effSpaceAfter + "-> " + effectiveLength); - } - this.effSpaceAfter = effectiveLength; - } - } - - /** {@inheritDoc} */ - public void notifyBorder(RelSide side, MinOptMax effectiveLength) { - if (effectiveLength == null) { - if (RelSide.BEFORE == side) { - this.discardBorderBefore = true; - } else { - this.discardBorderAfter = true; - } - } - if (log.isDebugEnabled()) { - log.debug(this + ": Border " + side + " -> " + effectiveLength); - } - } - - /** {@inheritDoc} */ - public void notifyPadding(RelSide side, MinOptMax effectiveLength) { - if (effectiveLength == null) { - if (RelSide.BEFORE == side) { - this.discardPaddingBefore = true; - } else { - this.discardPaddingAfter = true; - } - } - if (log.isDebugEnabled()) { - log.debug(this + ": Padding " + side + " -> " + effectiveLength); - } - } - /** {@inheritDoc} */ @Override public boolean isRestartable() { diff --git a/src/java/org/apache/fop/layoutmgr/LayoutManager.java b/src/java/org/apache/fop/layoutmgr/LayoutManager.java index 985131bf1..107a252b0 100644 --- a/src/java/org/apache/fop/layoutmgr/LayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/LayoutManager.java @@ -177,6 +177,23 @@ public interface LayoutManager extends PercentBaseContext { */ List getChangedKnuthElements(List oldList, int alignment); + /** + * Whether the FO handled by this layout manager had a descendant (including itself) + * that will generate a line-area. + * + * @return {@code true} if a descendant line-area will be generated, {@code false} otherwise + */ + boolean hasLineAreaDescendant(); + + /** + * Returns the position of the dominant-baseline of this FO's first descendant + * line-area, if any. + * + * @return this FO's space-before plus the distance from the before-edge of its + * allocation-rectangle to the dominant-baseline of the first line-area descendant + */ + int getBaselineOffset(); + /** * Returns the IPD of the content area * @return the IPD of the content area diff --git a/src/java/org/apache/fop/layoutmgr/SpacedBorderedPaddedBlockLayoutManager.java b/src/java/org/apache/fop/layoutmgr/SpacedBorderedPaddedBlockLayoutManager.java new file mode 100644 index 000000000..2ac41e96a --- /dev/null +++ b/src/java/org/apache/fop/layoutmgr/SpacedBorderedPaddedBlockLayoutManager.java @@ -0,0 +1,112 @@ +/* + * 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.layoutmgr; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.apache.fop.fo.FObj; +import org.apache.fop.fo.properties.CommonBorderPaddingBackground; +import org.apache.fop.traits.MinOptMax; + +/** + * A block-stacking layout manager for an FO that supports spaces, border and padding. + */ +public abstract class SpacedBorderedPaddedBlockLayoutManager extends BlockStackingLayoutManager + implements ConditionalElementListener { + + private static final Log LOG = LogFactory.getLog(BlockLayoutManager.class); + + protected MinOptMax effSpaceBefore; + + protected MinOptMax effSpaceAfter; + + protected boolean discardBorderBefore; + protected boolean discardBorderAfter; + protected boolean discardPaddingBefore; + protected boolean discardPaddingAfter; + + public SpacedBorderedPaddedBlockLayoutManager(FObj node) { + super(node); + } + + public void notifySpace(RelSide side, MinOptMax effectiveLength) { + if (RelSide.BEFORE == side) { + if (LOG.isDebugEnabled()) { + LOG.debug(this + ": Space " + side + ", " + + this.effSpaceBefore + "-> " + effectiveLength); + } + this.effSpaceBefore = effectiveLength; + } else { + if (LOG.isDebugEnabled()) { + LOG.debug(this + ": Space " + side + ", " + + this.effSpaceAfter + "-> " + effectiveLength); + } + this.effSpaceAfter = effectiveLength; + } + } + + public void notifyBorder(RelSide side, MinOptMax effectiveLength) { + if (effectiveLength == null) { + if (RelSide.BEFORE == side) { + this.discardBorderBefore = true; + } else { + this.discardBorderAfter = true; + } + } + if (LOG.isDebugEnabled()) { + LOG.debug(this + ": Border " + side + " -> " + effectiveLength); + } + } + + public void notifyPadding(RelSide side, MinOptMax effectiveLength) { + if (effectiveLength == null) { + if (RelSide.BEFORE == side) { + this.discardPaddingBefore = true; + } else { + this.discardPaddingAfter = true; + } + } + if (LOG.isDebugEnabled()) { + LOG.debug(this + ": Padding " + side + " -> " + effectiveLength); + } + } + + @Override + public int getBaselineOffset() { + int baselineOffset = super.getBaselineOffset(); + if (effSpaceBefore != null) { + baselineOffset += effSpaceBefore.getOpt(); + } + if (!discardBorderBefore) { + baselineOffset += getCommonBorderPaddingBackground().getBorderBeforeWidth(false); + } + if (!discardPaddingBefore) { + baselineOffset += getCommonBorderPaddingBackground().getPaddingBefore(false, this); + } + return baselineOffset; + } + + /** + * Returns the {@link CommonBorderPaddingBackground} instance from the FO handled by this layout manager. + */ + protected abstract CommonBorderPaddingBackground getCommonBorderPaddingBackground(); + +} diff --git a/src/java/org/apache/fop/layoutmgr/inline/ContentLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/ContentLayoutManager.java index b3c768987..c067b040f 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/ContentLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/ContentLayoutManager.java @@ -332,6 +332,15 @@ public class ContentLayoutManager extends AbstractBaseLayoutManager return parentLM.getPSLM(); } + + public boolean hasLineAreaDescendant() { + return true; + } + + public int getBaselineOffset() { + return childLM.getBaselineOffset(); + } + // --------- Property Resolution related functions --------- // /** diff --git a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java index 75eb12d5b..9495e2c73 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java @@ -31,9 +31,6 @@ import org.apache.fop.fo.flow.InlineContainer; import org.apache.fop.fo.properties.CommonBorderPaddingBackground; import org.apache.fop.fo.properties.LengthRangeProperty; import org.apache.fop.fo.properties.Property; -import org.apache.fop.fonts.Font; -import org.apache.fop.fonts.FontInfo; -import org.apache.fop.fonts.FontTriplet; import org.apache.fop.layoutmgr.AbstractLayoutManager; import org.apache.fop.layoutmgr.AreaAdditionUtil; import org.apache.fop.layoutmgr.BlockLevelEventProducer; @@ -64,6 +61,7 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen private List childElements; private int ipdOverflow; + private AlignmentContext alignmentContext; private InlineViewport currentViewport; private Container referenceArea; @@ -84,7 +82,7 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen childLC.setRefIPD(contentAreaIPD); childElements = getChildKnuthElements(childLC, alignment); // TODO which alignment? determineBPD(); - AlignmentContext alignmentContext = makeAlignmentContext(context); // TODO correct? + alignmentContext = makeAlignmentContext(context); // TODO correct? Position position = new Position(this, 0); KnuthSequence knuthSequence = new InlineKnuthSequence(); knuthSequence.add(new KnuthInlineBox(contentAreaIPD, alignmentContext, position, false)); @@ -121,6 +119,7 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen handleIPDOverflow(); wrapPositions(allChildElements); SpaceResolver.resolveElementList(allChildElements); + SpaceResolver.performConditionalsNotification(allChildElements, 0, allChildElements.size() - 1, -1); // TODO break-before, break-after return allChildElements; } @@ -149,13 +148,13 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen protected AlignmentContext makeAlignmentContext(LayoutContext context) { InlineContainer ic = (InlineContainer) fobj; - FontInfo fi = fobj.getFOEventHandler().getFontInfo(); - FontTriplet[] fontkeys = ic.getCommonFont().getFontState(fi); - Font fs = fi.getFontInstance(fontkeys[0], ic.getCommonFont().fontSize.getValue(this)); - return new AlignmentContext(contentAreaBPD, + AlignmentContext ac = new AlignmentContext(contentAreaBPD, ic.getAlignmentAdjust(), ic.getAlignmentBaseline(), ic.getBaselineShift(), ic.getDominantBaseline(), context.getAlignmentContext()); + int baselineOffset = hasLineAreaDescendant() ? getBaselineOffset() : contentAreaBPD; + ac.resizeLine(contentAreaBPD, baselineOffset); + return ac; } private void handleIPDOverflow() { @@ -183,6 +182,11 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen return ((InlineContainer) fobj).getOverflow() != EN_ERROR_IF_OVERFLOW; } + @Override + public boolean getGeneratesReferenceArea() { + return true; + } + @Override public void addAreas(PositionIterator posIter, LayoutContext context) { Position inlineContainerPosition = null; @@ -196,7 +200,6 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen assert inlineContainerPosition.getLM() == this; } assert inlineContainerPosition != null; - SpaceResolver.performConditionalsNotification(childElements, 0, childElements.size() - 1, -1); KnuthPossPosIter childPosIter = new KnuthPossPosIter(childElements); AreaAdditionUtil.addAreas(this, childPosIter, context); @@ -212,6 +215,7 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen TraitSetter.setProducerID(referenceArea, fobj.getId()); referenceArea.setIPD(contentAreaIPD); currentViewport = new InlineViewport(referenceArea); + currentViewport.setBlockProgressionOffset(alignmentContext.getOffset()); currentViewport.addTrait(Trait.IS_VIEWPORT_AREA, Boolean.TRUE); currentViewport.setIPD(getContentAreaIPD()); currentViewport.setBPD(getContentAreaBPD()); diff --git a/src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java index b3987a075..25d8c0872 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java @@ -160,6 +160,8 @@ public class LineLayoutManager extends InlineStackingLayoutManager private final int follow; private AlignmentContext alignmentContext; + private int baselineOffset = -1; + private List knuthParagraphs; private LineLayoutPossibilities lineLayouts; @@ -556,7 +558,6 @@ public class LineLayoutManager extends InlineStackingLayoutManager private int constantLineHeight = 12000; - /** * Create a new Line Layout Manager. * This is used by the block layout manager to create @@ -939,7 +940,11 @@ public class LineLayoutManager extends InlineStackingLayoutManager while (listIter.hasNext()) { ListElement tempElement; tempElement = (ListElement) listIter.next(); - if (tempElement.getLayoutManager() != this) { + LayoutManager lm = tempElement.getLayoutManager(); + if (baselineOffset < 0 && lm != null && lm.hasLineAreaDescendant()) { + baselineOffset = lm.getBaselineOffset(); + } + if (lm != this) { tempElement.setPosition(notifyPos(new NonLeafPosition(this, tempElement.getPosition()))); } @@ -987,6 +992,9 @@ public class LineLayoutManager extends InlineStackingLayoutManager } startIndex = endIndex + 1; LineBreakPosition lbp = (LineBreakPosition) llPoss.getChosenPosition(i); + if (baselineOffset < 0) { + baselineOffset = lbp.spaceBefore + lbp.baseline; + } returnList.add(new KnuthBlockBox( lbp.lineHeight + lbp.spaceBefore + lbp.spaceAfter, footnoteList, lbp, false)); @@ -1424,6 +1432,16 @@ public class LineLayoutManager extends InlineStackingLayoutManager } } + @Override + public boolean hasLineAreaDescendant() { + return true; + } + + @Override + public int getBaselineOffset() { + return baselineOffset; + } + /** * Add the areas with the break points. * diff --git a/src/java/org/apache/fop/layoutmgr/list/ListBlockLayoutManager.java b/src/java/org/apache/fop/layoutmgr/list/ListBlockLayoutManager.java index 61d8a891d..44a9720a4 100644 --- a/src/java/org/apache/fop/layoutmgr/list/ListBlockLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/list/ListBlockLayoutManager.java @@ -28,16 +28,15 @@ 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.fo.properties.CommonBorderPaddingBackground; import org.apache.fop.fo.properties.KeepProperty; -import org.apache.fop.layoutmgr.BlockStackingLayoutManager; -import org.apache.fop.layoutmgr.ConditionalElementListener; import org.apache.fop.layoutmgr.ElementListUtils; import org.apache.fop.layoutmgr.LayoutContext; 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.SpacedBorderedPaddedBlockLayoutManager; import org.apache.fop.layoutmgr.TraitSetter; import org.apache.fop.traits.MinOptMax; import org.apache.fop.traits.SpaceVal; @@ -47,21 +46,13 @@ import org.apache.fop.traits.SpaceVal; * A list block contains list items which are stacked within * the list block area.. */ -public class ListBlockLayoutManager extends BlockStackingLayoutManager - implements ConditionalElementListener { +public class ListBlockLayoutManager extends SpacedBorderedPaddedBlockLayoutManager { /** logging instance */ private static Log log = LogFactory.getLog(ListBlockLayoutManager.class); private Block curBlockArea; - private boolean discardBorderBefore; - private boolean discardBorderAfter; - private boolean discardPaddingBefore; - private boolean discardPaddingAfter; - private MinOptMax effSpaceBefore; - private MinOptMax effSpaceAfter; - /** * Create a new list block layout manager. * @param node list-block to create the layout manager for @@ -70,6 +61,11 @@ public class ListBlockLayoutManager extends BlockStackingLayoutManager super(node); } + @Override + protected CommonBorderPaddingBackground getCommonBorderPaddingBackground() { + return getListBlockFO().getCommonBorderPaddingBackground(); + } + /** * Convenience method. * @return the ListBlock node @@ -277,50 +273,5 @@ public class ListBlockLayoutManager extends BlockStackingLayoutManager return getListBlockFO().getKeepWithNext(); } - /** {@inheritDoc} */ - public void notifySpace(RelSide side, MinOptMax effectiveLength) { - if (RelSide.BEFORE == side) { - if (log.isDebugEnabled()) { - log.debug(this + ": Space " + side + ", " - + this.effSpaceBefore + "-> " + effectiveLength); - } - this.effSpaceBefore = effectiveLength; - } else { - if (log.isDebugEnabled()) { - log.debug(this + ": Space " + side + ", " - + this.effSpaceAfter + "-> " + effectiveLength); - } - this.effSpaceAfter = effectiveLength; - } - } - - /** {@inheritDoc} */ - public void notifyBorder(RelSide side, MinOptMax effectiveLength) { - if (effectiveLength == null) { - if (RelSide.BEFORE == side) { - this.discardBorderBefore = true; - } else { - this.discardBorderAfter = true; - } - } - if (log.isDebugEnabled()) { - log.debug(this + ": Border " + side + " -> " + effectiveLength); - } - } - - /** {@inheritDoc} */ - public void notifyPadding(RelSide side, MinOptMax effectiveLength) { - if (effectiveLength == null) { - if (RelSide.BEFORE == side) { - this.discardPaddingBefore = true; - } else { - this.discardPaddingAfter = true; - } - } - if (log.isDebugEnabled()) { - log.debug(this + ": Padding " + side + " -> " + effectiveLength); - } - } - } diff --git a/src/java/org/apache/fop/layoutmgr/list/ListItemLayoutManager.java b/src/java/org/apache/fop/layoutmgr/list/ListItemLayoutManager.java index 083e4ee1b..344f6722b 100644 --- a/src/java/org/apache/fop/layoutmgr/list/ListItemLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/list/ListItemLayoutManager.java @@ -32,12 +32,11 @@ 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; +import org.apache.fop.fo.properties.CommonBorderPaddingBackground; import org.apache.fop.fo.properties.KeepProperty; -import org.apache.fop.layoutmgr.BlockStackingLayoutManager; import org.apache.fop.layoutmgr.BreakElement; import org.apache.fop.layoutmgr.BreakOpportunity; import org.apache.fop.layoutmgr.BreakOpportunityHelper; -import org.apache.fop.layoutmgr.ConditionalElementListener; import org.apache.fop.layoutmgr.ElementListObserver; import org.apache.fop.layoutmgr.ElementListUtils; import org.apache.fop.layoutmgr.FootnoteBodyLayoutManager; @@ -53,10 +52,9 @@ import org.apache.fop.layoutmgr.ListElement; 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.SpacedBorderedPaddedBlockLayoutManager; import org.apache.fop.layoutmgr.TraitSetter; -import org.apache.fop.traits.MinOptMax; import org.apache.fop.traits.SpaceVal; import org.apache.fop.util.BreakUtil; @@ -64,8 +62,8 @@ import org.apache.fop.util.BreakUtil; * LayoutManager for a list-item FO. * The list item contains a list item label and a list item body. */ -public class ListItemLayoutManager extends BlockStackingLayoutManager implements ConditionalElementListener, - BreakOpportunity { +public class ListItemLayoutManager extends SpacedBorderedPaddedBlockLayoutManager + implements BreakOpportunity { /** logging instance */ private static Log log = LogFactory.getLog(ListItemLayoutManager.class); @@ -78,13 +76,6 @@ public class ListItemLayoutManager extends BlockStackingLayoutManager implements private List labelList = null; private List bodyList = null; - private boolean discardBorderBefore; - private boolean discardBorderAfter; - private boolean discardPaddingBefore; - private boolean discardPaddingAfter; - private MinOptMax effSpaceBefore; - private MinOptMax effSpaceAfter; - private Keep keepWithNextPendingOnLabel; private Keep keepWithNextPendingOnBody; @@ -145,6 +136,11 @@ public class ListItemLayoutManager extends BlockStackingLayoutManager implements setBody(node.getBody()); } + @Override + protected CommonBorderPaddingBackground getCommonBorderPaddingBackground() { + return getListItemFO().getCommonBorderPaddingBackground(); + } + /** * Convenience method. * @return the ListBlock node @@ -475,6 +471,23 @@ public class ListItemLayoutManager extends BlockStackingLayoutManager implements return returnedList; } + + @Override + public boolean hasLineAreaDescendant() { + return label.hasLineAreaDescendant() || body.hasLineAreaDescendant(); + } + + @Override + public int getBaselineOffset() { + if (label.hasLineAreaDescendant()) { + return label.getBaselineOffset(); + } else if (body.hasLineAreaDescendant()) { + return body.getBaselineOffset(); + } else { + throw newNoLineAreaDescendantException(); + } + } + /** * Add the areas for the break points. * @@ -652,51 +665,6 @@ public class ListItemLayoutManager extends BlockStackingLayoutManager implements return getListItemFO().getKeepWithNext(); } - /** {@inheritDoc} */ - public void notifySpace(RelSide side, MinOptMax effectiveLength) { - if (RelSide.BEFORE == side) { - if (log.isDebugEnabled()) { - log.debug(this + ": Space " + side + ", " - + this.effSpaceBefore + "-> " + effectiveLength); - } - this.effSpaceBefore = effectiveLength; - } else { - if (log.isDebugEnabled()) { - log.debug(this + ": Space " + side + ", " - + this.effSpaceAfter + "-> " + effectiveLength); - } - this.effSpaceAfter = effectiveLength; - } - } - - /** {@inheritDoc} */ - public void notifyBorder(RelSide side, MinOptMax effectiveLength) { - if (effectiveLength == null) { - if (RelSide.BEFORE == side) { - this.discardBorderBefore = true; - } else { - this.discardBorderAfter = true; - } - } - if (log.isDebugEnabled()) { - log.debug(this + ": Border " + side + " -> " + effectiveLength); - } - } - - /** {@inheritDoc} */ - public void notifyPadding(RelSide side, MinOptMax effectiveLength) { - if (effectiveLength == null) { - if (RelSide.BEFORE == side) { - this.discardPaddingBefore = true; - } else { - this.discardPaddingAfter = true; - } - } - if (log.isDebugEnabled()) { - log.debug(this + ": Padding " + side + " -> " + effectiveLength); - } - } - /** {@inheritDoc} */ @Override public void reset() { diff --git a/src/java/org/apache/fop/layoutmgr/table/TableLayoutManager.java b/src/java/org/apache/fop/layoutmgr/table/TableLayoutManager.java index 7f1754064..afb6547c0 100644 --- a/src/java/org/apache/fop/layoutmgr/table/TableLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/table/TableLayoutManager.java @@ -39,12 +39,11 @@ import org.apache.fop.fo.flow.Markers; import org.apache.fop.fo.flow.RetrieveTableMarker; import org.apache.fop.fo.flow.table.Table; import org.apache.fop.fo.flow.table.TableColumn; +import org.apache.fop.fo.properties.CommonBorderPaddingBackground; import org.apache.fop.fo.properties.KeepProperty; import org.apache.fop.layoutmgr.BlockLevelEventProducer; -import org.apache.fop.layoutmgr.BlockStackingLayoutManager; import org.apache.fop.layoutmgr.BreakElement; import org.apache.fop.layoutmgr.BreakOpportunity; -import org.apache.fop.layoutmgr.ConditionalElementListener; import org.apache.fop.layoutmgr.KnuthElement; import org.apache.fop.layoutmgr.KnuthGlue; import org.apache.fop.layoutmgr.LayoutContext; @@ -52,7 +51,7 @@ import org.apache.fop.layoutmgr.LeafPosition; import org.apache.fop.layoutmgr.ListElement; import org.apache.fop.layoutmgr.Position; import org.apache.fop.layoutmgr.PositionIterator; -import org.apache.fop.layoutmgr.RelSide; +import org.apache.fop.layoutmgr.SpacedBorderedPaddedBlockLayoutManager; import org.apache.fop.layoutmgr.TraitSetter; import org.apache.fop.traits.MinOptMax; import org.apache.fop.traits.SpaceVal; @@ -66,8 +65,8 @@ import org.apache.fop.util.BreakUtil; * The table then creates areas for the columns, bodies and rows * the render background. */ -public class TableLayoutManager extends BlockStackingLayoutManager - implements ConditionalElementListener, BreakOpportunity { +public class TableLayoutManager extends SpacedBorderedPaddedBlockLayoutManager + implements BreakOpportunity { /** * logging instance @@ -82,13 +81,6 @@ public class TableLayoutManager extends BlockStackingLayoutManager private double tableUnit; private boolean autoLayout = true; - private boolean discardBorderBefore; - private boolean discardBorderAfter; - private boolean discardPaddingBefore; - private boolean discardPaddingAfter; - private MinOptMax effSpaceBefore; - private MinOptMax effSpaceAfter; - private int halfBorderSeparationBPD; private int halfBorderSeparationIPD; @@ -132,6 +124,13 @@ public class TableLayoutManager extends BlockStackingLayoutManager this.columns = new ColumnSetup(node); } + + @Override + protected CommonBorderPaddingBackground getCommonBorderPaddingBackground() { + return getTable().getCommonBorderPaddingBackground(); + } + + /** @return the table FO */ public Table getTable() { return (Table)this.fobj; @@ -521,51 +520,6 @@ public class TableLayoutManager extends BlockStackingLayoutManager } } - /** {@inheritDoc} */ - public void notifySpace(RelSide side, MinOptMax effectiveLength) { - if (RelSide.BEFORE == side) { - if (log.isDebugEnabled()) { - log.debug(this + ": Space " + side + ", " - + this.effSpaceBefore + "-> " + effectiveLength); - } - this.effSpaceBefore = effectiveLength; - } else { - if (log.isDebugEnabled()) { - log.debug(this + ": Space " + side + ", " - + this.effSpaceAfter + "-> " + effectiveLength); - } - this.effSpaceAfter = effectiveLength; - } - } - - /** {@inheritDoc} */ - public void notifyBorder(RelSide side, MinOptMax effectiveLength) { - if (effectiveLength == null) { - if (RelSide.BEFORE == side) { - this.discardBorderBefore = true; - } else { - this.discardBorderAfter = true; - } - } - if (log.isDebugEnabled()) { - log.debug(this + ": Border " + side + " -> " + effectiveLength); - } - } - - /** {@inheritDoc} */ - public void notifyPadding(RelSide side, MinOptMax effectiveLength) { - if (effectiveLength == null) { - if (RelSide.BEFORE == side) { - this.discardPaddingBefore = true; - } else { - this.discardPaddingAfter = true; - } - } - if (log.isDebugEnabled()) { - log.debug(this + ": Padding " + side + " -> " + effectiveLength); - } - } - /** {@inheritDoc} */ public void reset() { super.reset(); diff --git a/test/layoutengine/standard-testcases/inline-container_alignment.xml b/test/layoutengine/standard-testcases/inline-container_alignment.xml new file mode 100644 index 000000000..153d9a9c4 --- /dev/null +++ b/test/layoutengine/standard-testcases/inline-container_alignment.xml @@ -0,0 +1,154 @@ + + + + + +

+ Checks that the inline-container is properly aligned with the parent area. +

+
+ + + + + + + + + + By default the alignment is with the baseline of the first descendant + line-area. + The line before. The line before. Before: + Inside the inline-container. + After the inline-container. + + + + + + The line before. The line before. Before: + + + Inside the + inline-container. + + + After the inline-container. + + + + + + This inline-container has no line-area descendant. The after edge of its + allocation rectangle should be aligned with the baseline. + The line before. The line before. Before: + + After the inline-container. + + + + + + The first line-area descendant is in fo:list-item-body. + The line before. The line before. Before: + + + + + + + + List item + + + + + After the inline-container. + + + + + + This inline-container contains a block that contains an inline that contains a + block. + The line before. The line before. Before: + inline + After the inline-container. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
-- cgit v1.2.3 From 89b34550c9bb72ba8dcf01e09038ad11ec5757f6 Mon Sep 17 00:00:00 2001 From: Vincent Hennebert Date: Thu, 7 Nov 2013 12:27:22 +0000 Subject: Added support for non-default values of alignment-adjust git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_InlineContainer@1539610 13f79535-47bb-0310-9956-ffa450edef68 --- .../inline/InlineContainerLayoutManager.java | 43 ++++- .../inline-container_alignment-adjust.xml | 210 +++++++++++++++++++++ 2 files changed, 252 insertions(+), 1 deletion(-) create mode 100644 test/layoutengine/standard-testcases/inline-container_alignment-adjust.xml (limited to 'src/java/org/apache') diff --git a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java index 9495e2c73..626ce8209 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java @@ -27,6 +27,8 @@ import org.apache.fop.area.Area; import org.apache.fop.area.Trait; import org.apache.fop.area.inline.Container; import org.apache.fop.area.inline.InlineViewport; +import org.apache.fop.datatypes.Length; +import org.apache.fop.fo.Constants; import org.apache.fop.fo.flow.InlineContainer; import org.apache.fop.fo.properties.CommonBorderPaddingBackground; import org.apache.fop.fo.properties.LengthRangeProperty; @@ -152,7 +154,7 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen ic.getAlignmentAdjust(), ic.getAlignmentBaseline(), ic.getBaselineShift(), ic.getDominantBaseline(), context.getAlignmentContext()); - int baselineOffset = hasLineAreaDescendant() ? getBaselineOffset() : contentAreaBPD; + int baselineOffset = getAlignmentPoint(); ac.resizeLine(contentAreaBPD, baselineOffset); return ac; } @@ -182,6 +184,45 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen return ((InlineContainer) fobj).getOverflow() != EN_ERROR_IF_OVERFLOW; } + private int getAlignmentPoint() { + Length alignmentAdjust = ((InlineContainer) fobj).getAlignmentAdjust(); + int baseline = alignmentAdjust.getEnum(); + if (baseline == Constants.EN_AUTO + || baseline == Constants.EN_BASELINE) { + return getInlineContainerBaselineOffset(alignmentBaseline); + } else if (baseline != 0) { + return getInlineContainerBaselineOffset(baseline); + } else { + return 0; + } + } + + private int getInlineContainerBaselineOffset(int property) { + switch (property) { + case Constants.EN_BEFORE_EDGE: + case Constants.EN_TEXT_BEFORE_EDGE: + return 0; + case Constants.EN_AFTER_EDGE: + case Constants.EN_TEXT_AFTER_EDGE: + return contentAreaBPD; + case Constants.EN_MIDDLE: + case Constants.EN_CENTRAL: + case Constants.EN_MATHEMATICAL: + return contentAreaBPD / 2; + case Constants.EN_IDEOGRAPHIC: + return contentAreaBPD * 7 / 10; + case Constants.EN_ALPHABETIC: + return contentAreaBPD * 6 / 10; + case Constants.EN_HANGING: + return contentAreaBPD * 2 / 10; + case Constants.EN_AUTO: + case Constants.EN_BASELINE: + return hasLineAreaDescendant() ? getBaselineOffset() : contentAreaBPD; + default: + throw new AssertionError("Unknown baseline value: " + property); + } + } + @Override public boolean getGeneratesReferenceArea() { return true; diff --git a/test/layoutengine/standard-testcases/inline-container_alignment-adjust.xml b/test/layoutengine/standard-testcases/inline-container_alignment-adjust.xml new file mode 100644 index 000000000..917a3a1d2 --- /dev/null +++ b/test/layoutengine/standard-testcases/inline-container_alignment-adjust.xml @@ -0,0 +1,210 @@ + + + + + +

+ Checks that the alignment-adjust property on inline-container behaves properly. +

+
+ + + + + + + + + + + alignment-adjust="before-edge": + The line before. The line before. Before: + Inside the inline-container. + After the inline-container. + + + + + + alignment-adjust="text-before-edge": + The line before. The line before. Before: + Inside the inline-container. + After the inline-container. + + + + + + alignment-adjust="after-edge": + The line before. The line before. Before: + Inside the inline-container. + After the inline-container. + + + + + + alignment-adjust="text-after-edge": + The line before. The line before. Before: + Inside the inline-container. + After the inline-container. + + + + + + alignment-adjust="middle": + The line before. The line before. Before: + Inside the inline-container. + After the inline-container. + + + + + + alignment-adjust="central": + The line before. The line before. Before: + Inside the inline-container. + After the inline-container. + + + + + + alignment-adjust="ideographic": + The line before. The line before. Before: + Inside the inline-container. + After the inline-container. + + + + + + alignment-adjust="alphabetic": + The line before. The line before. Before: + Inside the inline-container. + After the inline-container. + + + + + + alignment-adjust="hanging": + The line before. The line before. Before: + Inside the inline-container. + After the inline-container. + + + + + + alignment-adjust="mathematical": + The line before. The line before. Before: + Inside the inline-container. + After the inline-container. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
-- cgit v1.2.3 From 6e65f409038594b64900780044fc5e94a84504e3 Mon Sep 17 00:00:00 2001 From: Vincent Hennebert Date: Thu, 7 Nov 2013 12:29:17 +0000 Subject: Added support for alignment-baseline on fo:inline-container git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_InlineContainer@1539611 13f79535-47bb-0310-9956-ffa450edef68 --- .../inline/InlineContainerLayoutManager.java | 20 +++--- .../inline-container_alignment-baseline.xml | 74 ++++++++++++++++++++++ 2 files changed, 86 insertions(+), 8 deletions(-) create mode 100644 test/layoutengine/standard-testcases/inline-container_alignment-baseline.xml (limited to 'src/java/org/apache') diff --git a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java index 626ce8209..b043268bd 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java @@ -57,7 +57,6 @@ import org.apache.fop.layoutmgr.TraitSetter; public class InlineContainerLayoutManager extends AbstractLayoutManager implements InlineLevelLayoutManager { private CommonBorderPaddingBackground borderProps; - private int alignmentBaseline = EN_BASELINE; private int contentAreaIPD; private int contentAreaBPD; @@ -77,6 +76,11 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen borderProps = node.getCommonBorderPaddingBackground(); } + private InlineContainer getInlineContainer() { + assert fobj instanceof InlineContainer; + return (InlineContainer) fobj; + } + @Override public List getNextKnuthElements(LayoutContext context, int alignment) { determineIPD(context); @@ -95,7 +99,7 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen } private void determineIPD(LayoutContext layoutContext) { - LengthRangeProperty ipd = ((InlineContainer) fobj).getInlineProgressionDimension(); + LengthRangeProperty ipd = getInlineContainer().getInlineProgressionDimension(); Property optimum = ipd.getOptimum(this); if (optimum.isAuto()) { contentAreaIPD = layoutContext.getRefIPD(); @@ -114,7 +118,7 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen LayoutContext childLC = LayoutContext.offspringOf(layoutContext); // TODO copyOf? newInstance? childLC.setRefIPD(layoutContext.getRefIPD()); @SuppressWarnings("unchecked") - List childElements = childLM.getNextKnuthElements(childLC, alignmentBaseline); + List childElements = childLM.getNextKnuthElements(childLC, alignment); allChildElements.addAll(childElements); // TODO breaks, keeps, empty content } @@ -127,7 +131,7 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen } private void determineBPD() { - LengthRangeProperty bpd = ((InlineContainer) fobj).getBlockProgressionDimension(); + LengthRangeProperty bpd = getInlineContainer().getBlockProgressionDimension(); Property optimum = bpd.getOptimum(this); int actualBPD = ElementListUtils.calcContentLength(childElements); if (optimum.isAuto()) { @@ -181,15 +185,15 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen } private boolean canRecoverFromOverflow() { - return ((InlineContainer) fobj).getOverflow() != EN_ERROR_IF_OVERFLOW; + return getInlineContainer().getOverflow() != EN_ERROR_IF_OVERFLOW; } private int getAlignmentPoint() { - Length alignmentAdjust = ((InlineContainer) fobj).getAlignmentAdjust(); + Length alignmentAdjust = getInlineContainer().getAlignmentAdjust(); int baseline = alignmentAdjust.getEnum(); if (baseline == Constants.EN_AUTO || baseline == Constants.EN_BASELINE) { - return getInlineContainerBaselineOffset(alignmentBaseline); + return getInlineContainerBaselineOffset(getInlineContainer().getAlignmentBaseline()); } else if (baseline != 0) { return getInlineContainerBaselineOffset(baseline); } else { @@ -294,7 +298,7 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen } private boolean needClip() { - int overflow = ((InlineContainer) fobj).getOverflow(); + int overflow = getInlineContainer().getOverflow(); return (overflow == EN_HIDDEN || overflow == EN_ERROR_IF_OVERFLOW); } diff --git a/test/layoutengine/standard-testcases/inline-container_alignment-baseline.xml b/test/layoutengine/standard-testcases/inline-container_alignment-baseline.xml new file mode 100644 index 000000000..53b9be0a9 --- /dev/null +++ b/test/layoutengine/standard-testcases/inline-container_alignment-baseline.xml @@ -0,0 +1,74 @@ + + + + + +

+ Checks that the alignment-baseline property on inline-container behaves properly. +

+
+ + + + + + + + + + + alignment-baseline="before-edge": + The line before. The line before. Before: + Inside the inline-container. + After the inline-container. + + + + + + alignment-baseline="central": + The line before. The line before. Before: + Inside the inline-container. + After the inline-container. + + + + + + + + + + + + + + + + + + + + + + + +
-- cgit v1.2.3 From e1ee53b4c48a7f7781fe88a0336e77dad0df37cd Mon Sep 17 00:00:00 2001 From: Vincent Hennebert Date: Thu, 7 Nov 2013 12:29:56 +0000 Subject: Improved javadoc for getBaselineOffset git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_InlineContainer@1539612 13f79535-47bb-0310-9956-ffa450edef68 --- src/java/org/apache/fop/layoutmgr/LayoutManager.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src/java/org/apache') diff --git a/src/java/org/apache/fop/layoutmgr/LayoutManager.java b/src/java/org/apache/fop/layoutmgr/LayoutManager.java index 107a252b0..8d1e4001c 100644 --- a/src/java/org/apache/fop/layoutmgr/LayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/LayoutManager.java @@ -178,7 +178,7 @@ public interface LayoutManager extends PercentBaseContext { List getChangedKnuthElements(List oldList, int alignment); /** - * Whether the FO handled by this layout manager had a descendant (including itself) + * Whether the FO handled by this layout manager has a descendant (including itself) * that will generate a line-area. * * @return {@code true} if a descendant line-area will be generated, {@code false} otherwise @@ -187,10 +187,12 @@ public interface LayoutManager extends PercentBaseContext { /** * Returns the position of the dominant-baseline of this FO's first descendant - * line-area, if any. + * line-area.

The behavior of this method is undefined if this FO has no descendant + * line-area, and an exception may be thrown. See {@link #hasLineAreaDescendant()}

* * @return this FO's space-before plus the distance from the before-edge of its * allocation-rectangle to the dominant-baseline of the first line-area descendant + * @see #hasLineAreaDescendant() */ int getBaselineOffset(); -- cgit v1.2.3 From d08e7a0a304e7f3e3e0d5181bfc00959aa493eee Mon Sep 17 00:00:00 2001 From: Vincent Hennebert Date: Thu, 7 Nov 2013 12:31:31 +0000 Subject: Added support for dominant-baseline on fo:inline-container git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_InlineContainer@1539613 13f79535-47bb-0310-9956-ffa450edef68 --- .../fop/layoutmgr/inline/AlignmentContext.java | 2 +- .../inline/InlineContainerLayoutManager.java | 9 +-- .../inline-container_dominant-baseline.xml | 74 ++++++++++++++++++++++ 3 files changed, 80 insertions(+), 5 deletions(-) create mode 100644 test/layoutengine/standard-testcases/inline-container_dominant-baseline.xml (limited to 'src/java/org/apache') diff --git a/src/java/org/apache/fop/layoutmgr/inline/AlignmentContext.java b/src/java/org/apache/fop/layoutmgr/inline/AlignmentContext.java index c1992965c..192956abc 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/AlignmentContext.java +++ b/src/java/org/apache/fop/layoutmgr/inline/AlignmentContext.java @@ -295,7 +295,7 @@ public class AlignmentContext implements Constants { * Return the dominant baseline identifier. * @return the dominant baseline identifier */ - private int getDominantBaselineIdentifier() { + public int getDominantBaselineIdentifier() { return actualBaselineTable.getDominantBaselineIdentifier(); } diff --git a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java index b043268bd..21e3cb283 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java @@ -158,7 +158,7 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen ic.getAlignmentAdjust(), ic.getAlignmentBaseline(), ic.getBaselineShift(), ic.getDominantBaseline(), context.getAlignmentContext()); - int baselineOffset = getAlignmentPoint(); + int baselineOffset = getAlignmentPoint(ac.getDominantBaselineIdentifier()); ac.resizeLine(contentAreaBPD, baselineOffset); return ac; } @@ -188,12 +188,13 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen return getInlineContainer().getOverflow() != EN_ERROR_IF_OVERFLOW; } - private int getAlignmentPoint() { + private int getAlignmentPoint(int dominantBaseline) { Length alignmentAdjust = getInlineContainer().getAlignmentAdjust(); int baseline = alignmentAdjust.getEnum(); - if (baseline == Constants.EN_AUTO - || baseline == Constants.EN_BASELINE) { + if (baseline == Constants.EN_AUTO) { return getInlineContainerBaselineOffset(getInlineContainer().getAlignmentBaseline()); + } else if (baseline == Constants.EN_BASELINE) { + return getInlineContainerBaselineOffset(dominantBaseline); } else if (baseline != 0) { return getInlineContainerBaselineOffset(baseline); } else { diff --git a/test/layoutengine/standard-testcases/inline-container_dominant-baseline.xml b/test/layoutengine/standard-testcases/inline-container_dominant-baseline.xml new file mode 100644 index 000000000..402eb806d --- /dev/null +++ b/test/layoutengine/standard-testcases/inline-container_dominant-baseline.xml @@ -0,0 +1,74 @@ + + + + + +

+ Checks that the dominant-baseline property on inline-container behaves properly. +

+
+ + + + + + + + + + + dominant-baseline="alphabetic": + The line before. The line before. Before: + Inside the inline-container. + After the inline-container. + + + + + + dominant-baseline="central": + The line before. The line before. Before: + Inside the inline-container. + After the inline-container. + + + + + + + + + + + + + + + + + + + + + + + +
-- cgit v1.2.3 From f4bb629ab722bbc4991cd4e5ceb37fb0a060b613 Mon Sep 17 00:00:00 2001 From: Vincent Hennebert Date: Thu, 7 Nov 2013 12:32:20 +0000 Subject: Added support for values on alignment-adjust git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_InlineContainer@1539614 13f79535-47bb-0310-9956-ffa450edef68 --- .../layoutmgr/inline/InlineContainerLayoutManager.java | 7 ++++++- .../inline-container_alignment-adjust.xml | 17 +++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) (limited to 'src/java/org/apache') diff --git a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java index 21e3cb283..539ebc897 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java @@ -28,6 +28,8 @@ import org.apache.fop.area.Trait; import org.apache.fop.area.inline.Container; import org.apache.fop.area.inline.InlineViewport; import org.apache.fop.datatypes.Length; +import org.apache.fop.datatypes.LengthBase; +import org.apache.fop.datatypes.SimplePercentBaseContext; import org.apache.fop.fo.Constants; import org.apache.fop.fo.flow.InlineContainer; import org.apache.fop.fo.properties.CommonBorderPaddingBackground; @@ -198,7 +200,10 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen } else if (baseline != 0) { return getInlineContainerBaselineOffset(baseline); } else { - return 0; + int baselineOffset = getInlineContainerBaselineOffset(dominantBaseline); + int adjust = alignmentAdjust.getValue( + new SimplePercentBaseContext(null, LengthBase.ALIGNMENT_ADJUST, 0)); + return baselineOffset + adjust; } } diff --git a/test/layoutengine/standard-testcases/inline-container_alignment-adjust.xml b/test/layoutengine/standard-testcases/inline-container_alignment-adjust.xml index 917a3a1d2..b63e36b69 100644 --- a/test/layoutengine/standard-testcases/inline-container_alignment-adjust.xml +++ b/test/layoutengine/standard-testcases/inline-container_alignment-adjust.xml @@ -131,6 +131,16 @@ + + + alignment-adjust="30pt": + The line before. The line before. Before: + Inside the inline-container. + After the inline-container. + + + @@ -206,5 +216,12 @@ + + + + + + + -- cgit v1.2.3 From d1ac3aa05b692e9d9ead48b93e8e9e9ad4297416 Mon Sep 17 00:00:00 2001 From: Vincent Hennebert Date: Thu, 7 Nov 2013 12:33:11 +0000 Subject: Added support for values on alignment-adjust git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_InlineContainer@1539615 13f79535-47bb-0310-9956-ffa450edef68 --- .../inline/InlineContainerLayoutManager.java | 3 ++- .../inline-container_alignment-adjust.xml | 19 ++++++++++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) (limited to 'src/java/org/apache') diff --git a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java index 539ebc897..5127794c0 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java @@ -201,8 +201,9 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen return getInlineContainerBaselineOffset(baseline); } else { int baselineOffset = getInlineContainerBaselineOffset(dominantBaseline); + int lineHeight = getInlineContainer().getLineHeight().getOptimum(this).getLength().getValue(this); int adjust = alignmentAdjust.getValue( - new SimplePercentBaseContext(null, LengthBase.ALIGNMENT_ADJUST, 0)); + new SimplePercentBaseContext(null, LengthBase.ALIGNMENT_ADJUST, lineHeight)); return baselineOffset + adjust; } } diff --git a/test/layoutengine/standard-testcases/inline-container_alignment-adjust.xml b/test/layoutengine/standard-testcases/inline-container_alignment-adjust.xml index b63e36b69..3e696a696 100644 --- a/test/layoutengine/standard-testcases/inline-container_alignment-adjust.xml +++ b/test/layoutengine/standard-testcases/inline-container_alignment-adjust.xml @@ -141,6 +141,16 @@ + + + alignment-adjust="10%": + The line before. The line before. Before: + Inside the inline-container. + After the inline-container. + + + @@ -221,7 +231,14 @@ - + + + + + + + + -- cgit v1.2.3 From 7a151fd401340c0c4578a4d865a3245551ff85c4 Mon Sep 17 00:00:00 2001 From: Vincent Hennebert Date: Thu, 14 Nov 2013 09:45:39 +0000 Subject: Code clean-up and simplification git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_InlineContainer@1541863 13f79535-47bb-0310-9956-ffa450edef68 --- .../org/apache/fop/fo/flow/InlineContainer.java | 8 ++++++ .../inline/InlineContainerLayoutManager.java | 32 ++++++---------------- 2 files changed, 17 insertions(+), 23 deletions(-) (limited to 'src/java/org/apache') diff --git a/src/java/org/apache/fop/fo/flow/InlineContainer.java b/src/java/org/apache/fop/fo/flow/InlineContainer.java index 13ea9943a..bcff3dc72 100644 --- a/src/java/org/apache/fop/fo/flow/InlineContainer.java +++ b/src/java/org/apache/fop/fo/flow/InlineContainer.java @@ -158,10 +158,18 @@ public class InlineContainer extends FObj { return this.displayAlign; } + public KeepProperty getKeepWithPrevious() { + return keepWithPrevious; + } + public KeepProperty getKeepTogether() { return keepTogether; } + public KeepProperty getKeepWithNext() { + return keepWithNext; + } + public SpaceProperty getLineHeight() { return lineHeight; } diff --git a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java index 5127794c0..771bda255 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java @@ -19,6 +19,7 @@ package org.apache.fop.layoutmgr.inline; +import java.awt.geom.Rectangle2D; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; @@ -86,11 +87,9 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen @Override public List getNextKnuthElements(LayoutContext context, int alignment) { determineIPD(context); - LayoutContext childLC = LayoutContext.offspringOf(context); // TODO copyOf? - childLC.setRefIPD(contentAreaIPD); - childElements = getChildKnuthElements(childLC, alignment); // TODO which alignment? + childElements = getChildKnuthElements(context, alignment); determineBPD(); - alignmentContext = makeAlignmentContext(context); // TODO correct? + alignmentContext = makeAlignmentContext(context); Position position = new Position(this, 0); KnuthSequence knuthSequence = new InlineKnuthSequence(); knuthSequence.add(new KnuthInlineBox(contentAreaIPD, alignmentContext, position, false)); @@ -117,18 +116,16 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen List allChildElements = new LinkedList(); LayoutManager childLM; while ((childLM = getChildLM()) != null) { - LayoutContext childLC = LayoutContext.offspringOf(layoutContext); // TODO copyOf? newInstance? - childLC.setRefIPD(layoutContext.getRefIPD()); + LayoutContext childLC = LayoutContext.offspringOf(layoutContext); + childLC.setRefIPD(contentAreaIPD); @SuppressWarnings("unchecked") List childElements = childLM.getNextKnuthElements(childLC, alignment); allChildElements.addAll(childElements); - // TODO breaks, keeps, empty content } handleIPDOverflow(); wrapPositions(allChildElements); SpaceResolver.resolveElementList(allChildElements); SpaceResolver.performConditionalsNotification(allChildElements, 0, allChildElements.size() - 1, -1); - // TODO break-before, break-after return allChildElements; } @@ -254,9 +251,6 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen assert inlineContainerPosition != null; KnuthPossPosIter childPosIter = new KnuthPossPosIter(childElements); AreaAdditionUtil.addAreas(this, childPosIter, context); - -// boolean isLast = (context.isLastArea() && prevLM == lastChildLM); -// context.setFlags(LayoutContext.LAST_AREA, isLast); } @Override @@ -267,23 +261,15 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen TraitSetter.setProducerID(referenceArea, fobj.getId()); referenceArea.setIPD(contentAreaIPD); currentViewport = new InlineViewport(referenceArea); - currentViewport.setBlockProgressionOffset(alignmentContext.getOffset()); currentViewport.addTrait(Trait.IS_VIEWPORT_AREA, Boolean.TRUE); + TraitSetter.setProducerID(currentViewport, fobj.getId()); + currentViewport.setBlockProgressionOffset(alignmentContext.getOffset()); currentViewport.setIPD(getContentAreaIPD()); currentViewport.setBPD(getContentAreaBPD()); - TraitSetter.setProducerID(currentViewport, fobj.getId()); - TraitSetter.addBorders(currentViewport, - borderProps, - false, false, false, false, this); - TraitSetter.addPadding(currentViewport, - borderProps, - false, false, false, false, this); - TraitSetter.addBackground(currentViewport, - borderProps, - this); + TraitSetter.addBackground(currentViewport, borderProps, this); currentViewport.setClip(needClip()); currentViewport.setContentPosition( - new java.awt.geom.Rectangle2D.Float(0, 0, getContentAreaIPD(), getContentAreaBPD())); + new Rectangle2D.Float(0, 0, getContentAreaIPD(), getContentAreaBPD())); getParent().addChildArea(currentViewport); } return referenceArea; -- cgit v1.2.3 From b24031d061c8902b4aa37859250f2a1c7504ebef Mon Sep 17 00:00:00 2001 From: Vincent Hennebert Date: Thu, 14 Nov 2013 09:46:24 +0000 Subject: Implemented missing methods git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_InlineContainer@1541864 13f79535-47bb-0310-9956-ffa450edef68 --- .../fop/layoutmgr/inline/InlineContainerLayoutManager.java | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'src/java/org/apache') diff --git a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java index 771bda255..020eb9982 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java @@ -301,31 +301,30 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen } public List addALetterSpaceTo(List oldList) { - throw new UnsupportedOperationException("Not implemented"); + return oldList; } public List addALetterSpaceTo(List oldList, int depth) { - throw new UnsupportedOperationException("Not implemented"); + return oldList; } public String getWordChars(Position pos) { - throw new UnsupportedOperationException("Not implemented"); + return ""; } public void hyphenate(Position pos, HyphContext hyphContext) { - throw new UnsupportedOperationException("Not implemented"); } public boolean applyChanges(List oldList) { - throw new UnsupportedOperationException("Not implemented"); + return false; } public boolean applyChanges(List oldList, int depth) { - throw new UnsupportedOperationException("Not implemented"); + return false; } public List getChangedKnuthElements(List oldList, int alignment, int depth) { - throw new UnsupportedOperationException("Not implemented"); + return oldList; } } -- cgit v1.2.3 From a9bbaac9f246b964d95ab849d1aac999ce10a6a7 Mon Sep 17 00:00:00 2001 From: Vincent Hennebert Date: Thu, 14 Nov 2013 09:47:24 +0000 Subject: Fixed the setting of generates[Block|Reference]Area git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_InlineContainer@1541865 13f79535-47bb-0310-9956-ffa450edef68 --- src/java/org/apache/fop/layoutmgr/AbstractBaseLayoutManager.java | 3 --- src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java | 1 + src/java/org/apache/fop/layoutmgr/FlowLayoutManager.java | 1 + .../apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java | 6 +----- src/java/org/apache/fop/layoutmgr/table/TableCellLayoutManager.java | 1 + 5 files changed, 4 insertions(+), 8 deletions(-) (limited to 'src/java/org/apache') diff --git a/src/java/org/apache/fop/layoutmgr/AbstractBaseLayoutManager.java b/src/java/org/apache/fop/layoutmgr/AbstractBaseLayoutManager.java index 5d7cc0b64..635e267c1 100644 --- a/src/java/org/apache/fop/layoutmgr/AbstractBaseLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/AbstractBaseLayoutManager.java @@ -65,9 +65,6 @@ public abstract class AbstractBaseLayoutManager public AbstractBaseLayoutManager(FObj fo) { this.fobj = fo; setGeneratesReferenceArea(fo.generatesReferenceAreas()); - if (getGeneratesReferenceArea()) { - setGeneratesBlockArea(true); - } } // --------- Property Resolution related functions --------- // diff --git a/src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java b/src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java index 83990797b..3ab267d43 100644 --- a/src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java @@ -90,6 +90,7 @@ public class BlockContainerLayoutManager extends SpacedBorderedPaddedBlockLayout */ public BlockContainerLayoutManager(BlockContainer node) { super(node); + setGeneratesBlockArea(true); } /** {@inheritDoc} */ diff --git a/src/java/org/apache/fop/layoutmgr/FlowLayoutManager.java b/src/java/org/apache/fop/layoutmgr/FlowLayoutManager.java index 0260046e1..4c7afa8d6 100644 --- a/src/java/org/apache/fop/layoutmgr/FlowLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/FlowLayoutManager.java @@ -58,6 +58,7 @@ public class FlowLayoutManager extends BlockStackingLayoutManager */ public FlowLayoutManager(PageSequenceLayoutManager pslm, Flow node) { super(node); + setGeneratesBlockArea(true); setParent(pslm); } diff --git a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java index 020eb9982..54237a914 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java @@ -71,6 +71,7 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen public InlineContainerLayoutManager(InlineContainer node) { super(node); + setGeneratesReferenceArea(true); } @Override @@ -231,11 +232,6 @@ public class InlineContainerLayoutManager extends AbstractLayoutManager implemen } } - @Override - public boolean getGeneratesReferenceArea() { - return true; - } - @Override public void addAreas(PositionIterator posIter, LayoutContext context) { Position inlineContainerPosition = null; diff --git a/src/java/org/apache/fop/layoutmgr/table/TableCellLayoutManager.java b/src/java/org/apache/fop/layoutmgr/table/TableCellLayoutManager.java index c8f2cea85..2122df5ae 100644 --- a/src/java/org/apache/fop/layoutmgr/table/TableCellLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/table/TableCellLayoutManager.java @@ -117,6 +117,7 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager */ public TableCellLayoutManager(TableCell node, PrimaryGridUnit pgu) { super(node); + setGeneratesBlockArea(true); this.primaryGridUnit = pgu; this.isDescendantOfTableHeader = node.getParent().getParent() instanceof TableHeader || node.getParent() instanceof TableHeader; -- cgit v1.2.3 From 6f02e19db3d538e15486180ae3b345bbfe6ecf41 Mon Sep 17 00:00:00 2001 From: Vincent Hennebert Date: Tue, 17 Dec 2013 17:29:09 +0000 Subject: Smoke test: introduce Checkstyle violation and see if Gump will complain git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1551638 13f79535-47bb-0310-9956-ffa450edef68 --- src/java/org/apache/fop/Version.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/java/org/apache') diff --git a/src/java/org/apache/fop/Version.java b/src/java/org/apache/fop/Version.java index 86861a60b..dbd2a1c1c 100644 --- a/src/java/org/apache/fop/Version.java +++ b/src/java/org/apache/fop/Version.java @@ -18,7 +18,7 @@ /* $Id$ */ package org.apache.fop; - + /** * This class is used to evaluate the version information contained in the Manifest of FOP's JAR. * Note that this class can only find the version information if it's in the org.apache.fop package -- cgit v1.2.3 From 72fe70000ee1987c592f25dcd1930ec5b23320ef Mon Sep 17 00:00:00 2001 From: Glenn Adams Date: Sat, 21 Dec 2013 22:36:08 +0000 Subject: Fix checkstyle warning. git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1552956 13f79535-47bb-0310-9956-ffa450edef68 --- src/java/org/apache/fop/Version.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/java/org/apache') diff --git a/src/java/org/apache/fop/Version.java b/src/java/org/apache/fop/Version.java index dbd2a1c1c..86861a60b 100644 --- a/src/java/org/apache/fop/Version.java +++ b/src/java/org/apache/fop/Version.java @@ -18,7 +18,7 @@ /* $Id$ */ package org.apache.fop; - + /** * This class is used to evaluate the version information contained in the Manifest of FOP's JAR. * Note that this class can only find the version information if it's in the org.apache.fop package -- cgit v1.2.3 From e7d05ec67b2652cd6ede79067d42eae8c057c955 Mon Sep 17 00:00:00 2001 From: Vincent Hennebert Date: Tue, 7 Jan 2014 09:51:25 +0000 Subject: The length of $HeadURL$ cannot be controlled. Surround with CSOFF/CSON comments git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1556164 13f79535-47bb-0310-9956-ffa450edef68 --- src/java/org/apache/fop/Version.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src/java/org/apache') diff --git a/src/java/org/apache/fop/Version.java b/src/java/org/apache/fop/Version.java index 86861a60b..a8903c74e 100644 --- a/src/java/org/apache/fop/Version.java +++ b/src/java/org/apache/fop/Version.java @@ -40,8 +40,9 @@ public final class Version { } if (version == null) { //Fallback if FOP is used in a development environment - String headURL - = "$HeadURL$"; + // CSOFF: LineLength + String headURL = "$HeadURL$"; + // CSON: LineLength version = headURL; final String pathPrefix = "/xmlgraphics/fop/"; int pos = version.indexOf(pathPrefix); -- cgit v1.2.3 From f788f5d12b85ddecf7daa95f4eb735139080f399 Mon Sep 17 00:00:00 2001 From: Luis Bernardo Date: Fri, 17 Jan 2014 23:35:00 +0000 Subject: FOP-1801: conversion B&W GIF=>PDF creates PDF with colorspace RGB if FOP0.95 and Gray if FOP0.20.5; patch submitted by Thanasis Giannimaras following suggestion by Jeremias Maerki git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1559284 13f79535-47bb-0310-9956-ffa450edef68 --- lib/xmlgraphics-commons-svn-trunk.jar | Bin 631612 -> 632012 bytes .../fop/render/pdf/AbstractImageAdapter.java | 38 +++++++++++++++------ 2 files changed, 28 insertions(+), 10 deletions(-) (limited to 'src/java/org/apache') diff --git a/lib/xmlgraphics-commons-svn-trunk.jar b/lib/xmlgraphics-commons-svn-trunk.jar index 34e4d2b44..11b445c09 100644 Binary files a/lib/xmlgraphics-commons-svn-trunk.jar and b/lib/xmlgraphics-commons-svn-trunk.jar differ diff --git a/src/java/org/apache/fop/render/pdf/AbstractImageAdapter.java b/src/java/org/apache/fop/render/pdf/AbstractImageAdapter.java index 58299c528..471efc567 100644 --- a/src/java/org/apache/fop/render/pdf/AbstractImageAdapter.java +++ b/src/java/org/apache/fop/render/pdf/AbstractImageAdapter.java @@ -23,6 +23,8 @@ import java.awt.color.ICC_Profile; import java.awt.image.DataBufferByte; import java.awt.image.IndexColorModel; import java.awt.image.Raster; +import java.io.IOException; +import java.util.Arrays; import org.apache.commons.io.output.ByteArrayOutputStream; import org.apache.commons.logging.Log; @@ -239,24 +241,40 @@ public abstract class AbstractImageAdapter implements PDFImage { + " The image may not be handled correctly." + " Base color space: " + icm.getColorSpace() + " Image: " + image.getInfo()); } - indexed.add(new PDFName(toPDFColorSpace(icm.getColorSpace()).getName())); + ByteArrayOutputStream baout = new ByteArrayOutputStream(); int c = icm.getMapSize(); int hival = c - 1; if (hival > MAX_HIVAL) { throw new UnsupportedOperationException("hival must not go beyond " + MAX_HIVAL); } - indexed.add(Integer.valueOf(hival)); + boolean isDeviceGray = false; int[] palette = new int[c]; icm.getRGBs(palette); - ByteArrayOutputStream baout = new ByteArrayOutputStream(); - for (int i = 0; i < c; i++) { - // TODO Probably doesn't work for non RGB based color spaces - // See log warning above - int entry = palette[i]; - baout.write((entry & 0xFF0000) >> 16); - baout.write((entry & 0xFF00) >> 8); - baout.write(entry & 0xFF); + byte[] reds = new byte[c]; + byte[] greens = new byte[c]; + byte[] blues = new byte[c]; + icm.getReds(reds); + icm.getGreens(greens); + icm.getBlues(blues); + isDeviceGray = Arrays.equals(reds, blues) && Arrays.equals(blues, greens); + if (isDeviceGray) { + indexed.add(new PDFName("DeviceGray")); + try { + baout.write(blues); + } catch (IOException e) { + e.printStackTrace(); + } + } else { + indexed.add(new PDFName(toPDFColorSpace(icm.getColorSpace()).getName())); + for (int i = 0; i < c; i++) { + int entry = palette[i]; + baout.write((entry & 0xFF0000) >> 16); + baout.write((entry & 0xFF00) >> 8); + baout.write(entry & 0xFF); + } } + indexed.add(hival); + indexed.add(baout.toByteArray()); dict.put("ColorSpace", indexed); -- cgit v1.2.3 From a4d5aac4e5926a2d94d70e1d71b06d26c1996184 Mon Sep 17 00:00:00 2001 From: Vincent Hennebert Date: Thu, 30 Jan 2014 21:35:49 +0000 Subject: Fixed Checkstyle issues git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_FopFontsForSVG@1562983 13f79535-47bb-0310-9956-ffa450edef68 --- src/java/org/apache/fop/render/afp/AFPSVGHandler.java | 5 +++-- test/java/org/apache/fop/svg/OperatorValidator.java | 6 +++--- test/java/org/apache/fop/svg/PDFTextPainterTestCase.java | 1 - .../org/apache/fop/svg/font/FOPFontFamilyResolverTestCase.java | 10 +++++----- test/java/org/apache/fop/svg/font/FOPGVTFontTestCase.java | 1 - test/java/org/apache/fop/svg/font/GlyphLayoutTestCase.java | 4 ++-- 6 files changed, 13 insertions(+), 14 deletions(-) (limited to 'src/java/org/apache') diff --git a/src/java/org/apache/fop/render/afp/AFPSVGHandler.java b/src/java/org/apache/fop/render/afp/AFPSVGHandler.java index ccb4cc678..e05bc007c 100644 --- a/src/java/org/apache/fop/render/afp/AFPSVGHandler.java +++ b/src/java/org/apache/fop/render/afp/AFPSVGHandler.java @@ -200,8 +200,9 @@ public class AFPSVGHandler extends AbstractGenericSVGHandler { public static BridgeContext createBridgeContext(FOUserAgent userAgent, AFPGraphics2D g2d) { ImageManager imageManager = userAgent.getImageManager(); FontInfo fontInfo = g2d.getFontInfo(); - SVGUserAgent svgUserAgent = new SVGUserAgent(userAgent, new AggregatingFontFamilyResolver( - new AFPFontFamilyResolver(fontInfo, userAgent.getEventBroadcaster()), DefaultFontFamilyResolver.SINGLETON), + SVGUserAgent svgUserAgent = new SVGUserAgent(userAgent, + new AggregatingFontFamilyResolver(new AFPFontFamilyResolver(fontInfo, userAgent.getEventBroadcaster()), + DefaultFontFamilyResolver.SINGLETON), new AffineTransform()); ImageSessionContext imageSessionContext = userAgent.getImageSessionContext(); return new AFPBridgeContext(svgUserAgent, fontInfo, imageManager, imageSessionContext, diff --git a/test/java/org/apache/fop/svg/OperatorValidator.java b/test/java/org/apache/fop/svg/OperatorValidator.java index 959adc9a1..e94c8e404 100644 --- a/test/java/org/apache/fop/svg/OperatorValidator.java +++ b/test/java/org/apache/fop/svg/OperatorValidator.java @@ -27,7 +27,7 @@ import static org.junit.Assert.assertTrue; class OperatorValidator { - private static interface Match { + private interface Match { boolean match(String line); } @@ -60,7 +60,7 @@ class OperatorValidator { public boolean match(String line) { boolean match = currentMatch.match(line); if (match) { - if(expectedMatches.isEmpty()) { + if (expectedMatches.isEmpty()) { currentMatch = FINAL_MATCH; } else { currentMatch = expectedMatches.remove(); @@ -105,4 +105,4 @@ class OperatorValidator { assertTrue("Expected operators remain", matchSequence.isExhausted()); } -} \ No newline at end of file +} diff --git a/test/java/org/apache/fop/svg/PDFTextPainterTestCase.java b/test/java/org/apache/fop/svg/PDFTextPainterTestCase.java index 52f18bb5e..efef464bd 100644 --- a/test/java/org/apache/fop/svg/PDFTextPainterTestCase.java +++ b/test/java/org/apache/fop/svg/PDFTextPainterTestCase.java @@ -30,7 +30,6 @@ import org.apache.xmlgraphics.java2d.GraphicContext; import org.apache.fop.fonts.FontInfo; import org.apache.fop.pdf.PDFDocument; -import org.apache.fop.svg.font.FOPFontFamilyResolverImpl; public class PDFTextPainterTestCase extends NativeTextPainterTest { diff --git a/test/java/org/apache/fop/svg/font/FOPFontFamilyResolverTestCase.java b/test/java/org/apache/fop/svg/font/FOPFontFamilyResolverTestCase.java index 2f5668cbc..ca843ec36 100644 --- a/test/java/org/apache/fop/svg/font/FOPFontFamilyResolverTestCase.java +++ b/test/java/org/apache/fop/svg/font/FOPFontFamilyResolverTestCase.java @@ -33,17 +33,17 @@ import org.junit.BeforeClass; import org.junit.Ignore; import org.junit.Test; -import org.apache.batik.gvt.font.GVTFontFamily; -import org.apache.batik.gvt.font.GVTLineMetrics; - -import org.apache.fop.fonts.FontInfo; - import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import org.apache.batik.gvt.font.GVTFontFamily; +import org.apache.batik.gvt.font.GVTLineMetrics; + +import org.apache.fop.fonts.FontInfo; + public class FOPFontFamilyResolverTestCase { private static FontInfo fontInfo; diff --git a/test/java/org/apache/fop/svg/font/FOPGVTFontTestCase.java b/test/java/org/apache/fop/svg/font/FOPGVTFontTestCase.java index c7af068ce..b27dac5b2 100644 --- a/test/java/org/apache/fop/svg/font/FOPGVTFontTestCase.java +++ b/test/java/org/apache/fop/svg/font/FOPGVTFontTestCase.java @@ -30,7 +30,6 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import org.apache.fop.fonts.Font; -import org.apache.fop.svg.font.FOPGVTFont; public class FOPGVTFontTestCase { diff --git a/test/java/org/apache/fop/svg/font/GlyphLayoutTestCase.java b/test/java/org/apache/fop/svg/font/GlyphLayoutTestCase.java index 34cba604a..5c1fb2c86 100644 --- a/test/java/org/apache/fop/svg/font/GlyphLayoutTestCase.java +++ b/test/java/org/apache/fop/svg/font/GlyphLayoutTestCase.java @@ -23,10 +23,10 @@ import java.util.Collections; import org.junit.Test; -import org.apache.fop.fonts.FontInfo; - import static org.junit.Assert.assertEquals; +import org.apache.fop.fonts.FontInfo; + /** * Specifically tests glyph positioning from a real font. */ -- cgit v1.2.3 From 785ace252f252ff211330e93635631931d68b890 Mon Sep 17 00:00:00 2001 From: Robert Meyer Date: Mon, 10 Feb 2014 16:21:03 +0000 Subject: FOP-2252: OpenType CFF - Remove need for patch to fontbox git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1566674 13f79535-47bb-0310-9956-ffa450edef68 --- lib/fontbox-1.8.4-patched.jar | Bin 208442 -> 205376 bytes .../org/apache/fop/fonts/truetype/OTFFile.java | 76 +++++- .../apache/fop/fonts/truetype/OTFSubSetFile.java | 31 ++- .../fop/fonts/truetype/OTFSubSetFileTestCase.java | 291 +++++++++++++++++++-- 4 files changed, 362 insertions(+), 36 deletions(-) (limited to 'src/java/org/apache') diff --git a/lib/fontbox-1.8.4-patched.jar b/lib/fontbox-1.8.4-patched.jar index 489acb8ac..f608b4409 100644 Binary files a/lib/fontbox-1.8.4-patched.jar and b/lib/fontbox-1.8.4-patched.jar differ diff --git a/src/java/org/apache/fop/fonts/truetype/OTFFile.java b/src/java/org/apache/fop/fonts/truetype/OTFFile.java index 3976b5994..ab9654beb 100644 --- a/src/java/org/apache/fop/fonts/truetype/OTFFile.java +++ b/src/java/org/apache/fop/fonts/truetype/OTFFile.java @@ -20,11 +20,17 @@ package org.apache.fop.fonts.truetype; import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; import org.apache.fontbox.cff.CFFDataInput; import org.apache.fontbox.cff.CFFFont; -import org.apache.fontbox.cff.CFFFont.Mapping; import org.apache.fontbox.cff.CFFParser; +import org.apache.fontbox.cff.charset.CFFCharset; public class OTFFile extends OpenFont { @@ -45,17 +51,77 @@ public class OTFFile extends OpenFont { @Override protected void updateBBoxAndOffset() throws IOException { + List gidMappings = getGIDMappings(fileFont); + Map sidNames = constructNameMap(gidMappings); UnicodeMapping[] mappings = unicodeMappings.toArray(new UnicodeMapping[0]); for (int i = 0; i < mappings.length; i++) { int glyphIdx = mappings[i].getGlyphIndex(); - Mapping m = fileFont.getGIDMappings().get(glyphIdx); - int[] bbox = fileFont.getBoundingBox(m.getSID()); - String name = fileFont.getNameOfCharFromCode(m.getSID()); - mtxTab[glyphIdx].setBoundingBox(bbox); + Mapping m = gidMappings.get(glyphIdx); + String name = sidNames.get(m.getSID()); mtxTab[glyphIdx].setName(name); } } + private List getGIDMappings(CFFFont font) { + List gidMappings = new ArrayList(); + Mapping notdef = new Mapping(); + gidMappings.add(notdef); + for (CFFCharset.Entry entry : font.getCharset().getEntries()) { + String name = entry.getName(); + byte[] bytes = font.getCharStringsDict().get(name); + if (bytes == null) { + continue; + } + Mapping mapping = new Mapping(); + mapping.setSID(entry.getSID()); + mapping.setName(name); + mapping.setBytes(bytes); + gidMappings.add(mapping); + } + return gidMappings; + } + + private Map constructNameMap(Collection mappings) { + Map sidNames = new HashMap(); + Iterator it = mappings.iterator(); + while (it.hasNext()) { + Mapping mapping = it.next(); + sidNames.put(mapping.getSID(), mapping.getName()); + } + return sidNames; + } + + private static class Mapping { + private int sid; + private String name; + private byte[] bytes; + + public void setSID(int sid) { + this.sid = sid; + } + + public int getSID() { + return sid; + } + + public void setName(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setBytes(byte[] bytes) { + this.bytes = bytes; + } + + public byte[] getBytes() { + return bytes; + } + } + + @Override protected void initializeFont(FontFileReader in) throws IOException { fontFile = in; diff --git a/src/java/org/apache/fop/fonts/truetype/OTFSubSetFile.java b/src/java/org/apache/fop/fonts/truetype/OTFSubSetFile.java index 4d0cce67a..9cb3458c8 100644 --- a/src/java/org/apache/fop/fonts/truetype/OTFSubSetFile.java +++ b/src/java/org/apache/fop/fonts/truetype/OTFSubSetFile.java @@ -861,7 +861,6 @@ public class OTFSubSetFile extends OTFFile { return hdrTotal + total; } - private BytesNumber readNumber(int b0, byte[] input, int curPos) throws IOException { if (b0 == 28) { int b1 = input[curPos + 1] & 0xff; @@ -887,7 +886,7 @@ public class OTFSubSetFile extends OTFFile { /** * A class used to store the last number operand and also it's size in bytes */ - private static final class BytesNumber { + static class BytesNumber { private int number; private int numBytes; @@ -908,6 +907,26 @@ public class OTFSubSetFile extends OTFFile { this.number = -1; this.numBytes = -1; } + + public String toString() { + return Integer.toString(number); + } + + @Override + public boolean equals(Object entry) { + assert entry instanceof BytesNumber; + BytesNumber bnEntry = (BytesNumber)entry; + return this.number == bnEntry.getNumber() + && this.numBytes == bnEntry.getNumBytes(); + } + + @Override + public int hashCode() { + int hash = 1; + hash = hash * 17 + number; + hash = hash * 31 + numBytes; + return hash; + } } private void writeCharsetTable(boolean cidFont) throws IOException { @@ -1094,4 +1113,12 @@ public class OTFSubSetFile extends OTFFile { System.arraycopy(output, 0, ret, 0, realSize); return ret; } + + /** + * Returns the parsed CFF data for the original font. + * @return The CFFDataReader contaiing the parsed data + */ + public CFFDataReader getCFFReader() { + return cffReader; + } } diff --git a/test/java/org/apache/fop/fonts/truetype/OTFSubSetFileTestCase.java b/test/java/org/apache/fop/fonts/truetype/OTFSubSetFileTestCase.java index c555ebc20..8c92b5f63 100644 --- a/test/java/org/apache/fop/fonts/truetype/OTFSubSetFileTestCase.java +++ b/test/java/org/apache/fop/fonts/truetype/OTFSubSetFileTestCase.java @@ -20,6 +20,8 @@ package org.apache.fop.fonts.truetype; import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -27,18 +29,15 @@ import java.util.Map; import org.junit.Before; import org.junit.Test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import org.apache.fontbox.cff.CFFDataInput; import org.apache.fontbox.cff.CFFFont; -import org.apache.fontbox.cff.CFFParser; -import org.apache.fontbox.cff.IndexData; -import org.apache.fontbox.cff.Type2CharStringParser; import org.apache.fop.fonts.cff.CFFDataReader; import org.apache.fop.fonts.cff.CFFDataReader.CFFIndexData; import org.apache.fop.fonts.cff.CFFDataReader.DICTEntry; +import org.apache.fop.fonts.truetype.OTFSubSetFile.BytesNumber; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; public class OTFSubSetFileTestCase extends OTFFileTestCase { @@ -47,10 +46,6 @@ public class OTFSubSetFileTestCase extends OTFFileTestCase { private byte[] sourceSansData; CFFDataReader cffReaderHeitiStd; - public OTFSubSetFileTestCase() throws IOException { - super(); - } - /** * Initialises the test by creating the font subset. A CFFDataReader is * also created based on the subset data for use in the tests. @@ -80,9 +75,14 @@ public class OTFSubSetFileTestCase extends OTFFileTestCase { public void testCharStringIndex() throws IOException { assertEquals(256, cffReaderSourceSans.getCharStringIndex().getNumObjects()); assertTrue(checkCorrectOffsets(cffReaderSourceSans.getCharStringIndex())); - validateCharStrings(cffReaderSourceSans); + validateCharStrings(cffReaderSourceSans, sourceSansSubset.getCFFReader()); } + /** + * Checks the index data to ensure that the offsets are valid + * @param indexData The index data to check + * @return Returns true if it is found to be valid + */ private boolean checkCorrectOffsets(CFFIndexData indexData) { int last = 0; for (int i = 0; i < indexData.getOffsets().length; i++) { @@ -94,34 +94,267 @@ public class OTFSubSetFileTestCase extends OTFFileTestCase { return true; } - private void validateCharStrings(CFFDataReader cffReader) throws IOException { + /** + * Validates the subset font CharString data by comparing it with the original. + * @param subsetCFF The subset CFFDataReader containing the CharString data + * @param origCFF The original CFFDataReader containing the CharString data + * @throws IOException + */ + private void validateCharStrings(CFFDataReader subsetCFF, CFFDataReader origCFF) + throws IOException { CFFFont sourceSansOriginal = sourceSansProBold.fileFont; + CFFIndexData charStrings = subsetCFF.getCharStringIndex(); Map origCharStringData = sourceSansOriginal.getCharStringsDict(); - IndexData origGlobalIndex = sourceSansOriginal.getGlobalSubrIndex(); - IndexData origLocalIndex = sourceSansOriginal.getLocalSubrIndex(); + for (int i = 0; i < charStrings.getNumObjects(); i++) { + byte[] origCharData = origCharStringData.get(origCharStringData.keySet().toArray( + new String[0])[i]); + byte[] charData = charStrings.getValue(i); + List origOperands = getFullCharString(origCharData, origCFF); + List subsetOperands = getFullCharString(charData, subsetCFF); + for (int j = 0;j < origOperands.size();j++) { + assertTrue(origOperands.get(j).equals(subsetOperands.get(j))); + } + } + } + + /** + * Recursively reads and constructs the full CharString for comparison + * @param data The original byte data of the CharString + * @param cffData The CFFDataReader containing the subroutine indexes + * @return Returns a list of parsed operands and operators + * @throws IOException + */ + private List getFullCharString(byte[] data, CFFDataReader cffData) throws IOException { + CFFIndexData localIndexSubr = cffData.getLocalIndexSubr(); + CFFIndexData globalIndexSubr = cffData.getGlobalIndexSubr(); + boolean hasLocalSubroutines = localIndexSubr != null && localIndexSubr.getNumObjects() > 0; + boolean hasGlobalSubroutines = globalIndexSubr != null && globalIndexSubr.getNumObjects() > 0; + ArrayList operands = new ArrayList(); + for (int dataPos = 0; dataPos < data.length; dataPos++) { + int b0 = data[dataPos] & 0xff; + if (b0 == 10 && hasLocalSubroutines) { + int subrNumber = getSubrNumber(localIndexSubr.getNumObjects(), + operands.get(operands.size() - 1).getNumber()); + byte[] subr = localIndexSubr.getValue(subrNumber); + List subrOperands = getFullCharString(subr, cffData); + operands = mergeOperands(operands, subrOperands); + } else if (b0 == 29 && hasGlobalSubroutines) { + int subrNumber = getSubrNumber(globalIndexSubr.getNumObjects(), + operands.get(operands.size() - 1).getNumber()); + byte[] subr = globalIndexSubr.getValue(subrNumber); + ArrayList subrOperands = (ArrayList)getFullCharString(subr, cffData); + operands = mergeOperands(operands, subrOperands); + } else if ((b0 >= 0 && b0 <= 27) || (b0 >= 29 && b0 <= 31)) { + int size = 1; + int b1 = -1; + if (b0 == 12) { + b1 = data[dataPos++] & 0xff; + size = 2; + } + if (b0 == 19 || b0 == 20) { + dataPos += 1; + size = 2; + } + operands.add(new Operator(b0, size, getOperatorName(b0, b1))); + } else if (b0 == 28 || (b0 >= 32 && b0 <= 255)) { + operands.add(readNumber(b0, data, dataPos)); + dataPos += operands.get(operands.size() - 1).getNumBytes() - 1; + } + } + return operands; + } + + /** + * Merges two lists of operands. This is typically used to merge the CharString + * data with that of a parsed and referenced subroutine. + * @param charString The parsed CharString data so far + * @param subroutine The parsed elements from a subroutine + * @return Returns a merged list of both CharString and subroutine elements. + */ + private ArrayList mergeOperands(List charString, + List subroutine) { + BytesNumber[] charStringOperands = charString.toArray(new BytesNumber[0]); + BytesNumber[] subroutineOperands = subroutine.toArray(new BytesNumber[0]); + BytesNumber[] mergeData = new BytesNumber[charStringOperands.length - 1 + + subroutineOperands.length - 1]; + System.arraycopy(charStringOperands, 0, mergeData, 0, charStringOperands.length - 1); + System.arraycopy(subroutineOperands, 0, mergeData, charStringOperands.length - 1, + subroutineOperands.length - 1); + ArrayList hello = new ArrayList(); + hello.addAll(Arrays.asList(mergeData)); + return hello; + } - CFFDataInput globalSubrs = new CFFDataInput(cffReader.getGlobalIndexSubr().getByteData()); - CFFDataInput localSubrs = new CFFDataInput(cffReader.getLocalIndexSubr().getByteData()); + /** + * Parses a number from one or more bytes + * @param b0 The first byte to identify how to interpret the number + * @param input The original byte data containing the number + * @param curPos The current position of the number + * @return Returns the number + * @throws IOException + */ + private BytesNumber readNumber(int b0, byte[] input, int curPos) throws IOException { + if (b0 == 28) { + int b1 = input[curPos + 1] & 0xff; + int b2 = input[curPos + 2] & 0xff; + return new BytesNumber(Integer.valueOf((short) (b1 << 8 | b2)), 3); + } else if (b0 >= 32 && b0 <= 246) { + return new BytesNumber(Integer.valueOf(b0 - 139), 1); + } else if (b0 >= 247 && b0 <= 250) { + int b1 = input[curPos + 1] & 0xff; + return new BytesNumber(Integer.valueOf((b0 - 247) * 256 + b1 + 108), 2); + } else if (b0 >= 251 && b0 <= 254) { + int b1 = input[curPos + 1] & 0xff; + return new BytesNumber(Integer.valueOf(-(b0 - 251) * 256 - b1 - 108), 2); + } else if (b0 == 255) { + int b1 = input[curPos + 1] & 0xff; + int b2 = input[curPos + 2] & 0xff; + return new BytesNumber(Integer.valueOf((short)(b1 << 8 | b2)), 5); + } else { + throw new IllegalArgumentException(); + } + } - IndexData globalIndexData = CFFParser.readIndexData(globalSubrs); - IndexData localIndexData = CFFParser.readIndexData(localSubrs); + /** + * Gets the subroutine number according to the number of subroutines + * and the provided operand. + * @param numSubroutines The number of subroutines used to calculate the + * subroutine reference. + * @param operand The operand for the subroutine + * @return Returns the calculated subroutine number + */ + private int getSubrNumber(int numSubroutines, int operand) { + int bias = getBias(numSubroutines); + return bias + operand; + } - CFFIndexData charStrings = cffReader.getCharStringIndex(); - for (int i = 0; i < charStrings.getNumObjects(); i++) { - byte[] charData = charStrings.getValue(i); - Type2CharStringParser parser = new Type2CharStringParser(); + /** + * Gets the bias give the number of subroutines. This is used in the + * calculation to determine a subroutine's number + * @param subrCount The number of subroutines for a given index + * @return Returns the bias value + */ + private int getBias(int subrCount) { + if (subrCount < 1240) { + return 107; + } else if (subrCount < 33900) { + return 1131; + } else { + return 32768; + } + } - byte[] origCharData = origCharStringData.get(origCharStringData.keySet().toArray( - new String[0])[i]); - List origSeq = parser.parse(origCharData, origGlobalIndex, origLocalIndex); + /** + * A class representing an operator from the CharString data + */ + private class Operator extends BytesNumber { + private String opName = ""; - List subsetSeq = parser.parse(charData, globalIndexData, localIndexData); + public Operator(int number, int numBytes, String opName) { + super(number, numBytes); + this.opName = opName; + } + public String toString() { + return String.format("[%s]", opName); + } + } - //Validates the subset glyph render routines against the originals - assertEquals(origSeq, subsetSeq); + /** + * Gets the identifying name for the given operator. This is primarily + * used for debugging purposes. See the Type 2 CharString Format specification + * document (Technical Note #5177) Appendix A (Command Codes). + * @param operator The operator code + * @param codeb The second byte of the operator + * @return Returns the operator name. + */ + private String getOperatorName(int operator, int operatorB) { + switch (operator) { + case 0: return "Reserved"; + case 1: return "hstem"; + case 2: return "Reserved"; + case 3: return "vstem"; + case 4: return "vmoveto"; + case 5: return "rlineto"; + case 6: return "hlineto"; + case 7: return "vlineto"; + case 8: return "rrcurveto"; + case 9: return "Reserved"; + case 10: return "callsubr"; + case 11: return "return"; + case 12: return getDoubleOpName(operatorB); + case 13: return "Reserved"; + case 14: return "enchar"; + case 15: + case 16: + case 17: return "Reserved"; + case 18: return "hstemhm"; + case 19: return "hintmask"; + case 20: return "cntrmask"; + case 21: return "rmoveto"; + case 22: return "hmoveto"; + case 23: return "vstemhm"; + case 24: return "rcurveline"; + case 25: return "rlinecurve"; + case 26: return "vvcurveto"; + case 27: return "hhcurveto"; + case 28: return "shortint"; + case 29: return "callgsubr"; + case 30: return "vhcurveto"; + case 31: return "hvcurveto"; + default: return "Unknown"; } } + /** + * Gets the name of a double byte operator code + * @param operator The second byte of the operator + * @return Returns the name + */ + private String getDoubleOpName(int operator) { + switch (operator) { + case 0: + case 1: + case 2: return "Reserved"; + case 3: return "and"; + case 4: return "or"; + case 5: return "not"; + case 6: + case 7: + case 8: return "Reserved"; + case 9: return "abs"; + case 10: return "add"; + case 11: return "sub"; + case 12: return "div"; + case 13: return "Reserved"; + case 14: return "neg"; + case 15: return "eq"; + case 16: + case 17: return "Reserved"; + case 18: return "drop"; + case 19: return "Reserved"; + case 20: return "put"; + case 21: return "get"; + case 22: return "ifelse"; + case 23: return "random"; + case 24: return "mul"; + case 25: return "Reserved"; + case 26: return "sqrt"; + case 27: return "dup"; + case 28: return "exch"; + case 29: return "index"; + case 30: return "roll"; + case 31: + case 32: + case 33: return "Reserved"; + case 34: return "hflex"; + case 35: return "flex"; + case 36: return "hflex1"; + case 37: return "flex1"; + case 38: return "Reserved"; + default: return "Unknown"; + } + } + /** * Validates the String index data and size * @throws IOException -- cgit v1.2.3 From ce86f97648fbc8e168ec9ddb34c5cd340b4ff973 Mon Sep 17 00:00:00 2001 From: Robert Meyer Date: Mon, 17 Feb 2014 09:59:17 +0000 Subject: FOP-2341: Infinite loop when smaller used with a zero length font-size git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1568925 13f79535-47bb-0310-9956-ffa450edef68 --- src/java/org/apache/fop/fo/properties/FontSizePropertyMaker.java | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/java/org/apache') diff --git a/src/java/org/apache/fop/fo/properties/FontSizePropertyMaker.java b/src/java/org/apache/fop/fo/properties/FontSizePropertyMaker.java index 72884a177..fe9c64cb7 100644 --- a/src/java/org/apache/fop/fo/properties/FontSizePropertyMaker.java +++ b/src/java/org/apache/fop/fo/properties/FontSizePropertyMaker.java @@ -105,6 +105,9 @@ public class FontSizePropertyMaker // than the last caculated step lastStepFontSize = nextStepFontSize; nextStepFontSize = (int)Math.round(lastStepFontSize * scale); + if (nextStepFontSize == lastStepFontSize) { + break; + } } // baseFontSize is between last and next step font size // Return the step value closer to the baseFontSize -- cgit v1.2.3 From 342e75cf8a9bff942763520a07be9b955f16c852 Mon Sep 17 00:00:00 2001 From: Robert Meyer Date: Tue, 18 Feb 2014 15:16:45 +0000 Subject: FOP:2344 - SVG bleeds color between elements when outputting to AFP git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1569381 13f79535-47bb-0310-9956-ffa450edef68 --- src/java/org/apache/fop/afp/svg/AFPTextHandler.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src/java/org/apache') diff --git a/src/java/org/apache/fop/afp/svg/AFPTextHandler.java b/src/java/org/apache/fop/afp/svg/AFPTextHandler.java index 3e987648e..e8e391bdb 100644 --- a/src/java/org/apache/fop/afp/svg/AFPTextHandler.java +++ b/src/java/org/apache/fop/afp/svg/AFPTextHandler.java @@ -120,9 +120,8 @@ public class AFPTextHandler extends FOPTextHandlerAdapter { // set the color AFPPaintingState paintingState = g2d.getPaintingState(); - if (paintingState.setColor(color)) { - graphicsObj.setColor(color); - } + paintingState.setColor(color); + graphicsObj.setColor(color); // set the character set int fontReference = 0; -- cgit v1.2.3 From c51d59011a3071707e69f6f06e7def09395f75bc Mon Sep 17 00:00:00 2001 From: Vincent Hennebert Date: Thu, 20 Feb 2014 21:45:20 +0000 Subject: FOP-2346: UnsupportedOperationException when handling an SVG containing a font-face git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1570362 13f79535-47bb-0310-9956-ffa450edef68 --- lib/batik-all-trunk.jar | Bin 3313193 -> 3313410 bytes .../apache/fop/afp/svg/AFPFontFamilyResolver.java | 5 ++++- .../svg/font/AggregatingFontFamilyResolver.java | 25 ++++++++++++++++----- .../fop/svg/font/FOPFontFamilyResolverImpl.java | 25 ++++++++++++++++----- .../org/apache/fop/svg/font/FOPGVTFontFamily.java | 8 ++++--- .../fop/svg/font/FilteringFontFamilyResolver.java | 17 ++++++++++---- .../svg/font/FOPFontFamilyResolverTestCase.java | 4 ++-- 7 files changed, 62 insertions(+), 22 deletions(-) (limited to 'src/java/org/apache') diff --git a/lib/batik-all-trunk.jar b/lib/batik-all-trunk.jar index 4641118f6..08c1a383a 100644 Binary files a/lib/batik-all-trunk.jar and b/lib/batik-all-trunk.jar differ diff --git a/src/java/org/apache/fop/afp/svg/AFPFontFamilyResolver.java b/src/java/org/apache/fop/afp/svg/AFPFontFamilyResolver.java index 27026f4f3..a8217088b 100644 --- a/src/java/org/apache/fop/afp/svg/AFPFontFamilyResolver.java +++ b/src/java/org/apache/fop/afp/svg/AFPFontFamilyResolver.java @@ -22,6 +22,8 @@ package org.apache.fop.afp.svg; import java.util.HashMap; import java.util.Map; +import org.apache.batik.gvt.font.GVTFontFace; + import org.apache.fop.afp.AFPEventProducer; import org.apache.fop.afp.fonts.DoubleByteFont; import org.apache.fop.events.EventBroadcaster; @@ -69,7 +71,8 @@ public class AFPFontFamilyResolver extends FilteringFontFamilyResolver { notifyDBFontRejection(font.getFontName()); } else { return new FOPGVTFontFamily(fontInfo, fontFamily, - new FontTriplet(fontFamily, Font.STYLE_NORMAL, Font.WEIGHT_NORMAL)); + new FontTriplet(fontFamily, Font.STYLE_NORMAL, Font.WEIGHT_NORMAL), + new GVTFontFace(fontFamily)); } } diff --git a/src/java/org/apache/fop/svg/font/AggregatingFontFamilyResolver.java b/src/java/org/apache/fop/svg/font/AggregatingFontFamilyResolver.java index 313a6a74b..a5a25c4ce 100644 --- a/src/java/org/apache/fop/svg/font/AggregatingFontFamilyResolver.java +++ b/src/java/org/apache/fop/svg/font/AggregatingFontFamilyResolver.java @@ -19,9 +19,11 @@ package org.apache.fop.svg.font; +import java.io.InputStream; import java.util.Arrays; import java.util.List; +import org.apache.batik.bridge.FontFace; import org.apache.batik.gvt.font.FontFamilyResolver; import org.apache.batik.gvt.font.GVTFontFamily; @@ -33,19 +35,19 @@ public class AggregatingFontFamilyResolver implements FontFamilyResolver { this.resolvers = Arrays.asList(resolvers); } - public String lookup(String familyName) { + public GVTFontFamily resolve(String familyName) { for (FontFamilyResolver resolver : resolvers) { - String lookup = resolver.lookup(familyName); - if (lookup != null) { - return lookup; + GVTFontFamily family = resolver.resolve(familyName); + if (family != null) { + return family; } } return null; } - public GVTFontFamily resolve(String familyName) { + public GVTFontFamily resolve(String familyName, FontFace fontFace) { for (FontFamilyResolver resolver : resolvers) { - GVTFontFamily family = resolver.resolve(familyName); + GVTFontFamily family = resolver.resolve(familyName, fontFace); if (family != null) { return family; } @@ -53,6 +55,17 @@ public class AggregatingFontFamilyResolver implements FontFamilyResolver { return null; } + public GVTFontFamily loadFont(InputStream in, FontFace fontFace) throws Exception { + for (FontFamilyResolver resolver : resolvers) { + try { + return resolver.loadFont(in, fontFace); + } catch (Exception e) { + // Try the next one + } + } + return null; + } + public GVTFontFamily getDefault() { return resolve("any"); } diff --git a/src/java/org/apache/fop/svg/font/FOPFontFamilyResolverImpl.java b/src/java/org/apache/fop/svg/font/FOPFontFamilyResolverImpl.java index a9a631691..4305838da 100644 --- a/src/java/org/apache/fop/svg/font/FOPFontFamilyResolverImpl.java +++ b/src/java/org/apache/fop/svg/font/FOPFontFamilyResolverImpl.java @@ -19,8 +19,13 @@ package org.apache.fop.svg.font; +import java.io.InputStream; import java.util.Map; +import org.apache.batik.bridge.FontFace; +import org.apache.batik.gvt.font.GVTFontFace; +import org.apache.batik.gvt.font.GVTFontFamily; + import org.apache.fop.fonts.Font; import org.apache.fop.fonts.FontInfo; import org.apache.fop.fonts.FontTriplet; @@ -34,20 +39,27 @@ public class FOPFontFamilyResolverImpl implements FOPFontFamilyResolver { this.fontInfo = fontInfo; } - public String lookup(String familyName) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException(); + public FOPGVTFontFamily resolve(String familyName) { + return resolve(familyName, new GVTFontFace(familyName)); } - public FOPGVTFontFamily resolve(String familyName) { + public FOPGVTFontFamily resolve(String familyName, FontFace fontFace) { + return resolve(familyName, (GVTFontFace) FontFace.createFontFace(familyName, fontFace)); + } + + private FOPGVTFontFamily resolve(String familyName, GVTFontFace fontFace) { FOPGVTFontFamily gvtFontFamily = null; FontTriplet triplet = fontInfo.fontLookup(familyName, Font.STYLE_NORMAL, Font.WEIGHT_NORMAL); if (fontInfo.hasFont(familyName, Font.STYLE_NORMAL, Font.WEIGHT_NORMAL)) { - gvtFontFamily = new FOPGVTFontFamily(fontInfo, familyName, triplet); + gvtFontFamily = new FOPGVTFontFamily(fontInfo, familyName, triplet, fontFace); } return gvtFontFamily; } + public GVTFontFamily loadFont(InputStream in, FontFace fontFace) throws Exception { + throw new UnsupportedOperationException("Not implemented"); + } + public FOPGVTFontFamily getDefault() { return resolve("any"); } @@ -58,7 +70,8 @@ public class FOPFontFamilyResolverImpl implements FOPFontFamilyResolver { if (font.hasChar(c)) { String fontFamily = font.getFamilyNames().iterator().next(); return new FOPGVTFontFamily(fontInfo, fontFamily, - new FontTriplet(fontFamily, Font.STYLE_NORMAL, Font.WEIGHT_NORMAL)); + new FontTriplet(fontFamily, Font.STYLE_NORMAL, Font.WEIGHT_NORMAL), + new GVTFontFace(fontFamily)); } } return null; diff --git a/src/java/org/apache/fop/svg/font/FOPGVTFontFamily.java b/src/java/org/apache/fop/svg/font/FOPGVTFontFamily.java index 036b560a0..5be9419d3 100644 --- a/src/java/org/apache/fop/svg/font/FOPGVTFontFamily.java +++ b/src/java/org/apache/fop/svg/font/FOPGVTFontFamily.java @@ -38,10 +38,13 @@ public class FOPGVTFontFamily implements GVTFontFamily { private final String familyName; - public FOPGVTFontFamily(FontInfo fontInfo, String familyName, FontTriplet triplet) { + private GVTFontFace fontFace; + + public FOPGVTFontFamily(FontInfo fontInfo, String familyName, FontTriplet triplet, GVTFontFace fontFace) { this.fontInfo = fontInfo; this.fontTriplet = triplet; this.familyName = familyName; + this.fontFace = fontFace; } public String getFamilyName() { @@ -49,8 +52,7 @@ public class FOPGVTFontFamily implements GVTFontFamily { } public GVTFontFace getFontFace() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException(); + return fontFace; } public FOPGVTFont deriveFont(float size, AttributedCharacterIterator aci) { diff --git a/src/java/org/apache/fop/svg/font/FilteringFontFamilyResolver.java b/src/java/org/apache/fop/svg/font/FilteringFontFamilyResolver.java index 319d6b2b8..93b92ea36 100644 --- a/src/java/org/apache/fop/svg/font/FilteringFontFamilyResolver.java +++ b/src/java/org/apache/fop/svg/font/FilteringFontFamilyResolver.java @@ -19,6 +19,11 @@ package org.apache.fop.svg.font; +import java.io.InputStream; + +import org.apache.batik.bridge.FontFace; +import org.apache.batik.gvt.font.GVTFontFamily; + public class FilteringFontFamilyResolver implements FOPFontFamilyResolver { @@ -28,14 +33,18 @@ public class FilteringFontFamilyResolver implements FOPFontFamilyResolver { this.delegate = delegate; } - public String lookup(String familyName) { - return delegate.lookup(familyName); - } - public FOPGVTFontFamily resolve(String familyName) { return delegate.resolve(familyName); } + public GVTFontFamily resolve(String familyName, FontFace fontFace) { + return delegate.resolve(familyName, fontFace); + } + + public GVTFontFamily loadFont(InputStream in, FontFace fontFace) throws Exception { + return delegate.loadFont(in, fontFace); + } + public FOPGVTFontFamily getDefault() { return delegate.getDefault(); } diff --git a/test/java/org/apache/fop/svg/font/FOPFontFamilyResolverTestCase.java b/test/java/org/apache/fop/svg/font/FOPFontFamilyResolverTestCase.java index ca843ec36..d14752b90 100644 --- a/test/java/org/apache/fop/svg/font/FOPFontFamilyResolverTestCase.java +++ b/test/java/org/apache/fop/svg/font/FOPFontFamilyResolverTestCase.java @@ -81,7 +81,7 @@ public class FOPFontFamilyResolverTestCase { @Test public void testDeriveFont() { - FOPGVTFontFamily family = (FOPGVTFontFamily) resolver.resolve(FontInfoBuilder.DEJAVU_LGC_SERIF); + FOPGVTFontFamily family = resolver.resolve(FontInfoBuilder.DEJAVU_LGC_SERIF); FOPGVTFont font = family.deriveFont(10, Collections.emptyMap()); assertEquals(10, font.getSize(), 0); assertTrue(font.canDisplay('\u01F6')); @@ -91,7 +91,7 @@ public class FOPFontFamilyResolverTestCase { @Test @Ignore("FOP metrics don't match AWT, but not sure who is right and who is wrong") public void testLineMetrics() throws FontFormatException, IOException { - FOPGVTFontFamily family = (FOPGVTFontFamily) resolver.resolve(FontInfoBuilder.DEJAVU_LGC_SERIF); + FOPGVTFontFamily family = resolver.resolve(FontInfoBuilder.DEJAVU_LGC_SERIF); FOPGVTFont font = family.deriveFont(10, Collections.emptyMap()); GVTLineMetrics fopMetrics = font.getLineMetrics("", null); LineMetrics awtMetrics = getAWTLineMetrics(); -- cgit v1.2.3 From 6edf5c9f468b1a569065830500b3223434e2ef73 Mon Sep 17 00:00:00 2001 From: Luis Bernardo Date: Sat, 22 Feb 2014 18:38:31 +0000 Subject: FOP-2305: Poor resolution of PCL output in trunk vs. 1.1; fixed regression git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1570878 13f79535-47bb-0310-9956-ffa450edef68 --- src/java/org/apache/fop/cli/CommandLineOptions.java | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src/java/org/apache') diff --git a/src/java/org/apache/fop/cli/CommandLineOptions.java b/src/java/org/apache/fop/cli/CommandLineOptions.java index ad8019a7d..b559e90b8 100644 --- a/src/java/org/apache/fop/cli/CommandLineOptions.java +++ b/src/java/org/apache/fop/cli/CommandLineOptions.java @@ -124,6 +124,8 @@ public class CommandLineOptions { private boolean conserveMemoryPolicy = false; /* true if a complex script features are enabled */ private boolean useComplexScriptFeatures = true; + /* set to true if -dpi used in command line */ + private boolean overrideTargetResolution = false; private FopFactory factory; private FOUserAgent foUserAgent; @@ -440,6 +442,7 @@ public class CommandLineOptions { "if you use '-dpi', you must specify a resolution (dots per inch)"); } else { this.targetResolution = Integer.parseInt(args[i + 1]); + this.overrideTargetResolution = true; return 1; } } @@ -1017,6 +1020,9 @@ public class CommandLineOptions { try { FopConfParser fopConfParser = new FopConfParser(userConfigFile, baseURI); fopFactoryBuilder = fopConfParser.getFopFactoryBuilder(); + if (this.overrideTargetResolution) { + fopFactoryBuilder.setTargetResolution(targetResolution); + } } catch (SAXException e) { throw new FOPException(e); } -- cgit v1.2.3 From e7cc6d2790ecdb5e61644782b80187658be5bde7 Mon Sep 17 00:00:00 2001 From: Robert Meyer Date: Wed, 26 Feb 2014 10:23:18 +0000 Subject: FOP-2260: Smart quotes broken in RTF if at start of text git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1571989 13f79535-47bb-0310-9956-ffa450edef68 --- .../org/apache/fop/render/rtf/rtflib/rtfdoc/RtfStringConverter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/java/org/apache') diff --git a/src/java/org/apache/fop/render/rtf/rtflib/rtfdoc/RtfStringConverter.java b/src/java/org/apache/fop/render/rtf/rtflib/rtfdoc/RtfStringConverter.java index bf32eceeb..9f542dd75 100644 --- a/src/java/org/apache/fop/render/rtf/rtflib/rtfdoc/RtfStringConverter.java +++ b/src/java/org/apache/fop/render/rtf/rtflib/rtfdoc/RtfStringConverter.java @@ -102,7 +102,7 @@ public final class RtfStringConverter { if (i != 0) { d = new Character(str.charAt(i - 1)); } else { - d = new Character(str.charAt(i)); + d = new Character(SPACE); } //This section modified by Chris Scott -- cgit v1.2.3 From 28b4b3d4de6106f807de5836683c82c453d58b9e Mon Sep 17 00:00:00 2001 From: Robert Meyer Date: Wed, 26 Feb 2014 15:52:33 +0000 Subject: FOP-2321: Latest fop snapshot crashes when processing rindolf-spec.fo git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1572109 13f79535-47bb-0310-9956-ffa450edef68 --- src/java/org/apache/fop/render/rtf/RTFHandler.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src/java/org/apache') diff --git a/src/java/org/apache/fop/render/rtf/RTFHandler.java b/src/java/org/apache/fop/render/rtf/RTFHandler.java index 73fa3504c..2bb7befc1 100644 --- a/src/java/org/apache/fop/render/rtf/RTFHandler.java +++ b/src/java/org/apache/fop/render/rtf/RTFHandler.java @@ -218,8 +218,10 @@ public class RTFHandler extends FOEventHandler { eventProducer.onlySPMSupported(this, reference, pageSeq.getLocator()); PageSequenceMaster master = pageSeq.getRoot().getLayoutMasterSet().getPageSequenceMaster(reference); - this.pagemaster = master.getNextSimplePageMaster( - false, false, false, false, pageSeq.getMainFlow().getFlowName()); + if (pageSeq.getMainFlow() != null) { + this.pagemaster = master.getNextSimplePageMaster( + false, false, false, false, pageSeq.getMainFlow().getFlowName()); + } } } -- cgit v1.2.3 From 4c131bd42f52e394b4c6220a3af179cb4539bf40 Mon Sep 17 00:00:00 2001 From: Vincent Hennebert Date: Fri, 14 Mar 2014 10:44:23 +0000 Subject: FOP-2357: When an SVG image has transparency and a PDF profile is used that disallows it, ignore it and issue a warning rather than throw an error git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1577477 13f79535-47bb-0310-9956-ffa450edef68 --- src/java/org/apache/fop/pdf/PDFProfile.java | 23 ++++++++--- .../fop/render/pdf/PDFImageHandlerGraphics2D.java | 2 +- .../apache/fop/render/pdf/PDFImageHandlerSVG.java | 27 ++++++++++++- src/java/org/apache/fop/svg/PDFGraphics2D.java | 47 +++++++++++++++------- src/java/org/apache/fop/svg/SVGEventProducer.java | 9 +++++ src/java/org/apache/fop/svg/SVGEventProducer.xml | 1 + test/java/org/apache/fop/events/EventChecker.java | 8 ++-- .../fop/render/pdf/PDFAConformanceTestCase.java | 18 +++++++++ .../org/apache/fop/svg/PDFTextPainterTestCase.java | 2 +- test/xml/pdf-a/svg-transparency.fo | 24 +++++++++++ 10 files changed, 134 insertions(+), 27 deletions(-) create mode 100644 test/xml/pdf-a/svg-transparency.fo (limited to 'src/java/org/apache') diff --git a/src/java/org/apache/fop/pdf/PDFProfile.java b/src/java/org/apache/fop/pdf/PDFProfile.java index 6e0cb412e..4ae5ba8df 100644 --- a/src/java/org/apache/fop/pdf/PDFProfile.java +++ b/src/java/org/apache/fop/pdf/PDFProfile.java @@ -169,15 +169,28 @@ public class PDFProfile { * @param context Context information for the user to identify the problem spot */ public void verifyTransparencyAllowed(String context) { - final String err = "{0} does not allow the use of transparency. ({1})"; + Object profile = isTransparencyAllowed(); + if (profile != null) { + throw new PDFConformanceException(profile + " does not allow the use of transparency. (" + + profile + ")"); + } + } + + /** + * Returns {@code null} if transparency is allowed, otherwise returns the profile that + * prevents it. + * + * @return {@code null}, or an object whose {@code toString} method returns the name + * of the profile that disallows transparency + */ + public Object isTransparencyAllowed() { if (pdfAMode.isPart1()) { - throw new PDFConformanceException(MessageFormat.format(err, - new Object[] {getPDFAMode(), context})); + return getPDFAMode(); } if (isPDFXActive()) { - throw new PDFConformanceException(MessageFormat.format(err, - new Object[] {getPDFXMode(), context})); + return getPDFXMode(); } + return null; } /** Checks if the right PDF version is set. */ diff --git a/src/java/org/apache/fop/render/pdf/PDFImageHandlerGraphics2D.java b/src/java/org/apache/fop/render/pdf/PDFImageHandlerGraphics2D.java index a71ade911..5ab0b700f 100644 --- a/src/java/org/apache/fop/render/pdf/PDFImageHandlerGraphics2D.java +++ b/src/java/org/apache/fop/render/pdf/PDFImageHandlerGraphics2D.java @@ -86,7 +86,7 @@ public class PDFImageHandlerGraphics2D extends AbstractImageHandlerGraphics2D { PDFGraphics2D graphics = new PDFGraphics2D(textAsShapes, pdfContext.getFontInfo(), generator.getDocument(), generator.getResourceContext(), pdfContext.getPage().referencePDF(), - "", 0.0f); + "", 0.0f, null); graphics.setGraphicContext(new org.apache.xmlgraphics.java2d.GraphicContext()); AffineTransform transform = new AffineTransform(); diff --git a/src/java/org/apache/fop/render/pdf/PDFImageHandlerSVG.java b/src/java/org/apache/fop/render/pdf/PDFImageHandlerSVG.java index 3b7084a65..ae282f149 100644 --- a/src/java/org/apache/fop/render/pdf/PDFImageHandlerSVG.java +++ b/src/java/org/apache/fop/render/pdf/PDFImageHandlerSVG.java @@ -41,6 +41,7 @@ import org.apache.xmlgraphics.image.loader.impl.ImageXMLDOM; import org.apache.xmlgraphics.util.UnitConv; import org.apache.fop.apps.FOUserAgent; +import org.apache.fop.events.EventBroadcaster; import org.apache.fop.image.loader.batik.BatikImageFlavors; import org.apache.fop.image.loader.batik.BatikUtil; import org.apache.fop.render.ImageHandler; @@ -171,7 +172,7 @@ public class PDFImageHandlerSVG implements ImageHandler { PDFGraphics2D graphics = new PDFGraphics2D(true, pdfContext.getFontInfo(), generator.getDocument(), generator.getResourceContext(), pdfContext.getPage().referencePDF(), - "", 0); + "", 0, new TransparencyIgnoredEventListener(pdfContext, imageSVG)); graphics.setGraphicContext(new org.apache.xmlgraphics.java2d.GraphicContext()); if (!resolutionScaling.isIdentity()) { @@ -222,6 +223,30 @@ public class PDFImageHandlerSVG implements ImageHandler { } } + private static class TransparencyIgnoredEventListener + implements PDFGraphics2D.TransparencyIgnoredEventListener { + + private final RenderingContext context; + + private final Image image; + + public TransparencyIgnoredEventListener(RenderingContext context, Image image) { + this.context = context; + this.image = image; + } + + private boolean warningIssued; + + public void transparencyIgnored(Object pdfProfile) { + if (!warningIssued) { + EventBroadcaster broadcaster = context.getUserAgent().getEventBroadcaster(); + SVGEventProducer producer = SVGEventProducer.Provider.get(broadcaster); + producer.transparencyIgnored(this, pdfProfile, image.getInfo().getOriginalURI()); + warningIssued = true; + } + } + } + /** {@inheritDoc} */ public int getPriority() { return 400; diff --git a/src/java/org/apache/fop/svg/PDFGraphics2D.java b/src/java/org/apache/fop/svg/PDFGraphics2D.java index 1fcf9f870..42d79a931 100644 --- a/src/java/org/apache/fop/svg/PDFGraphics2D.java +++ b/src/java/org/apache/fop/svg/PDFGraphics2D.java @@ -189,6 +189,17 @@ public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHand */ protected OutputStream outputStream = null; + private TransparencyIgnoredEventListener transparencyIgnoredEventListener; + + /** + * May be used to give proper feedback to the user when a particular PDF profile is + * being used that disallows transparency. + */ + public interface TransparencyIgnoredEventListener { + + void transparencyIgnored(Object pdfProfile); + } + /** * Create a new PDFGraphics2D with the given pdf document info. * This is used to create a Graphics object for use inside an already @@ -203,7 +214,8 @@ public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHand * @param size the current font size */ public PDFGraphics2D(boolean textAsShapes, FontInfo fi, PDFDocument doc, - PDFResourceContext page, String pref, String font, float size) { + PDFResourceContext page, String pref, String font, float size, + TransparencyIgnoredEventListener listener) { this(textAsShapes); pdfDoc = doc; this.colorHandler = new PDFColorHandler(doc.getResources()); @@ -213,6 +225,7 @@ public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHand fontInfo = fi; pageRef = pref; paintingState = new PDFPaintingState(); + this.transparencyIgnoredEventListener = listener; } /** @@ -244,6 +257,7 @@ public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHand this.nativeCount = g.nativeCount; this.outputStream = g.outputStream; this.ovFontState = g.ovFontState; + this.transparencyIgnoredEventListener = g.transparencyIgnoredEventListener; } /** @@ -977,7 +991,7 @@ public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHand PDFResourceContext context = new PDFResourceContext(res); PDFGraphics2D pattGraphic = new PDFGraphics2D(textAsShapes, specialFontInfo, pdfDoc, context, getPageReference(), - "", 0); + "", 0, transparencyIgnoredEventListener); pattGraphic.setGraphicContext(new GraphicContext()); pattGraphic.gc.validateTransformStack(); pattGraphic.setRenderingHints(this.getRenderingHints()); @@ -1430,18 +1444,21 @@ public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHand */ protected void applyAlpha(int fillAlpha, int strokeAlpha) { if (fillAlpha != OPAQUE || strokeAlpha != OPAQUE) { - checkTransparencyAllowed(); - Map vals = new java.util.HashMap(); - if (fillAlpha != OPAQUE) { - vals.put(PDFGState.GSTATE_ALPHA_NONSTROKE, new Float(fillAlpha / 255f)); - } - if (strokeAlpha != OPAQUE) { - vals.put(PDFGState.GSTATE_ALPHA_STROKE, new Float(strokeAlpha / 255f)); + Object profile = isTransparencyAllowed(); + if (profile == null) { + Map vals = new java.util.HashMap(); + if (fillAlpha != OPAQUE) { + vals.put(PDFGState.GSTATE_ALPHA_NONSTROKE, new Float(fillAlpha / 255f)); + } + if (strokeAlpha != OPAQUE) { + vals.put(PDFGState.GSTATE_ALPHA_STROKE, new Float(strokeAlpha / 255f)); + } + PDFGState gstate = pdfDoc.getFactory().makeGState(vals, paintingState.getGState()); + resourceContext.addGState(gstate); + currentStream.write("/" + gstate.getName() + " gs\n"); + } else if (transparencyIgnoredEventListener != null) { + transparencyIgnoredEventListener.transparencyIgnored(profile); } - PDFGState gstate = pdfDoc.getFactory().makeGState( - vals, paintingState.getGState()); - resourceContext.addGState(gstate); - currentStream.write("/" + gstate.getName() + " gs\n"); } } @@ -1686,8 +1703,8 @@ public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHand } /** Checks whether the use of transparency is allowed. */ - protected void checkTransparencyAllowed() { - pdfDoc.getProfile().verifyTransparencyAllowed("Java2D graphics"); + protected Object isTransparencyAllowed() { + return pdfDoc.getProfile().isTransparencyAllowed(); } /** diff --git a/src/java/org/apache/fop/svg/SVGEventProducer.java b/src/java/org/apache/fop/svg/SVGEventProducer.java index 743649717..6bf7ed100 100644 --- a/src/java/org/apache/fop/svg/SVGEventProducer.java +++ b/src/java/org/apache/fop/svg/SVGEventProducer.java @@ -89,4 +89,13 @@ public interface SVGEventProducer extends EventProducer { */ void svgRenderingError(Object source, Exception e, String uri); + /** + * Transparency has been ignored due to restrictions from the PDF profile being used. + * @param source the event source + * @param pdfProfile the PDF profile + * @param uri the image URI, if available + * @event.severity WARN + */ + void transparencyIgnored(Object source, Object pdfProfile, String uri); + } diff --git a/src/java/org/apache/fop/svg/SVGEventProducer.xml b/src/java/org/apache/fop/svg/SVGEventProducer.xml index 9d229a14b..f5e1d300a 100644 --- a/src/java/org/apache/fop/svg/SVGEventProducer.xml +++ b/src/java/org/apache/fop/svg/SVGEventProducer.xml @@ -22,4 +22,5 @@ SVG info: {message} SVG graphic could not be built. Reason: {e} SVG graphic could not be rendered. Reason: {e} + Transparency in an SVG image will be ignored because {pdfProfile} does not allow it[ (see {uri})]. diff --git a/test/java/org/apache/fop/events/EventChecker.java b/test/java/org/apache/fop/events/EventChecker.java index 3f0eef6bb..506658467 100644 --- a/test/java/org/apache/fop/events/EventChecker.java +++ b/test/java/org/apache/fop/events/EventChecker.java @@ -27,7 +27,7 @@ import static org.junit.Assert.fail; /** * Class that checks that an expected event is produced, and only this one. */ -class EventChecker implements EventListener { +public class EventChecker implements EventListener { private final String expectedEventID; @@ -35,7 +35,7 @@ class EventChecker implements EventListener { private boolean eventReceived; - EventChecker(String expectedEventID, Map expectedParams) { + public EventChecker(String expectedEventID, Map expectedParams) { this.expectedEventID = expectedEventID; this.expectedParams = expectedParams; } @@ -51,9 +51,9 @@ class EventChecker implements EventListener { } } - void end() { + public void end() { if (!eventReceived) { - fail("Did not received expected event: " + expectedEventID); + fail("Did not receive expected event: " + expectedEventID); } } } diff --git a/test/java/org/apache/fop/render/pdf/PDFAConformanceTestCase.java b/test/java/org/apache/fop/render/pdf/PDFAConformanceTestCase.java index 6aad0320e..b2b77e842 100644 --- a/test/java/org/apache/fop/render/pdf/PDFAConformanceTestCase.java +++ b/test/java/org/apache/fop/render/pdf/PDFAConformanceTestCase.java @@ -21,6 +21,8 @@ package org.apache.fop.render.pdf; import java.io.File; import java.io.IOException; +import java.util.HashMap; +import java.util.Map; import org.junit.Test; import org.xml.sax.SAXException; @@ -28,7 +30,10 @@ import org.xml.sax.SAXException; import static org.junit.Assert.fail; import org.apache.fop.apps.FOUserAgent; +import org.apache.fop.events.EventChecker; +import org.apache.fop.pdf.PDFAMode; import org.apache.fop.pdf.PDFConformanceException; +import org.apache.fop.svg.SVGEventProducer; /** * Tests PDF/A-1 functionality. @@ -108,4 +113,17 @@ public class PDFAConformanceTestCase extends BasePDFTest { } } + @Test + public void svgTransparency() throws Exception { + Map params = new HashMap(); + params.put("pdfProfile", PDFAMode.PDFA_1B); + EventChecker eventChecker = new EventChecker(SVGEventProducer.class.getName() + + ".transparencyIgnored", params); + FOUserAgent ua = getUserAgent(); + ua.getEventBroadcaster().addEventListener(eventChecker); + File foFile = new File(foBaseDir, "svg-transparency.fo"); + convertFO(foFile, ua, dumpPDF); + eventChecker.end(); + } + } diff --git a/test/java/org/apache/fop/svg/PDFTextPainterTestCase.java b/test/java/org/apache/fop/svg/PDFTextPainterTestCase.java index efef464bd..e7e47e7ba 100644 --- a/test/java/org/apache/fop/svg/PDFTextPainterTestCase.java +++ b/test/java/org/apache/fop/svg/PDFTextPainterTestCase.java @@ -36,7 +36,7 @@ public class PDFTextPainterTestCase extends NativeTextPainterTest { private static class OperatorCheckingPDFGraphics2D extends PDFGraphics2D { OperatorCheckingPDFGraphics2D(FontInfo fontInfo, final OperatorValidator validator) { - super(false, fontInfo, new PDFDocument("test"), null, null, null, 0); + super(false, fontInfo, new PDFDocument("test"), null, null, null, 0, null); this.currentStream = new StringWriter() { @Override diff --git a/test/xml/pdf-a/svg-transparency.fo b/test/xml/pdf-a/svg-transparency.fo new file mode 100644 index 000000000..922416fbc --- /dev/null +++ b/test/xml/pdf-a/svg-transparency.fo @@ -0,0 +1,24 @@ + + + + + + + + + + RGB Circles + + + + + + + + + + + + -- cgit v1.2.3