Browse Source

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
tags/fop-1_0
Jeremias Maerki 14 years ago
parent
commit
bb2844e064

+ 14
- 0
conf/fop.xconf View File

@@ -396,6 +396,20 @@ the location of this file.
<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>


+ 14
- 1
src/documentation/content/xdocs/trunk/output.xml View File

@@ -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

+ 177
- 0
src/java/org/apache/fop/afp/fonts/AbstractOutlineFont.java View File

@@ -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();
}

}

+ 29
- 33
src/java/org/apache/fop/afp/fonts/CharacterSet.java View File

@@ -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();
}

}

src/java/org/apache/fop/afp/fonts/AFPFontReader.java → src/java/org/apache/fop/afp/fonts/CharacterSetBuilder.java View File

@@ -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;
}
}

+ 46
- 1
src/java/org/apache/fop/afp/fonts/CharacterSetOrientation.java View File

@@ -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;
}

}

+ 90
- 0
src/java/org/apache/fop/afp/fonts/DoubleByteFont.java View File

@@ -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();
}
}

}

+ 4
- 155
src/java/org/apache/fop/afp/fonts/OutlineFont.java View File

@@ -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);
}

}

+ 25
- 2
src/java/org/apache/fop/afp/modca/MapCodedFont.java View File

@@ -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);
@@ -211,6 +215,25 @@ public class MapCodedFont extends AbstractStructuredObject {
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,

+ 70
- 27
src/java/org/apache/fop/render/afp/AFPRendererConfigurator.java View File

@@ -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);
}
}


+ 3
- 0
status.xml View File

@@ -58,6 +58,9 @@
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.

Loading…
Cancel
Save