diff options
author | Jeremias Maerki <jeremias@apache.org> | 2010-01-21 17:37:07 +0000 |
---|---|---|
committer | Jeremias Maerki <jeremias@apache.org> | 2010-01-21 17:37:07 +0000 |
commit | bb2844e0645707a666fcf43e7fbcb32c8daca49c (patch) | |
tree | f4f06a9011c874cac381d97942cac868e0ff15f8 /src | |
parent | ab6e2cc40a0c0dde6b98eec000ab94d1c7e35cb1 (diff) | |
download | xmlgraphics-fop-bb2844e0645707a666fcf43e7fbcb32c8daca49c.tar.gz xmlgraphics-fop-bb2844e0645707a666fcf43e7fbcb32c8daca49c.zip |
Bugzilla #48567:
Initial support for CID-keyed double-byte fonts (Type 0) in AFP output.
Submitted by: Peter Hancock <peter.hancock.at.gmail.com>
Patch modified by jeremias:
- as discussed: removed fallback character code
- as discussed: changed "double-byte" to "CIDKeyed" for the font type.
- some cosmetic changes
- removed some dead code and commented code.
git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@901793 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'src')
-rw-r--r-- | src/documentation/content/xdocs/trunk/output.xml | 15 | ||||
-rw-r--r-- | src/java/org/apache/fop/afp/fonts/AbstractOutlineFont.java | 177 | ||||
-rw-r--r-- | src/java/org/apache/fop/afp/fonts/CharacterSet.java | 62 | ||||
-rw-r--r-- | src/java/org/apache/fop/afp/fonts/CharacterSetBuilder.java (renamed from src/java/org/apache/fop/afp/fonts/AFPFontReader.java) | 240 | ||||
-rw-r--r-- | src/java/org/apache/fop/afp/fonts/CharacterSetOrientation.java | 47 | ||||
-rw-r--r-- | src/java/org/apache/fop/afp/fonts/DoubleByteFont.java | 90 | ||||
-rw-r--r-- | src/java/org/apache/fop/afp/fonts/OutlineFont.java | 159 | ||||
-rw-r--r-- | src/java/org/apache/fop/afp/modca/MapCodedFont.java | 27 | ||||
-rw-r--r-- | src/java/org/apache/fop/render/afp/AFPRendererConfigurator.java | 97 |
9 files changed, 635 insertions, 279 deletions
diff --git a/src/documentation/content/xdocs/trunk/output.xml b/src/documentation/content/xdocs/trunk/output.xml index 01af40a56..54ed357a0 100644 --- a/src/documentation/content/xdocs/trunk/output.xml +++ b/src/documentation/content/xdocs/trunk/output.xml @@ -548,10 +548,11 @@ out = proc.getOutputStream();]]></source> <!-- AFP Renderer --> ... </renderer>]]></source> - <p>There are 3 font configuration variants supported:</p> + <p>There are 4 font configuration variants supported:</p> <ol> <li>IBM Raster fonts</li> <li>IBM Outline fonts</li> + <li>IBM CID-keyed (Type 0) fonts</li> <li>FOP built-in Base14 fonts</li> </ol> <p>A typical raster font configuration looks like:</p> @@ -600,6 +601,18 @@ out = proc.getOutputStream();]]></source> supported for the time being, but you should move to using the more flexible "base-uri" attribute so you can profit from the power of URI resolvers. </note> + <p>A CID-keyed font (Type 0, double-byte outline font) configuration is much the same as an outline font. + However, the characterset definition is now required within the afp-font element.</p> +<source><![CDATA[ <font> + <afp-font type="CIDKeyed" characterset="CZJHMNU" + codepage="T1120000" encoding="UnicodeBigUnmarked" + base-uri="file:/fonts/ibm" /> + <font-triplet name="J-Heisei Mincho" style="normal" weight="normal" /> + </font> +]]></source> + <p> +Note that the value of the encoding attribute in the example is the double-byte encoding 'UnicodeBigUnmarked' (UTF-16BE). + </p> <p>Experimentation has shown that the font metrics for the FOP built-in Base14 fonts are actually very similar to some of the IBM outline and raster fonts. In cases were the IBM font files are not available the base-uri attribute in the afp-font element can be replaced by a base14-font attribute diff --git a/src/java/org/apache/fop/afp/fonts/AbstractOutlineFont.java b/src/java/org/apache/fop/afp/fonts/AbstractOutlineFont.java new file mode 100644 index 000000000..2061e9d82 --- /dev/null +++ b/src/java/org/apache/fop/afp/fonts/AbstractOutlineFont.java @@ -0,0 +1,177 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.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 diff --git a/src/java/org/apache/fop/afp/fonts/CharacterSet.java b/src/java/org/apache/fop/afp/fonts/CharacterSet.java index 48d5f4f30..555d70158 100644 --- a/src/java/org/apache/fop/afp/fonts/CharacterSet.java +++ b/src/java/org/apache/fop/afp/fonts/CharacterSet.java @@ -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 @@ import org.apache.fop.afp.util.StringUtils; 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"; @@ -86,9 +85,6 @@ public class CharacterSet { /** The path to the installed fonts */ private ResourceAccessor accessor; - /** Indicator as to whether to metrics have been loaded */ - private boolean isMetricsLoaded = false; - /** The current orientation (currently only 0 is supported by FOP) */ private final String currentOrientation = "0"; @@ -122,11 +118,11 @@ public class CharacterSet { * @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 +188,7 @@ public class CharacterSet { * @return the ascender value in millipoints */ public int getAscender() { - load(); + return getCharacterSetOrientation().getAscender(); } @@ -204,7 +200,7 @@ public class CharacterSet { * @return the cap height value in millipoints */ public int getCapHeight() { - load(); + return getCharacterSetOrientation().getCapHeight(); } @@ -216,7 +212,7 @@ public class CharacterSet { * @return the descender value in millipoints */ public int getDescender() { - load(); + return getCharacterSetOrientation().getDescender(); } @@ -226,7 +222,7 @@ public class CharacterSet { * @return the first character in the character set */ public int getFirstChar() { - load(); + return getCharacterSetOrientation().getFirstChar(); } @@ -236,7 +232,7 @@ public class CharacterSet { * @return the last character in the character set */ public int getLastChar() { - load(); + return getCharacterSetOrientation().getLastChar(); } @@ -254,7 +250,7 @@ public class CharacterSet { * @return the widths of all characters */ public int[] getWidths() { - load(); + return getCharacterSetOrientation().getWidths(); } @@ -264,7 +260,7 @@ public class CharacterSet { * @return the typical height of characters */ public int getXHeight() { - load(); + return getCharacterSetOrientation().getXHeight(); } @@ -276,27 +272,11 @@ public class CharacterSet { * @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 +298,7 @@ public class CharacterSet { 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 +397,20 @@ public class CharacterSet { return c; } + /** + * Returns the increment for an space. + * @return the space increment + */ + public int getSpaceIncrement() { + return getCharacterSetOrientation().getSpaceIncrement(); + } + + /** + * Returns the increment for an em space. + * @return the em space increment + */ + public int getEmSpaceIncrement() { + return getCharacterSetOrientation().getEmSpaceIncrement(); + } + } diff --git a/src/java/org/apache/fop/afp/fonts/AFPFontReader.java b/src/java/org/apache/fop/afp/fonts/CharacterSetBuilder.java index 25ea15278..ea9703a08 100644 --- a/src/java/org/apache/fop/afp/fonts/AFPFontReader.java +++ b/src/java/org/apache/fop/afp/fonts/CharacterSetBuilder.java @@ -33,10 +33,15 @@ 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; +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.<p/> + * -*- 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 @@ import org.apache.fop.afp.util.StructuredFieldReader; * files in order to determine the correct metrics to use when rendering the * formatted object. <p/> * - * @author <a href="mailto:pete@townsend.uk.com">Pete Townsend </a> */ -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 @@ public final class AFPFontReader { private final Map/*<String, Map<String, String>>*/ codePagesCache = new java.util.HashMap/*<String, Map<String, String>>*/(); + + 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 @@ public final class AFPFontReader { * * @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,11 +163,15 @@ public final class AFPFontReader { * 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; @@ -154,24 +182,15 @@ public final class AFPFontReader { * information to map the unicode character id to the graphic * chracter global identifier. */ - String codePageId = new String(characterSet.getCodePage()); - ResourceAccessor accessor = characterSet.getResourceAccessor(); Map/*<String,String>*/ codePage - = (Map/*<String,String>*/)codePagesCache.get(codePageId); + = (Map/*<String,String>*/)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,15 +227,40 @@ public final class AFPFontReader { 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); } /** @@ -229,8 +273,9 @@ public final class AFPFontReader { * 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/*<String,String>*/ loadCodePage(String codePage, String encoding, + protected Map/*<String,String>*/ loadCodePage(String codePage, String encoding, ResourceAccessor accessor) throws IOException { // Create the HashMap to store code page information @@ -277,8 +322,10 @@ public final class AFPFontReader { * * @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 +337,10 @@ public final class AFPFontReader { * * @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 +369,10 @@ public final class AFPFontReader { * * @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); @@ -341,27 +392,15 @@ public final class AFPFontReader { 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"); - } + int orientation = determineOrientation(fnoData[2]); + // Space Increment + int space = ((fnoData[8] & 0xFF ) << 8) + (fnoData[9] & 0xFF); + // Em-Space Increment + int em = ((fnoData[14] & 0xFF ) << 8) + (fnoData[15] & 0xFF); - CharacterSetOrientation cso = new CharacterSetOrientation( - orientation); + CharacterSetOrientation cso = new CharacterSetOrientation(orientation); + cso.setSpaceIncrement(space); + cso.setEmSpaceIncrement(em); orientations.add(cso); } @@ -381,8 +420,9 @@ public final class AFPFontReader { * 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 +477,9 @@ public final class AFPFontReader { * @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/*<String,String>*/ codepage, double metricNormalizationFactor) throws IOException { @@ -483,10 +524,9 @@ public final class AFPFontReader { 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 + ": " + if (LOG.isTraceEnabled()) { + LOG.trace(gcgiString + ": " + a + " + " + b + " + " + c + " = " + (a + b + c) + " but found: " + width); } @@ -516,9 +556,9 @@ public final class AFPFontReader { 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 +624,84 @@ public final class AFPFontReader { } } + /** + * Double-byte (CID Keyed font (Type 0)) implementation of AFPFontReader. + */ + private static class DoubleByteLoader extends CharacterSetBuilder { + + protected Map/*<String,String>*/ loadCodePage(String codePage, String encoding, + ResourceAccessor accessor) throws IOException { + + // Create the HashMap to store code page information + Map/*<String,String>*/ codePages = new java.util.HashMap/*<String,String>*/(); + + InputStream inputStream = null; + try { + inputStream = openInputStream(accessor, codePage.trim()); + + StructuredFieldReader structuredFieldReader + = new StructuredFieldReader(inputStream); + byte[] data; + 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; + } + + } + + private static int determineOrientation(byte orientation) { + int degrees = 0; + + switch (orientation) { + case 0x00: + degrees = 0; + break; + case 0x2D: + degrees = 90; + break; + case 0x5A: + degrees = 180; + break; + case (byte) 0x87: + degrees = 270; + break; + default: + throw new IllegalStateException("Invalid orientation: " + orientation); + } + return degrees; + } } diff --git a/src/java/org/apache/fop/afp/fonts/CharacterSetOrientation.java b/src/java/org/apache/fop/afp/fonts/CharacterSetOrientation.java index 1946fd4a4..db0908acb 100644 --- a/src/java/org/apache/fop/afp/fonts/CharacterSetOrientation.java +++ b/src/java/org/apache/fop/afp/fonts/CharacterSetOrientation.java @@ -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 @@ public class CharacterSetOrientation { /** * The character widths in the character set */ - private int[] charsWidths = new int[256]; + private int[] charsWidths = null; /** * The height of lowercase letters @@ -81,6 +83,12 @@ public class CharacterSetOrientation { */ private int orientation = 0; + /** space increment */ + private int spaceIncrement; + /** em space increment */ + private int emSpaceIncrement = -1; + + /** * Constructor for the CharacterSetOrientation, the orientation is * expressed as the degrees rotation (i.e 0, 90, 180, 270) @@ -88,6 +96,8 @@ public class CharacterSetOrientation { */ public CharacterSetOrientation(int orientation) { this.orientation = orientation; + charsWidths = new int[256]; + Arrays.fill(charsWidths, -1); } /** @@ -245,8 +255,10 @@ public class CharacterSetOrientation { 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 +273,37 @@ public class CharacterSetOrientation { public void setXHeight(int xHeight) { this.xHeight = xHeight; } + + /** + * Returns the space increment. + * @return the space increment + */ + public int getSpaceIncrement(){ + return this.spaceIncrement; + } + + /** + * Sets the space increment. + * @param value the space increment + */ + public void setSpaceIncrement(int value) { + this.spaceIncrement = value; + } + + /** + * Returns the em space increment. + * @return the em space increment + */ + public int getEmSpaceIncrement(){ + return this.emSpaceIncrement; + } + + /** + * Sets the em space increment. + * @param value the em space increment + */ + public void setEmSpaceIncrement(int value) { + this.emSpaceIncrement = value; + } + } diff --git a/src/java/org/apache/fop/afp/fonts/DoubleByteFont.java b/src/java/org/apache/fop/afp/fonts/DoubleByteFont.java new file mode 100644 index 000000000..1d3b03f1f --- /dev/null +++ b/src/java/org/apache/fop/afp/fonts/DoubleByteFont.java @@ -0,0 +1,90 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.afp.fonts; + +import java.util.Set; + +/** + * 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.<p /> + */ +public class DoubleByteFont extends AbstractOutlineFont { + + //private static final Log LOG = LogFactory.getLog(DoubleByteFont.class); + + //See also http://unicode.org/reports/tr11/ which we've not closely looked at, yet + //TODO the Unicode block listed here is probably not complete (ex. Hiragana, Katakana etc.) + private static final Set IDEOGRAPHIC = new java.util.HashSet(); + static { + IDEOGRAPHIC.add(Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS); + //IDEOGRAPHIC.add(Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS_SUPPLEMENT); //Java 1.5 + 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); //Java 1.1 + } + + /** + * 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); + } + + /** {@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((char)character); + if (charBlock == null) { + isIdeographic = false; + } else if (IDEOGRAPHIC.contains(charBlock)) { + isIdeographic = true; + } else { //default + isIdeographic = false; + } + + if (isIdeographic) { + return charSet.getEmSpaceIncrement(); + } else { + return charSet.getSpaceIncrement(); + } + } + +} diff --git a/src/java/org/apache/fop/afp/fonts/OutlineFont.java b/src/java/org/apache/fop/afp/fonts/OutlineFont.java index 8dca69f9c..26488e54d 100644 --- a/src/java/org/apache/fop/afp/fonts/OutlineFont.java +++ b/src/java/org/apache/fop/afp/fonts/OutlineFont.java @@ -19,165 +19,14 @@ 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. <p/> - * + * Default implementation of AbstractOutlineFont. */ -public class OutlineFont extends AFPFont { - - /** 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 - */ - public OutlineFont(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); - } +public class OutlineFont extends AbstractOutlineFont { /** {@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(); + public OutlineFont(String name, CharacterSet charSet) { + super(name, charSet); } }
\ No newline at end of file diff --git a/src/java/org/apache/fop/afp/modca/MapCodedFont.java b/src/java/org/apache/fop/afp/modca/MapCodedFont.java index e732a8bb7..084ae4b4c 100644 --- a/src/java/org/apache/fop/afp/modca/MapCodedFont.java +++ b/src/java/org/apache/fop/afp/modca/MapCodedFont.java @@ -29,6 +29,7 @@ import java.util.List; 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; @@ -77,19 +78,22 @@ public class MapCodedFont extends AbstractStructuredObject { } // Font Character Set Name Reference - baos.write(0x0C); + baos.write(0x0C); //TODO Relax requirement for 8 chars in the name baos.write(0x02); baos.write((byte) 0x86); baos.write(0x00); baos.write(fd.characterSet); // Font Code Page Name Reference - baos.write(0x0C); + baos.write(0x0C); //TODO Relax requirement for 8 chars in the name baos.write(0x02); baos.write((byte) 0x85); baos.write(0x00); baos.write(fd.codePage); + //TODO idea: for CIDKeyed fonts, maybe hint at Unicode encoding with X'50' triplet + //to allow font substitution. + // Character Rotation baos.write(0x04); baos.write(0x26); @@ -217,6 +221,25 @@ public class MapCodedFont extends AbstractStructuredObject { 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); + + //TODO Relax requirement for 8 characters + 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."; diff --git a/src/java/org/apache/fop/render/afp/AFPRendererConfigurator.java b/src/java/org/apache/fop/render/afp/AFPRendererConfigurator.java index 1e15d4c72..27ffeae62 100644 --- a/src/java/org/apache/fop/render/afp/AFPRendererConfigurator.java +++ b/src/java/org/apache/fop/render/afp/AFPRendererConfigurator.java @@ -30,10 +30,12 @@ import org.apache.avalon.framework.configuration.ConfigurationException; 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 @@ import org.apache.fop.util.LogUtil; * AFP Renderer configurator */ public class AFPRendererConfigurator extends PrintRendererConfigurator - implements IFDocumentHandlerConfigurator { + implements IFDocumentHandlerConfigurator { /** * Default constructor @@ -69,7 +71,7 @@ public class AFPRendererConfigurator extends PrintRendererConfigurator } private AFPFontInfo buildFont(Configuration fontCfg, String fontPath) - throws ConfigurationException { + throws ConfigurationException { FontManager fontManager = this.userAgent.getFactory().getFontManager(); @@ -127,11 +129,33 @@ public class AFPRendererConfigurator extends PrintRendererConfigurator 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 @@ public class AFPRendererConfigurator extends PrintRendererConfigurator 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,47 @@ public class AFPRendererConfigurator extends PrintRendererConfigurator 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 ("CIDKeyed".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 = new DoubleByteFont(name, characterSet); + return font; + } else { - log.error("No or incorrect type attribute"); + log.error("No or incorrect type attribute: " + type); } + return null; } @@ -230,7 +272,7 @@ public class AFPRendererConfigurator extends PrintRendererConfigurator * @throws ConfigurationException if something's wrong with the config data */ private List/*<AFPFontInfo>*/ buildFontListFromConfiguration(Configuration cfg) - throws FOPException, ConfigurationException { + throws FOPException, ConfigurationException { Configuration fonts = cfg.getChild("fonts"); FontManager fontManager = this.userAgent.getFactory().getFontManager(); @@ -261,9 +303,9 @@ public class AFPRendererConfigurator extends PrintRendererConfigurator 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 +388,7 @@ public class AFPRendererConfigurator extends PrintRendererConfigurator // 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 +400,15 @@ public class AFPRendererConfigurator extends PrintRendererConfigurator 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); } } |