Index: /home/peter/fop_apache/fop_afpfonts/conf/fop.xconf =================================================================== --- /home/peter/fop_apache/fop_afpfonts/conf/fop.xconf (revision 900314) +++ /home/peter/fop_apache/fop_afpfonts/conf/fop.xconf (working copy) @@ -16,7 +16,7 @@ - . + /home/peter/resources/fop/afp_2byte 72 @@ -396,6 +396,37 @@ + + + + Index: /home/peter/fop_apache/fop_afpfonts/src/java/org/apache/fop/render/afp/AFPRendererConfigurator.java =================================================================== --- /home/peter/fop_apache/fop_afpfonts/src/java/org/apache/fop/render/afp/AFPRendererConfigurator.java (revision 900314) +++ /home/peter/fop_apache/fop_afpfonts/src/java/org/apache/fop/render/afp/AFPRendererConfigurator.java (working copy) @@ -30,10 +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.CharacterSet; -import org.apache.fop.afp.fonts.FopCharacterSet; +import org.apache.fop.afp.fonts.CharacterSetBuilder; +import org.apache.fop.afp.fonts.DoubleByteFont; import org.apache.fop.afp.fonts.OutlineFont; import org.apache.fop.afp.fonts.RasterFont; import org.apache.fop.afp.util.DefaultFOPResourceAccessor; @@ -57,7 +59,7 @@ * AFP Renderer configurator */ public class AFPRendererConfigurator extends PrintRendererConfigurator - implements IFDocumentHandlerConfigurator { +implements IFDocumentHandlerConfigurator { /** * Default constructor @@ -69,7 +71,7 @@ } private AFPFontInfo buildFont(Configuration fontCfg, String fontPath) - throws ConfigurationException { + throws ConfigurationException { FontManager fontManager = this.userAgent.getFactory().getFontManager(); @@ -127,11 +129,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 +185,28 @@ 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)); + font.addCharacterSet(sizeMpt, + CharacterSetBuilder.getInstance() + .build(characterset, codepage, encoding, 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)); + font.addCharacterSet(sizeMpt, CharacterSetBuilder.getInstance() + .build(characterset, codepage, encoding, 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); + characterSet = CharacterSetBuilder.getInstance() + .build(characterset, codepage, encoding, 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); + characterSet = CharacterSetBuilder.getInstance().build( + characterset, codepage, encoding, 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 = CharacterSetBuilder.getDoubleByteInstance() + .build(characterset, codepage, encoding, accessor); + // 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: /home/peter/fop_apache/fop_afpfonts/src/java/org/apache/fop/afp/modca/MapCodedFont.java =================================================================== --- /home/peter/fop_apache/fop_afpfonts/src/java/org/apache/fop/afp/modca/MapCodedFont.java (revision 900314) +++ /home/peter/fop_apache/fop_afpfonts/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: /home/peter/fop_apache/fop_afpfonts/src/java/org/apache/fop/afp/fonts/OutlineFont.java =================================================================== --- /home/peter/fop_apache/fop_afpfonts/src/java/org/apache/fop/afp/fonts/OutlineFont.java (revision 900314) +++ /home/peter/fop_apache/fop_afpfonts/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: /home/peter/fop_apache/fop_afpfonts/src/java/org/apache/fop/afp/fonts/CharacterSet.java =================================================================== --- /home/peter/fop_apache/fop_afpfonts/src/java/org/apache/fop/afp/fonts/CharacterSet.java (revision 900314) +++ /home/peter/fop_apache/fop_afpfonts/src/java/org/apache/fop/afp/fonts/CharacterSet.java (working copy) @@ -20,7 +20,6 @@ package org.apache.fop.afp.fonts; import java.io.File; -import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.URI; import java.nio.ByteBuffer; @@ -60,7 +59,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"; @@ -122,11 +121,11 @@ * @param name the character set name * @param accessor the resource accessor to load resource with */ - public CharacterSet(String codePage, String encoding, String name, ResourceAccessor accessor) { + CharacterSet(String codePage, String encoding, String name, ResourceAccessor accessor) { 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); } @@ -192,7 +191,7 @@ * @return the ascender value in millipoints */ public int getAscender() { - load(); + return getCharacterSetOrientation().getAscender(); } @@ -204,7 +203,7 @@ * @return the cap height value in millipoints */ public int getCapHeight() { - load(); + return getCharacterSetOrientation().getCapHeight(); } @@ -216,7 +215,7 @@ * @return the descender value in millipoints */ public int getDescender() { - load(); + return getCharacterSetOrientation().getDescender(); } @@ -226,7 +225,7 @@ * @return the first character in the character set */ public int getFirstChar() { - load(); + return getCharacterSetOrientation().getFirstChar(); } @@ -236,7 +235,7 @@ * @return the last character in the character set */ public int getLastChar() { - load(); + return getCharacterSetOrientation().getLastChar(); } @@ -254,7 +253,7 @@ * @return the widths of all characters */ public int[] getWidths() { - load(); + return getCharacterSetOrientation().getWidths(); } @@ -264,7 +263,7 @@ * @return the typical height of characters */ public int getXHeight() { - load(); + return getCharacterSetOrientation().getXHeight(); } @@ -276,28 +275,12 @@ * @return the width of the character */ public int getWidth(int character) { - load(); + return getCharacterSetOrientation().getWidth(character); } - /** - * Lazy creation of the character metrics, the afp font file will only - * be processed on a method call requiring the metric information. - */ - 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); - throw new RuntimeException(e.getMessage()); - } - } - } + /** * Returns the AFP character set identifier * @@ -318,7 +301,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 +400,13 @@ return c; } + /** + * + * @return the number units per em + */ + public int getEmSpaceIncrement() { + + return getCharacterSetOrientation().getUnitsPerEm(); + } + } Index: /home/peter/fop_apache/fop_afpfonts/src/java/org/apache/fop/afp/fonts/CharacterSetBuilder.java =================================================================== --- /home/peter/fop_apache/fop_afpfonts/src/java/org/apache/fop/afp/fonts/CharacterSetBuilder.java (revision 900314) +++ /home/peter/fop_apache/fop_afpfonts/src/java/org/apache/fop/afp/fonts/CharacterSetBuilder.java (working copy) @@ -33,10 +33,15 @@ import org.apache.fop.afp.AFPConstants; import org.apache.fop.afp.util.ResourceAccessor; import org.apache.fop.afp.util.StructuredFieldReader; +import org.apache.fop.fonts.Typeface; /** - * The AFPFontReader is responsible for reading the font attributes from binary - * code page files and the character set metric files. In IBM font structure, a + * The CharacterSetBuilder is responsible building the a CharacterSet instance that holds + * the font metric data. The data is either read from disk and passed to a CharacterSet (*) + * or a FopCharacterSet is instantiated that is composed of a Typeface instance configured + * with this data.

+ * -*- For referenced fonts CharacterSetBuilder is responsible for reading the font attributes + * from binary code page files and the character set metric files. In IBM font structure, a * code page maps each character of text to the characters in a character set. * Each character is translated into a code point. When the character is * printed, each code point is matched to a character ID on the code page @@ -49,14 +54,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 CharacterSetBuilder { /** * Static logging instance */ - protected static final Log log = LogFactory.getLog(AFPFontReader.class); + protected static final Log LOG = LogFactory.getLog(CharacterSetBuilder.class); /** * Template used to convert lists to arrays. @@ -97,16 +101,36 @@ private final Map/*>*/ codePagesCache = new java.util.HashMap/*>*/(); + + private CharacterSetBuilder() { } + /** + * Factory method for the single-byte implementation of AFPFontReader. + * @return AFPFontReader + */ + public static CharacterSetBuilder getInstance() { + return new CharacterSetBuilder(); + } + + /** + * Factory method for the double-byte (CID Keyed font (Type 0)) implementation of AFPFontReader. + * @return AFPFontReader + */ + public static CharacterSetBuilder getDoubleByteInstance() { + return new DoubleByteLoader(); + } + + + /** * 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 +148,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()); } } @@ -139,12 +163,16 @@ * Load the font details and metrics into the CharacterSetMetric object, * this will use the actual afp code page and character set files to load * the object with the necessary metrics. - * - * @param characterSet the CharacterSetMetric object to populate - * @throws IOException if an I/O exception of some sort has occurred. + * @param codePageName name of the code page file + * @param encoding + * @throws RuntimeException if an I/O exception of some sort has occurred. */ - public void loadCharacterSetMetric(CharacterSet characterSet) throws IOException { + public CharacterSet build(String characterSetName, String codePageName, + String encoding, ResourceAccessor accessor) { + CharacterSet characterSet = new CharacterSet( + codePageName, encoding, characterSetName, accessor); + InputStream inputStream = null; try { @@ -154,24 +182,18 @@ * information to map the unicode character id to the graphic * chracter global identifier. */ - String codePageId = new String(characterSet.getCodePage()); - ResourceAccessor accessor = characterSet.getResourceAccessor(); + Map/**/ codePage - = (Map/**/)codePagesCache.get(codePageId); + = (Map/**/)codePagesCache.get(codePageName); if (codePage == null) { - codePage = loadCodePage(codePageId, characterSet.getEncoding(), accessor); - codePagesCache.put(codePageId, codePage); + codePage = loadCodePage(codePageName, encoding, accessor); + codePagesCache.put(codePageName, codePage); } - /** - * Load the character set metric information, no need to cache this - * information as it should be cached by the objects that wish to - * load character set metric information. - */ - final String characterSetName = characterSet.getName(); + inputStream = openInputStream(accessor, characterSetName); StructuredFieldReader structuredFieldReader = new StructuredFieldReader(inputStream); @@ -208,18 +230,45 @@ characterSet.addCharacterSetOrientation(characterSetOrientations[i]); } } else { - throw new IOException( - "Failed to read font control structured field in character set " - + characterSetName); + + String msg = "Failed to load the character set metrics for code page " + + codePageName; + LOG.error(msg); + throw new RuntimeException("Failed to read font control structured field" + + "in character set " + characterSetName); + } - } finally { + } catch(IOException e){ + String msg = "Failed to load the character set metrics for code page " + codePageName; + LOG.error(msg); + throw new RuntimeException("Failed to read font control structured field" + + "in character set " + characterSetName); + } + finally { + closeInputStream(inputStream); } + return characterSet; + } /** + * Load the font details and metrics into the CharacterSetMetric object, + * this will use the actual afp code page and character set files to load + * the object with the necessary metrics. + * + * @param characterSet the CharacterSetMetric object to populate + */ + public CharacterSet build(String characterSetName, String codePageName, + String encoding, Typeface typeface) { + return new FopCharacterSet(codePageName, encoding, characterSetName, typeface); + } + + + + /** * 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'. * @@ -229,8 +278,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 +327,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 +342,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 +374,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 +437,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 +494,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 +543,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 +574,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 +642,115 @@ } } + /** + * Double-byte (CID Keyed font (Type 0)) implementation of AFPFontReader. + */ + private static class DoubleByteLoader extends CharacterSetBuilder { + + 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: /home/peter/fop_apache/fop_afpfonts/src/java/org/apache/fop/afp/fonts/AFPFontReader.java =================================================================== --- /home/peter/fop_apache/fop_afpfonts/src/java/org/apache/fop/afp/fonts/AFPFontReader.java (revision 900314) +++ /home/peter/fop_apache/fop_afpfonts/src/java/org/apache/fop/afp/fonts/AFPFontReader.java (working copy) @@ -1,587 +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.afp.fonts; - -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.net.URI; -import java.net.URISyntaxException; -import java.util.List; -import java.util.Map; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.apache.fop.afp.AFPConstants; -import org.apache.fop.afp.util.ResourceAccessor; -import org.apache.fop.afp.util.StructuredFieldReader; - -/** - * The AFPFontReader is responsible for reading the font attributes from binary - * code page files and the character set metric files. In IBM font structure, a - * code page maps each character of text to the characters in a character set. - * Each character is translated into a code point. When the character is - * printed, each code point is matched to a character ID on the code page - * specified. The character ID is then matched to the image (raster pattern or - * outline pattern) of the character in the character set specified. The image - * in the character set is the image that is printed in the document. To be a - * valid code page for a particular character set, all character IDs in the code - * page must be included in that character set.

This class will read the - * font information from the binary code page files and character set metric - * files in order to determine the correct metrics to use when rendering the - * formatted object.

- * - * @author Pete Townsend - */ -public final class AFPFontReader { - - /** - * Static logging instance - */ - protected static final Log log = LogFactory.getLog(AFPFontReader.class); - - /** - * Template used to convert lists to arrays. - */ - private static final CharacterSetOrientation[] EMPTY_CSO_ARRAY = new CharacterSetOrientation[0]; - - /** Codepage MO:DCA structured field. */ - private static final byte[] CODEPAGE_SF = new byte[] { - (byte) 0xD3, (byte) 0xA8, (byte) 0x87}; - - /** Character table MO:DCA structured field. */ - private static final byte[] CHARACTER_TABLE_SF = new byte[] { - (byte) 0xD3, (byte) 0x8C, (byte) 0x87}; - - /** Font descriptor MO:DCA structured field. */ - private static final byte[] FONT_DESCRIPTOR_SF = new byte[] { - (byte) 0xD3, (byte) 0xA6, (byte) 0x89 }; - - /** Font control MO:DCA structured field. */ - private static final byte[] FONT_CONTROL_SF = new byte[] { - (byte) 0xD3, (byte) 0xA7, (byte) 0x89 }; - - /** Font orientation MO:DCA structured field. */ - private static final byte[] FONT_ORIENTATION_SF = new byte[] { - (byte) 0xD3, (byte) 0xAE, (byte) 0x89 }; - - /** Font position MO:DCA structured field. */ - private static final byte[] FONT_POSITION_SF = new byte[] { - (byte) 0xD3, (byte) 0xAC, (byte) 0x89 }; - - /** Font index MO:DCA structured field. */ - private static final byte[] FONT_INDEX_SF = new byte[] { - (byte) 0xD3, (byte) 0x8C, (byte) 0x89 }; - - /** - * The collection of code pages - */ - private final Map/*>*/ codePagesCache - = new java.util.HashMap/*>*/(); - - /** - * Returns an InputStream to a given file path and filename - * - * @param path the file path - * @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) - throws IOException { - URI uri; - try { - uri = new URI(filename.trim()); - } catch (URISyntaxException e) { - throw new FileNotFoundException("Invalid filename: " - + filename + " (" + e.getMessage() + ")"); - } - InputStream inputStream = accessor.createInputStream(uri); - return inputStream; - } - - /** - * Closes the inputstream - * - * @param inputStream the inputstream to close - */ - private void closeInputStream(InputStream inputStream) { - try { - if (inputStream != null) { - inputStream.close(); - } - } catch (Exception ex) { - // Lets log at least! - log.error(ex.getMessage()); - } - } - - /** - * Load the font details and metrics into the CharacterSetMetric object, - * this will use the actual afp code page and character set files to load - * the object with the necessary metrics. - * - * @param characterSet the CharacterSetMetric object to populate - * @throws IOException if an I/O exception of some sort has occurred. - */ - public void loadCharacterSetMetric(CharacterSet characterSet) throws IOException { - - InputStream inputStream = null; - - try { - - /** - * Get the code page which contains the character mapping - * information to map the unicode character id to the graphic - * chracter global identifier. - */ - String codePageId = new String(characterSet.getCodePage()); - ResourceAccessor accessor = characterSet.getResourceAccessor(); - - Map/**/ codePage - = (Map/**/)codePagesCache.get(codePageId); - - if (codePage == null) { - codePage = loadCodePage(codePageId, characterSet.getEncoding(), accessor); - codePagesCache.put(codePageId, codePage); - } - - /** - * Load the character set metric information, no need to cache this - * information as it should be cached by the objects that wish to - * load character set metric information. - */ - final String characterSetName = characterSet.getName(); - - inputStream = openInputStream(accessor, characterSetName); - - StructuredFieldReader structuredFieldReader = new StructuredFieldReader(inputStream); - - // Process D3A689 Font Descriptor - FontDescriptor fontDescriptor = processFontDescriptor(structuredFieldReader); - characterSet.setNominalVerticalSize(fontDescriptor.getNominalFontSizeInMillipoints()); - - // Process D3A789 Font Control - FontControl fontControl = processFontControl(structuredFieldReader); - - if (fontControl != null) { - //process D3AE89 Font Orientation - CharacterSetOrientation[] characterSetOrientations - = processFontOrientation(structuredFieldReader); - - int metricNormalizationFactor; - if (fontControl.isRelative()) { - metricNormalizationFactor = 1; - } else { - int dpi = fontControl.getDpi(); - metricNormalizationFactor = 1000 * 72000 - / fontDescriptor.getNominalFontSizeInMillipoints() / dpi; - } - - //process D3AC89 Font Position - processFontPosition(structuredFieldReader, characterSetOrientations, - metricNormalizationFactor); - - //process D38C89 Font Index (per orientation) - for (int i = 0; i < characterSetOrientations.length; i++) { - processFontIndex(structuredFieldReader, - characterSetOrientations[i], codePage, metricNormalizationFactor); - characterSet.addCharacterSetOrientation(characterSetOrientations[i]); - } - } else { - throw new IOException( - "Failed to read font control structured field in character set " - + characterSetName); - } - - } finally { - closeInputStream(inputStream); - } - - } - - /** - * 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'. - * - * @param codePage - * the code page identifier - * @param encoding - * the encoding to use for the character decoding - * @param accessor the resource accessor - * @returns a code page mapping - */ - private 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); - - int position = 0; - byte[] gcgiBytes = new byte[8]; - byte[] charBytes = new byte[1]; - - // 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) { - position = 0; - // Set the character - charBytes[0] = 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; - } - - /** - * Process the font descriptor details using the structured field reader. - * - * @param structuredFieldReader the structured field reader - * @return a class representing the font descriptor - */ - private static FontDescriptor processFontDescriptor(StructuredFieldReader structuredFieldReader) - throws IOException { - - byte[] fndData = structuredFieldReader.getNext(FONT_DESCRIPTOR_SF); - return new FontDescriptor(fndData); - } - - /** - * Process the font control details using the structured field reader. - * - * @param structuredFieldReader - * the structured field reader - */ - private FontControl processFontControl(StructuredFieldReader structuredFieldReader) - throws IOException { - - byte[] fncData = structuredFieldReader.getNext(FONT_CONTROL_SF); - - FontControl fontControl = null; - if (fncData != null) { - fontControl = new FontControl(); - - if (fncData[7] == (byte) 0x02) { - fontControl.setRelative(true); - } - int metricResolution = getUBIN(fncData, 9); - if (metricResolution == 1000) { - //Special case: 1000 units per em (rather than dpi) - fontControl.setUnitsPerEm(1000); - } else { - fontControl.setDpi(metricResolution / 10); - } - } - return fontControl; - } - - /** - * Process the font orientation details from using the structured field - * reader. - * - * @param structuredFieldReader - * the structured field reader - */ - private 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"); - } - - CharacterSetOrientation cso = new CharacterSetOrientation( - orientation); - orientations.add(cso); - - } - } - - return (CharacterSetOrientation[]) orientations - .toArray(EMPTY_CSO_ARRAY); - } - - /** - * Populate the CharacterSetOrientation object in the suplied array with the - * font position details using the supplied structured field reader. - * - * @param structuredFieldReader - * the structured field reader - * @param characterSetOrientations - * the array of CharacterSetOrientation objects - * @param metricNormalizationFactor factor to apply to the metrics to get normalized - * font metric values - */ - private void processFontPosition(StructuredFieldReader structuredFieldReader, - CharacterSetOrientation[] characterSetOrientations, double metricNormalizationFactor) - throws IOException { - - byte[] data = structuredFieldReader.getNext(FONT_POSITION_SF); - - int position = 0; - byte[] fpData = new byte[26]; - - int characterSetOrientationIndex = 0; - - // Read data, ignoring bytes 0 - 2 - for (int index = 3; index < data.length; index++) { - if (position < 22) { - // Build the font orientation record - fpData[position] = data[index]; - 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)); - } - } 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 - */ - private void processFontIndex(StructuredFieldReader structuredFieldReader, - CharacterSetOrientation cso, Map/**/ codepage, - double metricNormalizationFactor) - throws IOException { - - byte[] data = structuredFieldReader.getNext(FONT_INDEX_SF); - - int position = 0; - - byte[] gcgid = new byte[8]; - byte[] fiData = new byte[20]; - - int lowest = 255; - int highest = 0; - String firstABCMismatch = null; - - // Read data, ignoring bytes 0 - 2 - for (int index = 3; index < data.length; index++) { - if (position < 8) { - gcgid[position] = data[index]; - position++; - } else if (position < 27) { - fiData[position - 8] = data[index]; - position++; - } else if (position == 27) { - - fiData[position - 8] = data[index]; - - position = 0; - - String gcgiString = new String(gcgid, AFPConstants.EBCIDIC_ENCODING); - - String idx = (String) codepage.get(gcgiString); - - if (idx != null) { - - int cidx = idx.charAt(0); - int width = getUBIN(fiData, 0); - 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; - //if difference > 2% - if (diffPercent > 2) { - if (log.isTraceEnabled()) { - log.trace(gcgiString + ": " - + a + " + " + b + " + " + c + " = " + (a + b + c) - + " but found: " + width); - } - if (firstABCMismatch == null) { - firstABCMismatch = gcgiString; - } - } - } - - if (cidx < lowest) { - lowest = cidx; - } - - if (cidx > highest) { - highest = cidx; - } - - int normalizedWidth = (int)Math.round(width * metricNormalizationFactor); - - cso.setWidth(cidx, normalizedWidth); - - } - - } - } - - 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" - + " character increment. The first such character found: " - + firstABCMismatch); - } - } - - private static int getUBIN(byte[] data, int start) { - return ((data[start] & 0xFF) << 8) + (data[start + 1] & 0xFF); - } - - private static int getSBIN(byte[] data, int start) { - int ubin = ((data[start] & 0xFF) << 8) + (data[start + 1] & 0xFF); - if ((ubin & 0x8000) != 0) { - //extend sign - return ubin | 0xFFFF0000; - } else { - return ubin; - } - } - - private class FontControl { - - private int dpi; - private int unitsPerEm; - - private boolean isRelative = false; - - public int getDpi() { - return dpi; - } - - public void setDpi(int i) { - dpi = i; - } - - public int getUnitsPerEm() { - return this.unitsPerEm; - } - - public void setUnitsPerEm(int value) { - this.unitsPerEm = value; - } - - public boolean isRelative() { - return isRelative; - } - - public void setRelative(boolean b) { - isRelative = b; - } - } - - private static class FontDescriptor { - - private byte[] data; - - public FontDescriptor(byte[] data) { - this.data = data; - } - - public int getNominalFontSizeInMillipoints() { - int nominalFontSize = 100 * getUBIN(data, 39); - return nominalFontSize; - } - } - -} Index: /home/peter/fop_apache/fop_afpfonts/src/java/org/apache/fop/afp/fonts/CharacterSetOrientation.java =================================================================== --- /home/peter/fop_apache/fop_afpfonts/src/java/org/apache/fop/afp/fonts/CharacterSetOrientation.java (revision 900314) +++ /home/peter/fop_apache/fop_afpfonts/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: /home/peter/fop_apache/fop_afpfonts/src/java/org/apache/fop/afp/fonts/DoubleByteFont.java =================================================================== --- /home/peter/fop_apache/fop_afpfonts/src/java/org/apache/fop/afp/fonts/DoubleByteFont.java (revision 0) +++ /home/peter/fop_apache/fop_afpfonts/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: /home/peter/fop_apache/fop_afpfonts/src/java/org/apache/fop/afp/fonts/AbstractOutlineFont.java =================================================================== --- /home/peter/fop_apache/fop_afpfonts/src/java/org/apache/fop/afp/fonts/AbstractOutlineFont.java (revision 0) +++ /home/peter/fop_apache/fop_afpfonts/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