git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1695082 13f79535-47bb-0310-9956-ffa450edef68tags/fop-2_1
<Bug pattern="OS_OPEN_STREAM_EXCEPTION_PATH"/> | <Bug pattern="OS_OPEN_STREAM_EXCEPTION_PATH"/> | ||||
</Or> | </Or> | ||||
</Match> | </Match> | ||||
<Match> | |||||
<Class name="org.apache.fop.render.pcl.fonts.truetype.PCLTTFTable"/> | |||||
<Bug pattern="ICAST_INTEGER_MULTIPLY_CAST_TO_LONG"/> | |||||
</Match> | |||||
<!-- END - APPROVED EXCLUSIONS --> | <!-- END - APPROVED EXCLUSIONS --> | ||||
<!-- START - TEMPORARY (UNAPPROVED) EXCLUSIONS --> | <!-- START - TEMPORARY (UNAPPROVED) EXCLUSIONS --> |
return index; | return index; | ||||
} | } | ||||
/** {@inheritDoc} */ | |||||
@Override | |||||
public char getUnicodeFromGID(int glyphIndex) { | |||||
return ' '; | |||||
} | |||||
/** {@inheritDoc} */ | |||||
@Override | |||||
public int getGIDFromChar(char ch) { | |||||
return ch; | |||||
} | |||||
/** {@inheritDoc} */ | /** {@inheritDoc} */ | ||||
public char getUnicode(int index) { | public char getUnicode(int index) { | ||||
initGlyphIndices(); | initGlyphIndices(); | ||||
public int[] getWidths() { | public int[] getWidths() { | ||||
return font.getWidths(); | return font.getWidths(); | ||||
} | } | ||||
} | } |
*/ | */ | ||||
char getUnicode(int index); | 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 | * 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 | * subset, yet, it is added and a new character selector returned. Otherwise, the already |
*/ | */ | ||||
private Map<Integer, Character> usedCharsIndex = new HashMap<Integer, Character>(); | 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; | private final MultiByteFont font; | ||||
public CIDSubset(MultiByteFont mbf) { | public CIDSubset(MultiByteFont mbf) { | ||||
usedGlyphs.put(glyphIndex, selector); | usedGlyphs.put(glyphIndex, selector); | ||||
usedGlyphsIndex.put(selector, glyphIndex); | usedGlyphsIndex.put(selector, glyphIndex); | ||||
usedCharsIndex.put(selector, unicode); | usedCharsIndex.put(selector, unicode); | ||||
charToGIDs.put(unicode, glyphIndex); | |||||
usedGlyphsCount++; | usedGlyphsCount++; | ||||
return selector; | return selector; | ||||
} else { | } else { | ||||
return Collections.unmodifiableMap(this.usedGlyphs); | 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} */ | /** {@inheritDoc} */ | ||||
public char[] getChars() { | public char[] getChars() { | ||||
char[] charArray = new char[usedGlyphsCount]; | char[] charArray = new char[usedGlyphsCount]; |
this.strikeoutThickness = strikeoutThickness; | 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); | |||||
} | } |
return cidSet.getGlyphs(); | 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. | * Establishes the glyph definition table. | ||||
* @param gdef the glyph definition table to be used by this font | * @param gdef the glyph definition table to be used by this font |
private LinkedHashMap<Integer, String> usedGlyphNames; | private LinkedHashMap<Integer, String> usedGlyphNames; | ||||
private Map<Integer, Integer> usedGlyphs; | private Map<Integer, Integer> usedGlyphs; | ||||
private Map<Integer, Character> usedCharsIndex; | private Map<Integer, Character> usedCharsIndex; | ||||
private Map<Character, Integer> charGIDMappings; | |||||
public SingleByteFont(InternalResourceResolver resourceResolver) { | public SingleByteFont(InternalResourceResolver resourceResolver) { | ||||
super(resourceResolver); | super(resourceResolver); | ||||
usedGlyphNames = new LinkedHashMap<Integer, String>(); | usedGlyphNames = new LinkedHashMap<Integer, String>(); | ||||
usedGlyphs = new HashMap<Integer, Integer>(); | usedGlyphs = new HashMap<Integer, Integer>(); | ||||
usedCharsIndex = new HashMap<Integer, Character>(); | usedCharsIndex = new HashMap<Integer, Character>(); | ||||
charGIDMappings = new HashMap<Character, Integer>(); | |||||
// The zeroth value is reserved for .notdef | // The zeroth value is reserved for .notdef | ||||
usedGlyphs.put(0, 0); | usedGlyphs.put(0, 0); | ||||
int selector = usedGlyphsCount; | int selector = usedGlyphsCount; | ||||
usedGlyphs.put(glyphIndex, selector); | usedGlyphs.put(glyphIndex, selector); | ||||
usedCharsIndex.put(selector, unicode); | usedCharsIndex.put(selector, unicode); | ||||
charGIDMappings.put(unicode, glyphIndex); | |||||
usedGlyphsCount++; | usedGlyphsCount++; | ||||
return selector; | return selector; | ||||
} else { | } else { | ||||
return getUnicode(selector); | 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) { | public void mapUsedGlyphName(int gid, String value) { | ||||
usedGlyphNames.put(gid, value); | usedGlyphNames.put(gid, value); | ||||
} | } |
/** All the glyphs that are composed, but do not appear in the subset. */ | /** All the glyphs that are composed, but do not appear in the subset. */ | ||||
protected Set<Integer> composedGlyphs = new TreeSet<Integer>(); | protected Set<Integer> composedGlyphs = new TreeSet<Integer>(); | ||||
protected GlyfTable(FontFileReader in, OFMtxEntry[] metrics, OFDirTabEntry dirTableEntry, | |||||
public GlyfTable(FontFileReader in, OFMtxEntry[] metrics, OFDirTabEntry dirTableEntry, | |||||
Map<Integer, Integer> glyphs) throws IOException { | Map<Integer, Integer> glyphs) throws IOException { | ||||
mtxTab = metrics; | mtxTab = metrics; | ||||
tableOffset = dirTableEntry.getOffset(); | tableOffset = dirTableEntry.getOffset(); | ||||
} while (GlyfFlags.hasMoreComposites(flags)); | } while (GlyfFlags.hasMoreComposites(flags)); | ||||
} | } | ||||
private boolean isComposite(int indexInOriginal) throws IOException { | |||||
public boolean isComposite(int indexInOriginal) throws IOException { | |||||
int numberOfContours = in.readTTFShort(tableOffset + mtxTab[indexInOriginal].getOffset()); | int numberOfContours = in.readTTFShort(tableOffset + mtxTab[indexInOriginal].getOffset()); | ||||
return numberOfContours < 0; | return numberOfContours < 0; | ||||
} | } | ||||
* @return the set of glyph indices this glyph composes | * @return the set of glyph indices this glyph composes | ||||
* @throws IOException an I/O error | * @throws IOException an I/O error | ||||
*/ | */ | ||||
private Set<Integer> retrieveComposedGlyphs(int indexInOriginal) | |||||
public Set<Integer> retrieveComposedGlyphs(int indexInOriginal) | |||||
throws IOException { | throws IOException { | ||||
Set<Integer> composedGlyphs = new HashSet<Integer>(); | Set<Integer> composedGlyphs = new HashSet<Integer>(); | ||||
long offset = tableOffset + mtxTab[indexInOriginal].getOffset() + 10; | long offset = tableOffset + mtxTab[indexInOriginal].getOffset() + 10; |
public class OFDirTabEntry { | public class OFDirTabEntry { | ||||
private byte[] tag = new byte[4]; | private byte[] tag = new byte[4]; | ||||
private int checksum; | |||||
private long checksum; | |||||
private long offset; | private long offset; | ||||
private long length; | private long length; | ||||
* Returns the checksum. | * Returns the checksum. | ||||
* @return int | * @return int | ||||
*/ | */ | ||||
public int getChecksum() { | |||||
public long getChecksum() { | |||||
return checksum; | return checksum; | ||||
} | } | ||||
long offset) throws IOException { | long offset) throws IOException { | ||||
OFDirTabEntry dt = dirTabs.get(tableName); | OFDirTabEntry dt = dirTabs.get(tableName); | ||||
if (dt == null) { | if (dt == null) { | ||||
log.error("Dirtab " + tableName.getName() + " not found."); | |||||
log.info("Dirtab " + tableName.getName() + " not found."); | |||||
return false; | return false; | ||||
} else { | } else { | ||||
in.seekSet(dt.getOffset() + offset); | in.seekSet(dt.getOffset() + offset); | ||||
return fbb; | return fbb; | ||||
} | } | ||||
/** | |||||
* Returns the original bounding box values from the HEAD table | |||||
* @return An array of bounding box values | |||||
*/ | |||||
public int[] getBBoxRaw() { | |||||
int[] bbox = {fontBBox1, fontBBox2, fontBBox3, fontBBox4}; | |||||
return bbox; | |||||
} | |||||
/** | /** | ||||
* Returns the LowerCaseAscent attribute of the font. | * Returns the LowerCaseAscent attribute of the font. | ||||
* @return int The LowerCaseAscent | * @return int The LowerCaseAscent | ||||
return convertTTFUnit2PDFUnit(ansiWidth[idx]); | return convertTTFUnit2PDFUnit(ansiWidth[idx]); | ||||
} | } | ||||
/** | |||||
* Returns the width of a given character in raw units | |||||
* @param idx Index of the character | |||||
* @return int Width in it's raw form stored in the font | |||||
*/ | |||||
public int getCharWidthRaw(int idx) { | |||||
if (ansiWidth != null) { | |||||
return ansiWidth[idx]; | |||||
} | |||||
return -1; | |||||
} | |||||
/** | /** | ||||
* Returns the kerning table. | * Returns the kerning table. | ||||
* @return Map The kerning table | * @return Map The kerning table | ||||
IOUtils.closeQuietly(stream); | IOUtils.closeQuietly(stream); | ||||
} | } | ||||
} | } | ||||
public String getCopyrightNotice() { | |||||
return notice; | |||||
} | |||||
} | } |
: (fontFile.readTTFUShort() << 1)); | : (fontFile.readTTFUShort() << 1)); | ||||
} | } | ||||
/** | |||||
* Gets the last location of the glyf table | |||||
* @return The last location as a long | |||||
*/ | |||||
public long getLastGlyfLocation() { | |||||
return lastLoca; | |||||
} | |||||
@Override | @Override | ||||
protected void initializeFont(FontFileReader in) throws IOException { | protected void initializeFont(FontFileReader in) throws IOException { | ||||
fontFile = in; | fontFile = in; |
font = new CustomFontMetricsMapper(fontMetrics, fontSource); | font = new CustomFontMetricsMapper(fontMetrics, fontSource); | ||||
} else { | } else { | ||||
FontUris fontUris = new FontUris(fontURI, null); | FontUris fontUris = new FontUris(fontURI, null); | ||||
CustomFont fontMetrics = FontLoader.loadFont(fontUris, null, true, | |||||
CustomFont fontMetrics = FontLoader.loadFont(fontUris, | |||||
configFontInfo.getSubFontName(), true, | |||||
configFontInfo.getEmbeddingMode(), configFontInfo.getEncodingMode(), | configFontInfo.getEmbeddingMode(), configFontInfo.getEncodingMode(), | ||||
configFontInfo.getKerning(), configFontInfo.getAdvanced(), resourceResolver); | configFontInfo.getKerning(), configFontInfo.getAdvanced(), resourceResolver); | ||||
font = new CustomFontMetricsMapper(fontMetrics); | font = new CustomFontMetricsMapper(fontMetrics); |
} | } | ||||
} | } | ||||
public Typeface getRealFont() { | |||||
return typeface; | |||||
} | |||||
} | } |
return selectFont(gen, name, size); | return selectFont(gen, name, size); | ||||
} | } | ||||
private static boolean selectFont(PCLGenerator gen, String name, int size) throws IOException { | |||||
protected static boolean selectFont(PCLGenerator gen, String name, int size) throws IOException { | |||||
int fontcode = 0; | int fontcode = 0; | ||||
if (name.length() > 1 && name.charAt(0) == 'F') { | if (name.length() > 1 && name.charAt(0) == 'F') { | ||||
try { | try { |
*/ | */ | ||||
void paperTypeUnavailable(Object source, long pageWidth, long pageHeight, String fallbackPaper); | void paperTypeUnavailable(Object source, long pageWidth, long pageHeight, String fallbackPaper); | ||||
/** | |||||
* The font type is not supported for PCL output. | |||||
* @param source The event source | |||||
* @param fontName The name of the font not supported | |||||
* @param supportedTypes The types of fonts currently supported | |||||
* @event.severity ERROR | |||||
*/ | |||||
void fontTypeNotSupported(Object source, String fontName, String supportedTypes); | |||||
} | } |
<?xml version="1.0" encoding="UTF-8"?> | <?xml version="1.0" encoding="UTF-8"?> | ||||
<catalogue xml:lang="en"> | <catalogue xml:lang="en"> | ||||
<message key="paperTypeUnavailable">Paper type ({pageWidth} x {pageHeight} mpt) could not be determined. Falling back to: {fallbackPaper}</message> | <message key="paperTypeUnavailable">Paper type ({pageWidth} x {pageHeight} mpt) could not be determined. Falling back to: {fallbackPaper}</message> | ||||
<message key="fontTypeNotSupported">The font '{fontName}' is not supported. PCL output currently only supports {supportedTypes}</message> | |||||
</catalogue> | </catalogue> |
out.write(s.getBytes(ISO_8859_1)); | out.write(s.getBytes(ISO_8859_1)); | ||||
} | } | ||||
/** | |||||
* Writes raw bytes to the output stream | |||||
* @param bytes The bytes | |||||
* @throws IOException In case of an I/O error | |||||
*/ | |||||
public void writeBytes(byte[] bytes) throws IOException { | |||||
out.write(bytes); | |||||
} | |||||
/** | /** | ||||
* Formats a double value with two decimal positions for PCL output. | * Formats a double value with two decimal positions for PCL output. | ||||
* | * |
import java.awt.geom.AffineTransform; | import java.awt.geom.AffineTransform; | ||||
import java.awt.geom.Point2D; | import java.awt.geom.Point2D; | ||||
import java.awt.geom.Rectangle2D; | import java.awt.geom.Rectangle2D; | ||||
import java.io.ByteArrayOutputStream; | |||||
import java.io.IOException; | import java.io.IOException; | ||||
import java.util.List; | |||||
import java.util.Map; | import java.util.Map; | ||||
import java.util.Stack; | import java.util.Stack; | ||||
import org.apache.xmlgraphics.java2d.GraphicContext; | import org.apache.xmlgraphics.java2d.GraphicContext; | ||||
import org.apache.xmlgraphics.java2d.Graphics2DImagePainter; | import org.apache.xmlgraphics.java2d.Graphics2DImagePainter; | ||||
import org.apache.fop.fonts.CIDFontType; | |||||
import org.apache.fop.fonts.Font; | import org.apache.fop.fonts.Font; | ||||
import org.apache.fop.fonts.FontTriplet; | import org.apache.fop.fonts.FontTriplet; | ||||
import org.apache.fop.fonts.FontType; | |||||
import org.apache.fop.fonts.LazyFont; | |||||
import org.apache.fop.fonts.MultiByteFont; | |||||
import org.apache.fop.fonts.Typeface; | |||||
import org.apache.fop.render.ImageHandlerUtil; | import org.apache.fop.render.ImageHandlerUtil; | ||||
import org.apache.fop.render.RenderingContext; | import org.apache.fop.render.RenderingContext; | ||||
import org.apache.fop.render.intermediate.AbstractIFPainter; | import org.apache.fop.render.intermediate.AbstractIFPainter; | ||||
import org.apache.fop.render.intermediate.IFException; | import org.apache.fop.render.intermediate.IFException; | ||||
import org.apache.fop.render.intermediate.IFState; | import org.apache.fop.render.intermediate.IFState; | ||||
import org.apache.fop.render.intermediate.IFUtil; | import org.apache.fop.render.intermediate.IFUtil; | ||||
import org.apache.fop.render.java2d.CustomFontMetricsMapper; | |||||
import org.apache.fop.render.java2d.FontMetricsMapper; | import org.apache.fop.render.java2d.FontMetricsMapper; | ||||
import org.apache.fop.render.java2d.Java2DPainter; | 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.BorderProps; | ||||
import org.apache.fop.traits.RuleStyle; | import org.apache.fop.traits.RuleStyle; | ||||
import org.apache.fop.util.CharUtilities; | import org.apache.fop.util.CharUtilities; | ||||
private Stack<GraphicContext> graphicContextStack = new Stack<GraphicContext>(); | private Stack<GraphicContext> graphicContextStack = new Stack<GraphicContext>(); | ||||
private GraphicContext graphicContext = new GraphicContext(); | private GraphicContext graphicContext = new GraphicContext(); | ||||
private PCLSoftFontManager sfManager = new PCLSoftFontManager(); | |||||
/** | /** | ||||
* Main constructor. | * Main constructor. | ||||
* @param parent the parent document handler | * @param parent the parent document handler | ||||
//TODO Ignored: state.getFontVariant() | //TODO Ignored: state.getFontVariant() | ||||
//TODO Opportunity for font caching if font state is more heavily used | //TODO Opportunity for font caching if font state is more heavily used | ||||
String fontKey = getFontKey(triplet); | String fontKey = getFontKey(triplet); | ||||
boolean pclFont = getPCLUtil().isAllTextAsBitmaps() ? false | |||||
: HardcodedFonts.setFont(gen, fontKey, state.getFontSize(), text); | |||||
Typeface tf = getTypeface(fontKey); | |||||
boolean drawAsBitmaps = getPCLUtil().isAllTextAsBitmaps(); | |||||
boolean pclFont = HardcodedFonts.setFont(gen, fontKey, state.getFontSize(), text); | |||||
if (pclFont) { | if (pclFont) { | ||||
drawTextNative(x, y, letterSpacing, wordSpacing, dp, text, triplet); | drawTextNative(x, y, letterSpacing, wordSpacing, dp, text, triplet); | ||||
} else { | } else { | ||||
drawTextAsBitmap(x, y, letterSpacing, wordSpacing, dp, text, triplet); | |||||
if (DEBUG) { | |||||
state.setTextColor(Color.GRAY); | |||||
HardcodedFonts.setFont(gen, "F1", state.getFontSize(), text); | |||||
drawTextNative(x, y, letterSpacing, wordSpacing, dp, text, triplet); | |||||
// TrueType conversion to a soft font (PCL 5 Technical Reference - Chapter 11) | |||||
if (!drawAsBitmaps && isTrueType(tf)) { | |||||
boolean madeSF = false; | |||||
if (sfManager.getSoftFont(tf, text) == null) { | |||||
madeSF = true; | |||||
ByteArrayOutputStream baos = sfManager.makeSoftFont(tf); | |||||
if (baos != null) { | |||||
gen.writeBytes(baos.toByteArray()); | |||||
} | |||||
} | |||||
String formattedSize = gen.formatDouble2(state.getFontSize() / 1000.0); | |||||
gen.writeCommand(String.format("(s%sV", formattedSize)); | |||||
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); | |||||
} | |||||
} | |||||
} else { | |||||
drawTextAsBitmap(x, y, letterSpacing, wordSpacing, dp, text, triplet); | |||||
if (DEBUG) { | |||||
state.setTextColor(Color.GRAY); | |||||
HardcodedFonts.setFont(gen, "F1", state.getFontSize(), text); | |||||
drawTextNative(x, y, letterSpacing, wordSpacing, dp, text, triplet); | |||||
} | |||||
} | } | ||||
} | } | ||||
} catch (IOException ioe) { | } catch (IOException ioe) { | ||||
} | } | ||||
} | } | ||||
private boolean isTrueType(Typeface tf) { | |||||
if (tf.getFontType().equals(FontType.TRUETYPE)) { | |||||
return true; | |||||
} else if (tf instanceof CustomFontMetricsMapper) { | |||||
Typeface realFont = ((CustomFontMetricsMapper) tf).getRealFont(); | |||||
if (realFont instanceof MultiByteFont) { | |||||
return ((MultiByteFont) realFont).getCIDType().equals(CIDFontType.CIDTYPE2); | |||||
} | |||||
} | |||||
return false; | |||||
} | |||||
private Typeface getTypeface(String fontName) { | |||||
if (fontName == null) { | |||||
throw new NullPointerException("fontName must not be null"); | |||||
} | |||||
Typeface tf = getFontInfo().getFonts().get(fontName); | |||||
if (tf instanceof LazyFont) { | |||||
tf = ((LazyFont)tf).getRealFont(); | |||||
} | |||||
return tf; | |||||
} | |||||
private void drawTextNative(int x, int y, int letterSpacing, int wordSpacing, int[][] dp, | private void drawTextNative(int x, int y, int letterSpacing, int wordSpacing, int[][] dp, | ||||
String text, FontTriplet triplet) throws IOException { | String text, FontTriplet triplet) throws IOException { | ||||
Color textColor = state.getTextColor(); | Color textColor = state.getTextColor(); | ||||
} | } | ||||
private void drawTextUsingSoftFont(int x, int y, int letterSpacing, int wordSpacing, int[][] dp, | |||||
String text, FontTriplet triplet, PCLSoftFont softFont) throws IOException { | |||||
Color textColor = state.getTextColor(); | |||||
if (textColor != null) { | |||||
gen.setTransparencyMode(true, false); | |||||
gen.selectGrayscale(textColor); | |||||
} | |||||
if (x != -1 && y != -1) { | |||||
setCursorPos(x, y); | |||||
} | |||||
float fontSize = state.getFontSize() / 1000f; | |||||
Font font = getFontInfo().getFontInstance(triplet, state.getFontSize()); | |||||
int l = text.length(); | |||||
int[] dx = IFUtil.convertDPToDX(dp); | |||||
int dxl = (dx != null ? dx.length : 0); | |||||
StringBuffer sb = new StringBuffer(Math.max(16, l)); | |||||
if (dx != null && dxl > 0 && dx[0] != 0) { | |||||
sb.append("\u001B&a+").append(gen.formatDouble2(dx[0] / 100.0)).append('H'); | |||||
} | |||||
String current = ""; | |||||
for (int i = 0; i < l; i++) { | |||||
char orgChar = text.charAt(i); | |||||
float glyphAdjust = 0; | |||||
if (!font.hasChar(orgChar)) { | |||||
if (CharUtilities.isFixedWidthSpace(orgChar)) { | |||||
//Fixed width space are rendered as spaces so copy/paste works in a reader | |||||
char ch = font.mapChar(CharUtilities.SPACE); | |||||
int spaceDiff = font.getCharWidth(ch) - font.getCharWidth(orgChar); | |||||
glyphAdjust = -(10 * spaceDiff / fontSize); | |||||
} | |||||
} | |||||
if ((wordSpacing != 0) && CharUtilities.isAdjustableSpace(orgChar)) { | |||||
glyphAdjust += wordSpacing; | |||||
} | |||||
current += orgChar; | |||||
glyphAdjust += letterSpacing; | |||||
if (dx != null && i < dxl - 1) { | |||||
glyphAdjust += dx[i + 1]; | |||||
} | |||||
if (glyphAdjust != 0) { | |||||
gen.getOutputStream().write(sb.toString().getBytes(gen.getTextEncoding())); | |||||
for (int j = 0; j < current.length(); j++) { | |||||
gen.getOutputStream().write(softFont.getCharCode(current.charAt(j))); | |||||
} | |||||
sb = new StringBuffer(); | |||||
String command = (glyphAdjust > 0) ? "\u001B&a+" : "\u001B&a"; | |||||
sb.append(command).append(gen.formatDouble2(glyphAdjust / 100.0)).append('H'); | |||||
current = ""; | |||||
} | |||||
} | |||||
if (!current.equals("")) { | |||||
gen.getOutputStream().write(sb.toString().getBytes(gen.getTextEncoding())); | |||||
for (int i = 0; i < current.length(); i++) { | |||||
gen.getOutputStream().write(softFont.getCharCode(current.charAt(i))); | |||||
} | |||||
} | |||||
} | |||||
private static final double SAFETY_MARGIN_FACTOR = 0.05; | private static final double SAFETY_MARGIN_FACTOR = 0.05; | ||||
private Rectangle getTextBoundingBox(int x, int y, | |||||
int letterSpacing, int wordSpacing, int[][] dp, | |||||
String text, | |||||
Font font, FontMetricsMapper metrics) { | |||||
private Rectangle getTextBoundingBox(int x, int y, int letterSpacing, int wordSpacing, | |||||
int[][] dp, String text, Font font, FontMetricsMapper metrics) { | |||||
int maxAscent = metrics.getMaxAscent(font.getFontSize()) / 1000; | int maxAscent = metrics.getMaxAscent(font.getFontSize()) / 1000; | ||||
int descent = metrics.getDescender(font.getFontSize()) / 1000; //is negative | |||||
int safetyMargin = (int)(SAFETY_MARGIN_FACTOR * font.getFontSize()); | |||||
Rectangle boundingRect = new Rectangle( | |||||
x, y - maxAscent - safetyMargin, | |||||
0, maxAscent - descent + 2 * safetyMargin); | |||||
int descent = metrics.getDescender(font.getFontSize()) / 1000; // is negative | |||||
int safetyMargin = (int) (SAFETY_MARGIN_FACTOR * font.getFontSize()); | |||||
Rectangle boundingRect = new Rectangle(x, y - maxAscent - safetyMargin, 0, maxAscent | |||||
- descent + 2 * safetyMargin); | |||||
int l = text.length(); | int l = text.length(); | ||||
int[] dx = IFUtil.convertDPToDX(dp); | int[] dx = IFUtil.convertDPToDX(dp); | ||||
int dxl = (dx != null ? dx.length : 0); | int dxl = (dx != null ? dx.length : 0); | ||||
if (dx != null && dxl > 0 && dx[0] != 0) { | if (dx != null && dxl > 0 && dx[0] != 0) { | ||||
boundingRect.setLocation(boundingRect.x - (int)Math.ceil(dx[0] / 10f), boundingRect.y); | |||||
boundingRect.setLocation(boundingRect.x - (int) Math.ceil(dx[0] / 10f), boundingRect.y); | |||||
} | } | ||||
float width = 0.0f; | float width = 0.0f; | ||||
for (int i = 0; i < l; i++) { | for (int i = 0; i < l; i++) { | ||||
width += cw + glyphAdjust; | width += cw + glyphAdjust; | ||||
} | } | ||||
int extraWidth = font.getFontSize() / 3; | int extraWidth = font.getFontSize() / 3; | ||||
boundingRect.setSize( | |||||
(int)Math.ceil(width) + extraWidth, | |||||
boundingRect.height); | |||||
boundingRect.setSize((int) Math.ceil(width) + extraWidth, boundingRect.height); | |||||
return boundingRect; | return boundingRect; | ||||
} | } | ||||
private void drawTextAsBitmap(final int x, final int y, | |||||
final int letterSpacing, final int wordSpacing, final int[][] dp, | |||||
final String text, FontTriplet triplet) throws IFException { | |||||
//Use Java2D to paint different fonts via bitmap | |||||
private void drawTextAsBitmap(final int x, final int y, final int letterSpacing, | |||||
final int wordSpacing, final int[][] dp, final String text, FontTriplet triplet) | |||||
throws IFException { | |||||
// Use Java2D to paint different fonts via bitmap | |||||
final Font font = getFontInfo().getFontInstance(triplet, state.getFontSize()); | final Font font = getFontInfo().getFontInstance(triplet, state.getFontSize()); | ||||
//for cursive fonts, so the text isn't clipped | |||||
// for cursive fonts, so the text isn't clipped | |||||
FontMetricsMapper mapper; | FontMetricsMapper mapper; | ||||
try { | try { | ||||
mapper = (FontMetricsMapper) getFontInfo().getMetricsFor(font.getFontName()); | mapper = (FontMetricsMapper) getFontInfo().getMetricsFor(font.getFontName()); | ||||
final int maxAscent = mapper.getMaxAscent(font.getFontSize()) / 1000; | final int maxAscent = mapper.getMaxAscent(font.getFontSize()) / 1000; | ||||
final int ascent = mapper.getAscender(font.getFontSize()) / 1000; | final int ascent = mapper.getAscender(font.getFontSize()) / 1000; | ||||
final int descent = mapper.getDescender(font.getFontSize()) / 1000; | final int descent = mapper.getDescender(font.getFontSize()) / 1000; | ||||
int safetyMargin = (int)(SAFETY_MARGIN_FACTOR * font.getFontSize()); | |||||
int safetyMargin = (int) (SAFETY_MARGIN_FACTOR * font.getFontSize()); | |||||
final int baselineOffset = maxAscent + safetyMargin; | final int baselineOffset = maxAscent + safetyMargin; | ||||
final Rectangle boundingBox = getTextBoundingBox(x, y, | |||||
letterSpacing, wordSpacing, dp, text, font, mapper); | |||||
final Rectangle boundingBox = getTextBoundingBox(x, y, letterSpacing, wordSpacing, dp, | |||||
text, font, mapper); | |||||
final Dimension dim = boundingBox.getSize(); | final Dimension dim = boundingBox.getSize(); | ||||
Graphics2DImagePainter painter = new Graphics2DImagePainter() { | Graphics2DImagePainter painter = new Graphics2DImagePainter() { | ||||
public void paint(Graphics2D g2d, Rectangle2D area) { | public void paint(Graphics2D g2d, Rectangle2D area) { | ||||
if (DEBUG) { | if (DEBUG) { | ||||
g2d.setBackground(Color.LIGHT_GRAY); | g2d.setBackground(Color.LIGHT_GRAY); | ||||
g2d.clearRect(0, 0, (int)area.getWidth(), (int)area.getHeight()); | |||||
g2d.clearRect(0, 0, (int) area.getWidth(), (int) area.getHeight()); | |||||
} | } | ||||
g2d.translate(-x, -y + baselineOffset); | g2d.translate(-x, -y + baselineOffset); | ||||
try { | try { | ||||
painter.drawText(x, y, letterSpacing, wordSpacing, dp, text); | painter.drawText(x, y, letterSpacing, wordSpacing, dp, text); | ||||
} catch (IFException e) { | } catch (IFException e) { | ||||
//This should never happen with the Java2DPainter | |||||
// This should never happen with the Java2DPainter | |||||
throw new RuntimeException("Unexpected error while painting text", e); | throw new RuntimeException("Unexpected error while painting text", e); | ||||
} | } | ||||
} | } |
if (config.isTextRendering() != null) { | if (config.isTextRendering() != null) { | ||||
pclUtil.setAllTextAsBitmaps(config.isTextRendering()); | pclUtil.setAllTextAsBitmaps(config.isTextRendering()); | ||||
} | } | ||||
} | } | ||||
@Override | @Override |
/** Controls the dithering quality when rendering gray or color images. */ | /** Controls the dithering quality when rendering gray or color images. */ | ||||
private float ditheringQuality = 0.5f; | private float ditheringQuality = 0.5f; | ||||
/** | |||||
* Controls whether all text should be painted as text. This is a fallback setting in case | |||||
* the mixture of native and bitmapped text does not provide the necessary quality. | |||||
*/ | |||||
private boolean allTextAsBitmaps; | |||||
/** | /** | ||||
* Controls whether an RGB canvas is used when converting Java2D graphics to bitmaps. | * Controls whether an RGB canvas is used when converting Java2D graphics to bitmaps. | ||||
* This can be used to work around problems with Apache Batik, for example, but setting | * This can be used to work around problems with Apache Batik, for example, but setting | ||||
*/ | */ | ||||
private boolean disabledPJL; | private boolean disabledPJL; | ||||
/** | |||||
* Controls whether all text should be painted as text. This is a fallback setting in case the mixture of native and | |||||
* bitmapped text does not provide the necessary quality. | |||||
*/ | |||||
private boolean allTextAsBitmaps; | |||||
PCLRenderingUtil(FOUserAgent userAgent) { | PCLRenderingUtil(FOUserAgent userAgent) { | ||||
this.userAgent = userAgent; | this.userAgent = userAgent; | ||||
initialize(); | initialize(); | ||||
} | } | ||||
/** | /** | ||||
* Controls whether all text should be generated as bitmaps or only text for which there's | |||||
* no native font. | |||||
* Controls whether all text should be generated as bitmaps or only text for which there's no native font. | |||||
* @param allTextAsBitmaps true if all text should be painted as bitmaps | * @param allTextAsBitmaps true if all text should be painted as bitmaps | ||||
*/ | */ | ||||
public void setAllTextAsBitmaps(boolean allTextAsBitmaps) { | public void setAllTextAsBitmaps(boolean allTextAsBitmaps) { |
/* | |||||
* 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.render.pcl.fonts; | |||||
import java.io.ByteArrayOutputStream; | |||||
import java.io.IOException; | |||||
import java.util.Arrays; | |||||
public class PCLByteWriterUtil { | |||||
public byte[] padBytes(byte[] in, int length) { | |||||
return padBytes(in, length, 0); | |||||
} | |||||
public byte[] padBytes(byte[] in, int length, int value) { | |||||
byte[] out = new byte[length]; | |||||
for (int i = 0; i < length; i++) { | |||||
if (i < in.length) { | |||||
out[i] = in[i]; | |||||
} else { | |||||
out[i] = (byte) value; | |||||
} | |||||
} | |||||
return out; | |||||
} | |||||
public byte[] signedInt(int s) { | |||||
byte b1 = (byte) (s >> 8); | |||||
byte b2 = (byte) s; | |||||
return new byte[]{b1, b2}; | |||||
} | |||||
public byte signedByte(int s) { | |||||
return (byte) s; | |||||
} | |||||
public byte[] unsignedLongInt(int s) { | |||||
return unsignedLongInt((long) s); | |||||
} | |||||
public byte[] unsignedLongInt(long s) { | |||||
byte b1 = (byte) ((s >> 24) & 0xff); | |||||
byte b2 = (byte) ((s >> 16) & 0xff); | |||||
byte b3 = (byte) ((s >> 8) & 0xff); | |||||
byte b4 = (byte) (s & 0xff); | |||||
return new byte[]{b1, b2, b3, b4}; | |||||
} | |||||
public byte[] unsignedInt(int s) { | |||||
byte b1 = (byte) ((s >> 8) & 0xff); | |||||
byte b2 = (byte) (s & 0xff); | |||||
return new byte[]{b1, b2}; | |||||
} | |||||
public int unsignedByte(int b) { | |||||
return (byte) b & 0xFF; | |||||
} | |||||
public int maxPower2(int value) { | |||||
int test = 2; | |||||
while (test < value) { | |||||
test *= 2; | |||||
} | |||||
return test; | |||||
} | |||||
public int log(int x, int base) { | |||||
return (int) (Math.log(x) / Math.log(base)); | |||||
} | |||||
public byte[] toByteArray(int[] s) { | |||||
byte[] values = new byte[s.length]; | |||||
for (int i = 0; i < s.length; i++) { | |||||
values[i] = (byte) s[i]; | |||||
} | |||||
return values; | |||||
} | |||||
public byte[] insertIntoArray(int index, byte[] insertTo, byte[] data) throws IOException { | |||||
byte[] preBytes = Arrays.copyOf(insertTo, index); | |||||
byte[] postBytes = Arrays.copyOfRange(insertTo, index, insertTo.length); | |||||
ByteArrayOutputStream baos = new ByteArrayOutputStream(); | |||||
baos.write(preBytes); | |||||
baos.write(data); | |||||
baos.write(postBytes); | |||||
return baos.toByteArray(); | |||||
} | |||||
public byte[] updateDataAtLocation(byte[] data, byte[] update, int offset) { | |||||
int count = 0; | |||||
for (int i = offset; i < offset + update.length; i++) { | |||||
data[i] = update[count++]; | |||||
} | |||||
return data; | |||||
} | |||||
/** | |||||
* Writes a PCL escape command to the output stream. | |||||
* @param cmd the command (without the ESCAPE character) | |||||
* @throws IOException In case of an I/O error | |||||
*/ | |||||
public byte[] writeCommand(String cmd) throws IOException { | |||||
ByteArrayOutputStream baos = new ByteArrayOutputStream(); | |||||
baos.write(27); // ESC | |||||
baos.write(cmd.getBytes("US-ASCII")); | |||||
return baos.toByteArray(); | |||||
} | |||||
} |
/* | |||||
* 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.render.pcl.fonts; | |||||
import java.io.ByteArrayOutputStream; | |||||
import java.io.IOException; | |||||
import java.util.ArrayList; | |||||
import java.util.List; | |||||
public class PCLCharacterDefinition { | |||||
private int charCode; | |||||
private int charDefinitionSize; | |||||
private byte[] glyfData; | |||||
private boolean hasContinuation; | |||||
private PCLCharacterFormat charFormat; | |||||
private PCLCharacterClass charClass; | |||||
private PCLByteWriterUtil pclByteWriter; | |||||
private List<PCLCharacterDefinition> composites; | |||||
private boolean isComposite; | |||||
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; | |||||
hasContinuation = charDefinitionSize > 32767; | |||||
composites = new ArrayList<PCLCharacterDefinition>(); | |||||
} | |||||
public byte[] getCharacterCommand() throws IOException { | |||||
return pclByteWriter.writeCommand(String.format("*c%dE", (isComposite) ? 65535 : charCode)); | |||||
} | |||||
public byte[] getCharacterDefinitionCommand() throws IOException { | |||||
return pclByteWriter.writeCommand(String.format("(s%dW", 10 + glyfData.length)); | |||||
} | |||||
public byte[] getData() throws IOException { | |||||
ByteArrayOutputStream baos = new ByteArrayOutputStream(); | |||||
// Write Character Descriptor | |||||
if (!hasContinuation) { | |||||
writeCharacterDescriptorHeader(0, baos); | |||||
baos.write(glyfData); | |||||
} else { | |||||
int continuations = glyfData.length / 32767; | |||||
for (int i = 0; i < continuations; i++) { | |||||
writeCharacterDescriptorHeader(i == 0 ? 0 : 1, baos); | |||||
int continuationStart = i * 32767; | |||||
int continuationLength = continuationStart - glyfData.length < 32767 | |||||
? continuationStart - glyfData.length : 32767; | |||||
baos.write(glyfData, continuationStart, continuationLength); | |||||
} | |||||
} | |||||
baos.write(0); // Reserved | |||||
byte[] charBytes = baos.toByteArray(); | |||||
long sum = 0; | |||||
for (int i = 4; i < charBytes.length; i++) { | |||||
sum += charBytes[i]; | |||||
} | |||||
int remainder = (int) (sum % 256); | |||||
baos.write(256 - remainder); // Checksum | |||||
return baos.toByteArray(); | |||||
} | |||||
private void writeCharacterDescriptorHeader(int continuation, ByteArrayOutputStream baos) throws IOException { | |||||
baos.write(pclByteWriter.unsignedByte(charFormat.getValue())); | |||||
baos.write(continuation); | |||||
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(charCode)); | |||||
} | |||||
public void addCompositeGlyph(PCLCharacterDefinition composite) { | |||||
composites.add(composite); | |||||
} | |||||
public List<PCLCharacterDefinition> getCompositeGlyphs() { | |||||
return composites; | |||||
} | |||||
/** | |||||
* Character Format used in PCL Character Descriptor See Table 11-50 from PCL 5 Specification | |||||
*/ | |||||
public enum PCLCharacterFormat { | |||||
LaserJet_Raster(4), | |||||
Intellifont(10), | |||||
TrueType(15); | |||||
private int value; | |||||
PCLCharacterFormat(int value) { | |||||
this.value = value; | |||||
} | |||||
public int getValue() { | |||||
return value; | |||||
} | |||||
} | |||||
/** | |||||
* Character Class used in PCL Character Descriptor See Table 11-51 from PCL 5 Specification | |||||
*/ | |||||
public enum PCLCharacterClass { | |||||
Bitmap(1), | |||||
CompressedBitmap(2), | |||||
Contour_Intellifont(3), | |||||
Compound_Contour_Intellifont(4), | |||||
TrueType(15); | |||||
private int value; | |||||
PCLCharacterClass(int value) { | |||||
this.value = value; | |||||
} | |||||
public int getValue() { | |||||
return value; | |||||
} | |||||
} | |||||
} |
/* | |||||
* 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.render.pcl.fonts; | |||||
import java.io.IOException; | |||||
import org.apache.fop.fonts.truetype.FontFileReader; | |||||
import org.apache.fop.fonts.truetype.OpenFont; | |||||
public abstract class PCLCharacterWriter { | |||||
protected PCLSoftFont font; | |||||
protected PCLByteWriterUtil pclByteWriter; | |||||
protected OpenFont openFont; | |||||
protected FontFileReader fontReader; | |||||
public PCLCharacterWriter(PCLSoftFont font) throws IOException { | |||||
this.font = font; | |||||
openFont = font.getOpenFont(); | |||||
fontReader = font.getReader(); | |||||
pclByteWriter = new PCLByteWriterUtil(); | |||||
} | |||||
public abstract byte[] writeCharacterDefinitions(String text) throws IOException; | |||||
} |
/* | |||||
* 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.render.pcl.fonts; | |||||
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; | |||||
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(); | |||||
public abstract int getFontType(); | |||||
public abstract int getStyleMSB(); | |||||
public abstract int getBaselinePosition(); | |||||
public abstract int getCellWidth(); | |||||
public abstract int getCellHeight(); | |||||
public abstract int getOrientation(); | |||||
public abstract int getSpacing(); | |||||
public abstract int getSymbolSet(); | |||||
public abstract int getPitch(); | |||||
public abstract int getHeight(); | |||||
public abstract int getXHeight(); | |||||
public abstract int getWidthType(); | |||||
public abstract int getStyleLSB(); | |||||
public abstract int getStrokeWeight(); | |||||
public abstract int getTypefaceLSB(); | |||||
public abstract int getTypefaceMSB(); | |||||
public abstract int getSerifStyle(); | |||||
public abstract int getQuality(); | |||||
public abstract int getPlacement(); | |||||
public abstract int getUnderlinePosition(); | |||||
public abstract int getUnderlineThickness(); | |||||
public abstract int getTextHeight(); | |||||
public abstract int getTextWidth(); | |||||
public abstract int getFirstCode(); | |||||
public abstract int getLastCode(); | |||||
public abstract int getPitchExtended(); | |||||
public abstract int getHeightExtended(); | |||||
public abstract int getCapHeight(); | |||||
public abstract int getFontNumber(); | |||||
public abstract String getFontName(); | |||||
public abstract int getScaleFactor() throws IOException; | |||||
public abstract int getMasterUnderlinePosition() throws IOException; | |||||
public abstract int getMasterUnderlineThickness() throws IOException; | |||||
public abstract int getFontScalingTechnology(); | |||||
public abstract int getVariety(); | |||||
/** Segmented Font Data **/ | |||||
public abstract List<PCLFontSegment> getFontSegments(Map<Character, Integer> mappedGlyphs) | |||||
throws IOException; | |||||
/** Character Definitions **/ | |||||
public abstract Map<Integer, int[]> getCharacterOffsets() throws IOException; | |||||
public abstract OpenFont getFontFile(); | |||||
public abstract FontFileReader getFontFileReader(); | |||||
/** | |||||
* Gets the most significant byte from a 16-bit integer | |||||
* @param s The number | |||||
* @return The resulting byte value as an integer | |||||
*/ | |||||
protected int getMSB(int s) { | |||||
return s >> 8; | |||||
} | |||||
/** | |||||
* Gets the least significant byte from a 16-bit integer | |||||
* @param s The number | |||||
* @return The resulting byte value as an integer | |||||
*/ | |||||
protected int getLSB(int s) { | |||||
byte b1 = (byte) (s >> 8); | |||||
return s; | |||||
} | |||||
} |
/* | |||||
* 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.render.pcl.fonts; | |||||
import java.io.IOException; | |||||
import org.apache.fop.fonts.CIDFontType; | |||||
import org.apache.fop.fonts.CustomFont; | |||||
import org.apache.fop.fonts.FontType; | |||||
import org.apache.fop.fonts.MultiByteFont; | |||||
import org.apache.fop.fonts.Typeface; | |||||
import org.apache.fop.render.java2d.CustomFontMetricsMapper; | |||||
import org.apache.fop.render.pcl.fonts.truetype.PCLTTFFontReader; | |||||
public final class PCLFontReaderFactory { | |||||
private PCLByteWriterUtil pclByteWriter; | |||||
private PCLFontReaderFactory(PCLByteWriterUtil pclByteWriter) { | |||||
this.pclByteWriter = pclByteWriter; | |||||
} | |||||
public static PCLFontReaderFactory getInstance(PCLByteWriterUtil pclByteWriter) { | |||||
return new PCLFontReaderFactory(pclByteWriter); | |||||
} | |||||
public PCLFontReader createInstance(Typeface font) throws IOException { | |||||
if (font.getFontType() == FontType.TRUETYPE || isCIDType2(font)) { | |||||
return new PCLTTFFontReader(font, pclByteWriter); | |||||
} | |||||
// else if (font instanceof MultiByteFont && ((MultiByteFont) font).isOTFFile()) { | |||||
// Placeholder for future Type 1 / OTF Soft font implementations e.g. | |||||
// return new PCLOTFFontReader(font, pclByteWriter); | |||||
// } | |||||
return null; | |||||
} | |||||
private boolean isCIDType2(Typeface font) { | |||||
CustomFontMetricsMapper fontMetrics = (CustomFontMetricsMapper) font; | |||||
CustomFont customFont = (CustomFont) fontMetrics.getRealFont(); | |||||
if (customFont instanceof MultiByteFont) { | |||||
return ((MultiByteFont) customFont).getCIDType() == CIDFontType.CIDTYPE2; | |||||
} | |||||
return false; | |||||
} | |||||
} |
/* | |||||
* 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.render.pcl.fonts; | |||||
public class PCLFontSegment { | |||||
private SegmentID identifier; | |||||
private byte[] data; | |||||
public PCLFontSegment(SegmentID identifier, byte[] data) { | |||||
this.identifier = identifier; | |||||
this.data = data; | |||||
} | |||||
public byte[] getData() { | |||||
return data; | |||||
} | |||||
public SegmentID getIdentifier() { | |||||
return identifier; | |||||
} | |||||
public int getSize() { | |||||
return (identifier == SegmentID.NULL) ? 0 : data.length; | |||||
} | |||||
public enum SegmentID { | |||||
CC(17219), // Character Complement | |||||
CP(17232), // Copyright | |||||
GT(18260), // Global TrueType Data | |||||
IF(18758), // Intellifont Face Data | |||||
PA(20545), // PANOSE Description | |||||
XW(22619), // XWindows Font Name | |||||
NULL(65535); // Null Segment | |||||
private int complementID; | |||||
SegmentID(int complementID) { | |||||
this.complementID = complementID; | |||||
} | |||||
public int getValue() { | |||||
return complementID; | |||||
} | |||||
} | |||||
} |
/* | |||||
* 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.render.pcl.fonts; | |||||
import java.io.InputStream; | |||||
import java.util.HashMap; | |||||
import java.util.Map; | |||||
import org.apache.fop.fonts.MultiByteFont; | |||||
import org.apache.fop.fonts.Typeface; | |||||
import org.apache.fop.fonts.truetype.FontFileReader; | |||||
import org.apache.fop.fonts.truetype.OpenFont; | |||||
import org.apache.fop.render.java2d.CustomFontMetricsMapper; | |||||
public class PCLSoftFont { | |||||
private int fontID; | |||||
private Typeface font; | |||||
private Map<Integer, int[]> charOffsets; | |||||
private OpenFont openFont; | |||||
private InputStream fontStream; | |||||
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, boolean multiByteFont) { | |||||
this.fontID = fontID; | |||||
this.font = font; | |||||
charsWritten = new HashMap<Integer, Integer>(); | |||||
mappedChars = new HashMap<Character, Integer>(); | |||||
this.multiByteFont = multiByteFont; | |||||
} | |||||
public Typeface getTypeface() { | |||||
return font; | |||||
} | |||||
public int getFontID() { | |||||
return fontID; | |||||
} | |||||
public void setCharacterOffsets(Map<Integer, int[]> charOffsets) { | |||||
this.charOffsets = charOffsets; | |||||
} | |||||
public Map<Integer, int[]> getCharacterOffsets() { | |||||
return charOffsets; | |||||
} | |||||
public OpenFont getOpenFont() { | |||||
return openFont; | |||||
} | |||||
public void setOpenFont(OpenFont openFont) { | |||||
this.openFont = openFont; | |||||
} | |||||
public InputStream getFontStream() { | |||||
return fontStream; | |||||
} | |||||
public void setFontStream(InputStream fontStream) { | |||||
this.fontStream = fontStream; | |||||
} | |||||
public FontFileReader getReader() { | |||||
return reader; | |||||
} | |||||
public void setReader(FontFileReader reader) { | |||||
this.reader = reader; | |||||
} | |||||
public void writeCharacter(int unicode) { | |||||
charsWritten.put(unicode, charCount++); | |||||
} | |||||
public int getUnicodeCodePoint(int unicode) { | |||||
if (charsWritten.containsKey(unicode)) { | |||||
return charsWritten.get(unicode); | |||||
} else { | |||||
return -1; | |||||
} | |||||
} | |||||
public boolean hasPreviouslyWritten(int unicode) { | |||||
return charsWritten.containsKey(unicode); | |||||
} | |||||
public int getMtxCharIndex(int unicode) { | |||||
if (charMtxPositions.get(unicode) != null) { | |||||
return charMtxPositions.get(unicode); | |||||
} | |||||
return 0; | |||||
} | |||||
public int getCmapGlyphIndex(int unicode) { | |||||
if (font instanceof CustomFontMetricsMapper) { | |||||
CustomFontMetricsMapper customFont = (CustomFontMetricsMapper) font; | |||||
Typeface realFont = customFont.getRealFont(); | |||||
if (realFont instanceof MultiByteFont) { | |||||
MultiByteFont mbFont = (MultiByteFont) realFont; | |||||
return mbFont.findGlyphIndex(unicode); | |||||
} | |||||
} | |||||
return 0; | |||||
} | |||||
public void setMtxCharIndexes(Map<Integer, Integer> charMtxPositions) { | |||||
this.charMtxPositions = charMtxPositions; | |||||
} | |||||
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); | |||||
} | |||||
} | |||||
} |
/* | |||||
* 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.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 List<PCLSoftFont> fonts = new ArrayList<PCLSoftFont>(); | |||||
private PCLFontReaderFactory fontReaderFactory; | |||||
private static final int SOFT_FONT_SIZE = 255; | |||||
public ByteArrayOutputStream makeSoftFont(Typeface font) throws IOException { | |||||
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) { | |||||
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(assignFontID(fonts.size() + 1)); | |||||
} | |||||
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())); | |||||
header.write(pclByteWriter.unsignedByte(fontReader.getFontType())); | |||||
header.write(pclByteWriter.unsignedByte(fontReader.getStyleMSB())); | |||||
header.write(0); // Reserved | |||||
header.write(pclByteWriter.unsignedInt(fontReader.getBaselinePosition())); | |||||
header.write(pclByteWriter.unsignedInt(fontReader.getCellWidth())); | |||||
header.write(pclByteWriter.unsignedInt(fontReader.getCellHeight())); | |||||
header.write(pclByteWriter.unsignedByte(fontReader.getOrientation())); | |||||
header.write(fontReader.getSpacing()); | |||||
header.write(pclByteWriter.unsignedInt(fontReader.getSymbolSet())); | |||||
header.write(pclByteWriter.unsignedInt(fontReader.getPitch())); | |||||
header.write(pclByteWriter.unsignedInt(fontReader.getHeight())); | |||||
header.write(pclByteWriter.unsignedInt(fontReader.getXHeight())); | |||||
header.write(pclByteWriter.signedByte(fontReader.getWidthType())); | |||||
header.write(pclByteWriter.unsignedByte(fontReader.getStyleLSB())); | |||||
header.write(pclByteWriter.signedByte(fontReader.getStrokeWeight())); | |||||
header.write(pclByteWriter.unsignedByte(fontReader.getTypefaceLSB())); | |||||
header.write(pclByteWriter.unsignedByte(fontReader.getTypefaceMSB())); | |||||
header.write(pclByteWriter.unsignedByte(fontReader.getSerifStyle())); | |||||
header.write(pclByteWriter.unsignedByte(fontReader.getQuality())); | |||||
header.write(pclByteWriter.signedByte(fontReader.getPlacement())); | |||||
header.write(pclByteWriter.signedByte(fontReader.getUnderlinePosition())); | |||||
header.write(pclByteWriter.unsignedByte(fontReader.getUnderlineThickness())); | |||||
header.write(pclByteWriter.unsignedInt(fontReader.getTextHeight())); | |||||
header.write(pclByteWriter.unsignedInt(fontReader.getTextWidth())); | |||||
header.write(pclByteWriter.unsignedInt(fontReader.getFirstCode())); | |||||
header.write(pclByteWriter.unsignedInt(fontReader.getLastCode())); | |||||
header.write(pclByteWriter.unsignedByte(fontReader.getPitchExtended())); | |||||
header.write(pclByteWriter.unsignedByte(fontReader.getHeightExtended())); | |||||
header.write(pclByteWriter.unsignedInt(fontReader.getCapHeight())); | |||||
header.write(pclByteWriter.unsignedLongInt(fontReader.getFontNumber())); | |||||
header.write(pclByteWriter.padBytes(fontReader.getFontName().getBytes("US-ASCII"), 16, 32)); | |||||
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, mappedGlyphs); | |||||
baos.write(getFontHeaderCommand(header.size())); | |||||
baos.write(header.toByteArray()); | |||||
} | |||||
private void writeSegmentedFontData(ByteArrayOutputStream header, | |||||
Map<Character, Integer> mappedGlyphs) throws IOException { | |||||
List<PCLFontSegment> fontSegments = fontReader.getFontSegments(mappedGlyphs); | |||||
for (PCLFontSegment segment : fontSegments) { | |||||
writeFontSegment(header, segment); | |||||
} | |||||
header.write(0); // Reserved | |||||
// Checksum must equal 0 when added to byte 64 offset (modulo 256) | |||||
long sum = 0; | |||||
byte[] headerBytes = header.toByteArray(); | |||||
for (int i = 64; i < headerBytes.length; i++) { | |||||
sum += headerBytes[i]; | |||||
} | |||||
int remainder = (int) (sum % 256); | |||||
header.write(256 - remainder); | |||||
} | |||||
private byte[] getFontHeaderCommand(int headerSize) throws IOException { | |||||
return pclByteWriter.writeCommand(String.format(")s%dW", headerSize)); | |||||
} | |||||
private void writeFontSegment(ByteArrayOutputStream header, PCLFontSegment segment) throws IOException { | |||||
header.write(pclByteWriter.unsignedInt(segment.getIdentifier().getValue())); | |||||
header.write(pclByteWriter.unsignedInt(segment.getData().length)); | |||||
header.write(segment.getData()); | |||||
} | |||||
/** | |||||
* 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, String text) { | |||||
for (PCLSoftFont sftFont : fonts) { | |||||
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, ""); | |||||
for (int i = 0; i < fonts.size(); i++) { | |||||
if (fonts.get(i).equals(font)) { | |||||
return i + 1; | |||||
} | |||||
} | |||||
return -1; | |||||
} | |||||
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; | |||||
} | |||||
} | |||||
} |
/* | |||||
* 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.render.pcl.fonts; | |||||
/** | |||||
* Table C-1 from http://www.lprng.com/DISTRIB/RESOURCES/DOCS/pcl5comp.pdf | |||||
*/ | |||||
public enum PCLSymbolSet { | |||||
// Unbound font containing > 256 characters | |||||
Unbound("1X", 56), | |||||
// Other symbol sets to use in bound fonts | |||||
Bound_Generic("0Q", 17), | |||||
GW_3212("18C", 597), | |||||
ISO_60_Danish_Norwegian("0D", 4), | |||||
Devanagari("2D", 68), | |||||
ISO_4_United_Kingdom("1E", 37), | |||||
Windows_3_1_Latin2("9E", 293), | |||||
ISO_69_French("1F", 38), | |||||
ISO_21_German("1G", 39), | |||||
Greek_8("8G", 283), | |||||
Windows_3_1_Latin_Greek("9G", 295), | |||||
PC_851_Latin_Greek("10G", 327), | |||||
PC_8_Latin_Greek("12G", 391), | |||||
Hebrew_7("0H", 8), | |||||
ISO_8859_8_Latin_Hebrew("7H", 232), | |||||
Hebrew_8("8H", 264), | |||||
PC_862_Latin_Hebrew("15H", 488), | |||||
ISO_15_Italian("0I", 9), | |||||
Microsoft_Publishing("6J", 202), | |||||
DeskTop("7J", 234), | |||||
Document("8J", 266), | |||||
PC_1004("9J", 298), | |||||
PS_Text("10J", 330), | |||||
PS_ISO_Latin1("11J", 362), | |||||
MC_Text("12J", 394), | |||||
Ventura_International3("13J", 426), | |||||
Ventura_US3("14J", 458), | |||||
Swash_Characters("16J", 522), | |||||
Small_Caps_Old_Style_Figures("17J", 554), | |||||
Old_Style_Figures("18J", 586), | |||||
Fractions("19J", 618), | |||||
Lining_Figures("21J", 682), | |||||
Small_Caps_and_Lining_Figures("22J", 714), | |||||
Alternate_Caps("23J", 746), | |||||
Kana_8_JIS_210("8K", 267), | |||||
Korean_8("9K", 299), | |||||
Line_Draw_7("0L", 12), | |||||
HP_Block_Characters("1L", 44), | |||||
Tax_Line_Draw("2L", 76), | |||||
Line_Draw_8("8L", 268), | |||||
Ventura_ITC_Zapf_Dingbats3("9L", 300), | |||||
PS_ITC_Zapf_Dingbats("10L", 332), | |||||
ITC_Zapf_Dingbats_Series_100("11L", 364), | |||||
ITC_Zapf_Dingbats_Series_200("12L", 396), | |||||
ITC_Zapf_Dingbats_Series_300("13L", 428), | |||||
Windows_Baltic("19L", 620), | |||||
Carta("20L", 652), | |||||
Ornaments("21L", 684), | |||||
Universal_News_Commercial_Pi("22L", 716), | |||||
Chess("23L", 748), | |||||
Astrology_1("24L", 780), | |||||
Pi_Set_1("31L", 1004), | |||||
Pi_Set_2("32L", 1036), | |||||
Pi_Set_3("33L", 1068), | |||||
Pi_Set_4("34L", 1100), | |||||
Pi_Set_5("35L", 1132), | |||||
Pi_Set_6("36L", 1164), | |||||
Wingdings("579L", 18540), | |||||
Math_7("0M", 13), | |||||
Tech_7("1M", 45), | |||||
PS_Math("5M", 173), | |||||
Ventura_Math3("6M", 205), | |||||
Math_8("8M", 269), | |||||
Universal_Greek_Math_Pi("10M", 333), | |||||
TeX_Math_Extension("11M", 365), | |||||
TeX_Math_Symbol("12M", 397), | |||||
TeX_Math_Italic("13M", 429), | |||||
Symbol("19M", 621), | |||||
ISO_8859_1_Latin_1("0N", 14), | |||||
ISO_8859_2_Latin_2("2N", 78), | |||||
ISO_8859_3_Latin_3("3N", 110), | |||||
ISO_8859_4_Latin_4("4N", 142), | |||||
ISO_8859_9_Latin_5("5N", 174), | |||||
ISO_8859_10_Latin_6("6N", 206), | |||||
ISO_8859_5_Latin_Cyrillic("10N", 334), | |||||
ISO_8859_6_Latin_Arabic("11N", 366), | |||||
ISO_8859_7_Latin_Greek("12N", 398), | |||||
OCR_A("0O", 15), | |||||
OCR_B("1O", 47), | |||||
OCR_M("2O", 79), | |||||
MICR_E13B("10O", 335), | |||||
Typewriter_Paired_APL("0P", 16), | |||||
Bit_Paired_APL("1P", 48), | |||||
Expert("10P", 336), | |||||
Alternate("11P", 368), | |||||
Fraktur("12P", 400), | |||||
Cyrillic_ASCII_8859_5_1986("0R", 18), | |||||
Cyrillic("1R", 50), | |||||
PC_Cyrillic("3R", 114), | |||||
Windows_3_1_Latin_Cyrillic("9R", 306), | |||||
ISO_11_Swedish("0S", 19), | |||||
ISO_17_Spanish3("2S", 83), | |||||
HP_European_Spanish("7S", 243), | |||||
HP_Latin_Spanish("8S", 275), | |||||
HP_GL_Download("16S", 531), | |||||
HP_GL_Drafting("17S", 563), | |||||
HP_GL_Special_Symbols("18S", 595), | |||||
Sonata("20S", 659), | |||||
Thai_8("0T", 20), | |||||
TISI_620_2533_Thai("1T", 52), | |||||
Windows_3_1_Latin_5("5T", 180), | |||||
Turkish_8("8T", 276), | |||||
PC_8_Turkish("9T", 308), | |||||
Teletex("10T", 340), | |||||
ISO_6_ASCII("0U", 21), | |||||
Legal("1U", 53), | |||||
HPL("5U", 181), | |||||
OEM_1("7U", 245), | |||||
Roman_8("8U", 277), | |||||
Windows_3_0_Latin_1("9U", 309), | |||||
PC_8_Code_Page_437("10U", 341), | |||||
PC_8_D_N_Danish_Norwegian("11U", 373), | |||||
PC_850_Multilingual("12U", 405), | |||||
Pi_Font("15U", 501), | |||||
PC_857("16U", 533), | |||||
PC_852_Latin_2("17U", 565), | |||||
Windows_3_1_Latin_1("19U", 629), | |||||
PC_860_Portugal("20U", 661), | |||||
PC_861_Iceland("21U", 693), | |||||
PC_863_Canada_French("23U", 757), | |||||
PC_865_Norway("25U", 821), | |||||
PC_775("26U", 853), | |||||
Arabic_8("8V", 278), | |||||
Windows_3_1_Latin_Arabic("9V", 310), | |||||
Code_Page_864_Latin_Arabic("10V", 342), | |||||
Barcode_3of9("0Y", 25), | |||||
Industrial_2_of_5_Barcode("1Y", 57), | |||||
Matrix_2_of_5_Barcode("2Y", 89), | |||||
Interleaved_2_of_5_Barcode("4Y", 153), | |||||
CODABAR_Barcode("5Y", 185), | |||||
MSI_Plessey_Barcode("6Y", 217), | |||||
Code_11_Barcode("7Y", 249), | |||||
UPC_EAN_Barcode("8Y", 281), | |||||
MICR_CMC_7("14Y", 473), | |||||
USPS_ZIP("5Y", 505), | |||||
Math_7_2("0A", 1), | |||||
Line_Draw_7_2("0B", 2), | |||||
HP_Large_Characters("0C", 3), | |||||
ISO_61_Norwegian_Version_2("1D", 36), | |||||
Roman_Extension("0E", 5), | |||||
ISO_25_French("0F", 6), | |||||
HP_German("0G", 7), | |||||
ISO_14_JIS_ASCII("0K", 11), | |||||
ISO_13_Katakana("1K", 43), | |||||
ISO_57_Chinese("2K", 75), | |||||
HP_Spanish("1S", 51), | |||||
ISO_10_Swedish("3S", 115), | |||||
ISO_16_Portuguese("4S", 147), | |||||
ISO_84_Portuguese("5S", 179), | |||||
ISO_85_Spanish("6S", 211), | |||||
ISO_2_International_Reference("2U", 85), | |||||
Arabic("0V", 22); | |||||
private String symbolSetID; | |||||
private int kind1; | |||||
PCLSymbolSet(String symbolSetID, int kind1) { | |||||
this.kind1 = kind1; | |||||
} | |||||
public String getSymbolSetID() { | |||||
return symbolSetID; | |||||
} | |||||
public int getKind1() { | |||||
return kind1; | |||||
} | |||||
} |
/* | |||||
* 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.render.pcl.fonts.truetype; | |||||
import java.io.ByteArrayOutputStream; | |||||
import java.io.IOException; | |||||
import java.util.HashMap; | |||||
import java.util.List; | |||||
import java.util.Map; | |||||
import java.util.Set; | |||||
import org.apache.fop.fonts.truetype.GlyfTable; | |||||
import org.apache.fop.fonts.truetype.OFDirTabEntry; | |||||
import org.apache.fop.fonts.truetype.OFMtxEntry; | |||||
import org.apache.fop.fonts.truetype.OFTableName; | |||||
import org.apache.fop.fonts.truetype.TTFFile; | |||||
import org.apache.fop.render.pcl.fonts.PCLCharacterDefinition; | |||||
import org.apache.fop.render.pcl.fonts.PCLCharacterDefinition.PCLCharacterClass; | |||||
import org.apache.fop.render.pcl.fonts.PCLCharacterDefinition.PCLCharacterFormat; | |||||
import org.apache.fop.render.pcl.fonts.PCLCharacterWriter; | |||||
import org.apache.fop.render.pcl.fonts.PCLSoftFont; | |||||
public class PCLTTFCharacterWriter extends PCLCharacterWriter { | |||||
private List<OFMtxEntry> mtx; | |||||
private OFDirTabEntry tabEntry; | |||||
public PCLTTFCharacterWriter(PCLSoftFont softFont) throws IOException { | |||||
super(softFont); | |||||
softFont.setMtxCharIndexes(scanMtxCharacters()); | |||||
} | |||||
@Override | |||||
public byte[] writeCharacterDefinitions(String text) throws IOException { | |||||
ByteArrayOutputStream baos = new ByteArrayOutputStream(); | |||||
for (char ch : text.toCharArray()) { | |||||
int character = (int) ch; | |||||
if (!font.hasPreviouslyWritten(character)) { | |||||
PCLCharacterDefinition pclChar = getCharacterDefinition(ch); | |||||
writePCLCharacter(baos, pclChar); | |||||
List<PCLCharacterDefinition> compositeGlyphs = pclChar.getCompositeGlyphs(); | |||||
for (PCLCharacterDefinition composite : compositeGlyphs) { | |||||
writePCLCharacter(baos, composite); | |||||
} | |||||
} | |||||
} | |||||
return baos.toByteArray(); | |||||
} | |||||
private void writePCLCharacter(ByteArrayOutputStream baos, PCLCharacterDefinition pclChar) throws IOException { | |||||
baos.write(pclChar.getCharacterCommand()); | |||||
baos.write(pclChar.getCharacterDefinitionCommand()); | |||||
baos.write(pclChar.getData()); | |||||
} | |||||
private Map<Integer, Integer> scanMtxCharacters() throws IOException { | |||||
Map<Integer, Integer> charMtxOffsets = new HashMap<Integer, Integer>(); | |||||
List<OFMtxEntry> mtx = openFont.getMtx(); | |||||
OFTableName glyfTag = OFTableName.GLYF; | |||||
if (openFont.seekTab(fontReader, glyfTag, 0)) { | |||||
for (int i = 1; i < mtx.size(); i++) { | |||||
OFMtxEntry entry = mtx.get(i); | |||||
int charCode = 0; | |||||
if (entry.getUnicodeIndex().size() > 0) { | |||||
charCode = (Integer) entry.getUnicodeIndex().get(0); | |||||
} else { | |||||
charCode = entry.getIndex(); | |||||
} | |||||
charMtxOffsets.put(charCode, i); | |||||
} | |||||
} | |||||
return charMtxOffsets; | |||||
} | |||||
private PCLCharacterDefinition getCharacterDefinition(int unicode) throws IOException { | |||||
if (mtx == null) { | |||||
mtx = openFont.getMtx(); | |||||
tabEntry = openFont.getDirectoryEntry(OFTableName.GLYF); | |||||
} | |||||
if (openFont.seekTab(fontReader, OFTableName.GLYF, 0)) { | |||||
int charIndex = font.getMtxCharIndex(unicode); | |||||
// Fallback - only works for MultiByte fonts | |||||
if (charIndex == 0) { | |||||
charIndex = font.getCmapGlyphIndex(unicode); | |||||
} | |||||
Map<Integer, Integer> subsetGlyphs = new HashMap<Integer, Integer>(); | |||||
subsetGlyphs.put(charIndex, 1); | |||||
byte[] glyphData = getGlyphData(charIndex); | |||||
font.writeCharacter(unicode); | |||||
PCLCharacterDefinition newChar = new PCLCharacterDefinition( | |||||
font.getCharCode((char) unicode), | |||||
PCLCharacterFormat.TrueType, | |||||
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> composites = glyfTable.retrieveComposedGlyphs(charIndex); | |||||
for (Integer compositeIndex : composites) { | |||||
byte[] compositeData = getGlyphData(compositeIndex); | |||||
newChar.addCompositeGlyph(new PCLCharacterDefinition(compositeIndex, | |||||
PCLCharacterFormat.TrueType, | |||||
PCLCharacterClass.TrueType, compositeData, pclByteWriter, true)); | |||||
} | |||||
} | |||||
return newChar; | |||||
} | |||||
return null; | |||||
} | |||||
private byte[] getGlyphData(int charIndex) throws IOException { | |||||
OFMtxEntry entry = mtx.get(charIndex); | |||||
OFMtxEntry nextEntry; | |||||
int nextOffset = 0; | |||||
if (charIndex < mtx.size() - 1) { | |||||
nextEntry = mtx.get(charIndex + 1); | |||||
nextOffset = (int) nextEntry.getOffset(); | |||||
} else { | |||||
nextOffset = (int) ((TTFFile) openFont).getLastGlyfLocation(); | |||||
} | |||||
int glyphOffset = (int) entry.getOffset(); | |||||
int glyphLength = nextOffset - glyphOffset; | |||||
byte[] glyphData = new byte[0]; | |||||
if (glyphLength > 0) { | |||||
glyphData = fontReader.getBytes((int) tabEntry.getOffset() + glyphOffset, glyphLength); | |||||
} | |||||
return glyphData; | |||||
} | |||||
} |
/* | |||||
* 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.render.pcl.fonts.truetype; | |||||
import java.io.ByteArrayOutputStream; | |||||
import java.io.IOException; | |||||
import java.io.InputStream; | |||||
import java.io.UnsupportedEncodingException; | |||||
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.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; | |||||
import org.apache.fop.fonts.truetype.OFFontLoader; | |||||
import org.apache.fop.fonts.truetype.OFMtxEntry; | |||||
import org.apache.fop.fonts.truetype.OFTableName; | |||||
import org.apache.fop.fonts.truetype.OpenFont; | |||||
import org.apache.fop.fonts.truetype.TTFFile; | |||||
import org.apache.fop.render.java2d.CustomFontMetricsMapper; | |||||
import org.apache.fop.render.pcl.fonts.PCLByteWriterUtil; | |||||
import org.apache.fop.render.pcl.fonts.PCLFontReader; | |||||
import org.apache.fop.render.pcl.fonts.PCLFontSegment; | |||||
import org.apache.fop.render.pcl.fonts.PCLFontSegment.SegmentID; | |||||
import org.apache.fop.render.pcl.fonts.PCLSymbolSet; | |||||
public class PCLTTFFontReader extends PCLFontReader { | |||||
protected TTFFile ttfFont; | |||||
protected InputStream fontStream; | |||||
protected FontFileReader reader; | |||||
private PCLTTFPCLTFontTable pcltTable; | |||||
private PCLTTFOS2FontTable os2Table; | |||||
private PCLTTFPOSTFontTable postTable; | |||||
private PCLTTFTableFactory ttfTableFactory; | |||||
private static final int HMTX_RESTRICT_SIZE = 50000; | |||||
private static final Map<Integer, Integer> FONT_WEIGHT = new HashMap<Integer, Integer>() { | |||||
private static final long serialVersionUID = 1L; | |||||
{ | |||||
put(100, -6); // 100 Thin | |||||
put(200, -4); // 200 Extra-Light | |||||
put(300, -3); // 300 Light | |||||
put(400, 0); // 400 Normal (Regular) | |||||
put(500, 0); // 500 Medium | |||||
put(600, 2); // 600 Semi-bold | |||||
put(700, 3); // 700 Bold | |||||
put(800, 4); // 800 Extra-bold | |||||
put(900, 5); // 900 Black (Heavy) | |||||
} | |||||
}; | |||||
private static final Map<Integer, Integer> FONT_SERIF = new HashMap<Integer, Integer>() { | |||||
private static final long serialVersionUID = 1L; | |||||
{ | |||||
/** The following are the best guess conversion between serif styles. Unfortunately | |||||
* there appears to be no standard and so each specification has it's own set of values. | |||||
* Please change if better fit found. **/ | |||||
put(0, 0); // Any = Normal Sans | |||||
put(1, 64); // No Fit = Sans Serif | |||||
put(2, 9); // Cove = Script Nonconnecting | |||||
put(3, 12); // Obtuse Cove = Script Broken Letter | |||||
put(4, 10); // Square Cove = Script Joining | |||||
put(5, 0); // Obtuse Square Cove = Sans Serif Square | |||||
put(6, 128); // Square = Serif | |||||
put(7, 2); // Thin = Serif Line | |||||
put(8, 7); // Bone = Rounded Bracket | |||||
put(9, 11); // Exeraggerated = Script Calligraphic | |||||
put(10, 3); // Triangle = Serif Triangle | |||||
put(11, 0); // Normal Sans = Sans Serif Square | |||||
put(12, 4); // Obtuse Sans = Serif Swath | |||||
put(13, 6); // Perp Sans = Serif Bracket | |||||
put(14, 8); // Flared = Flair Serif | |||||
put(15, 1); // Rounded = Sans Serif Round | |||||
} | |||||
}; | |||||
private static final Map<Integer, Integer> FONT_WIDTH = new HashMap<Integer, Integer>() { | |||||
private static final long serialVersionUID = 1L; | |||||
{ | |||||
/** The conversions between TTF and PCL are not 1 to 1 **/ | |||||
put(1, -5); // 1 = Ultra Compressed | |||||
put(2, -4); // 2 = Extra Compressed | |||||
put(3, -3); // 3 = Compresses | |||||
put(4, -2); // 4 = Condensed | |||||
put(5, 0); // 5 = Normal | |||||
put(6, 2); // 6 = Expanded | |||||
put(7, 3); // 5 = Extra Expanded | |||||
} | |||||
}; | |||||
private int scaleFactor = -1; | |||||
private PCLSymbolSet symbolSet = PCLSymbolSet.Bound_Generic; | |||||
public PCLTTFFontReader(Typeface font, PCLByteWriterUtil pclByteWriter) throws IOException { | |||||
super(font, pclByteWriter); | |||||
loadFont(); | |||||
} | |||||
protected void loadFont() throws IOException { | |||||
if (typeface instanceof CustomFontMetricsMapper) { | |||||
CustomFontMetricsMapper fontMetrics = (CustomFontMetricsMapper) typeface; | |||||
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, fontName); | |||||
readFontTables(); | |||||
} else { | |||||
// TODO - Handle when typeface is not in the expected format for a PCL TrueType object | |||||
} | |||||
} | |||||
protected void readFontTables() throws IOException { | |||||
PCLTTFTable fontTable; | |||||
fontTable = readFontTable(OFTableName.PCLT); | |||||
if (fontTable instanceof PCLTTFPCLTFontTable) { | |||||
pcltTable = (PCLTTFPCLTFontTable) fontTable; | |||||
} | |||||
fontTable = readFontTable(OFTableName.OS2); | |||||
if (fontTable instanceof PCLTTFOS2FontTable) { | |||||
os2Table = (PCLTTFOS2FontTable) fontTable; | |||||
} | |||||
fontTable = readFontTable(OFTableName.POST); | |||||
if (fontTable instanceof PCLTTFPOSTFontTable) { | |||||
postTable = (PCLTTFPOSTFontTable) fontTable; | |||||
} | |||||
} | |||||
private PCLTTFTable readFontTable(OFTableName tableName) throws IOException { | |||||
if (ttfFont.seekTab(reader, tableName, 0)) { | |||||
return getTTFTableFactory().newInstance(tableName); | |||||
} | |||||
return null; | |||||
} | |||||
private PCLTTFTableFactory getTTFTableFactory() { | |||||
if (ttfTableFactory == null) { | |||||
ttfTableFactory = PCLTTFTableFactory.getInstance(reader); | |||||
} | |||||
return ttfTableFactory; | |||||
} | |||||
@Override | |||||
public int getDescriptorSize() { | |||||
return 72; // Descriptor size (leave at 72 for our purposes) | |||||
} | |||||
@Override | |||||
public int getHeaderFormat() { | |||||
return 15; // TrueType Scalable Font | |||||
} | |||||
@Override | |||||
public int getFontType() { | |||||
if (symbolSet == PCLSymbolSet.Unbound) { | |||||
return 11; // Font Type - Unbound TrueType Scalable font | |||||
} else { | |||||
return 2; // 0-255 (except 0, 7 and 27) | |||||
} | |||||
} | |||||
@Override | |||||
public int getStyleMSB() { | |||||
if (pcltTable != null) { | |||||
return getMSB(pcltTable.getStyle()); | |||||
} | |||||
return 3; | |||||
} | |||||
@Override | |||||
public int getBaselinePosition() { | |||||
return 0; // Baseline position must be set to 0 for TTF fonts | |||||
} | |||||
@Override | |||||
public int getCellWidth() { | |||||
int[] bbox = ttfFont.getBBoxRaw(); | |||||
return bbox[2] - bbox[0]; | |||||
} | |||||
@Override | |||||
public int getCellHeight() { | |||||
int[] bbox = ttfFont.getBBoxRaw(); | |||||
return bbox[3] - bbox[1]; | |||||
} | |||||
@Override | |||||
public int getOrientation() { | |||||
return 0; // Scalable fonts (TrueType) must be 0 | |||||
} | |||||
@Override | |||||
public int getSpacing() { | |||||
if (os2Table != null) { | |||||
return (os2Table.getPanose()[4] == 9) ? 0 : 1; | |||||
} else if (postTable != null) { | |||||
return postTable.getIsFixedPitch(); | |||||
} | |||||
return 1; | |||||
} | |||||
@Override | |||||
public int getSymbolSet() { | |||||
if (pcltTable != null) { | |||||
return pcltTable.getSymbolSet(); | |||||
} else { | |||||
return symbolSet.getKind1(); | |||||
} | |||||
} | |||||
@Override | |||||
public int getPitch() { | |||||
int pitch = ttfFont.getCharWidthRaw(0x20); | |||||
if (pitch < 0) { | |||||
// No advance width found for the space character | |||||
return 0; | |||||
} | |||||
return pitch; | |||||
} | |||||
@Override | |||||
public int getHeight() { | |||||
return 0; // Fixed zero value for TrueType fonts | |||||
} | |||||
@Override | |||||
public int getXHeight() { | |||||
if (pcltTable != null) { | |||||
return pcltTable.getXHeight(); | |||||
} else if (os2Table != null) { | |||||
return os2Table.getXHeight(); | |||||
} | |||||
return 0; | |||||
} | |||||
@Override | |||||
public int getWidthType() { | |||||
if (pcltTable != null) { | |||||
return pcltTable.getWidthType(); | |||||
} else if (os2Table != null) { | |||||
return convertTTFWidthClass(os2Table.getWidthClass()); | |||||
} | |||||
return 0; | |||||
} | |||||
private int convertTTFWidthClass(int widthClass) { | |||||
if (FONT_WIDTH.containsKey(widthClass)) { | |||||
return FONT_WIDTH.get(widthClass); | |||||
} else { | |||||
return 0; // No match - return normal | |||||
} | |||||
} | |||||
@Override | |||||
public int getStyleLSB() { | |||||
if (pcltTable != null) { | |||||
return getLSB(pcltTable.getStyle()); | |||||
} | |||||
return 224; | |||||
} | |||||
@Override | |||||
public int getStrokeWeight() { | |||||
if (pcltTable != null) { | |||||
return pcltTable.getStrokeWeight(); | |||||
} else if (os2Table != null) { | |||||
return convertTTFWeightClass(os2Table.getWeightClass()); | |||||
} | |||||
return 0; | |||||
} | |||||
private int convertTTFWeightClass(int weightClass) { | |||||
if (FONT_WEIGHT.containsKey(weightClass)) { | |||||
return FONT_WEIGHT.get(weightClass); | |||||
} else { | |||||
return 0; // No match - return normal | |||||
} | |||||
} | |||||
@Override | |||||
public int getTypefaceLSB() { | |||||
if (pcltTable != null) { | |||||
return getLSB(pcltTable.getTypeFamily()); | |||||
} | |||||
return 254; | |||||
} | |||||
@Override | |||||
public int getTypefaceMSB() { | |||||
if (pcltTable != null) { | |||||
return getMSB(pcltTable.getTypeFamily()); | |||||
} | |||||
return 0; | |||||
} | |||||
@Override | |||||
public int getSerifStyle() { | |||||
if (pcltTable != null) { | |||||
return pcltTable.getSerifStyle(); | |||||
} else { | |||||
return convertFromTTFSerifStyle(); | |||||
} | |||||
} | |||||
private int convertFromTTFSerifStyle() { | |||||
if (os2Table != null) { | |||||
int serifStyle = os2Table.getPanose()[1]; | |||||
return FONT_SERIF.get(serifStyle); | |||||
} | |||||
return 0; | |||||
} | |||||
@Override | |||||
public int getQuality() { | |||||
return 2; // Letter quality | |||||
} | |||||
@Override | |||||
public int getPlacement() { | |||||
return 0; // Fixed value of 0 for TrueType (scalable fonts) | |||||
} | |||||
@Override | |||||
public int getUnderlinePosition() { | |||||
return 0; // Scalable fonts has a fixed value of 0 - See Master Underline Position | |||||
} | |||||
@Override | |||||
public int getUnderlineThickness() { | |||||
return 0; // Scalable fonts has a fixed value of 0 - See Master Underline Thickness | |||||
} | |||||
@Override | |||||
public int getTextHeight() { | |||||
return 2048; | |||||
} | |||||
@Override | |||||
public int getTextWidth() { | |||||
if (os2Table != null) { | |||||
return os2Table.getAvgCharWidth(); | |||||
} | |||||
return 0; | |||||
} | |||||
@Override | |||||
public int getFirstCode() { | |||||
return 32; | |||||
} | |||||
@Override | |||||
public int getLastCode() { | |||||
return 255; // Bound font with a maximum of 255 characters | |||||
} | |||||
@Override | |||||
public int getPitchExtended() { | |||||
return 0; // Zero for Scalable fonts | |||||
} | |||||
@Override | |||||
public int getHeightExtended() { | |||||
return 0; // Zero for Scalable fonts | |||||
} | |||||
@Override | |||||
public int getCapHeight() { | |||||
if (pcltTable != null) { | |||||
return pcltTable.getStrokeWeight(); | |||||
} else if (os2Table != null) { | |||||
return os2Table.getCapHeight(); | |||||
} | |||||
return 0; | |||||
} | |||||
@Override | |||||
public int getFontNumber() { | |||||
if (pcltTable != null) { | |||||
return (int) pcltTable.getFontNumber(); | |||||
} | |||||
return 0; | |||||
} | |||||
@Override | |||||
public String getFontName() { | |||||
if (pcltTable != null) { | |||||
return pcltTable.getTypeface(); | |||||
} else { | |||||
return ttfFont.getFullName(); | |||||
} | |||||
} | |||||
@Override | |||||
public int getScaleFactor() throws IOException { | |||||
if (scaleFactor == -1) { | |||||
OFTableName headTag = OFTableName.HEAD; | |||||
if (ttfFont.seekTab(reader, headTag, 0)) { | |||||
reader.readTTFLong(); // Version | |||||
reader.readTTFLong(); // Font revision | |||||
reader.readTTFLong(); // Check sum adjustment | |||||
reader.readTTFLong(); // Magic number | |||||
reader.readTTFShort(); // Flags | |||||
scaleFactor = reader.readTTFUShort(); // Units per em | |||||
return scaleFactor; | |||||
} | |||||
} else { | |||||
return scaleFactor; | |||||
} | |||||
return 0; | |||||
} | |||||
@Override | |||||
public int getMasterUnderlinePosition() throws IOException { | |||||
return (int) Math.round(getScaleFactor() * 0.2); | |||||
} | |||||
@Override | |||||
public int getMasterUnderlineThickness() throws IOException { | |||||
return (int) Math.round(getScaleFactor() * 0.05); | |||||
} | |||||
@Override | |||||
public int getFontScalingTechnology() { | |||||
return 1; // TrueType scalable font | |||||
} | |||||
@Override | |||||
public int getVariety() { | |||||
return 0; // TrueType fonts must be set to zero | |||||
} | |||||
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(mappedGlyphs))); | |||||
fontSegments.add(new PCLFontSegment(SegmentID.CP, ttfFont.getCopyrightNotice().getBytes("US-ASCII"))); | |||||
fontSegments.add(new PCLFontSegment(SegmentID.NULL, new byte[0])); | |||||
return fontSegments; | |||||
} | |||||
/** | |||||
* See Font Header Format 11-35 (Character Complement Array) in the PCL 5 Specification. Defined as an array of 8 | |||||
* bytes specific to certain character sets. In this case specifying 0 for all values (default complement) means the | |||||
* font is compatible with any character sets. '110' on least significant bits signifies unicode. See specification | |||||
* for further customization. | |||||
*/ | |||||
private byte[] getCharacterComplement() { | |||||
byte[] ccUnicode = new byte[8]; | |||||
ccUnicode[7] = 6; | |||||
return ccUnicode; | |||||
} | |||||
private byte[] getGlobalTrueTypeData(Map<Character, Integer> mappedGlyphs) throws IOException { | |||||
ByteArrayOutputStream baos = new ByteArrayOutputStream(); | |||||
List<TableOffset> tableOffsets = new ArrayList<TableOffset>(); | |||||
// Version | |||||
baos.write(pclByteWriter.unsignedInt(1)); // Major | |||||
baos.write(pclByteWriter.unsignedInt(0)); // Minor | |||||
int numTables = 5; // head, hhea, hmtx, maxp and gdir | |||||
// Optional Hint Tables | |||||
OFDirTabEntry headTable = ttfFont.getDirectoryEntry(OFTableName.CVT); | |||||
if (headTable != null) { | |||||
numTables++; | |||||
} | |||||
OFDirTabEntry fpgmTable = ttfFont.getDirectoryEntry(OFTableName.FPGM); | |||||
if (fpgmTable != null) { | |||||
numTables++; | |||||
} | |||||
OFDirTabEntry prepTable = ttfFont.getDirectoryEntry(OFTableName.PREP); | |||||
if (prepTable != null) { | |||||
numTables++; | |||||
} | |||||
baos.write(pclByteWriter.unsignedInt(numTables)); // numTables | |||||
int maxPowerNumTables = pclByteWriter.maxPower2(numTables); | |||||
int searchRange = maxPowerNumTables * 16; | |||||
baos.write(pclByteWriter.unsignedInt(searchRange)); | |||||
baos.write(pclByteWriter.unsignedInt(pclByteWriter.log(maxPowerNumTables, 2))); // Entry Selector | |||||
baos.write(pclByteWriter.unsignedInt(numTables * 16 - searchRange)); // Range shift | |||||
// Add default data tables | |||||
writeTrueTypeTable(baos, OFTableName.HEAD, tableOffsets); | |||||
writeTrueTypeTable(baos, OFTableName.HHEA, 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); | |||||
// Add optional data tables (for hints) | |||||
writeTrueTypeTable(baos, OFTableName.CVT, tableOffsets); | |||||
writeTrueTypeTable(baos, OFTableName.FPGM, tableOffsets); | |||||
writeTrueTypeTable(baos, OFTableName.PREP, tableOffsets); | |||||
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, | |||||
List<TableOffset> tableOffsets) throws IOException, UnsupportedEncodingException { | |||||
OFDirTabEntry tabEntry = ttfFont.getDirectoryEntry(table); | |||||
if (tabEntry != null) { | |||||
baos.write(tabEntry.getTag()); | |||||
baos.write(pclByteWriter.unsignedLongInt(tabEntry.getChecksum())); | |||||
TableOffset newTableOffset = new TableOffset(tabEntry.getOffset(), | |||||
tabEntry.getLength(), baos.size()); | |||||
tableOffsets.add(newTableOffset); | |||||
baos.write(pclByteWriter.unsignedLongInt(0)); // Offset to be set later | |||||
baos.write(pclByteWriter.unsignedLongInt(tabEntry.getLength())); | |||||
} | |||||
} | |||||
private void writeGDIR(ByteArrayOutputStream baos) throws UnsupportedEncodingException, IOException { | |||||
baos.write("gdir".getBytes("ISO-8859-1")); | |||||
baos.write(pclByteWriter.unsignedLongInt(0)); // Checksum | |||||
baos.write(pclByteWriter.unsignedLongInt(0)); // Offset | |||||
baos.write(pclByteWriter.unsignedLongInt(0)); // Length | |||||
} | |||||
private ByteArrayOutputStream copyTables(List<TableOffset> tableOffsets, | |||||
ByteArrayOutputStream baos, byte[] hmtxTable, int hmtxSize) | |||||
throws IOException { | |||||
Map<Integer, byte[]> offsetValues = new HashMap<Integer, byte[]>(); | |||||
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); | |||||
} | |||||
} | |||||
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(); | |||||
for (int offset : offsets.keySet()) { | |||||
pclByteWriter.updateDataAtLocation(softFont, offsets.get(offset), offset); | |||||
} | |||||
baos = new ByteArrayOutputStream(); | |||||
baos.write(softFont); | |||||
return baos; | |||||
} | |||||
@Override | |||||
public Map<Integer, int[]> getCharacterOffsets() throws IOException { | |||||
List<OFMtxEntry> mtx = ttfFont.getMtx(); | |||||
OFTableName glyfTag = OFTableName.GLYF; | |||||
Map<Integer, int[]> charOffsets = new HashMap<Integer, int[]>(); | |||||
OFDirTabEntry tabEntry = ttfFont.getDirectoryEntry(glyfTag); | |||||
if (ttfFont.seekTab(reader, glyfTag, 0)) { | |||||
for (int i = 1; i < mtx.size(); i++) { | |||||
OFMtxEntry entry = mtx.get(i); | |||||
OFMtxEntry nextEntry; | |||||
int nextOffset = 0; | |||||
int charCode = 0; | |||||
if (entry.getUnicodeIndex().size() > 0) { | |||||
charCode = (Integer) entry.getUnicodeIndex().get(0); | |||||
} else { | |||||
charCode = entry.getIndex(); | |||||
} | |||||
if (i < mtx.size() - 1) { | |||||
nextEntry = mtx.get(i + 1); | |||||
nextOffset = (int) nextEntry.getOffset(); | |||||
} else { | |||||
nextOffset = (int) ttfFont.getLastGlyfLocation(); | |||||
} | |||||
int glyphOffset = (int) entry.getOffset(); | |||||
int glyphLength = nextOffset - glyphOffset; | |||||
charOffsets.put(charCode, new int[]{(int) tabEntry.getOffset() + glyphOffset, glyphLength}); | |||||
} | |||||
} | |||||
return charOffsets; | |||||
} | |||||
@Override | |||||
public OpenFont getFontFile() { | |||||
return ttfFont; | |||||
} | |||||
@Override | |||||
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; | |||||
} | |||||
} |
/* | |||||
* 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.render.pcl.fonts.truetype; | |||||
import java.io.IOException; | |||||
import org.apache.fop.fonts.truetype.FontFileReader; | |||||
public class PCLTTFOS2FontTable extends PCLTTFTable { | |||||
private int avgCharWidth; | |||||
private int xHeight; | |||||
private int widthClass; | |||||
private int weightClass; | |||||
private int capHeight; | |||||
private int[] panose = new int[10]; | |||||
public PCLTTFOS2FontTable(FontFileReader in) throws IOException { | |||||
super(in); | |||||
int version = reader.readTTFUShort(); // Version | |||||
avgCharWidth = reader.readTTFShort(); | |||||
weightClass = reader.readTTFShort(); | |||||
widthClass = reader.readTTFShort(); | |||||
skipShort(reader, 12); | |||||
for (int i = 0; i < 10; i++) { | |||||
panose[i] = reader.readTTFByte(); | |||||
} | |||||
skipLong(reader, 4); | |||||
skipByte(reader, 4); | |||||
skipShort(reader, 8); | |||||
if (version >= 2) { | |||||
skipLong(reader, 2); | |||||
xHeight = reader.readTTFShort(); | |||||
capHeight = reader.readTTFShort(); | |||||
} | |||||
} | |||||
public int getAvgCharWidth() { | |||||
return avgCharWidth; | |||||
} | |||||
public int getXHeight() { | |||||
return xHeight; | |||||
} | |||||
public int getWidthClass() { | |||||
return widthClass; | |||||
} | |||||
public int getWeightClass() { | |||||
return weightClass; | |||||
} | |||||
public int getCapHeight() { | |||||
return capHeight; | |||||
} | |||||
public int[] getPanose() { | |||||
return panose; | |||||
} | |||||
} |
/* | |||||
* 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.render.pcl.fonts.truetype; | |||||
import java.io.IOException; | |||||
import org.apache.fop.fonts.truetype.FontFileReader; | |||||
public class PCLTTFPCLTFontTable extends PCLTTFTable { | |||||
private long version; | |||||
private long fontNumber; | |||||
private int pitch; | |||||
private int xHeight; | |||||
private int style; | |||||
private int typeFamily; | |||||
private int capHeight; | |||||
private int symbolSet; | |||||
private String typeface; | |||||
private String characterComplement; | |||||
private String filename; | |||||
private int strokeWeight; | |||||
private int widthType; | |||||
private int serifStyle; | |||||
public PCLTTFPCLTFontTable(FontFileReader in) throws IOException { | |||||
super(in); | |||||
version = reader.readTTFULong(); | |||||
fontNumber = reader.readTTFULong(); | |||||
pitch = reader.readTTFUShort(); | |||||
xHeight = reader.readTTFUShort(); | |||||
style = reader.readTTFUShort(); | |||||
typeFamily = reader.readTTFUShort(); | |||||
capHeight = reader.readTTFUShort(); | |||||
symbolSet = reader.readTTFUShort(); | |||||
typeface = reader.readTTFString(16); | |||||
characterComplement = reader.readTTFString(8); | |||||
filename = reader.readTTFString(6); | |||||
strokeWeight = reader.readTTFUShort(); | |||||
widthType = reader.readTTFUShort(); | |||||
serifStyle = reader.readTTFUByte(); | |||||
} | |||||
public long getVersion() { | |||||
return version; | |||||
} | |||||
public long getFontNumber() { | |||||
return fontNumber; | |||||
} | |||||
public int getPitch() { | |||||
return pitch; | |||||
} | |||||
public int getXHeight() { | |||||
return xHeight; | |||||
} | |||||
public int getStyle() { | |||||
return style; | |||||
} | |||||
public int getTypeFamily() { | |||||
return typeFamily; | |||||
} | |||||
public int getCapHeight() { | |||||
return capHeight; | |||||
} | |||||
public int getSymbolSet() { | |||||
return symbolSet; | |||||
} | |||||
public String getTypeface() { | |||||
return typeface; | |||||
} | |||||
public String getCharacterComplement() { | |||||
return characterComplement; | |||||
} | |||||
public String getFilename() { | |||||
return filename; | |||||
} | |||||
public int getStrokeWeight() { | |||||
return strokeWeight; | |||||
} | |||||
public int getWidthType() { | |||||
return widthType; | |||||
} | |||||
public int getSerifStyle() { | |||||
return serifStyle; | |||||
} | |||||
} |
/* | |||||
* 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.render.pcl.fonts.truetype; | |||||
import java.io.IOException; | |||||
import org.apache.fop.fonts.truetype.FontFileReader; | |||||
public class PCLTTFPOSTFontTable extends PCLTTFTable { | |||||
private int underlinePosition; | |||||
private int underlineThickness; | |||||
private int isFixedPitch; | |||||
public PCLTTFPOSTFontTable(FontFileReader in) throws IOException { | |||||
super(in); | |||||
reader.readTTFLong(); // Version | |||||
reader.readTTFLong(); // Italic Angle | |||||
underlinePosition = reader.readTTFShort(); | |||||
underlineThickness = reader.readTTFShort(); | |||||
isFixedPitch = (int) reader.readTTFULong(); | |||||
} | |||||
public int getUnderlinePosition() { | |||||
return underlinePosition; | |||||
} | |||||
public int getUnderlineThickness() { | |||||
return underlineThickness; | |||||
} | |||||
public int getIsFixedPitch() { | |||||
return isFixedPitch; | |||||
} | |||||
} |
/* | |||||
* 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.render.pcl.fonts.truetype; | |||||
import java.io.IOException; | |||||
import org.apache.fop.fonts.truetype.FontFileReader; | |||||
public class PCLTTFTable { | |||||
protected FontFileReader reader; | |||||
public PCLTTFTable(FontFileReader reader) { | |||||
this.reader = reader; | |||||
} | |||||
protected void skipShort(FontFileReader reader, int skips) | |||||
throws IOException { | |||||
reader.skip(skips * 2); | |||||
} | |||||
protected void skipLong(FontFileReader reader, int skips) | |||||
throws IOException { | |||||
reader.skip(skips * 4); | |||||
} | |||||
protected void skipByte(FontFileReader reader, int skips) | |||||
throws IOException { | |||||
reader.skip(skips); | |||||
} | |||||
} |
/* | |||||
* 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.render.pcl.fonts.truetype; | |||||
import java.io.IOException; | |||||
import org.apache.fop.fonts.truetype.FontFileReader; | |||||
import org.apache.fop.fonts.truetype.OFTableName; | |||||
public final class PCLTTFTableFactory { | |||||
private FontFileReader reader; | |||||
private PCLTTFTableFactory(FontFileReader reader) { | |||||
this.reader = reader; | |||||
} | |||||
public static PCLTTFTableFactory getInstance(FontFileReader reader) { | |||||
return new PCLTTFTableFactory(reader); | |||||
} | |||||
public PCLTTFTable newInstance(OFTableName tableName) | |||||
throws IOException { | |||||
if (tableName == OFTableName.PCLT) { | |||||
return new PCLTTFPCLTFontTable(reader); | |||||
} else if (tableName == OFTableName.OS2) { | |||||
return new PCLTTFOS2FontTable(reader); | |||||
} else if (tableName == OFTableName.POST) { | |||||
return new PCLTTFPOSTFontTable(reader); | |||||
} | |||||
return null; | |||||
} | |||||
} |
/* | |||||
* Licensed to the Apache Software Foundation (ASF) under one or more | |||||
* contributor license agreements. See the NOTICE file distributed with | |||||
* this work for additional information regarding copyright ownership. | |||||
* The ASF licenses this file to You under the Apache License, Version 2.0 | |||||
* (the "License"); you may not use this file except in compliance with | |||||
* the License. You may obtain a copy of the License at | |||||
* | |||||
* http://www.apache.org/licenses/LICENSE-2.0 | |||||
* | |||||
* Unless required by applicable law or agreed to in writing, software | |||||
* distributed under the License is distributed on an "AS IS" BASIS, | |||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
* See the License for the specific language governing permissions and | |||||
* limitations under the License. | |||||
*/ | |||||
/* $Id$ */ | |||||
package org.apache.fop.render.pcl.fonts; | |||||
import java.io.IOException; | |||||
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.TTFFile; | |||||
import org.apache.fop.render.java2d.CustomFontMetricsMapper; | |||||
import org.apache.fop.render.pcl.fonts.truetype.PCLTTFFontReader; | |||||
public class MockPCLTTFFontReader extends PCLTTFFontReader { | |||||
public MockPCLTTFFontReader(Typeface font, PCLByteWriterUtil pclByteWriter) throws IOException { | |||||
super(font, pclByteWriter); | |||||
} | |||||
@Override | |||||
protected void loadFont() throws IOException { | |||||
if (typeface instanceof CustomFontMetricsMapper) { | |||||
CustomFontMetricsMapper fontMetrics = (CustomFontMetricsMapper) typeface; | |||||
CustomFont customFont = (CustomFont) fontMetrics.getRealFont(); | |||||
fontStream = customFont.getInputStream(); | |||||
reader = new FontFileReader(fontStream); | |||||
ttfFont = new TTFFile(); | |||||
ttfFont.readFont(reader, customFont.getFullName()); | |||||
readFontTables(); | |||||
} else { | |||||
// TODO - Handle when typeface is not in the expected format for a PCL TrueType object | |||||
} | |||||
} | |||||
} |
/* | |||||
* 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.render.pcl.fonts; | |||||
import java.io.IOException; | |||||
import org.junit.Before; | |||||
import org.junit.Test; | |||||
import static org.junit.Assert.assertArrayEquals; | |||||
public class PCLByteWriterUtilTestCase { | |||||
private PCLByteWriterUtil byteWriter; | |||||
@Before | |||||
public void setUp() { | |||||
byteWriter = new PCLByteWriterUtil(); | |||||
} | |||||
@Test | |||||
public void testWriteMethods() throws IOException { | |||||
byte[] output = byteWriter.writeCommand("(s4X"); | |||||
// 27 = PCL escape character with rest in ASCII format | |||||
byte[] command = {27, 40, 115, 52, 88}; | |||||
assertArrayEquals(command, output); | |||||
byte[] resultB = byteWriter.unsignedLongInt(102494); | |||||
byte[] compareB = {0, 1, -112, 94}; | |||||
assertArrayEquals(compareB, resultB); | |||||
byte[] resultC = byteWriter.unsignedInt(1024); | |||||
byte[] compareC = {4, 0}; | |||||
assertArrayEquals(compareC, resultC); | |||||
} | |||||
@Test | |||||
public void testUtilMethods() throws IOException { | |||||
byte[] anArray = {1, 2, 3, 4, 5, 9, 10}; | |||||
byte[] insertArray = {6, 7, 8}; | |||||
byte[] result = byteWriter.insertIntoArray(5, anArray, insertArray); | |||||
byte[] compareA = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; | |||||
assertArrayEquals(compareA, result); | |||||
byte[] reverse = {10, 9, 8, 7, 6}; | |||||
byteWriter.updateDataAtLocation(compareA, reverse, 5); | |||||
byte[] compareB = {1, 2, 3, 4, 5, 10, 9, 8, 7, 6}; | |||||
assertArrayEquals(compareB, compareA); | |||||
byte[] anArrayC = {1, 2, 3, 4, 5}; | |||||
byte[] resultC = byteWriter.padBytes(anArrayC, 10); | |||||
byte[] compareC = {1, 2, 3, 4, 5, 0, 0, 0, 0, 0}; | |||||
assertArrayEquals(compareC, resultC); | |||||
byte[] resultD = byteWriter.padBytes(anArrayC, 10, 1); | |||||
byte[] compareD = {1, 2, 3, 4, 5, 1, 1, 1, 1, 1}; | |||||
assertArrayEquals(compareD, resultD); | |||||
} | |||||
} |
/* | |||||
* 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.render.pcl.fonts; | |||||
import java.io.File; | |||||
import java.io.FileInputStream; | |||||
import java.net.URI; | |||||
import org.junit.Test; | |||||
import static org.junit.Assert.assertTrue; | |||||
import static org.mockito.Mockito.mock; | |||||
import static org.mockito.Mockito.when; | |||||
import org.apache.fop.fonts.CustomFont; | |||||
import org.apache.fop.fonts.FontType; | |||||
import org.apache.fop.render.java2d.CustomFontMetricsMapper; | |||||
import org.apache.fop.render.pcl.fonts.truetype.PCLTTFFontReader; | |||||
public class PCLFontReaderFactoryTestCase { | |||||
private static final String TEST_FONT_TTF = "./test/resources/fonts/ttf/DejaVuLGCSerif.ttf"; | |||||
@Test | |||||
public void verifyTypeIdentification() throws Exception { | |||||
CustomFont sbFont = mock(CustomFont.class); | |||||
when(sbFont.getInputStream()).thenReturn(new FileInputStream(new File(TEST_FONT_TTF))); | |||||
when(sbFont.getEmbedFileURI()).thenReturn(new URI(TEST_FONT_TTF)); | |||||
CustomFontMetricsMapper customFont = new CustomFontMetricsMapper(sbFont); | |||||
when(customFont.getFontType()).thenReturn(FontType.TRUETYPE); | |||||
// Have to mock the input stream twice otherwise get a Stream is closed exception | |||||
when(((CustomFont) customFont.getRealFont()).getInputStream()).thenReturn( | |||||
new FileInputStream(new File(TEST_FONT_TTF))); | |||||
PCLFontReaderFactory fontReaderFactory = PCLFontReaderFactory.getInstance(null); | |||||
assertTrue(fontReaderFactory.createInstance(customFont) instanceof PCLTTFFontReader); | |||||
} | |||||
} |
/* | |||||
* 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.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; | |||||
import org.junit.Before; | |||||
import org.junit.Test; | |||||
import static org.junit.Assert.assertArrayEquals; | |||||
import static org.junit.Assert.assertEquals; | |||||
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; | |||||
public class PCLTTFFontReaderTestCase { | |||||
private CustomFontMetricsMapper customFont = mock(CustomFontMetricsMapper.class); | |||||
private PCLByteWriterUtil byteWriter; | |||||
private static final String TEST_FONT_A = "./test/resources/fonts/ttf/DejaVuLGCSerif.ttf"; | |||||
@Before | |||||
public void setUp() { | |||||
byteWriter = new PCLByteWriterUtil(); | |||||
} | |||||
@Test | |||||
public void verifyFontAData() throws Exception { | |||||
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); | |||||
} | |||||
/** | |||||
* Compares the input font data against a sample of the data read and calculated by the reader. The assertions are | |||||
* made against data taken from the TrueType Font Analyzer tool. | |||||
* @param reader The reader | |||||
*/ | |||||
private void verifyFontData(PCLTTFFontReader reader) { | |||||
assertEquals(reader.getCellWidth(), 5015); // Bounding box X2 - X1 | |||||
assertEquals(reader.getCellHeight(), 3254); // Bounding box Y2 - Y1 | |||||
assertEquals(reader.getCapHeight(), 0); // OS2Table.capHeight | |||||
assertEquals(reader.getFontName(), "DejaVu LGC Serif"); // Full name read by TTFFont object | |||||
assertEquals(reader.getFirstCode(), 32); // Always 32 for bound font | |||||
assertEquals(reader.getLastCode(), 255); // Always 255 for bound font | |||||
// Values that require conversion tables (See PCLTTFFontReader.java) | |||||
assertEquals(reader.getStrokeWeight(), 0); // Weight Class 400 (regular) should be equivalent 0 | |||||
assertEquals(reader.getSerifStyle(), 128); // Serif Style 0 should equal 0 | |||||
assertEquals(reader.getWidthType(), 0); // Width Class 5 (regular) should be equivalent 0 | |||||
} | |||||
private void validateOffsets(PCLTTFFontReader reader) throws IOException { | |||||
// Offsets are stored with their character ID with the array [offset, length] | |||||
Map<Integer, int[]> offsets = reader.getCharacterOffsets(); | |||||
// Test data | |||||
int[] charC = {27644, 144}; // Char index = 99 | |||||
int[] charDollar = {16044, 264}; // Char index = 36 | |||||
int[] charOne = {17808, 176}; // Char index = 49 | |||||
int[] charUpperD = {21236, 148}; // Char index = 68 | |||||
int[] charUpperJ = {22140, 176}; // Char index = 74 | |||||
assertArrayEquals(offsets.get(99), charC); | |||||
assertArrayEquals(offsets.get(36), charDollar); | |||||
assertArrayEquals(offsets.get(49), charOne); | |||||
assertArrayEquals(offsets.get(68), charUpperD); | |||||
assertArrayEquals(offsets.get(74), charUpperJ); | |||||
} | |||||
/** | |||||
* Verifies the font segment data copied originally from the TrueType font. Data was verified using TrueType Font | |||||
* Analyzer and PCLParaphernalia tool. | |||||
* @param reader The reader | |||||
* @throws IOException | |||||
*/ | |||||
private void validateFontSegments(PCLTTFFontReader reader) throws IOException { | |||||
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) { | |||||
// Panose | |||||
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); | |||||
} | |||||
} |
/* | |||||
* 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.render.pcl.fonts.truetype; | |||||
import java.io.File; | |||||
import java.io.FileInputStream; | |||||
import org.junit.Test; | |||||
import static org.junit.Assert.assertArrayEquals; | |||||
import static org.mockito.Mockito.mock; | |||||
import static org.mockito.Mockito.when; | |||||
import org.apache.fop.fonts.CustomFont; | |||||
import org.apache.fop.fonts.truetype.FontFileReader; | |||||
import org.apache.fop.fonts.truetype.OFFontLoader; | |||||
import org.apache.fop.fonts.truetype.TTFFile; | |||||
import org.apache.fop.render.java2d.CustomFontMetricsMapper; | |||||
import org.apache.fop.render.pcl.fonts.PCLByteWriterUtil; | |||||
import org.apache.fop.render.pcl.fonts.PCLSoftFont; | |||||
public class PCLTTFCharacterWriterTestCase { | |||||
private PCLTTFCharacterWriter characterWriter; | |||||
private PCLSoftFont softFont; | |||||
private CustomFontMetricsMapper customFont = mock(CustomFontMetricsMapper.class); | |||||
private static final String TEST_FONT_A = "./test/resources/fonts/ttf/DejaVuLGCSerif.ttf"; | |||||
@Test | |||||
public void verifyCharacterDefinition() throws Exception { | |||||
CustomFont sbFont = mock(CustomFont.class); | |||||
when(customFont.getRealFont()).thenReturn(sbFont); | |||||
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); | |||||
openFont.readFont(reader, header); | |||||
softFont.setOpenFont(openFont); | |||||
softFont.setReader(reader); | |||||
characterWriter = new PCLTTFCharacterWriter(softFont); | |||||
byte[] charDefinition = characterWriter.writeCharacterDefinitions("f"); | |||||
PCLByteWriterUtil pclByteWriter = new PCLByteWriterUtil(); | |||||
// Character command | |||||
byte[] command = pclByteWriter.writeCommand(String.format("*c%dE", 32)); | |||||
assertArrayEquals(getBytes(charDefinition, 0, 6), command); | |||||
// Character definition command | |||||
byte[] charDefCommand = pclByteWriter.writeCommand(String.format("(s%dW", 210)); | |||||
assertArrayEquals(getBytes(charDefinition, 6, 7), charDefCommand); | |||||
} | |||||
private byte[] getBytes(byte[] byteArray, int offset, int length) { | |||||
byte[] result = new byte[length]; | |||||
int count = 0; | |||||
for (int i = offset; i < offset + length; i++) { | |||||
result[count++] = byteArray[i]; | |||||
} | |||||
return result; | |||||
} | |||||
} |