diff options
Diffstat (limited to 'src')
35 files changed, 2738 insertions, 47 deletions
diff --git a/src/java/org/apache/fop/fonts/CIDFull.java b/src/java/org/apache/fop/fonts/CIDFull.java index ee062a2bb..1130459b7 100644 --- a/src/java/org/apache/fop/fonts/CIDFull.java +++ b/src/java/org/apache/fop/fonts/CIDFull.java @@ -57,6 +57,18 @@ public class CIDFull implements CIDSet { } /** {@inheritDoc} */ + @Override + public char getUnicodeFromGID(int glyphIndex) { + return ' '; + } + + /** {@inheritDoc} */ + @Override + public int getGIDFromChar(char ch) { + return ch; + } + + /** {@inheritDoc} */ public char getUnicode(int index) { initGlyphIndices(); if (glyphIndices.get(index)) { @@ -109,5 +121,4 @@ public class CIDFull implements CIDSet { public int[] getWidths() { return font.getWidths(); } - } diff --git a/src/java/org/apache/fop/fonts/CIDSet.java b/src/java/org/apache/fop/fonts/CIDSet.java index 7530ea6e7..acfc705c8 100644 --- a/src/java/org/apache/fop/fonts/CIDSet.java +++ b/src/java/org/apache/fop/fonts/CIDSet.java @@ -44,6 +44,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 * allocated character selector is returned from the existing map/subset. diff --git a/src/java/org/apache/fop/fonts/CIDSubset.java b/src/java/org/apache/fop/fonts/CIDSubset.java index f442c13ed..01b8495f8 100644 --- a/src/java/org/apache/fop/fonts/CIDSubset.java +++ b/src/java/org/apache/fop/fonts/CIDSubset.java @@ -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 { @@ -106,6 +113,17 @@ public class CIDSubset implements CIDSet { } /** {@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]; for (int i = 0; i < usedGlyphsCount; i++) { diff --git a/src/java/org/apache/fop/fonts/CustomFont.java b/src/java/org/apache/fop/fonts/CustomFont.java index fffb429ed..6f325d96d 100644 --- a/src/java/org/apache/fop/fonts/CustomFont.java +++ b/src/java/org/apache/fop/fonts/CustomFont.java @@ -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); } diff --git a/src/java/org/apache/fop/fonts/MultiByteFont.java b/src/java/org/apache/fop/fonts/MultiByteFont.java index 296c86de2..22b5116bb 100644 --- a/src/java/org/apache/fop/fonts/MultiByteFont.java +++ b/src/java/org/apache/fop/fonts/MultiByteFont.java @@ -426,6 +426,24 @@ public class MultiByteFont extends CIDFont implements Substitutable, Positionabl } /** + * 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 */ diff --git a/src/java/org/apache/fop/fonts/SingleByteFont.java b/src/java/org/apache/fop/fonts/SingleByteFont.java index e12da81cc..e3037a524 100644 --- a/src/java/org/apache/fop/fonts/SingleByteFont.java +++ b/src/java/org/apache/fop/fonts/SingleByteFont.java @@ -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); } diff --git a/src/java/org/apache/fop/fonts/truetype/GlyfTable.java b/src/java/org/apache/fop/fonts/truetype/GlyfTable.java index 1233a586e..6ad479a0e 100644 --- a/src/java/org/apache/fop/fonts/truetype/GlyfTable.java +++ b/src/java/org/apache/fop/fonts/truetype/GlyfTable.java @@ -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; diff --git a/src/java/org/apache/fop/fonts/truetype/OFDirTabEntry.java b/src/java/org/apache/fop/fonts/truetype/OFDirTabEntry.java index 0e468eb7b..34e7fba14 100644 --- a/src/java/org/apache/fop/fonts/truetype/OFDirTabEntry.java +++ b/src/java/org/apache/fop/fonts/truetype/OFDirTabEntry.java @@ -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; } diff --git a/src/java/org/apache/fop/fonts/truetype/OpenFont.java b/src/java/org/apache/fop/fonts/truetype/OpenFont.java index b9dcfb936..e94f31bb1 100644 --- a/src/java/org/apache/fop/fonts/truetype/OpenFont.java +++ b/src/java/org/apache/fop/fonts/truetype/OpenFont.java @@ -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); @@ -966,6 +966,15 @@ public abstract class OpenFont { } /** + * 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 */ @@ -1048,6 +1057,18 @@ public abstract class OpenFont { } /** + * 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; + } } diff --git a/src/java/org/apache/fop/fonts/truetype/TTFFile.java b/src/java/org/apache/fop/fonts/truetype/TTFFile.java index 52df45ffb..4b0e3d628 100644 --- a/src/java/org/apache/fop/fonts/truetype/TTFFile.java +++ b/src/java/org/apache/fop/fonts/truetype/TTFFile.java @@ -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; diff --git a/src/java/org/apache/fop/render/java2d/ConfiguredFontCollection.java b/src/java/org/apache/fop/render/java2d/ConfiguredFontCollection.java index b9b7539fc..c6f02506e 100644 --- a/src/java/org/apache/fop/render/java2d/ConfiguredFontCollection.java +++ b/src/java/org/apache/fop/render/java2d/ConfiguredFontCollection.java @@ -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); diff --git a/src/java/org/apache/fop/render/java2d/CustomFontMetricsMapper.java b/src/java/org/apache/fop/render/java2d/CustomFontMetricsMapper.java index 62283915f..c7f5da116 100644 --- a/src/java/org/apache/fop/render/java2d/CustomFontMetricsMapper.java +++ b/src/java/org/apache/fop/render/java2d/CustomFontMetricsMapper.java @@ -289,4 +289,8 @@ public class CustomFontMetricsMapper extends Typeface implements FontMetricsMapp } } + public Typeface getRealFont() { + return typeface; + } + } diff --git a/src/java/org/apache/fop/render/pcl/HardcodedFonts.java b/src/java/org/apache/fop/render/pcl/HardcodedFonts.java index 185e1ece5..82e622098 100644 --- a/src/java/org/apache/fop/render/pcl/HardcodedFonts.java +++ b/src/java/org/apache/fop/render/pcl/HardcodedFonts.java @@ -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 { diff --git a/src/java/org/apache/fop/render/pcl/PCLEventProducer.java b/src/java/org/apache/fop/render/pcl/PCLEventProducer.java index 7d2ed252e..3a8a1de55 100644 --- a/src/java/org/apache/fop/render/pcl/PCLEventProducer.java +++ b/src/java/org/apache/fop/render/pcl/PCLEventProducer.java @@ -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); + } diff --git a/src/java/org/apache/fop/render/pcl/PCLEventProducer.xml b/src/java/org/apache/fop/render/pcl/PCLEventProducer.xml index 5d5785a4c..463fcb16a 100644 --- a/src/java/org/apache/fop/render/pcl/PCLEventProducer.xml +++ b/src/java/org/apache/fop/render/pcl/PCLEventProducer.xml @@ -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> diff --git a/src/java/org/apache/fop/render/pcl/PCLGenerator.java b/src/java/org/apache/fop/render/pcl/PCLGenerator.java index 05a0fc666..67d37fbb5 100644 --- a/src/java/org/apache/fop/render/pcl/PCLGenerator.java +++ b/src/java/org/apache/fop/render/pcl/PCLGenerator.java @@ -141,6 +141,15 @@ public class PCLGenerator { } /** + * 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. * * @param value value to format diff --git a/src/java/org/apache/fop/render/pcl/PCLPainter.java b/src/java/org/apache/fop/render/pcl/PCLPainter.java index 9f7de4930..69465a7fd 100644 --- a/src/java/org/apache/fop/render/pcl/PCLPainter.java +++ b/src/java/org/apache/fop/render/pcl/PCLPainter.java @@ -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); } } diff --git a/src/java/org/apache/fop/render/pcl/PCLRendererConfigurator.java b/src/java/org/apache/fop/render/pcl/PCLRendererConfigurator.java index dcda49059..33376655f 100644 --- a/src/java/org/apache/fop/render/pcl/PCLRendererConfigurator.java +++ b/src/java/org/apache/fop/render/pcl/PCLRendererConfigurator.java @@ -67,6 +67,7 @@ public class PCLRendererConfigurator extends PrintRendererConfigurator { if (config.isTextRendering() != null) { pclUtil.setAllTextAsBitmaps(config.isTextRendering()); } + } @Override diff --git a/src/java/org/apache/fop/render/pcl/PCLRenderingUtil.java b/src/java/org/apache/fop/render/pcl/PCLRenderingUtil.java index 3fc8b5258..32693764f 100644 --- a/src/java/org/apache/fop/render/pcl/PCLRenderingUtil.java +++ b/src/java/org/apache/fop/render/pcl/PCLRenderingUtil.java @@ -51,12 +51,6 @@ public class PCLRenderingUtil { 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 * this to true will increase memory consumption. @@ -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) { diff --git a/src/java/org/apache/fop/render/pcl/fonts/PCLByteWriterUtil.java b/src/java/org/apache/fop/render/pcl/fonts/PCLByteWriterUtil.java new file mode 100644 index 000000000..1b9382f7b --- /dev/null +++ b/src/java/org/apache/fop/render/pcl/fonts/PCLByteWriterUtil.java @@ -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(); + } +} diff --git a/src/java/org/apache/fop/render/pcl/fonts/PCLCharacterDefinition.java b/src/java/org/apache/fop/render/pcl/fonts/PCLCharacterDefinition.java new file mode 100644 index 000000000..c275084dc --- /dev/null +++ b/src/java/org/apache/fop/render/pcl/fonts/PCLCharacterDefinition.java @@ -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; + } + } +} diff --git a/src/java/org/apache/fop/render/pcl/fonts/PCLCharacterWriter.java b/src/java/org/apache/fop/render/pcl/fonts/PCLCharacterWriter.java new file mode 100644 index 000000000..df04371e0 --- /dev/null +++ b/src/java/org/apache/fop/render/pcl/fonts/PCLCharacterWriter.java @@ -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; +} diff --git a/src/java/org/apache/fop/render/pcl/fonts/PCLFontReader.java b/src/java/org/apache/fop/render/pcl/fonts/PCLFontReader.java new file mode 100644 index 000000000..6bd5192ac --- /dev/null +++ b/src/java/org/apache/fop/render/pcl/fonts/PCLFontReader.java @@ -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; + } +} diff --git a/src/java/org/apache/fop/render/pcl/fonts/PCLFontReaderFactory.java b/src/java/org/apache/fop/render/pcl/fonts/PCLFontReaderFactory.java new file mode 100644 index 000000000..15c4e8d54 --- /dev/null +++ b/src/java/org/apache/fop/render/pcl/fonts/PCLFontReaderFactory.java @@ -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; + } + +} diff --git a/src/java/org/apache/fop/render/pcl/fonts/PCLFontSegment.java b/src/java/org/apache/fop/render/pcl/fonts/PCLFontSegment.java new file mode 100644 index 000000000..111dbf932 --- /dev/null +++ b/src/java/org/apache/fop/render/pcl/fonts/PCLFontSegment.java @@ -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; + } + } +} diff --git a/src/java/org/apache/fop/render/pcl/fonts/PCLSoftFont.java b/src/java/org/apache/fop/render/pcl/fonts/PCLSoftFont.java new file mode 100644 index 000000000..8412534eb --- /dev/null +++ b/src/java/org/apache/fop/render/pcl/fonts/PCLSoftFont.java @@ -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); + } + } +} diff --git a/src/java/org/apache/fop/render/pcl/fonts/PCLSoftFontManager.java b/src/java/org/apache/fop/render/pcl/fonts/PCLSoftFontManager.java new file mode 100644 index 000000000..621ea4f18 --- /dev/null +++ b/src/java/org/apache/fop/render/pcl/fonts/PCLSoftFontManager.java @@ -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; + } + } +} diff --git a/src/java/org/apache/fop/render/pcl/fonts/PCLSymbolSet.java b/src/java/org/apache/fop/render/pcl/fonts/PCLSymbolSet.java new file mode 100644 index 000000000..a2c50df23 --- /dev/null +++ b/src/java/org/apache/fop/render/pcl/fonts/PCLSymbolSet.java @@ -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; + } +} diff --git a/src/java/org/apache/fop/render/pcl/fonts/truetype/PCLTTFCharacterWriter.java b/src/java/org/apache/fop/render/pcl/fonts/truetype/PCLTTFCharacterWriter.java new file mode 100644 index 000000000..41fefaaf0 --- /dev/null +++ b/src/java/org/apache/fop/render/pcl/fonts/truetype/PCLTTFCharacterWriter.java @@ -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; + } +} diff --git a/src/java/org/apache/fop/render/pcl/fonts/truetype/PCLTTFFontReader.java b/src/java/org/apache/fop/render/pcl/fonts/truetype/PCLTTFFontReader.java new file mode 100644 index 000000000..1a054953c --- /dev/null +++ b/src/java/org/apache/fop/render/pcl/fonts/truetype/PCLTTFFontReader.java @@ -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; + } +}
\ No newline at end of file diff --git a/src/java/org/apache/fop/render/pcl/fonts/truetype/PCLTTFOS2FontTable.java b/src/java/org/apache/fop/render/pcl/fonts/truetype/PCLTTFOS2FontTable.java new file mode 100644 index 000000000..4c5f98d8b --- /dev/null +++ b/src/java/org/apache/fop/render/pcl/fonts/truetype/PCLTTFOS2FontTable.java @@ -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; + } +} diff --git a/src/java/org/apache/fop/render/pcl/fonts/truetype/PCLTTFPCLTFontTable.java b/src/java/org/apache/fop/render/pcl/fonts/truetype/PCLTTFPCLTFontTable.java new file mode 100644 index 000000000..5be46a4ec --- /dev/null +++ b/src/java/org/apache/fop/render/pcl/fonts/truetype/PCLTTFPCLTFontTable.java @@ -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; + } +} diff --git a/src/java/org/apache/fop/render/pcl/fonts/truetype/PCLTTFPOSTFontTable.java b/src/java/org/apache/fop/render/pcl/fonts/truetype/PCLTTFPOSTFontTable.java new file mode 100644 index 000000000..9995f7f55 --- /dev/null +++ b/src/java/org/apache/fop/render/pcl/fonts/truetype/PCLTTFPOSTFontTable.java @@ -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; + } +} diff --git a/src/java/org/apache/fop/render/pcl/fonts/truetype/PCLTTFTable.java b/src/java/org/apache/fop/render/pcl/fonts/truetype/PCLTTFTable.java new file mode 100644 index 000000000..cfe866474 --- /dev/null +++ b/src/java/org/apache/fop/render/pcl/fonts/truetype/PCLTTFTable.java @@ -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); + } +} diff --git a/src/java/org/apache/fop/render/pcl/fonts/truetype/PCLTTFTableFactory.java b/src/java/org/apache/fop/render/pcl/fonts/truetype/PCLTTFTableFactory.java new file mode 100644 index 000000000..e72563e96 --- /dev/null +++ b/src/java/org/apache/fop/render/pcl/fonts/truetype/PCLTTFTableFactory.java @@ -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; + } +} |