<font-triplet name="Courier" style="italic" weight="bold"/>
<font-triplet name="monospace" style="italic" weight="bold"/>
</font>
+
+ <!--
+ Configure double-byte (CID Keyed font (Type 0)) AFP fonts with type="CIDKeyed".
+
+ example:
+ <font>
+ <afp-font type="CIDKeyed" encoding="UnicodeBigUnmarked"
+ codepage="T1120000" characterset="CZJHMNU"
+ base-uri="fonts" />
+ <font-triplet name="J-Heisei Mincho" style="normal" weight="normal" />
+ </font>
+ -->
+
+
</fonts>
</renderer>
<!-- 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>
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
+++ /dev/null
-/*
- * 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. <p/>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. <p/>
- *
- * @author <a href="mailto:pete@townsend.uk.com">Pete Townsend </a>
- */
-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/*<String, Map<String, String>>*/ codePagesCache
- = new java.util.HashMap/*<String, Map<String, String>>*/();
-
- /**
- * 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/*<String,String>*/ codePage
- = (Map/*<String,String>*/)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/*<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 = 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/*<String,String>*/ 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;
- }
- }
-
-}
--- /dev/null
+/*
+ * 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
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;
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";
/** 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";
* @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);
}
* @return the ascender value in millipoints
*/
public int getAscender() {
- load();
+
return getCharacterSetOrientation().getAscender();
}
* @return the cap height value in millipoints
*/
public int getCapHeight() {
- load();
+
return getCharacterSetOrientation().getCapHeight();
}
* @return the descender value in millipoints
*/
public int getDescender() {
- load();
+
return getCharacterSetOrientation().getDescender();
}
* @return the first character in the character set
*/
public int getFirstChar() {
- load();
+
return getCharacterSetOrientation().getFirstChar();
}
* @return the last character in the character set
*/
public int getLastChar() {
- load();
+
return getCharacterSetOrientation().getLastChar();
}
* @return the widths of all characters
*/
public int[] getWidths() {
- load();
+
return getCharacterSetOrientation().getWidths();
}
* @return the typical height of characters
*/
public int getXHeight() {
- load();
+
return getCharacterSetOrientation().getXHeight();
}
* @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
nameBytes = name.getBytes(AFPConstants.EBCIDIC_ENCODING);
} catch (UnsupportedEncodingException usee) {
nameBytes = name.getBytes();
- log.warn(
+ LOG.warn(
"UnsupportedEncodingException translating the name " + name);
}
return nameBytes;
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();
+ }
+
}
--- /dev/null
+/*
+ * 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;
+import org.apache.fop.fonts.Typeface;
+
+/**
+ * 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
+ * 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. <p/>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. <p/>
+ *
+ */
+public class CharacterSetBuilder {
+
+ /**
+ * Static logging instance
+ */
+ protected static final Log LOG = LogFactory.getLog(CharacterSetBuilder.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/*<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 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
+ */
+ protected 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
+ */
+ protected 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 codePageName name of the code page file
+ * @param encoding
+ * @throws RuntimeException if an I/O exception of some sort has occurred.
+ */
+ public CharacterSet build(String characterSetName, String codePageName,
+ String encoding, ResourceAccessor accessor) {
+
+ CharacterSet characterSet = new CharacterSet(
+ codePageName, encoding, characterSetName, accessor);
+
+ 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.
+ */
+
+ Map/*<String,String>*/ codePage
+ = (Map/*<String,String>*/)codePagesCache.get(codePageName);
+
+ if (codePage == null) {
+ codePage = loadCodePage(codePageName, encoding, accessor);
+ codePagesCache.put(codePageName, codePage);
+ }
+
+ 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 {
+
+ 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);
+
+ }
+
+ } 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'.
+ *
+ * @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
+ * @throws IOException if an I/O exception of some sort has occurred.
+ */
+ 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 = 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
+ * @throws IOException if an I/O exception of some sort has occurred.
+ */
+ protected 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
+ * @return the FontControl
+ * @throws IOException if an I/O exception of some sort has occurred.
+ */
+ protected 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
+ * @return CharacterSetOrientation array
+ * @throws IOException if an I/O exception of some sort has occurred.
+ */
+ 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 = 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);
+ cso.setSpaceIncrement(space);
+ cso.setEmSpaceIncrement(em);
+ 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
+ * @throws IOException if an I/O exception of some sort has occurred.
+ */
+ protected 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
+ * @throws IOException if an I/O exception of some sort has occurred.
+ */
+ protected void processFontIndex(StructuredFieldReader structuredFieldReader,
+ CharacterSetOrientation cso, Map/*<String,String>*/ 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 (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;
+ }
+ }
+
+ /**
+ * 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;
+ }
+}
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
/**
* The character widths in the character set
*/
- private int[] charsWidths = new int[256];
+ private int[] charsWidths = null;
/**
* The height of lowercase letters
*/
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)
*/
public CharacterSetOrientation(int orientation) {
this.orientation = orientation;
+ charsWidths = new int[256];
+ Arrays.fill(charsWidths, -1);
}
/**
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;
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;
+ }
+
}
--- /dev/null
+/*
+ * 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();
+ }
+ }
+
+}
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
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;
}
// 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);
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 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,
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;
* AFP Renderer configurator
*/
public class AFPRendererConfigurator extends PrintRendererConfigurator
- implements IFDocumentHandlerConfigurator {
+ implements IFDocumentHandlerConfigurator {
/**
* Default constructor
}
private AFPFontInfo buildFont(Configuration fontCfg, String fontPath)
- throws ConfigurationException {
+ throws ConfigurationException {
FontManager fontManager = this.userAgent.getFactory().getFontManager();
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");
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");
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;
}
* @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();
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))
// 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 {
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);
}
}
documents. Example: the fix of marks layering will be such a case when it's done.
-->
<release version="FOP Trunk" date="TBD">
+ <action context="Renderers" dev="JM" type="add" fixes-bug="48567" due-to="Peter Hancock">
+ Initial support for CID-keyed double-byte fonts (Type 0) in AFP output.
+ </action>
<action context="Layout" dev="VH" type="fix" fixes-bug="46486">
Bugfix: having a special page-master for the last page caused loss of content when normal
blocks were mixed with blocks spanning all columns.