Browse Source

FOP-2486 - Enhancements and fixes to existing patch

git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_PCLSoftFonts@1694075 13f79535-47bb-0310-9956-ffa450edef68
tags/fop-2_1
Robert Meyer 8 years ago
parent
commit
1e7790e823

+ 12
- 1
src/java/org/apache/fop/fonts/CIDFull.java View File

@@ -56,6 +56,18 @@ public class CIDFull implements CIDSet {
return index;
}

/** {@inheritDoc} */
@Override
public char getUnicodeFromGID(int glyphIndex) {
return ' ';
}

/** {@inheritDoc} */
@Override
public int getGIDFromChar(char ch) {
return ch;
}

/** {@inheritDoc} */
public char getUnicode(int index) {
initGlyphIndices();
@@ -109,5 +121,4 @@ public class CIDFull implements CIDSet {
public int[] getWidths() {
return font.getWidths();
}

}

+ 14
- 0
src/java/org/apache/fop/fonts/CIDSet.java View File

@@ -43,6 +43,20 @@ public interface CIDSet {
*/
char getUnicode(int index);

/**
* Gets the unicode character from the original font glyph index
* @param glyphIndex The original glyph index of the character in the font
* @return The character represented by the passed GID
*/
char getUnicodeFromGID(int glyphIndex);

/**
* Returns the glyph index from the original font from a character
* @param ch The character
* @return The glyph index in the original font.
*/
int getGIDFromChar(char ch);

/**
* Maps a character to a character selector for a font subset. If the character isn't in the
* subset, yet, it is added and a new character selector returned. Otherwise, the already

+ 18
- 0
src/java/org/apache/fop/fonts/CIDSubset.java View File

@@ -53,6 +53,12 @@ public class CIDSubset implements CIDSet {
*/
private Map<Integer, Character> usedCharsIndex = new HashMap<Integer, Character>();

/**
* A map between the original character and it's GID in the original font.
*/
private Map<Character, Integer> charToGIDs = new HashMap<Character, Integer>();


private final MultiByteFont font;

public CIDSubset(MultiByteFont mbf) {
@@ -93,6 +99,7 @@ public class CIDSubset implements CIDSet {
usedGlyphs.put(glyphIndex, selector);
usedGlyphsIndex.put(selector, glyphIndex);
usedCharsIndex.put(selector, unicode);
charToGIDs.put(unicode, glyphIndex);
usedGlyphsCount++;
return selector;
} else {
@@ -105,6 +112,17 @@ public class CIDSubset implements CIDSet {
return Collections.unmodifiableMap(this.usedGlyphs);
}

/** {@inheritDoc} */
public char getUnicodeFromGID(int glyphIndex) {
int selector = usedGlyphs.get(glyphIndex);
return usedCharsIndex.get(selector);
}

/** {@inheritDoc} */
public int getGIDFromChar(char ch) {
return charToGIDs.get(ch);
}

/** {@inheritDoc} */
public char[] getChars() {
char[] charArray = new char[usedGlyphsCount];

+ 12
- 0
src/java/org/apache/fop/fonts/CustomFont.java View File

@@ -568,4 +568,16 @@ public abstract class CustomFont extends Typeface
this.strikeoutThickness = strikeoutThickness;
}

/**
* Returns a Map of used Glyphs.
* @return Map Map of used Glyphs
*/
public abstract Map<Integer, Integer> getUsedGlyphs();

/**
* Returns the character from it's original glyph index in the font
* @param glyphIndex The original index of the character
* @return The character
*/
public abstract char getUnicodeFromGID(int glyphIndex);
}

+ 18
- 0
src/java/org/apache/fop/fonts/MultiByteFont.java View File

@@ -425,6 +425,24 @@ public class MultiByteFont extends CIDFont implements Substitutable, Positionabl
return cidSet.getGlyphs();
}

/**
* Returns the character from it's original glyph index in the font
* @param glyphIndex The original index of the character
* @return The character
*/
public char getUnicodeFromGID(int glyphIndex) {
return cidSet.getUnicodeFromGID(glyphIndex);
}

/**
* Gets the original glyph index in the font from a character.
* @param ch The character
* @return The glyph index in the font
*/
public int getGIDFromChar(char ch) {
return cidSet.getGIDFromChar(ch);
}

/**
* Establishes the glyph definition table.
* @param gdef the glyph definition table to be used by this font

+ 12
- 0
src/java/org/apache/fop/fonts/SingleByteFont.java View File

@@ -63,6 +63,7 @@ public class SingleByteFont extends CustomFont {
private LinkedHashMap<Integer, String> usedGlyphNames;
private Map<Integer, Integer> usedGlyphs;
private Map<Integer, Character> usedCharsIndex;
private Map<Character, Integer> charGIDMappings;

public SingleByteFont(InternalResourceResolver resourceResolver) {
super(resourceResolver);
@@ -76,6 +77,7 @@ public class SingleByteFont extends CustomFont {
usedGlyphNames = new LinkedHashMap<Integer, String>();
usedGlyphs = new HashMap<Integer, Integer>();
usedCharsIndex = new HashMap<Integer, Character>();
charGIDMappings = new HashMap<Character, Integer>();

// The zeroth value is reserved for .notdef
usedGlyphs.put(0, 0);
@@ -234,6 +236,7 @@ public class SingleByteFont extends CustomFont {
int selector = usedGlyphsCount;
usedGlyphs.put(glyphIndex, selector);
usedCharsIndex.put(selector, unicode);
charGIDMappings.put(unicode, glyphIndex);
usedGlyphsCount++;
return selector;
} else {
@@ -519,6 +522,15 @@ public class SingleByteFont extends CustomFont {
return getUnicode(selector);
}

public int getGIDFromChar(char ch) {
return charGIDMappings.get(ch);
}

public char getUnicodeFromGID(int glyphIndex) {
int selector = usedGlyphs.get(glyphIndex);
return usedCharsIndex.get(selector);
}

public void mapUsedGlyphName(int gid, String value) {
usedGlyphNames.put(gid, value);
}

+ 27
- 13
src/java/org/apache/fop/render/pcl/PCLPainter.java View File

@@ -30,6 +30,7 @@ import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Stack;

@@ -62,6 +63,7 @@ import org.apache.fop.render.java2d.Java2DPainter;
import org.apache.fop.render.pcl.fonts.PCLCharacterWriter;
import org.apache.fop.render.pcl.fonts.PCLSoftFont;
import org.apache.fop.render.pcl.fonts.PCLSoftFontManager;
import org.apache.fop.render.pcl.fonts.PCLSoftFontManager.PCLTextSegment;
import org.apache.fop.render.pcl.fonts.truetype.PCLTTFCharacterWriter;
import org.apache.fop.traits.BorderProps;
import org.apache.fop.traits.RuleStyle;
@@ -337,24 +339,35 @@ public class PCLPainter extends AbstractIFPainter<PCLDocumentHandler> implements
// TrueType conversion to a soft font (PCL 5 Technical Reference - Chapter 11)
if (!drawAsBitmaps && isTrueType(tf)) {
boolean madeSF = false;
if (sfManager.getSoftFont(tf) == null) {
if (sfManager.getSoftFont(tf, text) == null) {
madeSF = true;
ByteArrayOutputStream baos = sfManager.makeSoftFont(tf);
if (baos != null) {
gen.writeBytes(baos.toByteArray());
}
}
int fontID = sfManager.getSoftFontID(tf);
String formattedSize = gen.formatDouble2(state.getFontSize() / 1000.0);
gen.writeCommand(String.format("(s%sV", formattedSize));
gen.writeCommand(String.format("(%dX", fontID));
PCLSoftFont softFont = sfManager.getSoftFont(tf);
PCLCharacterWriter charWriter = new PCLTTFCharacterWriter(softFont);
if (!madeSF) {
gen.writeBytes(sfManager.writeFontIDCommand(fontID));
List<PCLTextSegment> textSegments = sfManager.getTextSegments(text, tf);
if (textSegments.isEmpty()) {
textSegments.add(new PCLTextSegment(sfManager.getSoftFontID(tf), text));
}
boolean first = true;
for (PCLTextSegment textSegment : textSegments) {
gen.writeCommand(String.format("(%dX", textSegment.getFontID()));
PCLSoftFont softFont = sfManager.getSoftFontFromID(textSegment.getFontID());
PCLCharacterWriter charWriter = new PCLTTFCharacterWriter(softFont);
gen.writeBytes(sfManager.assignFontID(textSegment.getFontID()));
gen.writeBytes(charWriter.writeCharacterDefinitions(textSegment.getText()));
if (first) {
drawTextUsingSoftFont(x, y, letterSpacing, wordSpacing, dp,
textSegment.getText(), triplet, softFont);
first = false;
} else {
drawTextUsingSoftFont(-1, -1, letterSpacing, wordSpacing, dp,
textSegment.getText(), triplet, softFont);
}
}
gen.writeBytes(charWriter.writeCharacterDefinitions(text));
drawTextUsingSoftFont(x, y, letterSpacing, wordSpacing, dp, text, triplet, softFont);
} else {
drawTextAsBitmap(x, y, letterSpacing, wordSpacing, dp, text, triplet);
if (DEBUG) {
@@ -482,7 +495,9 @@ public class PCLPainter extends AbstractIFPainter<PCLDocumentHandler> implements
gen.selectGrayscale(textColor);
}

setCursorPos(x, y);
if (x != -1 && y != -1) {
setCursorPos(x, y);
}

float fontSize = state.getFontSize() / 1000f;
Font font = getFontInfo().getFontInstance(triplet, state.getFontSize());
@@ -519,8 +534,7 @@ public class PCLPainter extends AbstractIFPainter<PCLDocumentHandler> implements
if (glyphAdjust != 0) {
gen.getOutputStream().write(sb.toString().getBytes(gen.getTextEncoding()));
for (int j = 0; j < current.length(); j++) {
gen.getOutputStream().write(
softFont.getUnicodeCodePoint((int) current.charAt(j)));
gen.getOutputStream().write(softFont.getCharCode(current.charAt(j)));
}
sb = new StringBuffer();

@@ -533,7 +547,7 @@ public class PCLPainter extends AbstractIFPainter<PCLDocumentHandler> implements
if (!current.equals("")) {
gen.getOutputStream().write(sb.toString().getBytes(gen.getTextEncoding()));
for (int i = 0; i < current.length(); i++) {
gen.getOutputStream().write(softFont.getUnicodeCodePoint((int) current.charAt(i)));
gen.getOutputStream().write(softFont.getCharCode(current.charAt(i)));
}
}
}

+ 7
- 6
src/java/org/apache/fop/render/pcl/fonts/PCLCharacterDefinition.java View File

@@ -25,7 +25,6 @@ import java.util.ArrayList;
import java.util.List;

public class PCLCharacterDefinition {
private int glyphID;
private int charCode;
private int charDefinitionSize;
private byte[] glyfData;
@@ -34,15 +33,17 @@ public class PCLCharacterDefinition {
private PCLCharacterClass charClass;
private PCLByteWriterUtil pclByteWriter;
private List<PCLCharacterDefinition> composites;
private boolean isComposite;

public PCLCharacterDefinition(int glyphID, int charCode, PCLCharacterFormat charFormat,
PCLCharacterClass charClass, byte[] glyfData, PCLByteWriterUtil pclByteWriter) {
this.glyphID = glyphID;
public PCLCharacterDefinition(int charCode, PCLCharacterFormat charFormat,
PCLCharacterClass charClass, byte[] glyfData, PCLByteWriterUtil pclByteWriter,
boolean isComposite) {
this.charCode = charCode;
this.charFormat = charFormat;
this.charClass = charClass;
this.glyfData = glyfData;
this.pclByteWriter = pclByteWriter;
this.isComposite = isComposite;
// Glyph Data + (Descriptor Size) + (Character Data Size) + (Glyph ID) must
// be less than 32767 otherwise it will result in a continuation structure.
charDefinitionSize = glyfData.length + 4 + 2 + 2;
@@ -51,7 +52,7 @@ public class PCLCharacterDefinition {
}

public byte[] getCharacterCommand() throws IOException {
return pclByteWriter.writeCommand(String.format("*c%dE", charCode));
return pclByteWriter.writeCommand(String.format("*c%dE", (isComposite) ? 65535 : charCode));
}

public byte[] getCharacterDefinitionCommand() throws IOException {
@@ -93,7 +94,7 @@ public class PCLCharacterDefinition {
baos.write(pclByteWriter.unsignedByte(2)); // Descriptor size (from this byte to character data)
baos.write(pclByteWriter.unsignedByte(charClass.getValue()));
baos.write(pclByteWriter.unsignedInt(glyfData.length + 4));
baos.write(pclByteWriter.unsignedInt(glyphID));
baos.write(pclByteWriter.unsignedInt(charCode));
}

public void addCompositeGlyph(PCLCharacterDefinition composite) {

+ 8
- 1
src/java/org/apache/fop/render/pcl/fonts/PCLFontReader.java View File

@@ -23,6 +23,7 @@ import java.io.IOException;
import java.util.List;
import java.util.Map;

import org.apache.fop.fonts.CustomFont;
import org.apache.fop.fonts.Typeface;
import org.apache.fop.fonts.truetype.FontFileReader;
import org.apache.fop.fonts.truetype.OpenFont;
@@ -31,12 +32,17 @@ public abstract class PCLFontReader {

protected Typeface typeface;
protected PCLByteWriterUtil pclByteWriter;
protected CustomFont font;

public PCLFontReader(Typeface font, PCLByteWriterUtil pclByteWriter) {
this.typeface = font;
this.pclByteWriter = pclByteWriter;
}

public void setFont(CustomFont mbFont) {
this.font = mbFont;
}

/** Header Data **/
public abstract int getDescriptorSize();
public abstract int getHeaderFormat();
@@ -77,7 +83,8 @@ public abstract class PCLFontReader {
public abstract int getVariety();

/** Segmented Font Data **/
public abstract List<PCLFontSegment> getFontSegments() throws IOException;
public abstract List<PCLFontSegment> getFontSegments(Map<Character, Integer> mappedGlyphs)
throws IOException;

/** Character Definitions **/
public abstract Map<Integer, int[]> getCharacterOffsets() throws IOException;

+ 34
- 2
src/java/org/apache/fop/render/pcl/fonts/PCLSoftFont.java View File

@@ -38,13 +38,17 @@ public class PCLSoftFont {
private FontFileReader reader;
/** Map containing unicode character and it's soft font codepoint **/
private Map<Integer, Integer> charsWritten;
private Map<Character, Integer> mappedChars;
private Map<Integer, Integer> charMtxPositions;
private boolean multiByteFont;
private int charCount = 32;

public PCLSoftFont(int fontID, Typeface font) {
public PCLSoftFont(int fontID, Typeface font, boolean multiByteFont) {
this.fontID = fontID;
this.font = font;
charsWritten = new HashMap<Integer, Integer>();
mappedChars = new HashMap<Character, Integer>();
this.multiByteFont = multiByteFont;
}

public Typeface getTypeface() {
@@ -92,7 +96,11 @@ public class PCLSoftFont {
}

public int getUnicodeCodePoint(int unicode) {
return charsWritten.get(unicode);
if (charsWritten.containsKey(unicode)) {
return charsWritten.get(unicode);
} else {
return -1;
}
}

public boolean hasPreviouslyWritten(int unicode) {
@@ -125,4 +133,28 @@ public class PCLSoftFont {
public int getCharCount() {
return charCount;
}

public void setMappedChars(Map<Character, Integer> mappedChars) {
this.mappedChars = mappedChars;
}

public Map<Character, Integer> getMappedChars() {
return mappedChars;
}

public int getCharIndex(char ch) {
if (mappedChars.containsKey(ch)) {
return mappedChars.get(ch);
} else {
return -1;
}
}

public int getCharCode(char ch) {
if (multiByteFont) {
return getCharIndex(ch);
} else {
return getUnicodeCodePoint(ch);
}
}
}

+ 134
- 26
src/java/org/apache/fop/render/pcl/fonts/PCLSoftFontManager.java View File

@@ -22,45 +22,98 @@ package org.apache.fop.render.pcl.fonts;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.fop.fonts.CustomFont;
import org.apache.fop.fonts.Typeface;
import org.apache.fop.render.java2d.CustomFontMetricsMapper;

public class PCLSoftFontManager {
private ByteArrayOutputStream baos = new ByteArrayOutputStream();
private PCLFontReader fontReader;
private PCLByteWriterUtil pclByteWriter = new PCLByteWriterUtil();
private int byte64Offset;
private List<PCLSoftFont> fonts = new ArrayList<PCLSoftFont>();
private PCLFontReaderFactory fontReaderFactory;

private static final int SOFT_FONT_SIZE = 255;

public ByteArrayOutputStream makeSoftFont(Typeface font) throws IOException {
PCLSoftFont softFont = new PCLSoftFont(fonts.size() + 1, font);
fontReaderFactory = PCLFontReaderFactory.getInstance(pclByteWriter);
List<Map<Character, Integer>> mappedGlyphs = mapFontGlyphs(font);
if (fontReaderFactory == null) {
fontReaderFactory = PCLFontReaderFactory.getInstance(pclByteWriter);
}
fontReader = fontReaderFactory.createInstance(font);
initialize();
if (mappedGlyphs.isEmpty()) {
mappedGlyphs.add(new HashMap<Character, Integer>());
}
if (fontReader != null) {
initialize();
assignFontID();
writeFontHeader();
softFont.setCharacterOffsets(fontReader.getCharacterOffsets());
softFont.setOpenFont(fontReader.getFontFile());
softFont.setReader(fontReader.getFontFileReader());
fonts.add(softFont);
for (Map<Character, Integer> glyphSet : mappedGlyphs) {
PCLSoftFont softFont = new PCLSoftFont(fonts.size() + 1, font,
mappedGlyphs.get(0).size() != 0);
softFont.setMappedChars(glyphSet);
assignFontID();
writeFontHeader(softFont.getMappedChars());
softFont.setCharacterOffsets(fontReader.getCharacterOffsets());
softFont.setOpenFont(fontReader.getFontFile());
softFont.setReader(fontReader.getFontFileReader());
fonts.add(softFont);
}
return baos;
} else {
return null;
}
}

private List<Map<Character, Integer>> mapFontGlyphs(Typeface tf) {
List<Map<Character, Integer>> mappedGlyphs = new ArrayList<Map<Character, Integer>>();
if (tf instanceof CustomFontMetricsMapper) {
CustomFontMetricsMapper fontMetrics = (CustomFontMetricsMapper) tf;
CustomFont customFont = (CustomFont) fontMetrics.getRealFont();
mappedGlyphs = mapGlyphs(customFont.getUsedGlyphs(), customFont);
}
return mappedGlyphs;
}

private List<Map<Character, Integer>> mapGlyphs(Map<Integer, Integer> usedGlyphs, CustomFont font) {
int charCount = 32;
List<Map<Character, Integer>> mappedGlyphs = new ArrayList<Map<Character, Integer>>();
Map<Character, Integer> fontGlyphs = new HashMap<Character, Integer>();
for (Entry<Integer, Integer> entry : usedGlyphs.entrySet()) {
int glyphID = entry.getKey();
if (glyphID == 0) {
continue;
}
char unicode = font.getUnicodeFromGID(glyphID);
if (charCount > SOFT_FONT_SIZE) {
mappedGlyphs.add(fontGlyphs);
charCount = 32;
fontGlyphs = new HashMap<Character, Integer>();
}
fontGlyphs.put(unicode, charCount++);
}
if (fontGlyphs.size() > 0) {
mappedGlyphs.add(fontGlyphs);
}
return mappedGlyphs;
}

private void initialize() {
baos.reset();
}

private void assignFontID() throws IOException {
baos.write(pclByteWriter.writeCommand(String.format("*c%dD", fonts.size() + 1)));
baos.write(assignFontID(fonts.size() + 1));
}

private void writeFontHeader() throws IOException {
public byte[] assignFontID(int fontID) throws IOException {
return pclByteWriter.writeCommand(String.format("*c%dD", fontID));
}

private void writeFontHeader(Map<Character, Integer> mappedGlyphs) throws IOException {
ByteArrayOutputStream header = new ByteArrayOutputStream();
header.write(pclByteWriter.unsignedInt(fontReader.getDescriptorSize()));
header.write(pclByteWriter.unsignedByte(fontReader.getHeaderFormat()));
@@ -95,22 +148,21 @@ public class PCLSoftFontManager {
header.write(pclByteWriter.unsignedInt(fontReader.getCapHeight()));
header.write(pclByteWriter.unsignedLongInt(fontReader.getFontNumber()));
header.write(pclByteWriter.padBytes(fontReader.getFontName().getBytes("US-ASCII"), 16, 32));
// Byte 64 starting point stored for checksum
byte64Offset = header.size();
header.write(pclByteWriter.unsignedInt(fontReader.getScaleFactor()));
header.write(pclByteWriter.signedInt(fontReader.getMasterUnderlinePosition()));
header.write(pclByteWriter.unsignedInt(fontReader.getMasterUnderlineThickness()));
header.write(pclByteWriter.unsignedByte(fontReader.getFontScalingTechnology()));
header.write(pclByteWriter.unsignedByte(fontReader.getVariety()));

writeSegmentedFontData(header, byte64Offset);
writeSegmentedFontData(header, mappedGlyphs);

baos.write(getFontHeaderCommand(header.size()));
baos.write(header.toByteArray());
}

private void writeSegmentedFontData(ByteArrayOutputStream header, int byte64Offset) throws IOException {
List<PCLFontSegment> fontSegments = fontReader.getFontSegments();
private void writeSegmentedFontData(ByteArrayOutputStream header,
Map<Character, Integer> mappedGlyphs) throws IOException {
List<PCLFontSegment> fontSegments = fontReader.getFontSegments(mappedGlyphs);
for (PCLFontSegment segment : fontSegments) {
writeFontSegment(header, segment);
}
@@ -135,27 +187,39 @@ public class PCLSoftFontManager {
header.write(segment.getData());
}

public List<PCLSoftFont> getSoftFonts() {
return fonts;
}

/**
* Finds a soft font associated with the given typeface. If more than one instance of the font exists (as each font
* is bound and restricted to 255 characters) it will find the last font with available capacity.
* @param font The typeface associated with the soft font
* @return Returns the PCLSoftFont with available capacity
*/
public PCLSoftFont getSoftFont(Typeface font) {
public PCLSoftFont getSoftFont(Typeface font, String text) {
for (PCLSoftFont sftFont : fonts) {
if (sftFont.getTypeface().equals(font) && sftFont.getCharCount() < 255) {
if (sftFont.getTypeface().equals(font)
&& sftFont.getCharCount() + countNonMatches(sftFont, text) < SOFT_FONT_SIZE) {
return sftFont;
}
}
return null;
}

public PCLSoftFont getSoftFontFromID(int index) {
return fonts.get(index - 1);
}

private int countNonMatches(PCLSoftFont font, String text) {
int result = 0;
for (char ch : text.toCharArray()) {
int value = font.getUnicodeCodePoint(ch);
if (value == -1) {
result++;
}
}
return result;
}

public int getSoftFontID(Typeface tf) throws IOException {
PCLSoftFont font = getSoftFont(tf);
PCLSoftFont font = getSoftFont(tf, "");
for (int i = 0; i < fonts.size(); i++) {
if (fonts.get(i).equals(font)) {
return i + 1;
@@ -164,7 +228,51 @@ public class PCLSoftFontManager {
return -1;
}

public byte[] writeFontIDCommand(int fontID) throws IOException {
return pclByteWriter.writeCommand(String.format("*c%dD", fontID));
public List<PCLTextSegment> getTextSegments(String text, Typeface font) {
List<PCLTextSegment> textSegments = new ArrayList<PCLTextSegment>();
int curFontID = -1;
String current = "";
for (char ch : text.toCharArray()) {
for (PCLSoftFont softFont : fonts) {
if (curFontID == -1) {
curFontID = softFont.getFontID();
}
if (softFont.getCharIndex(ch) == -1 || !softFont.getTypeface().equals(font)) {
continue;
}
if (current.length() > 0 && curFontID != softFont.getFontID()) {
textSegments.add(new PCLTextSegment(curFontID, current));
current = "";
curFontID = softFont.getFontID();
}
if (curFontID != softFont.getFontID()) {
curFontID = softFont.getFontID();
}
current += ch;
break;
}
}
if (current.length() > 0) {
textSegments.add(new PCLTextSegment(curFontID, current));
}
return textSegments;
}

public static class PCLTextSegment {
private String text;
private int fontID;

public PCLTextSegment(int fontID, String text) {
this.text = text;
this.fontID = fontID;
}

public String getText() {
return text;
}

public int getFontID() {
return fontID;
}
}
}

+ 8
- 6
src/java/org/apache/fop/render/pcl/fonts/truetype/PCLTTFCharacterWriter.java View File

@@ -109,19 +109,21 @@ public class PCLTTFCharacterWriter extends PCLCharacterWriter {

font.writeCharacter(unicode);

PCLCharacterDefinition newChar = new PCLCharacterDefinition(charIndex, font.getUnicodeCodePoint(unicode),
PCLCharacterDefinition newChar = new PCLCharacterDefinition(
font.getCharCode((char) unicode),
PCLCharacterFormat.TrueType,
PCLCharacterClass.TrueType, glyphData, pclByteWriter);
PCLCharacterClass.TrueType, glyphData, pclByteWriter, false);

// Handle composite character definitions
GlyfTable glyfTable = new GlyfTable(fontReader, mtx.toArray(new OFMtxEntry[mtx.size()]),
tabEntry, subsetGlyphs);
if (glyfTable.isComposite(charIndex)) {
Set<Integer> composite = glyfTable.retrieveComposedGlyphs(charIndex);
for (Integer compositeIndex : composite) {
Set<Integer> composites = glyfTable.retrieveComposedGlyphs(charIndex);
for (Integer compositeIndex : composites) {
byte[] compositeData = getGlyphData(compositeIndex);
newChar.addCompositeGlyph(new PCLCharacterDefinition(compositeIndex, 65535,
PCLCharacterFormat.TrueType, PCLCharacterClass.TrueType, compositeData, pclByteWriter));
newChar.addCompositeGlyph(new PCLCharacterDefinition(compositeIndex,
PCLCharacterFormat.TrueType,
PCLCharacterClass.TrueType, compositeData, pclByteWriter, true));
}
}


+ 141
- 29
src/java/org/apache/fop/render/pcl/fonts/truetype/PCLTTFFontReader.java View File

@@ -30,6 +30,8 @@ import java.util.Map;
import java.util.Map.Entry;

import org.apache.fop.fonts.CustomFont;
import org.apache.fop.fonts.MultiByteFont;
import org.apache.fop.fonts.SingleByteFont;
import org.apache.fop.fonts.Typeface;
import org.apache.fop.fonts.truetype.FontFileReader;
import org.apache.fop.fonts.truetype.OFDirTabEntry;
@@ -121,13 +123,15 @@ public class PCLTTFFontReader extends PCLFontReader {
protected void loadFont() throws IOException {
if (typeface instanceof CustomFontMetricsMapper) {
CustomFontMetricsMapper fontMetrics = (CustomFontMetricsMapper) typeface;
CustomFont customFont = (CustomFont) fontMetrics.getRealFont();
fontStream = customFont.getInputStream();
CustomFont font = (CustomFont) fontMetrics.getRealFont();
setFont((CustomFont) fontMetrics.getRealFont());
String fontName = font.getFullName();
fontStream = font.getInputStream();
reader = new FontFileReader(fontStream);

ttfFont = new TTFFile();
String header = OFFontLoader.readHeader(reader);
ttfFont.readFont(reader, header, customFont.getFullName());
ttfFont.readFont(reader, header, fontName);
readFontTables();
} else {
// TODO - Handle when typeface is not in the expected format for a PCL TrueType object
@@ -453,11 +457,12 @@ public class PCLTTFFontReader extends PCLFontReader {
return 0; // TrueType fonts must be set to zero
}

public List<PCLFontSegment> getFontSegments() throws IOException {
public List<PCLFontSegment> getFontSegments(Map<Character, Integer> mappedGlyphs)
throws IOException {
List<PCLFontSegment> fontSegments = new ArrayList<PCLFontSegment>();
fontSegments.add(new PCLFontSegment(SegmentID.CC, getCharacterComplement()));
fontSegments.add(new PCLFontSegment(SegmentID.PA, pclByteWriter.toByteArray(os2Table.getPanose())));
fontSegments.add(new PCLFontSegment(SegmentID.GT, getGlobalTrueTypeData()));
fontSegments.add(new PCLFontSegment(SegmentID.GT, getGlobalTrueTypeData(mappedGlyphs)));
fontSegments.add(new PCLFontSegment(SegmentID.CP, ttfFont.getCopyrightNotice().getBytes("US-ASCII")));
fontSegments.add(new PCLFontSegment(SegmentID.NULL, new byte[0]));
return fontSegments;
@@ -475,9 +480,9 @@ public class PCLTTFFontReader extends PCLFontReader {
return ccUnicode;
}

private byte[] getGlobalTrueTypeData() throws IOException {
private byte[] getGlobalTrueTypeData(Map<Character, Integer> mappedGlyphs) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
Map<OFDirTabEntry, Integer> tableOffsets = new HashMap<OFDirTabEntry, Integer>();
List<TableOffset> tableOffsets = new ArrayList<TableOffset>();
// Version
baos.write(pclByteWriter.unsignedInt(1)); // Major
baos.write(pclByteWriter.unsignedInt(0)); // Minor
@@ -505,9 +510,9 @@ public class PCLTTFFontReader extends PCLFontReader {
// Add default data tables
writeTrueTypeTable(baos, OFTableName.HEAD, tableOffsets);
writeTrueTypeTable(baos, OFTableName.HHEA, tableOffsets);
writeTrueTypeTable(baos, OFTableName.HMTX, tableOffsets);
byte[] hmtxTable = createHmtx(mappedGlyphs);
writeSubsetHMTX(baos, OFTableName.HMTX, tableOffsets, hmtxTable);
writeTrueTypeTable(baos, OFTableName.MAXP, tableOffsets);

// Write the blank GDIR directory which is built in memory on the printer
writeGDIR(baos);

@@ -516,23 +521,46 @@ public class PCLTTFFontReader extends PCLFontReader {
writeTrueTypeTable(baos, OFTableName.FPGM, tableOffsets);
writeTrueTypeTable(baos, OFTableName.PREP, tableOffsets);

baos = copyTables(tableOffsets, baos);
baos = copyTables(tableOffsets, baos, hmtxTable, mappedGlyphs.size());

return baos.toByteArray();
}

private static class TableOffset {
private long originOffset;
private long originLength;
private int newOffset;

public TableOffset(long originOffset, long originLength, int newOffset) {
this.originOffset = originOffset;
this.originLength = originLength;
this.newOffset = newOffset;
}

public long getOriginOffset() {
return originOffset;
}

public long getOriginLength() {
return originLength;
}

public int getNewOffset() {
return newOffset;
}
}

private void writeTrueTypeTable(ByteArrayOutputStream baos, OFTableName table,
Map<OFDirTabEntry, Integer> tableOffsets) throws IOException, UnsupportedEncodingException {
List<TableOffset> tableOffsets) throws IOException, UnsupportedEncodingException {
OFDirTabEntry tabEntry = ttfFont.getDirectoryEntry(table);
if (tabEntry != null) {
baos.write(tabEntry.getTag());
baos.write(pclByteWriter.unsignedLongInt(tabEntry.getChecksum()));
tableOffsets.put(tabEntry, baos.size());
TableOffset newTableOffset = new TableOffset(tabEntry.getOffset(),
tabEntry.getLength(), baos.size());
tableOffsets.add(newTableOffset);
baos.write(pclByteWriter.unsignedLongInt(0)); // Offset to be set later
long length = (tabEntry.getLength() > HMTX_RESTRICT_SIZE)
? HMTX_RESTRICT_SIZE
: tabEntry.getLength();
baos.write(pclByteWriter.unsignedLongInt(length));
baos.write(pclByteWriter.unsignedLongInt(tabEntry.getLength()));
}
}

@@ -543,26 +571,35 @@ public class PCLTTFFontReader extends PCLFontReader {
baos.write(pclByteWriter.unsignedLongInt(0)); // Length
}

private ByteArrayOutputStream copyTables(Map<OFDirTabEntry, Integer> tableOffsets, ByteArrayOutputStream baos)
private ByteArrayOutputStream copyTables(List<TableOffset> tableOffsets,
ByteArrayOutputStream baos, byte[] hmtxTable, int hmtxSize)
throws IOException {
Map<Integer, byte[]> offsetValues = new HashMap<Integer, byte[]>();
//for (OFDirTabEntry table : tableOffsets.keySet()) {
for (Entry<OFDirTabEntry, Integer> table : tableOffsets.entrySet()) {
byte[] tableData = reader.getBytes((int) table.getKey().getOffset(), (int) table.getKey().getLength());
if (tableData.length > HMTX_RESTRICT_SIZE) {
byte[] truncated = new byte[HMTX_RESTRICT_SIZE];
System.arraycopy(tableData, 0, truncated, 0, HMTX_RESTRICT_SIZE);
tableData = truncated;
for (TableOffset tableOffset : tableOffsets) {
offsetValues.put(tableOffset.getNewOffset(), pclByteWriter.unsignedLongInt(baos.size()));
if (tableOffset.getOriginOffset() == -1) { // Update the offset in the table directory
baos.write(hmtxTable);
} else {
byte[] tableData = reader.getBytes((int) tableOffset.getOriginOffset(),
(int) tableOffset.getOriginLength());
int index = tableOffsets.indexOf(tableOffset);
if (index == 1) {
tableData = updateHHEA(tableData, hmtxSize + 33);
}

// Write the table data to the end of the TrueType segment output
baos.write(tableData);
}
// Update the offset in the table directory
offsetValues.put(table.getValue(), pclByteWriter.unsignedLongInt(baos.size()));
// Write the table data to the end of the TrueType segment output
baos.write(tableData);
}
baos = updateOffsets(baos, offsetValues);
return baos;
}

private byte[] updateHHEA(byte[] tableData, int hmtxSize) {
writeUShort(tableData, tableData.length - 2, hmtxSize);
return tableData;
}

private ByteArrayOutputStream updateOffsets(ByteArrayOutputStream baos, Map<Integer, byte[]> offsets)
throws IOException {
byte[] softFont = baos.toByteArray();
@@ -616,4 +653,79 @@ public class PCLTTFFontReader extends PCLFontReader {
public FontFileReader getFontFileReader() {
return reader;
}
}

private void writeSubsetHMTX(ByteArrayOutputStream baos, OFTableName table,
List<TableOffset> tableOffsets, byte[] hmtxTable) throws IOException {
OFDirTabEntry tabEntry = ttfFont.getDirectoryEntry(table);
if (tabEntry != null) {
baos.write(tabEntry.getTag());
// Override the original checksum for the subset version
baos.write(pclByteWriter.unsignedLongInt(getCheckSum(hmtxTable, 0, hmtxTable.length)));
TableOffset newTableOffset = new TableOffset(-1, hmtxTable.length, baos.size());
tableOffsets.add(newTableOffset);
baos.write(pclByteWriter.unsignedLongInt(0)); // Offset to be set later
baos.write(pclByteWriter.unsignedLongInt(hmtxTable.length));
}
}

protected static int getCheckSum(byte[] data, int start, int size) {
// All the tables here are aligned on four byte boundaries
// Add remainder to size if it's not a multiple of 4
int remainder = size % 4;
if (remainder != 0) {
size += remainder;
}

long sum = 0;

for (int i = 0; i < size; i += 4) {
long l = 0;
for (int j = 0; j < 4; j++) {
l <<= 8;
if (data.length > (start + i + j)) {
l |= data[start + i + j] & 0xff;
}
}
sum += l;
}
return (int) sum;
}

protected byte[] createHmtx(Map<Character, Integer> mappedGlyphs) throws IOException {
byte[] hmtxTable = new byte[((mappedGlyphs.size() + 32) * 4)];
OFDirTabEntry entry = ttfFont.getDirectoryEntry(OFTableName.HMTX);

if (entry != null) {
for (Entry<Character, Integer> glyphSubset : mappedGlyphs.entrySet()) {
char unicode = glyphSubset.getKey();
int originalIndex = 0;
int softFontGlyphIndex = glyphSubset.getValue();
if (font instanceof MultiByteFont) {
originalIndex = ((MultiByteFont) font).getGIDFromChar(unicode);

writeUShort(hmtxTable, (softFontGlyphIndex) * 4,
ttfFont.getMtx().get(originalIndex).getWx());
writeUShort(hmtxTable, (softFontGlyphIndex) * 4 + 2,
ttfFont.getMtx().get(originalIndex).getLsb());
} else {
originalIndex = ((SingleByteFont) font).getGIDFromChar(unicode);

writeUShort(hmtxTable, (softFontGlyphIndex) * 4,
((SingleByteFont) font).getWidth(originalIndex, 1));
writeUShort(hmtxTable, (softFontGlyphIndex) * 4 + 2, 0);
}
}
}
return hmtxTable;
}

/**
* Appends a USHORT to the output array, updates currentPost but not realSize
*/
private void writeUShort(byte[] out, int offset, int s) {
byte b1 = (byte) ((s >> 8) & 0xff);
byte b2 = (byte) (s & 0xff);
out[offset] = b1;
out[offset + 1] = b2;
}
}

+ 79
- 1
test/java/org/apache/fop/render/pcl/fonts/PCLTTFFontReaderTestCase.java View File

@@ -18,9 +18,12 @@
/* $Id$ */
package org.apache.fop.render.pcl.fonts;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@@ -33,6 +36,7 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import org.apache.fop.fonts.CustomFont;
import org.apache.fop.fonts.SingleByteFont;
import org.apache.fop.render.java2d.CustomFontMetricsMapper;
import org.apache.fop.render.pcl.fonts.PCLFontSegment.SegmentID;
import org.apache.fop.render.pcl.fonts.truetype.PCLTTFFontReader;
@@ -53,7 +57,13 @@ public class PCLTTFFontReaderTestCase {
CustomFont sbFont = mock(CustomFont.class);
when(sbFont.getInputStream()).thenReturn(new FileInputStream(new File(TEST_FONT_A)));
when(customFont.getRealFont()).thenReturn(sbFont);
SingleByteFont font = mock(SingleByteFont.class);
when(font.getGIDFromChar('h')).thenReturn(104);
when(font.getGIDFromChar('e')).thenReturn(101);
when(font.getGIDFromChar('l')).thenReturn(108);
when(font.getGIDFromChar('o')).thenReturn(111);
PCLTTFFontReader reader = new MockPCLTTFFontReader(customFont, byteWriter);
reader.setFont(font);
verifyFontData(reader);
validateOffsets(reader);
validateFontSegments(reader);
@@ -103,7 +113,13 @@ public class PCLTTFFontReaderTestCase {
* @throws IOException
*/
private void validateFontSegments(PCLTTFFontReader reader) throws IOException {
List<PCLFontSegment> segments = reader.getFontSegments();
HashMap<Character, Integer> mappedChars = new HashMap<Character, Integer>();
mappedChars.put('H', 1);
mappedChars.put('e', 1);
mappedChars.put('l', 1);
mappedChars.put('o', 1);

List<PCLFontSegment> segments = reader.getFontSegments(mappedChars);
assertEquals(segments.size(), 5);
for (PCLFontSegment segment : segments) {
if (segment.getIdentifier() == SegmentID.PA) {
@@ -111,10 +127,72 @@ public class PCLTTFFontReaderTestCase {
assertEquals(segment.getData().length, 10);
byte[] panose = {2, 6, 6, 3, 5, 6, 5, 2, 2, 4};
assertArrayEquals(segment.getData(), panose);
} else if (segment.getIdentifier() == SegmentID.GT) {
verifyGlobalTrueTypeData(segment, mappedChars.size());
} else if (segment.getIdentifier() == SegmentID.NULL) {
// Terminating segment
assertEquals(segment.getData().length, 0);
}
}
}

private void verifyGlobalTrueTypeData(PCLFontSegment segment, int mappedCharsSize)
throws IOException {
byte[] ttfData = segment.getData();
int currentPos = 0;
//Version
assertEquals(readInt(new byte[]{ttfData[currentPos++], ttfData[currentPos++]}), 1);
assertEquals(readInt(new byte[]{ttfData[currentPos++], ttfData[currentPos++]}), 0);
//Number of tables
int numTables = readInt(new byte[]{ttfData[currentPos++], ttfData[currentPos++]});
assertEquals(numTables, 8);
//Search range
assertEquals(readInt(new byte[]{ttfData[currentPos++], ttfData[currentPos++]}), 128);
//Entry Selector
assertEquals(readInt(new byte[]{ttfData[currentPos++], ttfData[currentPos++]}), 3);
//Range shift
assertEquals(readInt(new byte[]{ttfData[currentPos++], ttfData[currentPos++]}), 0);
String[] validTags = {"head", "hhea", "hmtx", "maxp", "gdir"};
int matches = 0;
for (int i = 0; i < numTables; i++) {
String tag = readTag(new byte[]{ttfData[currentPos++], ttfData[currentPos++],
ttfData[currentPos++], ttfData[currentPos++]});
if (Arrays.asList(validTags).contains(tag)) {
matches++;
}
if (tag.equals("hmtx")) {
currentPos += 4;
int offset = readLong(new byte[]{ttfData[currentPos++], ttfData[currentPos++],
ttfData[currentPos++], ttfData[currentPos++]});
int length = readLong(new byte[]{ttfData[currentPos++], ttfData[currentPos++],
ttfData[currentPos++], ttfData[currentPos++]});
verifyHmtx(ttfData, offset, length, mappedCharsSize);
} else {
currentPos += 12;
}
}
assertEquals(matches, 5);
}

private void verifyHmtx(byte[] ttfData, int offset, int length, int mappedCharsSize)
throws IOException {
ByteArrayInputStream bais = new ByteArrayInputStream(ttfData);
byte[] subsetHmtx = new byte[length];
bais.skip(offset);
bais.read(subsetHmtx);
assertEquals(subsetHmtx.length, (mappedCharsSize + 32) * 4);
}

private int readInt(byte[] bytes) {
return ((0xFF & bytes[0]) << 8) | (0xFF & bytes[1]);
}

private int readLong(byte[] bytes) {
return ((0xFF & bytes[0]) << 24) | ((0xFF & bytes[1]) << 16) | ((0xFF & bytes[2]) << 8)
| (0xFF & bytes[3]);
}

private String readTag(byte[] tag) {
return new String(tag);
}
}

+ 1
- 1
test/java/org/apache/fop/render/pcl/fonts/truetype/PCLTTFCharacterWriterTestCase.java View File

@@ -46,7 +46,7 @@ public class PCLTTFCharacterWriterTestCase {
public void verifyCharacterDefinition() throws Exception {
CustomFont sbFont = mock(CustomFont.class);
when(customFont.getRealFont()).thenReturn(sbFont);
softFont = new PCLSoftFont(1, customFont);
softFont = new PCLSoftFont(1, customFont, false);
TTFFile openFont = new TTFFile();
FontFileReader reader = new FontFileReader(new FileInputStream(new File(TEST_FONT_A)));
String header = OFFontLoader.readHeader(reader);

Loading…
Cancel
Save