git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1695082 13f79535-47bb-0310-9956-ffa450edef68tags/fop-2_1
@@ -238,6 +238,10 @@ | |||
<Bug pattern="OS_OPEN_STREAM_EXCEPTION_PATH"/> | |||
</Or> | |||
</Match> | |||
<Match> | |||
<Class name="org.apache.fop.render.pcl.fonts.truetype.PCLTTFTable"/> | |||
<Bug pattern="ICAST_INTEGER_MULTIPLY_CAST_TO_LONG"/> | |||
</Match> | |||
<!-- END - APPROVED EXCLUSIONS --> | |||
<!-- START - TEMPORARY (UNAPPROVED) EXCLUSIONS --> |
@@ -56,6 +56,18 @@ public class CIDFull implements CIDSet { | |||
return index; | |||
} | |||
/** {@inheritDoc} */ | |||
@Override | |||
public char getUnicodeFromGID(int glyphIndex) { | |||
return ' '; | |||
} | |||
/** {@inheritDoc} */ | |||
@Override | |||
public int getGIDFromChar(char ch) { | |||
return ch; | |||
} | |||
/** {@inheritDoc} */ | |||
public char getUnicode(int index) { | |||
initGlyphIndices(); | |||
@@ -109,5 +121,4 @@ public class CIDFull implements CIDSet { | |||
public int[] getWidths() { | |||
return font.getWidths(); | |||
} | |||
} |
@@ -43,6 +43,20 @@ public interface CIDSet { | |||
*/ | |||
char getUnicode(int index); | |||
/** | |||
* Gets the unicode character from the original font glyph index | |||
* @param glyphIndex The original glyph index of the character in the font | |||
* @return The character represented by the passed GID | |||
*/ | |||
char getUnicodeFromGID(int glyphIndex); | |||
/** | |||
* Returns the glyph index from the original font from a character | |||
* @param ch The character | |||
* @return The glyph index in the original font. | |||
*/ | |||
int getGIDFromChar(char ch); | |||
/** | |||
* Maps a character to a character selector for a font subset. If the character isn't in the | |||
* subset, yet, it is added and a new character selector returned. Otherwise, the already |
@@ -53,6 +53,12 @@ public class CIDSubset implements CIDSet { | |||
*/ | |||
private Map<Integer, Character> usedCharsIndex = new HashMap<Integer, Character>(); | |||
/** | |||
* A map between the original character and it's GID in the original font. | |||
*/ | |||
private Map<Character, Integer> charToGIDs = new HashMap<Character, Integer>(); | |||
private final MultiByteFont font; | |||
public CIDSubset(MultiByteFont mbf) { | |||
@@ -93,6 +99,7 @@ public class CIDSubset implements CIDSet { | |||
usedGlyphs.put(glyphIndex, selector); | |||
usedGlyphsIndex.put(selector, glyphIndex); | |||
usedCharsIndex.put(selector, unicode); | |||
charToGIDs.put(unicode, glyphIndex); | |||
usedGlyphsCount++; | |||
return selector; | |||
} else { | |||
@@ -105,6 +112,17 @@ public class CIDSubset implements CIDSet { | |||
return Collections.unmodifiableMap(this.usedGlyphs); | |||
} | |||
/** {@inheritDoc} */ | |||
public char getUnicodeFromGID(int glyphIndex) { | |||
int selector = usedGlyphs.get(glyphIndex); | |||
return usedCharsIndex.get(selector); | |||
} | |||
/** {@inheritDoc} */ | |||
public int getGIDFromChar(char ch) { | |||
return charToGIDs.get(ch); | |||
} | |||
/** {@inheritDoc} */ | |||
public char[] getChars() { | |||
char[] charArray = new char[usedGlyphsCount]; |
@@ -568,4 +568,16 @@ public abstract class CustomFont extends Typeface | |||
this.strikeoutThickness = strikeoutThickness; | |||
} | |||
/** | |||
* Returns a Map of used Glyphs. | |||
* @return Map Map of used Glyphs | |||
*/ | |||
public abstract Map<Integer, Integer> getUsedGlyphs(); | |||
/** | |||
* Returns the character from it's original glyph index in the font | |||
* @param glyphIndex The original index of the character | |||
* @return The character | |||
*/ | |||
public abstract char getUnicodeFromGID(int glyphIndex); | |||
} |
@@ -425,6 +425,24 @@ public class MultiByteFont extends CIDFont implements Substitutable, Positionabl | |||
return cidSet.getGlyphs(); | |||
} | |||
/** | |||
* Returns the character from it's original glyph index in the font | |||
* @param glyphIndex The original index of the character | |||
* @return The character | |||
*/ | |||
public char getUnicodeFromGID(int glyphIndex) { | |||
return cidSet.getUnicodeFromGID(glyphIndex); | |||
} | |||
/** | |||
* Gets the original glyph index in the font from a character. | |||
* @param ch The character | |||
* @return The glyph index in the font | |||
*/ | |||
public int getGIDFromChar(char ch) { | |||
return cidSet.getGIDFromChar(ch); | |||
} | |||
/** | |||
* Establishes the glyph definition table. | |||
* @param gdef the glyph definition table to be used by this font |
@@ -63,6 +63,7 @@ public class SingleByteFont extends CustomFont { | |||
private LinkedHashMap<Integer, String> usedGlyphNames; | |||
private Map<Integer, Integer> usedGlyphs; | |||
private Map<Integer, Character> usedCharsIndex; | |||
private Map<Character, Integer> charGIDMappings; | |||
public SingleByteFont(InternalResourceResolver resourceResolver) { | |||
super(resourceResolver); | |||
@@ -76,6 +77,7 @@ public class SingleByteFont extends CustomFont { | |||
usedGlyphNames = new LinkedHashMap<Integer, String>(); | |||
usedGlyphs = new HashMap<Integer, Integer>(); | |||
usedCharsIndex = new HashMap<Integer, Character>(); | |||
charGIDMappings = new HashMap<Character, Integer>(); | |||
// The zeroth value is reserved for .notdef | |||
usedGlyphs.put(0, 0); | |||
@@ -234,6 +236,7 @@ public class SingleByteFont extends CustomFont { | |||
int selector = usedGlyphsCount; | |||
usedGlyphs.put(glyphIndex, selector); | |||
usedCharsIndex.put(selector, unicode); | |||
charGIDMappings.put(unicode, glyphIndex); | |||
usedGlyphsCount++; | |||
return selector; | |||
} else { | |||
@@ -519,6 +522,15 @@ public class SingleByteFont extends CustomFont { | |||
return getUnicode(selector); | |||
} | |||
public int getGIDFromChar(char ch) { | |||
return charGIDMappings.get(ch); | |||
} | |||
public char getUnicodeFromGID(int glyphIndex) { | |||
int selector = usedGlyphs.get(glyphIndex); | |||
return usedCharsIndex.get(selector); | |||
} | |||
public void mapUsedGlyphName(int gid, String value) { | |||
usedGlyphNames.put(gid, value); | |||
} |
@@ -47,7 +47,7 @@ public class GlyfTable { | |||
/** All the glyphs that are composed, but do not appear in the subset. */ | |||
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 { | |||
mtxTab = metrics; | |||
tableOffset = dirTableEntry.getOffset(); | |||
@@ -202,7 +202,7 @@ public class GlyfTable { | |||
} 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()); | |||
return numberOfContours < 0; | |||
} | |||
@@ -215,7 +215,7 @@ public class GlyfTable { | |||
* @return the set of glyph indices this glyph composes | |||
* @throws IOException an I/O error | |||
*/ | |||
private Set<Integer> retrieveComposedGlyphs(int indexInOriginal) | |||
public Set<Integer> retrieveComposedGlyphs(int indexInOriginal) | |||
throws IOException { | |||
Set<Integer> composedGlyphs = new HashSet<Integer>(); | |||
long offset = tableOffset + mtxTab[indexInOriginal].getOffset() + 10; |
@@ -30,7 +30,7 @@ import java.util.Arrays; | |||
public class OFDirTabEntry { | |||
private byte[] tag = new byte[4]; | |||
private int checksum; | |||
private long checksum; | |||
private long offset; | |||
private long length; | |||
@@ -74,7 +74,7 @@ public class OFDirTabEntry { | |||
* Returns the checksum. | |||
* @return int | |||
*/ | |||
public int getChecksum() { | |||
public long getChecksum() { | |||
return checksum; | |||
} | |||
@@ -355,7 +355,7 @@ public abstract class OpenFont { | |||
long offset) throws IOException { | |||
OFDirTabEntry dt = dirTabs.get(tableName); | |||
if (dt == null) { | |||
log.error("Dirtab " + tableName.getName() + " not found."); | |||
log.info("Dirtab " + tableName.getName() + " not found."); | |||
return false; | |||
} else { | |||
in.seekSet(dt.getOffset() + offset); | |||
@@ -965,6 +965,15 @@ public abstract class OpenFont { | |||
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. | |||
* @return int The LowerCaseAscent | |||
@@ -1047,6 +1056,18 @@ public abstract class OpenFont { | |||
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. | |||
* @return Map The kerning table | |||
@@ -1978,4 +1999,8 @@ public abstract class OpenFont { | |||
IOUtils.closeQuietly(stream); | |||
} | |||
} | |||
public String getCopyrightNotice() { | |||
return notice; | |||
} | |||
} |
@@ -188,6 +188,14 @@ public class TTFFile extends OpenFont { | |||
: (fontFile.readTTFUShort() << 1)); | |||
} | |||
/** | |||
* Gets the last location of the glyf table | |||
* @return The last location as a long | |||
*/ | |||
public long getLastGlyfLocation() { | |||
return lastLoca; | |||
} | |||
@Override | |||
protected void initializeFont(FontFileReader in) throws IOException { | |||
fontFile = in; |
@@ -83,7 +83,8 @@ public class ConfiguredFontCollection implements FontCollection { | |||
font = new CustomFontMetricsMapper(fontMetrics, fontSource); | |||
} else { | |||
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.getKerning(), configFontInfo.getAdvanced(), resourceResolver); | |||
font = new CustomFontMetricsMapper(fontMetrics); |
@@ -289,4 +289,8 @@ public class CustomFontMetricsMapper extends Typeface implements FontMetricsMapp | |||
} | |||
} | |||
public Typeface getRealFont() { | |||
return typeface; | |||
} | |||
} |
@@ -55,7 +55,7 @@ final class HardcodedFonts { | |||
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; | |||
if (name.length() > 1 && name.charAt(0) == 'F') { | |||
try { |
@@ -53,4 +53,13 @@ public interface PCLEventProducer extends EventProducer { | |||
*/ | |||
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); | |||
} |
@@ -1,4 +1,5 @@ | |||
<?xml version="1.0" encoding="UTF-8"?> | |||
<catalogue xml:lang="en"> | |||
<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> |
@@ -140,6 +140,15 @@ public class PCLGenerator { | |||
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. | |||
* |
@@ -28,7 +28,9 @@ import java.awt.Rectangle; | |||
import java.awt.geom.AffineTransform; | |||
import java.awt.geom.Point2D; | |||
import java.awt.geom.Rectangle2D; | |||
import java.io.ByteArrayOutputStream; | |||
import java.io.IOException; | |||
import java.util.List; | |||
import java.util.Map; | |||
import java.util.Stack; | |||
@@ -42,16 +44,27 @@ import org.apache.xmlgraphics.image.loader.impl.ImageGraphics2D; | |||
import org.apache.xmlgraphics.java2d.GraphicContext; | |||
import org.apache.xmlgraphics.java2d.Graphics2DImagePainter; | |||
import org.apache.fop.fonts.CIDFontType; | |||
import org.apache.fop.fonts.Font; | |||
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.RenderingContext; | |||
import org.apache.fop.render.intermediate.AbstractIFPainter; | |||
import org.apache.fop.render.intermediate.IFException; | |||
import org.apache.fop.render.intermediate.IFState; | |||
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.Java2DPainter; | |||
import org.apache.fop.render.pcl.fonts.PCLCharacterWriter; | |||
import org.apache.fop.render.pcl.fonts.PCLSoftFont; | |||
import org.apache.fop.render.pcl.fonts.PCLSoftFontManager; | |||
import org.apache.fop.render.pcl.fonts.PCLSoftFontManager.PCLTextSegment; | |||
import org.apache.fop.render.pcl.fonts.truetype.PCLTTFCharacterWriter; | |||
import org.apache.fop.traits.BorderProps; | |||
import org.apache.fop.traits.RuleStyle; | |||
import org.apache.fop.util.CharUtilities; | |||
@@ -73,6 +86,8 @@ public class PCLPainter extends AbstractIFPainter<PCLDocumentHandler> implements | |||
private Stack<GraphicContext> graphicContextStack = new Stack<GraphicContext>(); | |||
private GraphicContext graphicContext = new GraphicContext(); | |||
private PCLSoftFontManager sfManager = new PCLSoftFontManager(); | |||
/** | |||
* Main constructor. | |||
* @param parent the parent document handler | |||
@@ -315,16 +330,51 @@ public class PCLPainter extends AbstractIFPainter<PCLDocumentHandler> implements | |||
//TODO Ignored: state.getFontVariant() | |||
//TODO Opportunity for font caching if font state is more heavily used | |||
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) { | |||
drawTextNative(x, y, letterSpacing, wordSpacing, dp, text, triplet); | |||
} 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) { | |||
@@ -332,6 +382,29 @@ public class PCLPainter extends AbstractIFPainter<PCLDocumentHandler> implements | |||
} | |||
} | |||
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, | |||
String text, FontTriplet triplet) throws IOException { | |||
Color textColor = state.getTextColor(); | |||
@@ -414,25 +487,87 @@ public class PCLPainter extends AbstractIFPainter<PCLDocumentHandler> implements | |||
} | |||
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 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 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[] dx = IFUtil.convertDPToDX(dp); | |||
int dxl = (dx != null ? dx.length : 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; | |||
for (int i = 0; i < l; i++) { | |||
@@ -451,19 +586,17 @@ public class PCLPainter extends AbstractIFPainter<PCLDocumentHandler> implements | |||
width += cw + glyphAdjust; | |||
} | |||
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; | |||
} | |||
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()); | |||
//for cursive fonts, so the text isn't clipped | |||
// for cursive fonts, so the text isn't clipped | |||
FontMetricsMapper mapper; | |||
try { | |||
mapper = (FontMetricsMapper) getFontInfo().getMetricsFor(font.getFontName()); | |||
@@ -473,11 +606,11 @@ public class PCLPainter extends AbstractIFPainter<PCLDocumentHandler> implements | |||
final int maxAscent = mapper.getMaxAscent(font.getFontSize()) / 1000; | |||
final int ascent = mapper.getAscender(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 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(); | |||
Graphics2DImagePainter painter = new Graphics2DImagePainter() { | |||
@@ -485,7 +618,7 @@ public class PCLPainter extends AbstractIFPainter<PCLDocumentHandler> implements | |||
public void paint(Graphics2D g2d, Rectangle2D area) { | |||
if (DEBUG) { | |||
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); | |||
@@ -501,7 +634,7 @@ public class PCLPainter extends AbstractIFPainter<PCLDocumentHandler> implements | |||
try { | |||
painter.drawText(x, y, letterSpacing, wordSpacing, dp, text); | |||
} 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); | |||
} | |||
} |
@@ -67,6 +67,7 @@ public class PCLRendererConfigurator extends PrintRendererConfigurator { | |||
if (config.isTextRendering() != null) { | |||
pclUtil.setAllTextAsBitmaps(config.isTextRendering()); | |||
} | |||
} | |||
@Override |
@@ -50,12 +50,6 @@ public class PCLRenderingUtil { | |||
/** Controls the dithering quality when rendering gray or color images. */ | |||
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. | |||
* This can be used to work around problems with Apache Batik, for example, but setting | |||
@@ -68,6 +62,12 @@ public class PCLRenderingUtil { | |||
*/ | |||
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) { | |||
this.userAgent = userAgent; | |||
initialize(); | |||
@@ -127,8 +127,7 @@ public class PCLRenderingUtil { | |||
} | |||
/** | |||
* 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 | |||
*/ | |||
public void setAllTextAsBitmaps(boolean allTextAsBitmaps) { |
@@ -0,0 +1,125 @@ | |||
/* | |||
* 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(); | |||
} | |||
} |
@@ -0,0 +1,147 @@ | |||
/* | |||
* 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; | |||
} | |||
} | |||
} |
@@ -0,0 +1,42 @@ | |||
/* | |||
* 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; | |||
} |
@@ -0,0 +1,113 @@ | |||
/* | |||
* 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; | |||
} | |||
} |
@@ -0,0 +1,65 @@ | |||
/* | |||
* 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; | |||
} | |||
} |
@@ -0,0 +1,62 @@ | |||
/* | |||
* 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; | |||
} | |||
} | |||
} |
@@ -0,0 +1,160 @@ | |||
/* | |||
* 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); | |||
} | |||
} | |||
} |
@@ -0,0 +1,278 @@ | |||
/* | |||
* 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; | |||
} | |||
} | |||
} |
@@ -0,0 +1,200 @@ | |||
/* | |||
* 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; | |||
} | |||
} |
@@ -0,0 +1,154 @@ | |||
/* | |||
* 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; | |||
} | |||
} |
@@ -0,0 +1,731 @@ | |||
/* | |||
* 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; | |||
} | |||
} |
@@ -0,0 +1,77 @@ | |||
/* | |||
* 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; | |||
} | |||
} |
@@ -0,0 +1,115 @@ | |||
/* | |||
* 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; | |||
} | |||
} |
@@ -0,0 +1,51 @@ | |||
/* | |||
* 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; | |||
} | |||
} |
@@ -0,0 +1,47 @@ | |||
/* | |||
* 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); | |||
} | |||
} |
@@ -0,0 +1,49 @@ | |||
/* | |||
* 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; | |||
} | |||
} |
@@ -0,0 +1,51 @@ | |||
/* | |||
* 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 | |||
} | |||
} | |||
} |
@@ -0,0 +1,74 @@ | |||
/* | |||
* 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); | |||
} | |||
} |
@@ -0,0 +1,52 @@ | |||
/* | |||
* 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); | |||
} | |||
} |
@@ -0,0 +1,198 @@ | |||
/* | |||
* 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); | |||
} | |||
} |
@@ -0,0 +1,76 @@ | |||
/* | |||
* 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; | |||
} | |||
} |