Index: conf/fop.xconf =================================================================== --- conf/fop.xconf (revision 900314) +++ conf/fop.xconf (working copy) @@ -396,6 +396,35 @@ + + Index: src/java/org/apache/fop/render/afp/AFPRendererConfigurator.java =================================================================== --- src/java/org/apache/fop/render/afp/AFPRendererConfigurator.java (revision 900314) +++ src/java/org/apache/fop/render/afp/AFPRendererConfigurator.java (working copy) @@ -30,9 +30,12 @@ import org.apache.fop.afp.AFPResourceLevel; import org.apache.fop.afp.AFPResourceLevelDefaults; +import org.apache.fop.afp.fonts.AFPFont; import org.apache.fop.afp.fonts.AFPFontCollection; import org.apache.fop.afp.fonts.AFPFontInfo; +import org.apache.fop.afp.fonts.AFPFontReader; import org.apache.fop.afp.fonts.CharacterSet; +import org.apache.fop.afp.fonts.DoubleByteFont; import org.apache.fop.afp.fonts.FopCharacterSet; import org.apache.fop.afp.fonts.OutlineFont; import org.apache.fop.afp.fonts.RasterFont; @@ -57,7 +60,7 @@ * AFP Renderer configurator */ public class AFPRendererConfigurator extends PrintRendererConfigurator - implements IFDocumentHandlerConfigurator { +implements IFDocumentHandlerConfigurator { /** * Default constructor @@ -69,7 +72,7 @@ } private AFPFontInfo buildFont(Configuration fontCfg, String fontPath) - throws ConfigurationException { + throws ConfigurationException { FontManager fontManager = this.userAgent.getFactory().getFontManager(); @@ -127,11 +130,33 @@ return null; } String encoding = afpFontCfg.getAttribute("encoding"); + if (encoding == null) { log.error("Mandatory afp-font configuration attribute 'encoding=' is missing"); return null; } + AFPFont font = fontFromType(type, codepage, encoding, accessor, afpFontCfg); + + return font != null ? new AFPFontInfo(font, tripletList) : null; + } + + + /** + * Create the AFPFont based on type and type-dependent configuration. + * + * @param type font type e.g. 'raster', 'outline' + * @param codepage codepage file + * @param encoding character encoding e.g. 'Cp500', 'UnicodeBigUnmarked' + * @param accessor + * @param afpFontCfg + * @return + * @throws ConfigurationException + */ + private AFPFont fontFromType(String type, String codepage, String encoding, + ResourceAccessor accessor, Configuration afpFontCfg) + throws ConfigurationException { + if ("raster".equalsIgnoreCase(type)) { String name = afpFontCfg.getAttribute("name", "Unknown"); @@ -161,27 +186,27 @@ if (base14 != null) { try { Class clazz = Class.forName("org.apache.fop.fonts.base14." - + base14); + + base14); try { Typeface tf = (Typeface)clazz.newInstance(); font.addCharacterSet(sizeMpt, new FopCharacterSet( - codepage, encoding, characterset, tf)); + codepage, encoding, characterset, tf)); } catch (Exception ie) { String msg = "The base 14 font class " + clazz.getName() - + " could not be instantiated"; + + " could not be instantiated"; log.error(msg); } } catch (ClassNotFoundException cnfe) { String msg = "The base 14 font class for " + characterset - + " could not be found"; + + " could not be found"; log.error(msg); } } else { font.addCharacterSet(sizeMpt, new CharacterSet( - codepage, encoding, characterset, accessor)); + codepage, encoding, characterset, accessor)); } } - return new AFPFontInfo(font, tripletList); + return font; } else if ("outline".equalsIgnoreCase(type)) { String characterset = afpFontCfg.getAttribute("characterset"); @@ -195,30 +220,83 @@ if (base14 != null) { try { Class clazz = Class.forName("org.apache.fop.fonts.base14." - + base14); + + base14); try { Typeface tf = (Typeface)clazz.newInstance(); characterSet = new FopCharacterSet( codepage, encoding, characterset, tf); } catch (Exception ie) { String msg = "The base 14 font class " + clazz.getName() - + " could not be instantiated"; + + " could not be instantiated"; log.error(msg); } } catch (ClassNotFoundException cnfe) { String msg = "The base 14 font class for " + characterset - + " could not be found"; + + " could not be found"; log.error(msg); } } else { characterSet = new CharacterSet(codepage, encoding, characterset, accessor); } + // Return new font object + return new OutlineFont(name, characterSet); + + } else if ("double-byte".equalsIgnoreCase(type)) { + String characterset = afpFontCfg.getAttribute("characterset"); + if (characterset == null) { + log.error("Mandatory afp-font configuration attribute 'characterset=' is missing"); + return null; + } + String name = afpFontCfg.getAttribute("name", characterset); + CharacterSet characterSet = null; + characterSet = new CharacterSet(codepage, encoding, characterset, accessor); + characterSet.setAFPFontReader(AFPFontReader.getDoubleByteInstance()); + + // Create a new font object - OutlineFont font = new OutlineFont(name, characterSet); - return new AFPFontInfo(font, tripletList); + DoubleByteFont font; + + String fallbackTypeAttr = afpFontCfg.getAttribute("fallback-type", null); + + if (fallbackTypeAttr != null) { + String fallbackValueAttr = afpFontCfg.getAttribute("fallback-value", null); + if (fallbackTypeAttr.equalsIgnoreCase("char") && fallbackValueAttr != null) { + + try { + int fallbackChar; + if (fallbackValueAttr.matches("[Uu]\\+.*")) { + fallbackChar = Integer.parseInt(fallbackValueAttr.substring(2), 16); + } else { + fallbackChar = Integer.parseInt(fallbackValueAttr); + } + + font = new DoubleByteFont(name, characterSet, fallbackChar); + } catch (NumberFormatException e) { + font = new DoubleByteFont(name, characterSet); + log.warn("The value of the fallback-value attribute is badly formed: '" + + fallbackValueAttr + "' not integral."); + } + } else if (fallbackTypeAttr.equalsIgnoreCase("em")) { + // use the em value + font = new DoubleByteFont(name, characterSet); + } else { + + log.warn("The value of the fallback-type attribute '" + + fallbackTypeAttr + "' is not recognised."); + font = new DoubleByteFont(name, characterSet); + } + + } else { + font = new DoubleByteFont(name, characterSet); + } + + + return font; + } else { log.error("No or incorrect type attribute"); } + return null; } @@ -230,7 +308,7 @@ * @throws ConfigurationException if something's wrong with the config data */ private List/**/ buildFontListFromConfiguration(Configuration cfg) - throws FOPException, ConfigurationException { + throws FOPException, ConfigurationException { Configuration fonts = cfg.getChild("fonts"); FontManager fontManager = this.userAgent.getFactory().getFontManager(); @@ -261,9 +339,9 @@ FontTriplet triplet = (FontTriplet) fontTriplets.get(j); if (log.isDebugEnabled()) { log.debug(" Font triplet " - + triplet.getName() + ", " - + triplet.getStyle() + ", " - + triplet.getWeight()); + + triplet.getName() + ", " + + triplet.getStyle() + ", " + + triplet.getWeight()); } if ((referencedFontsMatcher != null && referencedFontsMatcher.matches(triplet)) @@ -346,7 +424,7 @@ // a default external resource group file setting Configuration resourceGroupFileCfg - = cfg.getChild("resource-group-file", false); + = cfg.getChild("resource-group-file", false); if (resourceGroupFileCfg != null) { String resourceGroupDest = null; try { @@ -358,14 +436,15 @@ customizable.setDefaultResourceGroupFilePath(resourceGroupDest); } else { log.warn("Unable to write to default external resource group file '" - + resourceGroupDest + "'"); + + resourceGroupDest + "'"); } } } catch (ConfigurationException e) { LogUtil.handleException(log, e, userAgent.getFactory().validateUserConfigStrictly()); } catch (IOException ioe) { - throw new FOPException("Could not create default external resource group file", ioe); + throw new FOPException("Could not create default external resource group file" + , ioe); } } @@ -401,7 +480,7 @@ /** {@inheritDoc} */ public void setupFontInfo(IFDocumentHandler documentHandler, FontInfo fontInfo) - throws FOPException { + throws FOPException { FontManager fontManager = userAgent.getFactory().getFontManager(); List fontCollections = new java.util.ArrayList(); Index: src/java/org/apache/fop/afp/modca/MapCodedFont.java =================================================================== --- src/java/org/apache/fop/afp/modca/MapCodedFont.java (revision 900314) +++ src/java/org/apache/fop/afp/modca/MapCodedFont.java (working copy) @@ -29,6 +29,7 @@ import org.apache.fop.afp.AFPConstants; import org.apache.fop.afp.fonts.AFPFont; import org.apache.fop.afp.fonts.CharacterSet; +import org.apache.fop.afp.fonts.DoubleByteFont; import org.apache.fop.afp.fonts.FontRuntimeException; import org.apache.fop.afp.fonts.OutlineFont; import org.apache.fop.afp.fonts.RasterFont; @@ -217,6 +218,24 @@ AFPConstants.EBCIDIC_ENCODING) + " must have a fixed length of 8 characters."); } + } else if (font instanceof DoubleByteFont) { + DoubleByteFont outline = (DoubleByteFont) font; + CharacterSet cs = outline.getCharacterSet(); + fontDefinition.characterSet = cs.getNameBytes(); + + // There are approximately 72 points to 1 inch or 20 1440ths per point. + + fontDefinition.scale = 20 * size / 1000; + + fontDefinition.codePage = cs.getCodePage().getBytes( + AFPConstants.EBCIDIC_ENCODING); + + if (fontDefinition.codePage.length != 8) { + throw new IllegalArgumentException("The code page " + + new String(fontDefinition.codePage, + AFPConstants.EBCIDIC_ENCODING) + + " must have a fixed length of 8 characters."); + } } else { String msg = "Font of type " + font.getClass().getName() + " not recognized."; Index: src/java/org/apache/fop/afp/fonts/OutlineFont.java =================================================================== --- src/java/org/apache/fop/afp/fonts/OutlineFont.java (revision 900314) +++ src/java/org/apache/fop/afp/fonts/OutlineFont.java (working copy) @@ -21,163 +21,17 @@ /** - * A font defined as a set of lines and curves as opposed to a bitmap font. An - * outline font can be scaled to any size and otherwise transformed more easily - * than a bitmap font, and with more attractive results.

