diff options
Diffstat (limited to 'src/java/org/apache/fop/afp')
16 files changed, 490 insertions, 344 deletions
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 @@ -71,22 +71,6 @@ public abstract class AbstractOutlineFont extends AFPFont { } /** - * 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 * used to denote the part of the letter extending above the x-height. @@ -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<String, CharacterSetOrientation> 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<String, CharacterSetOrientation>(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,11 +163,24 @@ 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 * usually the height of the uppercase M. @@ -177,7 +188,6 @@ public class CharacterSet { * @return the cap height value in millipoints */ public int getCapHeight() { - return getCharacterSetOrientation().getCapHeight(); } @@ -194,24 +204,6 @@ public class CharacterSet { } /** - * 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 */ @@ -220,16 +212,6 @@ public class CharacterSet { } /** - * 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. * * @return the typical height of characters @@ -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<String, String> codepage, - double metricNormalizationFactor) - throws IOException { + + private void processFontIndex(StructuredFieldReader structuredFieldReader, CharacterSetOrientation cso, + Map<String, String> 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> 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<CharacterMetrics>(); } /** @@ -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; } /** @@ -162,17 +153,6 @@ public class CharacterSetOrientation { } /** - * 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. * @return heightX the typical height of characters @@ -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<T> { + + private static final int RANGE_BIT_SIZE = 8; + + private static final int RANGE_SIZE = 1 << RANGE_BIT_SIZE; + + private final Map<Integer, ArrayList<T>> arrays = new HashMap<Integer, ArrayList<T>>(); + + /** + * + * @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<T> range = arrays.get(rangeKey); + if (range == null) { + range = new ArrayList<T>(Collections.<T>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<T> 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<CharacterSet> 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<CharacterSet> 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<String, Typeface> 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; + } + } + } |