Преглед изворни кода

Fix problem with alternate Unicode code point overriding existing better ones in CodePointMapping (ex. a char code for NBSP was used in place of SPACE for non-standard encodings).

Made PFM completely optional if an AFM is available. Widths and Kerning are now also read from the AFM. Fallbacks for missing values are in place. If both AFM and PFM are available, both are used to get the best possible result for certain metrics.

git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@627702 13f79535-47bb-0310-9956-ffa450edef68
tags/fop-0_95beta
Jeremias Maerki пре 16 година
родитељ
комит
e1c8b0065b

+ 11
- 8
src/codegen/fonts/code-point-mapping.xsl Прегледај датотеку

@@ -63,14 +63,17 @@ public class CodePointMapping {
unicodeMap = new char[256];
Arrays.fill(unicodeMap, CharUtilities.NOT_A_CHARACTER);
for (int i = 0; i < table.length; i += 2) {
if (table[i + 1] < 256) {
latin1Map[table[i + 1]] = (char) table[i];
} else {
++nonLatin1;
}
if (unicodeMap[table[i]] == CharUtilities.NOT_A_CHARACTER) {
unicodeMap[table[i]] = (char)table[i + 1];
}
char unicode = (char)table[i + 1];
if (unicode < 256) {
if (latin1Map[unicode] == 0) {
latin1Map[unicode] = (char) table[i];
}
} else {
++nonLatin1;
}
if (unicodeMap[table[i]] == CharUtilities.NOT_A_CHARACTER) {
unicodeMap[table[i]] = unicode;
}
}
characters = new char[nonLatin1];
codepoints = new char[nonLatin1];

+ 3
- 3
src/java/org/apache/fop/fo/FOPropertyMapping.java Прегледај датотеку

@@ -1153,7 +1153,7 @@ public final class FOPropertyMapping implements Constants {
m.useGeneric(genericSpace);
corr = new SpacePropertyMaker(m);
corr.setCorresponding(PR_MARGIN_BOTTOM, PR_MARGIN_BOTTOM, PR_MARGIN_LEFT);
corr.setUseParent(true);
corr.setUseParent(false);
corr.setRelative(true);
addPropertyMaker("space-after", m);

@@ -1163,7 +1163,7 @@ public final class FOPropertyMapping implements Constants {
m.setDefault("0pt");
IndentPropertyMaker sCorr = new IndentPropertyMaker(m);
sCorr.setCorresponding(PR_MARGIN_LEFT, PR_MARGIN_RIGHT, PR_MARGIN_TOP);
sCorr.setUseParent(true);
sCorr.setUseParent(false);
sCorr.setRelative(true);
sCorr.setPaddingCorresponding(new int[] {
PR_PADDING_LEFT, PR_PADDING_RIGHT, PR_PADDING_TOP
@@ -1179,7 +1179,7 @@ public final class FOPropertyMapping implements Constants {
m.setDefault("0pt");
IndentPropertyMaker eCorr = new IndentPropertyMaker(m);
eCorr.setCorresponding(PR_MARGIN_RIGHT, PR_MARGIN_LEFT, PR_MARGIN_BOTTOM);
eCorr.setUseParent(true);
eCorr.setUseParent(false);
eCorr.setRelative(true);
eCorr.setPaddingCorresponding(new int[] {
PR_PADDING_RIGHT, PR_PADDING_LEFT, PR_PADDING_BOTTOM

+ 29
- 2
src/java/org/apache/fop/fonts/type1/AFMCharMetrics.java Прегледај датотеку

@@ -19,26 +19,37 @@

package org.apache.fop.fonts.type1;

import java.awt.geom.RectangularShape;


/**
* Holds the metrics of a single character from an AFM file.
*/
public class AFMCharMetrics {

private int charCode;
private int charCode = -1;
private String unicodeChars;
private String charName;
private double widthX;
private double widthY;
private RectangularShape bBox;
/**
* Returns the character code.
* @return the charCode
* @return the charCode (-1 if not part of the encoding)
*/
public int getCharCode() {
return charCode;
}
/**
* Indicates whether the character has a character code, i.e. is part of the default encoding.
* @return true if there is a character code.
*/
public boolean hasCharCode() {
return charCode >= 0;
}
/**
* Sets the character code.
* @param charCode the charCode to set
@@ -113,6 +124,22 @@ public class AFMCharMetrics {
this.widthY = widthY;
}
/**
* Returns the character's bounding box.
* @return the bounding box (or null if it isn't available)
*/
public RectangularShape getBBox() {
return bBox;
}

/**
* Sets the character's bounding box.
* @param box the bounding box
*/
public void setBBox(RectangularShape box) {
bBox = box;
}

/** {@inheritDoc} */
public String toString() {
StringBuffer sb = new StringBuffer("AFM Char: ");

+ 89
- 1
src/java/org/apache/fop/fonts/type1/AFMFile.java Прегледај датотеку

@@ -19,8 +19,11 @@

package org.apache.fop.fonts.type1;

import java.awt.geom.Dimension2D;
import java.awt.geom.RectangularShape;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

@@ -56,9 +59,11 @@ public class AFMFile {
//List<AFMCharMetrics>
private Map charNameToMetrics = new java.util.HashMap();
//Map<String, AFMCharMetrics>
private int firstChar = -1;
private int lastChar = -1;
private Map kerningMap;
//Map<String, Map<String, Dimension>>
//Map<String, Map<String, Dimension2D>>
/**
* Default constructor.
@@ -314,6 +319,13 @@ public class AFMFile {
if (name != null) {
String u = Glyphs.getUnicodeCodePointsForGlyphName(metrics.getCharName());
if (u != null) {
if (u.length() > 1) {
//Lower values (ex. space) are most probably more interesting than
//higher values (ex. non-break-space), so sort just to be sure:
char[] chars = u.toCharArray();
Arrays.sort(chars);
u = String.valueOf(chars);
}
metrics.setUnicodeChars(u);
}
} else {
@@ -325,6 +337,15 @@ public class AFMFile {
if (name != null) {
this.charNameToMetrics.put(name, metrics);
}
int idx = metrics.getCharCode();
if (idx >= 0) { //Only if the character is part of the encoding
if (firstChar < 0 || idx < firstChar) {
firstChar = idx;
}
if (lastChar < 0 || idx > lastChar) {
lastChar = idx;
}
}
}
/**
@@ -335,6 +356,22 @@ public class AFMFile {
return this.charMetrics.size();
}
/**
* Returns the first character index in the encoding that has a glyph.
* @return the first character index with a glyph
*/
public int getFirstChar() {
return this.firstChar;
}
/**
* Returns the last character index in the encoding that has a glyph.
* @return the last character index with a glyph
*/
public int getLastChar() {
return this.lastChar;
}
/**
* Returns the character metrics associated with the character name.
* @param name the character name
@@ -370,6 +407,57 @@ public class AFMFile {
entries.put(name2, new Dimension2DDouble(kx, 0));
}
/**
* Indicates whether the font has kerning information.
* @return true if there is kerning information
*/
public boolean hasKerning() {
return this.kerningMap != null;
}
/**
* Creates and returns a kerning map for writing mode 0 (ltr) with character codes.
* @return the kerning map or null if there is no kerning information.
*/
public Map createXKerningMapEncoded() {
if (!hasKerning()) {
return null;
}
Map m = new java.util.HashMap();
Iterator iterFrom = this.kerningMap.entrySet().iterator();
while (iterFrom.hasNext()) {
Map.Entry entryFrom = (Map.Entry)iterFrom.next();
String name1 = (String)entryFrom.getKey();
AFMCharMetrics chm1 = getChar(name1);
if (!chm1.hasCharCode()) {
continue;
}
Map container = null;
Map entriesTo = (Map)entryFrom.getValue();
Iterator iterTo = entriesTo.entrySet().iterator();
while (iterTo.hasNext()) {
Map.Entry entryTo = (Map.Entry)iterTo.next();
String name2 = (String)entryTo.getKey();
AFMCharMetrics chm2 = getChar(name2);
if (!chm2.hasCharCode()) {
continue;
}
if (container == null) {
Integer k1 = new Integer(chm1.getCharCode());
container = (Map)m.get(k1);
if (container == null) {
container = new java.util.HashMap();
m.put(k1, container);
}
}
Dimension2D dim = (Dimension2D)entryTo.getValue();
container.put(new Integer(chm2.getCharCode()),
new Integer((int)Math.round(dim.getWidth())));
}
}
return m;
}
/** {@inheritDoc} */
public String toString() {
return "AFM: " + getFullName();

+ 18
- 2
src/java/org/apache/fop/fonts/type1/AFMParser.java Прегледај датотеку

@@ -73,6 +73,7 @@ public class AFMParser {
private static final String W0 = "W0";
private static final String W1 = "W1";
private static final String N = "N";
private static final String B = "B";
private static final String START_TRACK_KERN = "StartTrackKern";
private static final String END_TRACK_KERN = "EndTrackKern";
//private static final String START_KERN_PAIRS = "StartKernPairs";
@@ -126,6 +127,7 @@ public class AFMParser {
VALUE_PARSERS.put(W0, new NotImplementedYet(W0));
VALUE_PARSERS.put(W1, new NotImplementedYet(W1));
VALUE_PARSERS.put(N, new StringSetter("CharName"));
VALUE_PARSERS.put(B, new CharBBox());
VALUE_PARSERS.put(START_TRACK_KERN, new NotImplementedYet(START_TRACK_KERN));
VALUE_PARSERS.put(END_TRACK_KERN, new NotImplementedYet(END_TRACK_KERN));
VALUE_PARSERS.put(START_KERN_PAIRS1, new NotImplementedYet(START_KERN_PAIRS1));
@@ -497,7 +499,13 @@ public class AFMParser {
private static class FontBBox extends AbstractValueHandler {
public void parse(String line, int startpos, Stack stack) throws IOException {
Rectangle rect = parseBBox(line, startpos);
AFMFile afm = (AFMFile)stack.peek();
afm.setFontBBox(rect);
}

protected Rectangle parseBBox(String line, int startpos) {
Rectangle rect = new Rectangle();
int endpos;
@@ -518,11 +526,19 @@ public class AFMParser {
v = Integer.parseInt(line.substring(startpos, endpos));
rect.height = v - rect.y;
startpos = skipToNonWhiteSpace(line, endpos);
afm.setFontBBox(rect);
return rect;
}
}
private static class CharBBox extends FontBBox {
public void parse(String line, int startpos, Stack stack) throws IOException {
Rectangle rect = parseBBox(line, startpos);
AFMCharMetrics metrics = (AFMCharMetrics)stack.peek();
metrics.setBBox(rect);
}
}

private static class IsBaseFont extends AbstractValueHandler {
public void parse(String line, int startpos, Stack stack) throws IOException {
if (getBooleanValue(line, startpos).booleanValue()) {

+ 97
- 45
src/java/org/apache/fop/fonts/type1/Type1FontLoader.java Прегледај датотеку

@@ -19,6 +19,7 @@

package org.apache.fop.fonts.type1;

import java.awt.geom.RectangularShape;
import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;
@@ -107,11 +108,6 @@ public class Type1FontLoader extends FontLoader {
throw new java.io.FileNotFoundException(
"Neither an AFM nor a PFM file was found for " + this.fontFileURI);
}
if (pfm == null) {
//Cannot do without for now
throw new java.io.FileNotFoundException(
"No PFM file was found for " + this.fontFileURI);
}
buildFont(afm, pfm);
this.loaded = true;
}
@@ -122,33 +118,10 @@ public class Type1FontLoader extends FontLoader {
}
singleFont = new SingleByteFont();
singleFont.setFontType(FontType.TYPE1);
if (pfm.getCharSet() >= 0 && pfm.getCharSet() <= 2) {
singleFont.setEncoding(pfm.getCharSetName() + "Encoding");
} else {
log.warn("The PFM reports an unsupported encoding ("
+ pfm.getCharSetName() + "). The font may not work as expected.");
singleFont.setEncoding("WinAnsiEncoding"); //Try fallback, no guarantees!
}
singleFont.setResolver(this.resolver);
singleFont.setEmbedFileName(this.fontFileURI);
returnFont = singleFont;
//Font name
if (afm != null) {
returnFont.setFontName(afm.getFontName()); //PostScript font name
returnFont.setFullName(afm.getFullName());
Set names = new java.util.HashSet();
names.add(afm.getFamilyName());
returnFont.setFamilyNames(names);
} else {
returnFont.setFontName(pfm.getPostscriptName());
String fullName = pfm.getPostscriptName();
fullName = fullName.replace('-', ' '); //Hack! Try to emulate full name
returnFont.setFullName(fullName); //emulate afm.getFullName()
Set names = new java.util.HashSet();
names.add(pfm.getWindowsName()); //emulate afm.getFamilyName()
returnFont.setFamilyNames(names);
}
//Encoding
if (afm != null) {
String encoding = afm.getEncodingScheme();
@@ -169,6 +142,31 @@ public class Type1FontLoader extends FontLoader {
CodePointMapping mapping = buildCustomEncoding(effEncodingName, afm);
singleFont.setEncoding(mapping);
}
} else {
if (pfm.getCharSet() >= 0 && pfm.getCharSet() <= 2) {
singleFont.setEncoding(pfm.getCharSetName() + "Encoding");
} else {
log.warn("The PFM reports an unsupported encoding ("
+ pfm.getCharSetName() + "). The font may not work as expected.");
singleFont.setEncoding("WinAnsiEncoding"); //Try fallback, no guarantees!
}
}
//Font name
if (afm != null) {
returnFont.setFontName(afm.getFontName()); //PostScript font name
returnFont.setFullName(afm.getFullName());
Set names = new java.util.HashSet();
names.add(afm.getFamilyName());
returnFont.setFamilyNames(names);
} else {
returnFont.setFontName(pfm.getPostscriptName());
String fullName = pfm.getPostscriptName();
fullName = fullName.replace('-', ' '); //Hack! Try to emulate full name
returnFont.setFullName(fullName); //emulate afm.getFullName()
Set names = new java.util.HashSet();
names.add(pfm.getWindowsName()); //emulate afm.getFamilyName()
returnFont.setFamilyNames(names);
}
//Basic metrics
@@ -185,6 +183,7 @@ public class Type1FontLoader extends FontLoader {
if (afm.getDescender() != null) {
returnFont.setDescender(afm.getDescender().intValue());
}
returnFont.setFontBBox(afm.getFontBBoxAsIntArray());
if (afm.getStdVW() != null) {
returnFont.setStemV(afm.getStdVW().intValue());
@@ -198,28 +197,81 @@ public class Type1FontLoader extends FontLoader {
returnFont.setItalicAngle(pfm.getItalicAngle());
}
if (pfm != null) {
if (returnFont.getCapHeight() == 0) {
returnFont.setCapHeight(pfm.getCapHeight());
//Sometimes the PFM has these metrics while the AFM doesn't (ex. Symbol)
returnFont.setCapHeight(pfm.getCapHeight());
returnFont.setXHeight(pfm.getXHeight());
returnFont.setAscender(pfm.getLowerCaseAscent());
returnFont.setDescender(pfm.getLowerCaseDescent());
}
//Fallbacks when some crucial font metrics aren't available
//(the following are all optional in AFM, but FontBBox is always available)
if (returnFont.getXHeight(1) == 0) {
int xHeight = 0;
AFMCharMetrics chm = afm.getChar("x");
if (chm != null) {
RectangularShape rect = chm.getBBox();
if (rect != null) {
xHeight = (int)Math.round(rect.getMinX());
}
}
if (returnFont.getXHeight(1) == 0) {
returnFont.setXHeight(pfm.getXHeight());
if (xHeight == 0) {
xHeight = Math.round(returnFont.getFontBBox()[3] * 0.6f);
}
if (returnFont.getAscender() == 0) {
returnFont.setAscender(pfm.getLowerCaseAscent());
returnFont.setXHeight(xHeight);
}
if (returnFont.getAscender() == 0) {
int asc = 0;
AFMCharMetrics chm = afm.getChar("d");
if (chm != null) {
RectangularShape rect = chm.getBBox();
if (rect != null) {
asc = (int)Math.round(rect.getMinX());
}
}
if (returnFont.getDescender() == 0) {
returnFont.setDescender(pfm.getLowerCaseDescent());
if (asc == 0) {
asc = Math.round(returnFont.getFontBBox()[3] * 0.9f);
}
returnFont.setAscender(asc);
}
returnFont.setFirstChar(pfm.getFirstChar());
returnFont.setLastChar(pfm.getLastChar());
returnFont.setFlags(pfm.getFlags());
returnFont.setMissingWidth(0);
for (short i = pfm.getFirstChar(); i <= pfm.getLastChar(); i++) {
singleFont.setWidth(i, pfm.getCharWidth(i));
if (returnFont.getDescender() == 0) {
int desc = 0;
AFMCharMetrics chm = afm.getChar("p");
if (chm != null) {
RectangularShape rect = chm.getBBox();
if (rect != null) {
desc = (int)Math.round(rect.getMinX());
}
}
if (desc == 0) {
desc = returnFont.getFontBBox()[1];
}
returnFont.setDescender(desc);
}
if (returnFont.getCapHeight() == 0) {
returnFont.setCapHeight(returnFont.getAscender());
}
if (afm != null) {
returnFont.setFirstChar(afm.getFirstChar());
returnFont.setLastChar(afm.getLastChar());
Iterator iter = afm.getCharMetrics().iterator();
while (iter.hasNext()) {
AFMCharMetrics chm = (AFMCharMetrics)iter.next();
if (chm.hasCharCode()) {
singleFont.setWidth(chm.getCharCode(), (int)Math.round(chm.getWidthX()));
}
}
returnFont.replaceKerningMap(afm.createXKerningMapEncoded());
} else {
returnFont.setFirstChar(pfm.getFirstChar());
returnFont.setLastChar(pfm.getLastChar());
returnFont.setFlags(pfm.getFlags());
for (short i = pfm.getFirstChar(); i <= pfm.getLastChar(); i++) {
singleFont.setWidth(i, pfm.getCharWidth(i));
}
returnFont.replaceKerningMap(pfm.getKerning());
}
returnFont.replaceKerningMap(pfm.getKerning());
singleFont.setEmbedFileName(this.fontFileURI);
}

private CodePointMapping buildCustomEncoding(String encodingName, AFMFile afm) {

Loading…
Откажи
Сачувај