+ * Default implementation of AbstractOutlineFont. * */ -public class OutlineFont extends AFPFont { +public class OutlineFont extends AbstractOutlineFont { - /** The character set for this font */ - private CharacterSet charSet = null; - - /** - * Constructor for an outline font. - * - * @param name - * the name of the font - * @param charSet - * the chracter set - */ + /** {@inheritDoc} */ public OutlineFont(String name, CharacterSet charSet) { - super(name); - this.charSet = charSet; - } + super(name, charSet); - /** - * Get the character set metrics. - * - * @return the character set - */ - public CharacterSet getCharacterSet() { - - return charSet; - } - /** - * Get the character set metrics. - * @param size ignored - * @return the character set - */ - public CharacterSet getCharacterSet(int size) { - 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 - * used to denote the part of the letter extending above the x-height. - * - * @param size the font size (in mpt) - * @return the ascender for the given size - */ - public int getAscender(int size) { - return charSet.getAscender() * size; - } - - /** - * Obtains the height of capital letters for the specified point size. - * - * @param size the font size (in mpt) - * @return the cap height for the given size - */ - public int getCapHeight(int size) { - return charSet.getCapHeight() * size; - } - - /** - * The descender is the part of a lowercase letter that extends below the - * base line, such as "g", "j", or "p". Also used to denote the part of the - * letter extending below the base line. - * - * @param size the font size (in mpt) - * @return the descender for the given size - */ - public int getDescender(int size) { - return charSet.getDescender() * size; - } - - /** - * The "x-height" (the height of the letter "x"). - * - * @param size the font size (in mpt) - * @return the x height for the given size - */ - public int getXHeight(int size) { - 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(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) { - return charSet.hasChar(c); - } - - /** - * Map a Unicode character to a code point in the font. - * @param c character to map - * @return the mapped character - */ - public char mapChar(char c) { - return charSet.mapChar(c); - } - - /** {@inheritDoc} */ - public String getEncodingName() { - return charSet.getEncoding(); - } - } \ No newline at end of file Index: src/java/org/apache/fop/afp/fonts/CharacterSet.java =================================================================== --- src/java/org/apache/fop/afp/fonts/CharacterSet.java (revision 900314) +++ src/java/org/apache/fop/afp/fonts/CharacterSet.java (working copy) @@ -60,7 +60,7 @@ public class CharacterSet { /** Static logging instance */ - protected static final Log log = LogFactory.getLog(CharacterSet.class.getName()); + protected static final Log LOG = LogFactory.getLog(CharacterSet.class.getName()); /** default codepage */ public static final String DEFAULT_CODEPAGE = "T1V10500"; @@ -98,6 +98,9 @@ /** The nominal vertical size (in millipoints) for bitmap fonts. 0 for outline fonts. */ private int nominalVerticalSize = 0; + /** The AFPFontReader for extracting font metrics */ + private AFPFontReader afpFontReader = AFPFontReader.getInstance(); + /** * Constructor for the CharacterSetMetric object, the character set is used * to load the font information from the actual AFP font. @@ -126,7 +129,7 @@ if (name.length() > MAX_NAME_LEN) { String msg = "Character set name '" + name + "' must be a maximum of " + MAX_NAME_LEN + " characters"; - log.error("Constructor:: " + msg); + LOG.error("Constructor:: " + msg); throw new IllegalArgumentException(msg); } @@ -286,13 +289,12 @@ */ private void load() { if (!isMetricsLoaded) { - AFPFontReader afpFontReader = new AFPFontReader(); try { afpFontReader.loadCharacterSetMetric(this); isMetricsLoaded = true; } catch (IOException e) { String msg = "Failed to load the character set metrics for code page " + codePage; - log.error(msg); + LOG.error(msg); throw new RuntimeException(e.getMessage()); } } @@ -318,7 +320,7 @@ nameBytes = name.getBytes(AFPConstants.EBCIDIC_ENCODING); } catch (UnsupportedEncodingException usee) { nameBytes = name.getBytes(); - log.warn( + LOG.warn( "UnsupportedEncodingException translating the name " + name); } return nameBytes; @@ -417,4 +419,21 @@ return c; } + /** + *The AFPFontReader is used to extract font metric data + * @param afpFontReader to set + */ + public void setAFPFontReader(AFPFontReader afpFontReader) { + this.afpFontReader = afpFontReader; + } + + /** + * + * @return the number units per em + */ + public int getEmSpaceIncrement() { + load(); + return getCharacterSetOrientation().getUnitsPerEm(); + } + } Index: src/java/org/apache/fop/afp/fonts/AFPFontReader.java =================================================================== --- src/java/org/apache/fop/afp/fonts/AFPFontReader.java (revision 900314) +++ src/java/org/apache/fop/afp/fonts/AFPFontReader.java (working copy) @@ -49,14 +49,13 @@ * files in order to determine the correct metrics to use when rendering the * formatted object.

* - * @author Pete Townsend */ -public final class AFPFontReader { +public class AFPFontReader { /** * Static logging instance */ - protected static final Log log = LogFactory.getLog(AFPFontReader.class); + protected static final Log LOG = LogFactory.getLog(AFPFontReader.class); /** * Template used to convert lists to arrays. @@ -97,16 +96,36 @@ private final Map/*>*/ codePagesCache = new java.util.HashMap/*>*/(); + + private AFPFontReader() { } + /** + * Factory method for the single-byte implementation of AFPFontReader. + * @return AFPFontReader + */ + public static AFPFontReader getInstance() { + return new AFPFontReader(); + } + + /** + * Factory method for the double-byte (CID Keyed font (Type 0)) implementation of AFPFontReader. + * @return AFPFontReader + */ + public static AFPFontReader getDoubleByteInstance() { + return new DoubleByteAFPFontReader(); + } + + + /** * Returns an InputStream to a given file path and filename * - * @param path the file path + * * @param accessor the resource accessor * @param filename the file name * @return an inputStream * * @throws IOException in the event that an I/O exception of some sort has occurred */ - private InputStream openInputStream(ResourceAccessor accessor, String filename) + protected InputStream openInputStream(ResourceAccessor accessor, String filename) throws IOException { URI uri; try { @@ -124,14 +143,14 @@ * * @param inputStream the inputstream to close */ - private void closeInputStream(InputStream inputStream) { + protected void closeInputStream(InputStream inputStream) { try { if (inputStream != null) { inputStream.close(); } } catch (Exception ex) { // Lets log at least! - log.error(ex.getMessage()); + LOG.error(ex.getMessage()); } } @@ -229,8 +248,9 @@ * the encoding to use for the character decoding * @param accessor the resource accessor * @returns a code page mapping + * @throws IOException if an I/O exception of some sort has occurred. */ - private Map/**/ loadCodePage(String codePage, String encoding, + protected Map/**/ loadCodePage(String codePage, String encoding, ResourceAccessor accessor) throws IOException { // Create the HashMap to store code page information @@ -277,8 +297,10 @@ * * @param structuredFieldReader the structured field reader * @return a class representing the font descriptor + * @throws IOException if an I/O exception of some sort has occurred. */ - private static FontDescriptor processFontDescriptor(StructuredFieldReader structuredFieldReader) + protected static FontDescriptor processFontDescriptor( + StructuredFieldReader structuredFieldReader) throws IOException { byte[] fndData = structuredFieldReader.getNext(FONT_DESCRIPTOR_SF); @@ -290,8 +312,10 @@ * * @param structuredFieldReader * the structured field reader + * @return the FontControl + * @throws IOException if an I/O exception of some sort has occurred. */ - private FontControl processFontControl(StructuredFieldReader structuredFieldReader) + protected FontControl processFontControl(StructuredFieldReader structuredFieldReader) throws IOException { byte[] fncData = structuredFieldReader.getNext(FONT_CONTROL_SF); @@ -320,8 +344,10 @@ * * @param structuredFieldReader * the structured field reader + * @return CharacterSetOrientation array + * @throws IOException if an I/O exception of some sort has occurred. */ - private CharacterSetOrientation[] processFontOrientation( + protected CharacterSetOrientation[] processFontOrientation( StructuredFieldReader structuredFieldReader) throws IOException { byte[] data = structuredFieldReader.getNext(FONT_ORIENTATION_SF); @@ -381,8 +407,9 @@ * the array of CharacterSetOrientation objects * @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 processFontPosition(StructuredFieldReader structuredFieldReader, + protected void processFontPosition(StructuredFieldReader structuredFieldReader, CharacterSetOrientation[] characterSetOrientations, double metricNormalizationFactor) throws IOException { @@ -437,8 +464,9 @@ * @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, + protected void processFontIndex(StructuredFieldReader structuredFieldReader, CharacterSetOrientation cso, Map/**/ codepage, double metricNormalizationFactor) throws IOException { @@ -485,8 +513,8 @@ double diffPercent = 100 * diff / (double)width; //if difference > 2% if (diffPercent > 2) { - if (log.isTraceEnabled()) { - log.trace(gcgiString + ": " + if (LOG.isTraceEnabled()) { + LOG.trace(gcgiString + ": " + a + " + " + b + " + " + c + " = " + (a + b + c) + " but found: " + width); } @@ -516,9 +544,9 @@ cso.setFirstChar(lowest); cso.setLastChar(highest); - if (log.isDebugEnabled() && firstABCMismatch != null) { + 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" + LOG.debug("Font has metrics inconsitencies where A+B+C doesn't equal the" + " character increment. The first such character found: " + firstABCMismatch); } @@ -584,4 +612,115 @@ } } + /** + * Double-byte (CID Keyed font (Type 0)) implementation of AFPFontReader. + */ + private static class DoubleByteAFPFontReader extends AFPFontReader { + + protected Map/**/ loadCodePage(String codePage, String encoding, + ResourceAccessor accessor) throws IOException { + + // Create the HashMap to store code page information + Map/**/ codePages = new java.util.HashMap/**/(); + + InputStream inputStream = null; + try { + inputStream = openInputStream(accessor, codePage.trim()); + + StructuredFieldReader structuredFieldReader + = new StructuredFieldReader(inputStream); + byte[] data; // = structuredFieldReader.getNext(CHARACTER_TABLE_SF); + while ((data = structuredFieldReader.getNext(CHARACTER_TABLE_SF)) != null) { + int position = 0; + + byte[] gcgiBytes = new byte[8]; + byte[] charBytes = new byte[2]; + // Read data, ignoring bytes 0 - 2 + for (int index = 3; index < data.length; index++) { + + if (position < 8) { + // Build the graphic character global identifier key + gcgiBytes[position] = data[index]; + position++; + } else if (position == 9) { + // Set the character + charBytes[0] = data[index]; + position++; + } else if (position == 10) { + position = 0; + // Set the character + charBytes[1] = data[index]; + + String gcgiString = new String(gcgiBytes, + AFPConstants.EBCIDIC_ENCODING); + String charString = new String(charBytes, encoding); + codePages.put(gcgiString, charString); + + } + else { + position++; + } + } + } + } finally { + closeInputStream(inputStream); + } + + return codePages; + } + + protected CharacterSetOrientation[] processFontOrientation( + StructuredFieldReader structuredFieldReader) throws IOException { + + byte[] data = structuredFieldReader.getNext(FONT_ORIENTATION_SF); + + int position = 0; + byte[] fnoData = new byte[26]; + + List orientations = new java.util.ArrayList(); + + // Read data, ignoring bytes 0 - 2 + for (int index = 3; index < data.length; index++) { + // Build the font orientation record + fnoData[position] = data[index]; + position++; + + if (position == 26) { + + position = 0; + + int orientation = 0; + + switch (fnoData[2]) { + case 0x00: + orientation = 0; + break; + case 0x2D: + orientation = 90; + break; + case 0x5A: + orientation = 180; + break; + case (byte) 0x87: + orientation = 270; + break; + default: + System.out.println("ERROR: Oriantation"); + } + + // Em-Space Increment + int em = ((fnoData[14] & 0xFF ) << 8) + (fnoData[15] & 0xFF); + + CharacterSetOrientation cso = new CharacterSetOrientation( + orientation, em); + orientations.add(cso); + + } + } + + return (CharacterSetOrientation[]) orientations + .toArray(EMPTY_CSO_ARRAY); + } + } + } Index: src/java/org/apache/fop/afp/fonts/CharacterSetOrientation.java =================================================================== --- src/java/org/apache/fop/afp/fonts/CharacterSetOrientation.java (revision 900314) +++ src/java/org/apache/fop/afp/fonts/CharacterSetOrientation.java (working copy) @@ -19,6 +19,8 @@ package org.apache.fop.afp.fonts; +import java.util.Arrays; + /** * The IBM Font Object Content Architecture (FOCA) supports presentation * of character shapes by defining their characteristics, which include @@ -58,7 +60,7 @@ /** * The character widths in the character set */ - private int[] charsWidths = new int[256]; + private int[] charsWidths = null; /** * The height of lowercase letters @@ -82,15 +84,33 @@ private int orientation = 0; /** + * Units per em + */ + private int unitsPerEm = -1; + /** * Constructor for the CharacterSetOrientation, the orientation is * expressed as the degrees rotation (i.e 0, 90, 180, 270) * @param orientation the character set orientation */ public CharacterSetOrientation(int orientation) { this.orientation = orientation; + charsWidths = new int[256]; + Arrays.fill(charsWidths, -1); } /** + * Constructor for the CharacterSetOrientation, the orientation is + * expressed as the degrees rotation (i.e 0, 90, 180, 270) + * @param orientation the character set orientation + * @param unitsPerEm the units per em + */ + public CharacterSetOrientation(int orientation, int unitsPerEm) { + this(orientation); + this.unitsPerEm = unitsPerEm; + + } + + /** * Ascender height is the distance from the character baseline to the * top of the character box. A negative ascender height signifies that * all of the graphic character is below the character baseline. For @@ -245,8 +265,10 @@ public void setWidth(int 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; @@ -261,4 +283,13 @@ public void setXHeight(int xHeight) { this.xHeight = xHeight; } + + /** + * + * @return units per em + */ + + public int getUnitsPerEm(){ + return this.unitsPerEm; + } } Index: src/java/org/apache/fop/afp/fonts/DoubleByteFont.java =================================================================== --- src/java/org/apache/fop/afp/fonts/DoubleByteFont.java (revision 0) +++ src/java/org/apache/fop/afp/fonts/DoubleByteFont.java (revision 0) @@ -0,0 +1,178 @@ +/* + * 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: OutlineFont.java 791781 2009-07-07 10:21:07Z cbowditch $ */ + +package org.apache.fop.afp.fonts; + +import java.util.HashSet; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + + +/** + * Implementation of AbstractOutlineFont that supports double-byte fonts (CID Keyed font (Type 0)). + * The width of characters that are not prescribed a width metrics in the font resource use + * a fallback width. The default width is 1 em. A character can be supplied and queried for the + * fallback width of all non-ideograph characters.

+ */ +public class DoubleByteFont extends AbstractOutlineFont { + + private static final Log LOG = LogFactory.getLog(DoubleByteFont.class); + + private static final HashSet IDEOGRAPHIC = new HashSet(); + static { + IDEOGRAPHIC.add(Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS); + IDEOGRAPHIC.add(Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS_SUPPLEMENT); + IDEOGRAPHIC.add(Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS); + IDEOGRAPHIC.add(Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A); + IDEOGRAPHIC.add(Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B); + } + + /* The fallback character is used for assigning width metrics to characters + with no mapped width in font resource*/ + private int fallbackChar; + + // lazy assigned fallbackCharWidth + private int fallbackCharWidth = -1; // -1 to code for not assigned + // lazy assigned emWidth + private int emWidth = -1; // -1 to code for not assigned + // use em as fallback width + private boolean isFallbackEmWidth = true; + + + /** + * Constructor for an double-byte outline font. + * + * @param name + * the name of the font + * @param charSet + * the character set + + */ + public DoubleByteFont(String name, CharacterSet charSet) { + super(name, charSet); + + } + + /** + * Constructor for an outline font. + * + * @param name + * the name of the font + * @param charSet + * the character set + * @param fallbackCharacter + * fallback character for assigning width metrics to characters + * with no mapped width in font resource + */ + public DoubleByteFont(String name, CharacterSet charSet, int fallbackCharacter) { + super(name, charSet); + this.fallbackChar = fallbackCharacter; + this.isFallbackEmWidth = false; + } + + + + + /** {@inheritDoc} */ + public int getWidth(int character, int size) { + int charWidth; + try { + charWidth = charSet.getWidth(character); + } catch (IllegalArgumentException e) { + // We shall try and handle characters that have no mapped width metric in font resource + charWidth = -1; + } + + if (charWidth == -1) { + charWidth = inferCharWidth(character); + } + return charWidth * size; + } + + + + private int inferCharWidth(int character) { + + //Is this character an ideograph? + boolean isIdeographic = false; + Character.UnicodeBlock charBlock = Character.UnicodeBlock.of(character); + if (charBlock == null) { + isIdeographic = false; + } else if (IDEOGRAPHIC.contains(charBlock)) { + isIdeographic = true; + } else { //default + isIdeographic = false; + } + + return isIdeographic ? getEmWidth() : getFallbackWidth(); + } + + + private int getEmWidth() { + + if (emWidth == -1) { + + int width = charSet.getEmSpaceIncrement(); + + LOG.debug("em is " + width); + + if (width == -1) { + LOG.warn("Could not determine 'em' of font " + name + + ".\n\tUsing the fallback width instead.)"); + width = 0; + } + emWidth = width; + } + + return emWidth; + + } + + private int getFallbackWidth() { + //We shall cache the width in fallbackCharWidth + + if (isFallbackEmWidth) { + return getEmWidth(); + } else { + if (fallbackCharWidth == -1) { + // cache the + try { + fallbackCharWidth = charSet.getWidth(fallbackChar); + + if (fallbackCharWidth == -1) { + LOG.warn("No width metric found for the FALLBACK character (codepoint=" + + fallbackChar + ") in font " + name); + fallbackCharWidth = getEmWidth(); + } + } catch (IllegalArgumentException e) { + LOG.warn("No width metric found for the FALLBACK character (codepoint=" + + fallbackChar + ") in font " + name); + fallbackCharWidth = getEmWidth(); + } + LOG.warn("Fallback width of font " + name + " is " + + fallbackCharWidth + "*font-size"); + } + return fallbackCharWidth; + } + + } + + +} Index: src/java/org/apache/fop/afp/fonts/AbstractOutlineFont.java =================================================================== --- src/java/org/apache/fop/afp/fonts/AbstractOutlineFont.java (revision 0) +++ src/java/org/apache/fop/afp/fonts/AbstractOutlineFont.java (revision 0) @@ -0,0 +1,183 @@ +/* + * 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; + + +/** + * A font defined as a set of lines and curves as opposed to a bitmap font. An + * outline font can be scaled to any size and otherwise transformed more easily + * than a bitmap font, and with more attractive results.

+ * + */ +public abstract class AbstractOutlineFont extends AFPFont { + + /** The character set for this font */ + protected CharacterSet charSet = null; + + /** + * Constructor for an outline font. + * + * @param name + * the name of the font + * @param charSet + * the chracter set + */ + public AbstractOutlineFont(String name, CharacterSet charSet) { + super(name); + this.charSet = charSet; + } + + /** + * Get the character set metrics. + * + * @return the character set + */ + public CharacterSet getCharacterSet() { + + return charSet; + + } + + /** + * Get the character set metrics. + * @param size ignored + * @return the character set + */ + public CharacterSet getCharacterSet(int size) { + + 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 + * used to denote the part of the letter extending above the x-height. + * + * @param size the font size (in mpt) + * @return the ascender for the given size + */ + public int getAscender(int size) { + return charSet.getAscender() * size; + } + + /** + * Obtains the height of capital letters for the specified point size. + * + * @param size the font size (in mpt) + * @return the cap height for the given size + */ + public int getCapHeight(int size) { + return charSet.getCapHeight() * size; + } + + /** + * The descender is the part of a lowercase letter that extends below the + * base line, such as "g", "j", or "p". Also used to denote the part of the + * letter extending below the base line. + * + * @param size the font size (in mpt) + * @return the descender for the given size + */ + public int getDescender(int size) { + return charSet.getDescender() * size; + } + + /** + * The "x-height" (the height of the letter "x"). + * + * @param size the font size (in mpt) + * @return the x height for the given size + */ + public int getXHeight(int size) { + 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(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) { + return charSet.hasChar(c); + } + + /** + * Map a Unicode character to a code point in the font. + * @param c character to map + * @return the mapped character + */ + public char mapChar(char c) { + return charSet.mapChar(c); + } + + /** {@inheritDoc} */ + public String getEncodingName() { + return charSet.getEncoding(); + } + +} \ No newline at end of file