Refactored code and added unit tests Patch by Mehdi Houshmand git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_TrueTypeInPostScript@1307574 13f79535-47bb-0310-9956-ffa450edef68pull/26/head
@@ -90,7 +90,10 @@ list of possible build targets. | |||
<include name="lib/build/jaxen*"/> | |||
<include name="lib/build/pmd*"/> | |||
<include name="lib/build/qdox*"/> | |||
<include name="lib/build/xmlunit*"/> | |||
<include name="lib/build/xmlunit*"/> | |||
<include name="lib/build/mockito*"/> | |||
<include name="lib/build/hamcrest*"/> | |||
<include name="lib/build/objenesis*"/> | |||
</patternset> | |||
<fileset dir="${basedir}" id="dist.src"> | |||
<include name="src/**"/> | |||
@@ -944,7 +947,15 @@ list of possible build targets. | |||
<test name="org.apache.fop.text.linebreak.LineBreakStatusTest" todir="${junit.reports.dir}"/> | |||
</junit> | |||
</target> | |||
<target name="junit-reduced" depends="junit-userconfig, junit-basic, junit-transcoder, junit-text-linebreak, junit-fotree"/> | |||
<target name="junit-fonts" depends="junit-compile"> | |||
<echo message="Running tests for the fonts package"/> | |||
<junit-run title="fonts" testsuite="org.apache.fop.fonts.FOPFontsTestSuite" outfile="TEST-fonts"/> | |||
</target> | |||
<target name="junit-render-ps" depends="junit-compile"> | |||
<echo message="Running tests for the render ps package"/> | |||
<junit-run title="render-ps" testsuite="org.apache.fop.render.ps.RenderPSTestSuite" outfile="TEST-render-ps"/> | |||
</target> | |||
<target name="junit-reduced" depends="junit-userconfig, junit-basic, junit-transcoder, junit-text-linebreak, junit-fotree, junit-fonts, junit-render-ps"/> | |||
<target name="junit-full" depends="junit-reduced, junit-layout, junit-area-tree-xml-format, junit-intermediate-format"/> | |||
<target name="junit" depends="junit-full" description="Runs all of FOP's JUnit tests" if="junit.present"> | |||
<fail><condition><or><isset property="fop.junit.error"/><isset property="fop.junit.failure"/><not><isset property="hyphenation.present"/></not></or></condition> |
@@ -37,18 +37,16 @@ import javax.xml.transform.sax.SAXTransformerFactory; | |||
import javax.xml.transform.stream.StreamResult; | |||
import javax.xml.transform.stream.StreamSource; | |||
import org.w3c.dom.Node; | |||
import org.apache.commons.io.IOUtils; | |||
import org.apache.fop.events.model.EventModel; | |||
import org.apache.fop.events.model.EventProducerModel; | |||
import org.apache.tools.ant.BuildException; | |||
import org.apache.tools.ant.DirectoryScanner; | |||
import org.apache.tools.ant.Project; | |||
import org.apache.tools.ant.Task; | |||
import org.apache.tools.ant.types.FileSet; | |||
import org.apache.tools.ant.types.selectors.FilenameSelector; | |||
import org.apache.fop.events.model.EventModel; | |||
import org.apache.fop.events.model.EventProducerModel; | |||
import org.w3c.dom.Node; | |||
/** | |||
* Ant task which inspects a file set for Java interfaces which extend the |
@@ -22,14 +22,13 @@ package org.apache.fop.fonts; | |||
/** | |||
* This is just a holder class for bfentries, groups of characters of a base font (bf). | |||
*/ | |||
public class BFEntry { | |||
public final class BFEntry { | |||
//TODO Think about renaming this class to CMapRange or something. | |||
//TODO Copy equals() and hashCode() from TTFCmapEntry | |||
private int unicodeStart; | |||
private int unicodeEnd; | |||
private int glyphStartIndex; | |||
private final int unicodeStart; | |||
private final int unicodeEnd; | |||
private final int glyphStartIndex; | |||
/** | |||
* Main constructor. | |||
@@ -43,6 +42,32 @@ public class BFEntry { | |||
this.glyphStartIndex = glyphStartIndex; | |||
} | |||
/** | |||
* {@inheritDoc} | |||
*/ | |||
@Override | |||
public int hashCode() { | |||
int hc = 17; | |||
hc = 31 * hc + unicodeStart; | |||
hc = 31 * hc + unicodeEnd; | |||
hc = 31 * hc + glyphStartIndex; | |||
return hc; | |||
} | |||
/** | |||
* {@inheritDoc} | |||
*/ | |||
@Override | |||
public boolean equals(Object o) { | |||
if (o instanceof BFEntry) { | |||
BFEntry ce = (BFEntry) o; | |||
return ce.unicodeStart == this.unicodeStart | |||
&& ce.unicodeEnd == this.unicodeEnd | |||
&& ce.glyphStartIndex == this.glyphStartIndex; | |||
} | |||
return false; | |||
} | |||
/** | |||
* Returns the unicodeStart. | |||
* @return the Unicode start index |
@@ -42,6 +42,7 @@ public abstract class CustomFont extends Typeface | |||
private String embedFileName = null; | |||
private String embedResourceName = null; | |||
private FontResolver resolver = null; | |||
private EmbeddingMode embeddingMode = EmbeddingMode.AUTO; | |||
private int capHeight = 0; | |||
private int xHeight = 0; | |||
@@ -113,6 +114,14 @@ public abstract class CustomFont extends Typeface | |||
return embedFileName; | |||
} | |||
/** | |||
* Returns the embedding mode for this font. | |||
* @return embedding mode | |||
*/ | |||
public EmbeddingMode getEmbeddingMode() { | |||
return embeddingMode; | |||
} | |||
/** | |||
* Returns a Source representing an embeddable font file. | |||
* @return Source for an embeddable font file | |||
@@ -262,7 +271,7 @@ public abstract class CustomFont extends Typeface | |||
return lastChar; | |||
} | |||
/** | |||
/**MutableFont | |||
* Used to determine if kerning is enabled. | |||
* @return True if kerning is enabled. | |||
*/ | |||
@@ -327,6 +336,13 @@ public abstract class CustomFont extends Typeface | |||
this.embedResourceName = name; | |||
} | |||
/** | |||
* {@inheritDoc} | |||
*/ | |||
public void setEmbeddingMode(EmbeddingMode embeddingMode) { | |||
this.embeddingMode = embeddingMode; | |||
} | |||
/** | |||
* {@inheritDoc} | |||
*/ |
@@ -71,7 +71,7 @@ public class CustomFontCollection implements FontCollection { | |||
List<FontTriplet> triplets = embedFontInfo.getFontTriplets(); | |||
for (int tripletIndex = 0; tripletIndex < triplets.size(); tripletIndex++) { | |||
FontTriplet triplet = (FontTriplet) triplets.get(tripletIndex); | |||
FontTriplet triplet = triplets.get(tripletIndex); | |||
fontInfo.addFontProperties(internalName, triplet); | |||
} | |||
} |
@@ -25,6 +25,8 @@ import java.util.List; | |||
/** | |||
* FontInfo contains meta information on fonts (where is the metrics file etc.) | |||
* TODO: We need to remove this class and think about more intelligent design patterns | |||
* (Data classes => Procedural code) | |||
*/ | |||
public class EmbedFontInfo implements Serializable { | |||
@@ -39,6 +41,8 @@ public class EmbedFontInfo implements Serializable { | |||
protected boolean kerning; | |||
/** the requested encoding mode for the font */ | |||
protected EncodingMode encodingMode = EncodingMode.AUTO; | |||
/** the requested embedding mode for this font */ | |||
protected EmbeddingMode embeddingMode = EmbeddingMode.AUTO; | |||
/** the PostScript name of the font */ | |||
protected String postScriptName = null; | |||
@@ -136,6 +140,14 @@ public class EmbedFontInfo implements Serializable { | |||
} | |||
} | |||
/** | |||
* Returns the embedding mode for this font. | |||
* @return the embedding mode. | |||
*/ | |||
public EmbeddingMode getEmbeddingMode() { | |||
return embeddingMode; | |||
} | |||
/** | |||
* Defines whether the font is embedded or not. | |||
* @param value true to embed the font, false to reference it | |||
@@ -163,6 +175,17 @@ public class EmbedFontInfo implements Serializable { | |||
this.encodingMode = mode; | |||
} | |||
/** | |||
* Sets the embedding mode for this font, currently not supported for type1 fonts. | |||
* @param embeddingMode the new embedding mode. | |||
*/ | |||
public void setEmbeddingMode(EmbeddingMode embeddingMode) { | |||
if (embeddingMode == null) { | |||
throw new NullPointerException("embeddingMode must not be null"); | |||
} | |||
this.embeddingMode = embeddingMode; | |||
} | |||
private void readObject(java.io.ObjectInputStream in) | |||
throws IOException, ClassNotFoundException { | |||
in.defaultReadObject(); |
@@ -0,0 +1,56 @@ | |||
/* | |||
* 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.fonts; | |||
/** | |||
* This enumerates the embedding mode of fonts; full; subset; auto (auto defaults to full for | |||
* type1 fonts and subset for truetype fonts. | |||
*/ | |||
public enum EmbeddingMode { | |||
/** Default option: assumes FULL for type1 fonts and SUBSET for truetype fonts. */ | |||
AUTO, | |||
/** Full font embedding: This means the whole of the font is written to file. */ | |||
FULL, | |||
/** Subset font embedding: Only the mandatory tables and a subset of glyphs are written | |||
* to file.*/ | |||
SUBSET; | |||
/** | |||
* Returns the name of this embedding mode. | |||
* @return String - lower case. | |||
*/ | |||
public String getName() { | |||
return this.toString().toLowerCase(); | |||
} | |||
/** | |||
* Returns {@link EmbeddingMode} by name. | |||
* @param value String - the name of the embedding mode (not case sensitive). | |||
* @return embedding mode constant. | |||
*/ | |||
public static EmbeddingMode getValue(String value) { | |||
for (EmbeddingMode mode : EmbeddingMode.values()) { | |||
if (mode.toString().equalsIgnoreCase(value)) { | |||
return mode; | |||
} | |||
} | |||
throw new IllegalArgumentException("Invalid embedding-mode: " + value); | |||
} | |||
} |
@@ -52,7 +52,7 @@ public enum EncodingMode { | |||
* @param name the name of the encoding mode to look up | |||
* @return the encoding mode constant | |||
*/ | |||
public static EncodingMode getEncodingMode(String name) { | |||
public static EncodingMode getValue(String name) { | |||
for (EncodingMode em : EncodingMode.values()) { | |||
if (name.equalsIgnoreCase(em.getName())) { | |||
return em; |
@@ -251,11 +251,15 @@ public class FontInfoConfigurator { | |||
} | |||
boolean useKerning = fontCfg.getAttributeAsBoolean("kerning", true); | |||
EncodingMode encodingMode = EncodingMode.getEncodingMode( | |||
EncodingMode encodingMode = EncodingMode.getValue( | |||
fontCfg.getAttribute("encoding-mode", EncodingMode.AUTO.getName())); | |||
EmbeddingMode embeddingMode = EmbeddingMode.getValue( | |||
fontCfg.getAttribute("embedding-mode", EmbeddingMode.AUTO.toString())); | |||
EmbedFontInfo embedFontInfo | |||
= new EmbedFontInfo(metricsUrl, useKerning, tripletList, embedUrl, subFont); | |||
embedFontInfo.setEncodingMode(encodingMode); | |||
embedFontInfo.setEmbeddingMode(embeddingMode); | |||
if (fontCache != null) { | |||
if (!fontCache.containsFont(embedFontInfo)) { | |||
fontCache.addFont(embedFontInfo); |
@@ -30,7 +30,6 @@ import javax.xml.transform.stream.StreamSource; | |||
import org.apache.commons.logging.Log; | |||
import org.apache.commons.logging.LogFactory; | |||
import org.apache.fop.fonts.truetype.TTFFontLoader; | |||
import org.apache.fop.fonts.type1.Type1FontLoader; | |||
@@ -80,15 +79,17 @@ public abstract class FontLoader { | |||
* @param fontFile the File representation of the font | |||
* @param subFontName the sub-fontname of a font (for TrueType Collections, null otherwise) | |||
* @param embedded indicates whether the font is embedded or referenced | |||
* @param embeddingMode the embedding mode | |||
* @param encodingMode the requested encoding mode | |||
* @param resolver the font resolver to use when resolving URIs | |||
* @return the newly loaded font | |||
* @throws IOException In case of an I/O error | |||
*/ | |||
public static CustomFont loadFont(File fontFile, String subFontName, | |||
boolean embedded, EncodingMode encodingMode, FontResolver resolver) throws IOException { | |||
boolean embedded, EmbeddingMode embeddingMode, EncodingMode encodingMode, | |||
FontResolver resolver) throws IOException { | |||
return loadFont(fontFile.toURI().toURL(), subFontName, | |||
embedded, encodingMode, resolver); | |||
embedded, embeddingMode, encodingMode, resolver); | |||
} | |||
/** | |||
@@ -96,16 +97,17 @@ public abstract class FontLoader { | |||
* @param fontUrl the URL representation of the font | |||
* @param subFontName the sub-fontname of a font (for TrueType Collections, null otherwise) | |||
* @param embedded indicates whether the font is embedded or referenced | |||
* @param embeddingMode the embedding mode of the font | |||
* @param encodingMode the requested encoding mode | |||
* @param resolver the font resolver to use when resolving URIs | |||
* @return the newly loaded font | |||
* @throws IOException In case of an I/O error | |||
*/ | |||
public static CustomFont loadFont(URL fontUrl, String subFontName, | |||
boolean embedded, EncodingMode encodingMode, | |||
boolean embedded, EmbeddingMode embeddingMode, EncodingMode encodingMode, | |||
FontResolver resolver) throws IOException { | |||
return loadFont(fontUrl.toExternalForm(), subFontName, | |||
embedded, encodingMode, true, | |||
embedded, embeddingMode, encodingMode, true, | |||
resolver); | |||
} | |||
@@ -114,6 +116,7 @@ public abstract class FontLoader { | |||
* @param fontFileURI the URI to the font | |||
* @param subFontName the sub-fontname of a font (for TrueType Collections, null otherwise) | |||
* @param embedded indicates whether the font is embedded or referenced | |||
* @param embeddingMode the embedding mode of the font | |||
* @param encodingMode the requested encoding mode | |||
* @param useKerning indicates whether kerning information should be loaded if available | |||
* @param resolver the font resolver to use when resolving URIs | |||
@@ -121,8 +124,8 @@ public abstract class FontLoader { | |||
* @throws IOException In case of an I/O error | |||
*/ | |||
public static CustomFont loadFont(String fontFileURI, String subFontName, | |||
boolean embedded, EncodingMode encodingMode, boolean useKerning, | |||
FontResolver resolver) throws IOException { | |||
boolean embedded, EmbeddingMode embeddingMode, EncodingMode encodingMode, | |||
boolean useKerning, FontResolver resolver) throws IOException { | |||
fontFileURI = fontFileURI.trim(); | |||
boolean type1 = isType1(fontFileURI); | |||
FontLoader loader; | |||
@@ -131,10 +134,14 @@ public abstract class FontLoader { | |||
throw new IllegalArgumentException( | |||
"CID encoding mode not supported for Type 1 fonts"); | |||
} | |||
if (embeddingMode == EmbeddingMode.SUBSET) { | |||
throw new IllegalArgumentException( | |||
"Subset embedding for Type 1 fonts is not supported"); | |||
} | |||
loader = new Type1FontLoader(fontFileURI, embedded, useKerning, resolver); | |||
} else { | |||
loader = new TTFFontLoader(fontFileURI, subFontName, | |||
embedded, encodingMode, useKerning, resolver); | |||
embedded, embeddingMode, encodingMode, useKerning, resolver); | |||
} | |||
return loader.getFont(); | |||
} |
@@ -27,12 +27,10 @@ import java.util.Set; | |||
import javax.xml.transform.Source; | |||
import javax.xml.transform.stream.StreamSource; | |||
import org.xml.sax.InputSource; | |||
import org.apache.commons.logging.Log; | |||
import org.apache.commons.logging.LogFactory; | |||
import org.apache.fop.apps.FOPException; | |||
import org.xml.sax.InputSource; | |||
/** | |||
* This class is used to defer the loading of a font until it is really used. | |||
@@ -45,6 +43,7 @@ public class LazyFont extends Typeface implements FontDescriptor { | |||
private String fontEmbedPath = null; | |||
private boolean useKerning = false; | |||
private EncodingMode encodingMode = EncodingMode.AUTO; | |||
private EmbeddingMode embeddingMode = EmbeddingMode.AUTO; | |||
private boolean embedded = true; | |||
private String subFontName = null; | |||
@@ -65,6 +64,7 @@ public class LazyFont extends Typeface implements FontDescriptor { | |||
this.fontEmbedPath = fontInfo.getEmbedFile(); | |||
this.useKerning = fontInfo.getKerning(); | |||
this.encodingMode = fontInfo.getEncodingMode(); | |||
this.embeddingMode = fontInfo.getEmbeddingMode(); | |||
this.subFontName = fontInfo.getSubFontName(); | |||
this.embedded = fontInfo.isEmbedded(); | |||
this.resolver = resolver; | |||
@@ -131,8 +131,9 @@ public class LazyFont extends Typeface implements FontDescriptor { | |||
if (fontEmbedPath == null) { | |||
throw new RuntimeException("Cannot load font. No font URIs available."); | |||
} | |||
realFont = FontLoader.loadFont(fontEmbedPath, this.subFontName, | |||
this.embedded, this.encodingMode, useKerning, resolver); | |||
realFont = FontLoader.loadFont(fontEmbedPath, subFontName, | |||
embedded, embeddingMode, encodingMode, | |||
useKerning, resolver); | |||
} | |||
if (realFont instanceof FontDescriptor) { | |||
realFontDescriptor = (FontDescriptor) realFont; |
@@ -60,6 +60,12 @@ public interface MutableFont { | |||
*/ | |||
void setEmbedResourceName(String name); | |||
/** | |||
* Set the embedding mode for this font. | |||
* @param embeddingMode the embedding mode. | |||
*/ | |||
void setEmbeddingMode(EmbeddingMode embeddingMode); | |||
/** | |||
* Sets the capital height value. | |||
* @param capHeight capital height |
@@ -20,7 +20,6 @@ | |||
package org.apache.fop.fonts.apps; | |||
import java.io.IOException; | |||
import java.util.Iterator; | |||
import java.util.Map; | |||
import java.util.Set; | |||
@@ -28,9 +27,9 @@ import javax.xml.parsers.DocumentBuilderFactory; | |||
import org.apache.commons.logging.LogFactory; | |||
import org.apache.fop.Version; | |||
import org.apache.fop.fonts.BFEntry; | |||
import org.apache.fop.fonts.FontUtil; | |||
import org.apache.fop.fonts.truetype.FontFileReader; | |||
import org.apache.fop.fonts.truetype.TTFCmapEntry; | |||
import org.apache.fop.fonts.truetype.TTFFile; | |||
import org.apache.fop.util.CommandLineLogger; | |||
import org.w3c.dom.Document; | |||
@@ -272,9 +271,9 @@ public class TTFReader extends AbstractFontReader { | |||
root.appendChild(el); | |||
el.appendChild(doc.createTextNode(ttf.getFullName())); | |||
} | |||
Set familyNames = ttf.getFamilyNames(); | |||
Set<String> familyNames = ttf.getFamilyNames(); | |||
if (familyNames.size() > 0) { | |||
String familyName = (String)familyNames.iterator().next(); | |||
String familyName = familyNames.iterator().next(); | |||
el = doc.createElement("family-name"); | |||
root.appendChild(el); | |||
el.appendChild(doc.createTextNode(familyName)); | |||
@@ -370,9 +369,7 @@ public class TTFReader extends AbstractFontReader { | |||
el = doc.createElement("bfranges"); | |||
mel.appendChild(el); | |||
Iterator iter = ttf.getCMaps().listIterator(); | |||
while (iter.hasNext()) { | |||
TTFCmapEntry ce = (TTFCmapEntry)iter.next(); | |||
for (BFEntry ce : ttf.getCMaps()) { | |||
Element el2 = doc.createElement("bf"); | |||
el.appendChild(el2); | |||
el2.setAttribute("us", String.valueOf(ce.getUnicodeStart())); | |||
@@ -427,31 +424,28 @@ public class TTFReader extends AbstractFontReader { | |||
Document doc = parent.getOwnerDocument(); | |||
// Get kerning | |||
Iterator iter; | |||
Set<Integer> kerningKeys; | |||
if (isCid) { | |||
iter = ttf.getKerning().keySet().iterator(); | |||
kerningKeys = ttf.getKerning().keySet(); | |||
} else { | |||
iter = ttf.getAnsiKerning().keySet().iterator(); | |||
kerningKeys = ttf.getAnsiKerning().keySet(); | |||
} | |||
while (iter.hasNext()) { | |||
Integer kpx1 = (Integer)iter.next(); | |||
for (Integer kpx1 : kerningKeys) { | |||
el = doc.createElement("kerning"); | |||
el.setAttribute("kpx1", kpx1.toString()); | |||
parent.appendChild(el); | |||
Element el2 = null; | |||
Map h2; | |||
Map<Integer, Integer> h2; | |||
if (isCid) { | |||
h2 = (Map)ttf.getKerning().get(kpx1); | |||
h2 = ttf.getKerning().get(kpx1); | |||
} else { | |||
h2 = (Map)ttf.getAnsiKerning().get(kpx1); | |||
h2 = ttf.getAnsiKerning().get(kpx1); | |||
} | |||
Iterator iter2 = h2.keySet().iterator(); | |||
while (iter2.hasNext()) { | |||
Integer kpx2 = (Integer)iter2.next(); | |||
for (Integer kpx2 : h2.keySet()) { | |||
if (isCid || kpx2.intValue() < 256) { | |||
el2 = doc.createElement("pair"); | |||
el2.setAttribute("kpx2", kpx2.toString()); |
@@ -31,6 +31,7 @@ import org.apache.commons.logging.Log; | |||
import org.apache.commons.logging.LogFactory; | |||
import org.apache.fop.fonts.CustomFont; | |||
import org.apache.fop.fonts.EmbedFontInfo; | |||
import org.apache.fop.fonts.EmbeddingMode; | |||
import org.apache.fop.fonts.EncodingMode; | |||
import org.apache.fop.fonts.Font; | |||
import org.apache.fop.fonts.FontCache; | |||
@@ -218,7 +219,8 @@ public class FontInfoFinder { | |||
} | |||
try { | |||
TTFFontLoader ttfLoader = new TTFFontLoader( | |||
fontFileURL, fontName, true, EncodingMode.AUTO, true, resolver); | |||
fontFileURL, fontName, true, EmbeddingMode.AUTO, EncodingMode.AUTO, | |||
true, resolver); | |||
customFont = ttfLoader.getFont(); | |||
if (this.eventListener != null) { | |||
customFont.setEventListener(this.eventListener); | |||
@@ -242,7 +244,8 @@ public class FontInfoFinder { | |||
} else { | |||
// The normal case | |||
try { | |||
customFont = FontLoader.loadFont(fontURL, null, true, EncodingMode.AUTO, resolver); | |||
customFont = FontLoader.loadFont(fontURL, null, true, EmbeddingMode.AUTO, | |||
EncodingMode.AUTO, resolver); | |||
if (this.eventListener != null) { | |||
customFont.setEventListener(this.eventListener); | |||
} |
@@ -89,16 +89,6 @@ public class FontFileReader { | |||
current = (int)offset; | |||
} | |||
/** | |||
* Set current file position to offset | |||
* | |||
* @param add The number of bytes to advance | |||
* @throws IOException In case of an I/O problem | |||
*/ | |||
public void seekAdd(long add) throws IOException { | |||
seekSet(current + add); | |||
} | |||
/** | |||
* Skip a given number of bytes. | |||
* | |||
@@ -106,7 +96,7 @@ public class FontFileReader { | |||
* @throws IOException In case of an I/O problem | |||
*/ | |||
public void skip(long add) throws IOException { | |||
seekAdd(add); | |||
seekSet(current + add); | |||
} | |||
/** | |||
@@ -133,7 +123,7 @@ public class FontFileReader { | |||
* @return One byte | |||
* @throws IOException If EOF is reached | |||
*/ | |||
public byte read() throws IOException { | |||
private byte read() throws IOException { | |||
if (current >= fsize) { | |||
throw new java.io.EOFException("Reached EOF, file size=" + fsize); | |||
} | |||
@@ -277,14 +267,14 @@ public class FontFileReader { | |||
public final String readTTFString() throws IOException { | |||
int i = current; | |||
while (file[i++] != 0) { | |||
if (i > fsize) { | |||
if (i >= fsize) { | |||
throw new java.io.EOFException("Reached EOF, file size=" | |||
+ fsize); | |||
} | |||
} | |||
byte[] tmp = new byte[i - current]; | |||
System.arraycopy(file, current, tmp, 0, i - current); | |||
byte[] tmp = new byte[i - current - 1]; | |||
System.arraycopy(file, current, tmp, 0, i - current - 1); | |||
return new String(tmp, "ISO-8859-1"); | |||
} | |||
@@ -352,6 +342,11 @@ public class FontFileReader { | |||
System.arraycopy(file, offset, ret, 0, length); | |||
return ret; | |||
} | |||
/** | |||
* Returns the full byte array representation of the file. | |||
* @return byte array. | |||
*/ | |||
public byte[] getAllBytes() { | |||
return file; | |||
} | |||
} |
@@ -1,122 +0,0 @@ | |||
/* | |||
* 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.fonts.truetype; | |||
/** | |||
* The CMap entry contains information of a Unicode range and the | |||
* the glyph indexes related to the range | |||
*/ | |||
public class TTFCmapEntry { | |||
//TODO this class is redundant: BFEntry does the same but doesn't have an intuitive name | |||
private int unicodeStart; | |||
private int unicodeEnd; | |||
private int glyphStartIndex; | |||
TTFCmapEntry() { | |||
unicodeStart = 0; | |||
unicodeEnd = 0; | |||
glyphStartIndex = 0; | |||
} | |||
TTFCmapEntry(int unicodeStart, int unicodeEnd, int glyphStartIndex) { | |||
this.unicodeStart = unicodeStart; | |||
this.unicodeEnd = unicodeEnd; | |||
this.glyphStartIndex = glyphStartIndex; | |||
} | |||
/** | |||
* {@inheritDoc} | |||
*/ | |||
@Override | |||
public int hashCode() { | |||
int hc = super.hashCode(); | |||
hc ^= ( hc * 11 ) + unicodeStart; | |||
hc ^= ( hc * 19 ) + unicodeEnd; | |||
hc ^= ( hc * 23 ) + glyphStartIndex; | |||
return hc; | |||
} | |||
/** | |||
* {@inheritDoc} | |||
*/ | |||
@Override | |||
public boolean equals(Object o) { | |||
if (o instanceof TTFCmapEntry) { | |||
TTFCmapEntry ce = (TTFCmapEntry)o; | |||
if (ce.unicodeStart == this.unicodeStart | |||
&& ce.unicodeEnd == this.unicodeEnd | |||
&& ce.glyphStartIndex == this.glyphStartIndex) { | |||
return true; | |||
} | |||
} | |||
return false; | |||
} | |||
/** | |||
* Returns the glyphStartIndex. | |||
* @return int | |||
*/ | |||
public int getGlyphStartIndex() { | |||
return glyphStartIndex; | |||
} | |||
/** | |||
* Returns the unicodeEnd. | |||
* @return int | |||
*/ | |||
public int getUnicodeEnd() { | |||
return unicodeEnd; | |||
} | |||
/** | |||
* Returns the unicodeStart. | |||
* @return int | |||
*/ | |||
public int getUnicodeStart() { | |||
return unicodeStart; | |||
} | |||
/** | |||
* Sets the glyphStartIndex. | |||
* @param glyphStartIndex The glyphStartIndex to set | |||
*/ | |||
public void setGlyphStartIndex(int glyphStartIndex) { | |||
this.glyphStartIndex = glyphStartIndex; | |||
} | |||
/** | |||
* Sets the unicodeEnd. | |||
* @param unicodeEnd The unicodeEnd to set | |||
*/ | |||
public void setUnicodeEnd(int unicodeEnd) { | |||
this.unicodeEnd = unicodeEnd; | |||
} | |||
/** | |||
* Sets the unicodeStart. | |||
* @param unicodeStart The unicodeStart to set | |||
*/ | |||
public void setUnicodeStart(int unicodeStart) { | |||
this.unicodeStart = unicodeStart; | |||
} | |||
} |
@@ -33,6 +33,14 @@ class TTFDirTabEntry { | |||
private long offset; | |||
private long length; | |||
public TTFDirTabEntry() { | |||
} | |||
public TTFDirTabEntry(long offset, long length) { | |||
this.offset = offset; | |||
this.length = length; | |||
} | |||
/** | |||
* Read Dir Tab, return tag name | |||
*/ |
@@ -21,15 +21,13 @@ package org.apache.fop.fonts.truetype; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.util.Iterator; | |||
import java.util.List; | |||
import java.util.Map; | |||
import java.util.Set; | |||
import org.apache.commons.io.IOUtils; | |||
import org.apache.fop.fonts.BFEntry; | |||
import org.apache.fop.fonts.CIDFontType; | |||
import org.apache.fop.fonts.EmbeddingMode; | |||
import org.apache.fop.fonts.EncodingMode; | |||
import org.apache.fop.fonts.FontLoader; | |||
import org.apache.fop.fonts.FontResolver; | |||
@@ -49,6 +47,7 @@ public class TTFFontLoader extends FontLoader { | |||
private SingleByteFont singleFont; | |||
private final String subFontName; | |||
private EncodingMode encodingMode; | |||
private EmbeddingMode embeddingMode; | |||
/** | |||
* Default constructor | |||
@@ -56,7 +55,7 @@ public class TTFFontLoader extends FontLoader { | |||
* @param resolver the FontResolver for font URI resolution | |||
*/ | |||
public TTFFontLoader(String fontFileURI, FontResolver resolver) { | |||
this(fontFileURI, null, true, EncodingMode.AUTO, true, resolver); | |||
this(fontFileURI, null, true, EmbeddingMode.AUTO, EncodingMode.AUTO, true, resolver); | |||
} | |||
/** | |||
@@ -65,23 +64,27 @@ public class TTFFontLoader extends FontLoader { | |||
* @param subFontName the sub-fontname of a font in a TrueType Collection (or null for normal | |||
* TrueType fonts) | |||
* @param embedded indicates whether the font is embedded or referenced | |||
* @param embeddingMode the embedding mode of the font | |||
* @param encodingMode the requested encoding mode | |||
* @param useKerning true to enable loading kerning info if available, false to disable | |||
* @param resolver the FontResolver for font URI resolution | |||
*/ | |||
public TTFFontLoader(String fontFileURI, String subFontName, | |||
boolean embedded, EncodingMode encodingMode, boolean useKerning, | |||
FontResolver resolver) { | |||
boolean embedded, EmbeddingMode embeddingMode, EncodingMode encodingMode, | |||
boolean useKerning, FontResolver resolver) { | |||
super(fontFileURI, embedded, true, resolver); | |||
this.subFontName = subFontName; | |||
this.encodingMode = encodingMode; | |||
this.embeddingMode = embeddingMode; | |||
if (this.encodingMode == EncodingMode.AUTO) { | |||
this.encodingMode = EncodingMode.CID; //Default to CID mode for TrueType | |||
} | |||
if (this.embeddingMode == EmbeddingMode.AUTO) { | |||
this.embeddingMode = EmbeddingMode.SUBSET; | |||
} | |||
} | |||
/** {@inheritDoc} */ | |||
@Override | |||
protected void read() throws IOException { | |||
read(this.subFontName); | |||
} | |||
@@ -144,7 +147,7 @@ public class TTFFontLoader extends FontLoader { | |||
returnFont.setItalicAngle(Integer.parseInt(ttf.getItalicAngle())); | |||
returnFont.setMissingWidth(0); | |||
returnFont.setWeight(ttf.getWeightClass()); | |||
returnFont.setEmbeddingMode(this.embeddingMode); | |||
if (isCid) { | |||
multiFont.setCIDType(CIDFontType.CIDTYPE2); | |||
int[] wx = ttf.getWidths(); | |||
@@ -168,15 +171,8 @@ public class TTFFontLoader extends FontLoader { | |||
} | |||
private BFEntry[] getCMap(TTFFile ttf) { | |||
List<TTFCmapEntry> entries = ttf.getCMaps(); | |||
BFEntry[] bfentries = new BFEntry[entries.size()]; | |||
int pos = 0; | |||
for (TTFCmapEntry ce : ttf.getCMaps()) { | |||
bfentries[pos] = new BFEntry(ce.getUnicodeStart(), ce.getUnicodeEnd(), | |||
ce.getGlyphStartIndex()); | |||
pos++; | |||
} | |||
return bfentries; | |||
BFEntry[] array = new BFEntry[ttf.getCMaps().size()]; | |||
return ttf.getCMaps().toArray(array); | |||
} | |||
private void copyWidthsSingleByte(TTFFile ttf) { | |||
@@ -184,9 +180,8 @@ public class TTFFontLoader extends FontLoader { | |||
for (int i = singleFont.getFirstChar(); i <= singleFont.getLastChar(); i++) { | |||
singleFont.setWidth(i, ttf.getCharWidth(i)); | |||
} | |||
Iterator iter = ttf.getCMaps().listIterator(); | |||
while (iter.hasNext()) { | |||
TTFCmapEntry ce = (TTFCmapEntry)iter.next(); | |||
for (BFEntry ce : ttf.getCMaps()) { | |||
if (ce.getUnicodeStart() < 0xFFFE) { | |||
for (char u = (char)ce.getUnicodeStart(); u <= ce.getUnicodeEnd(); u++) { | |||
int codePoint = singleFont.getEncoding().mapChar(u); | |||
@@ -221,7 +216,6 @@ public class TTFFontLoader extends FontLoader { | |||
} | |||
for (Integer kpx1 : kerningSet) { | |||
Map<Integer, Integer> h2; | |||
if (isCid) { | |||
h2 = ttf.getKerning().get(kpx1); |
@@ -0,0 +1,48 @@ | |||
/* | |||
* 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.fonts.truetype; | |||
import java.io.IOException; | |||
/** | |||
* This is an interface for streaming individual glyphs from the glyf table in a True Type font. | |||
*/ | |||
public interface TTFGlyphOutputStream { | |||
/** | |||
* Begins the streaming of glyphs. | |||
* @throws IOException file write exception | |||
*/ | |||
void startGlyphStream() throws IOException; | |||
/** | |||
* Streams an individual glyph at offset from a byte array. | |||
* @param byteArray byte[] the font byte array. | |||
* @param offset int the starting position to stream from. | |||
* @param length int the number of bytes to stream. | |||
* @throws IOException file write exception. | |||
*/ | |||
void streamGlyph(byte[] byteArray, int offset, int length) throws IOException; | |||
/** | |||
* Ends the streaming of glyphs. | |||
* @throws IOException file write exception. | |||
*/ | |||
void endGlyphStream() throws IOException; | |||
} |
@@ -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.fonts.truetype; | |||
import java.io.IOException; | |||
/** | |||
* This is an interface for streaming True Type font. | |||
*/ | |||
public interface TTFOutputStream { | |||
/** | |||
* Starts writing the font to file. | |||
* @throws IOException file write exception. | |||
*/ | |||
void startFontStream() throws IOException; | |||
/** | |||
* Returns an object for streaming True Type tables. | |||
* @return {@link TTFTableOutputStream} | |||
*/ | |||
TTFTableOutputStream getTableOutputStream(); | |||
/** | |||
* Returns an object for streaming True Type glyphs in the glyf table. | |||
* @return {@link TTFGlyphOutputStream} | |||
*/ | |||
TTFGlyphOutputStream getGlyphOutputStream(); | |||
/** | |||
* Ends writing the font to file. | |||
* @throws IOException file write exception. | |||
*/ | |||
void endFontStream() throws IOException; | |||
} |
@@ -20,8 +20,10 @@ | |||
package org.apache.fop.fonts.truetype; | |||
import java.io.IOException; | |||
import java.util.HashMap; | |||
import java.util.List; | |||
import java.util.Map; | |||
import java.util.SortedSet; | |||
/** | |||
@@ -34,10 +36,6 @@ import java.util.Map; | |||
*/ | |||
public class TTFSubSetFile extends TTFFile { | |||
private static enum OperatingMode { | |||
PDF, POSTSCRIPT_GLYPH_DIRECTORY; | |||
} | |||
private byte[] output = null; | |||
private int realSize = 0; | |||
private int currentPos = 0; | |||
@@ -46,27 +44,25 @@ public class TTFSubSetFile extends TTFFile { | |||
* Offsets in name table to be filled out by table. | |||
* The offsets are to the checkSum field | |||
*/ | |||
private Map<String, Integer> offsets = new java.util.HashMap<String, Integer>(); | |||
private int glyfDirOffset = 0; | |||
private int headDirOffset = 0; | |||
private int hmtxDirOffset = 0; | |||
private int locaDirOffset = 0; | |||
private int maxpDirOffset = 0; | |||
private Map<TTFTableName, Integer> offsets = new HashMap<TTFTableName, Integer>(); | |||
private int checkSumAdjustmentOffset = 0; | |||
private int locaOffset = 0; | |||
private int determineTableCount(OperatingMode operatingMode) { | |||
int numTables = 4; //4 req'd tables: head,hhea,hmtx,maxp | |||
/** Stores the glyph offsets so that we can end strings at glyph boundaries */ | |||
private int[] glyphOffsets; | |||
/** The dir tab entries in the new subset font. */ | |||
private Map<TTFTableName, TTFDirTabEntry> newDirTabs | |||
= new HashMap<TTFTableName, TTFDirTabEntry>(); | |||
private int determineTableCount() { | |||
int numTables = 4; //4 req'd tables: head,hhea,hmtx,maxp, | |||
if (isCFF()) { | |||
throw new UnsupportedOperationException( | |||
"OpenType fonts with CFF glyphs are not supported"); | |||
} else { | |||
if (operatingMode == OperatingMode.POSTSCRIPT_GLYPH_DIRECTORY) { | |||
numTables++; //1 table: gdir | |||
} else { | |||
numTables += 2; //2 req'd tables: glyf,loca | |||
} | |||
numTables += 5; //5 req'd tables: glyf,loca,post,name,OS/2 | |||
if (hasCvt()) { | |||
numTables++; | |||
} | |||
@@ -83,8 +79,8 @@ public class TTFSubSetFile extends TTFFile { | |||
/** | |||
* Create the directory table | |||
*/ | |||
private void createDirectory(OperatingMode operatingMode) { | |||
int numTables = determineTableCount(operatingMode); | |||
private void createDirectory() { | |||
int numTables = determineTableCount(); | |||
// Create the TrueType header | |||
writeByte((byte)0); | |||
writeByte((byte)1); | |||
@@ -97,7 +93,7 @@ public class TTFSubSetFile extends TTFFile { | |||
// Create searchRange, entrySelector and rangeShift | |||
int maxPow = maxPow2(numTables); | |||
int searchRange = maxPow * 16; | |||
int searchRange = (int) Math.pow(2, maxPow) * 16; | |||
writeUShort(searchRange); | |||
realSize += 2; | |||
@@ -106,83 +102,47 @@ public class TTFSubSetFile extends TTFFile { | |||
writeUShort((numTables * 16) - searchRange); | |||
realSize += 2; | |||
// Create space for the table entries (these must be in ASCII alphabetical order[A-Z]then[a-z]) | |||
writeTableName(TTFTableName.OS2); | |||
// Create space for the table entries | |||
if (hasCvt()) { | |||
writeString("cvt "); | |||
offsets.put("cvt ", currentPos); | |||
currentPos += 12; | |||
realSize += 16; | |||
writeTableName(TTFTableName.CVT); | |||
} | |||
if (hasFpgm()) { | |||
writeString("fpgm"); | |||
offsets.put("fpgm", currentPos); | |||
currentPos += 12; | |||
realSize += 16; | |||
} | |||
if (operatingMode != OperatingMode.POSTSCRIPT_GLYPH_DIRECTORY) { | |||
writeString("glyf"); | |||
glyfDirOffset = currentPos; | |||
currentPos += 12; | |||
realSize += 16; | |||
} | |||
writeString("head"); | |||
headDirOffset = currentPos; | |||
currentPos += 12; | |||
realSize += 16; | |||
writeString("hhea"); | |||
offsets.put("hhea", currentPos); | |||
currentPos += 12; | |||
realSize += 16; | |||
writeString("hmtx"); | |||
hmtxDirOffset = currentPos; | |||
currentPos += 12; | |||
realSize += 16; | |||
if (operatingMode != OperatingMode.POSTSCRIPT_GLYPH_DIRECTORY) { | |||
writeString("loca"); | |||
locaDirOffset = currentPos; | |||
currentPos += 12; | |||
realSize += 16; | |||
writeTableName(TTFTableName.FPGM); | |||
} | |||
writeTableName(TTFTableName.GLYF); | |||
writeTableName(TTFTableName.HEAD); | |||
writeTableName(TTFTableName.HHEA); | |||
writeTableName(TTFTableName.HMTX); | |||
writeTableName(TTFTableName.LOCA); | |||
writeTableName(TTFTableName.MAXP); | |||
writeTableName(TTFTableName.NAME); | |||
writeTableName(TTFTableName.POST); | |||
if (hasPrep()) { | |||
writeTableName(TTFTableName.PREP); | |||
} | |||
newDirTabs.put(TTFTableName.DIRECTORY_TABLE, new TTFDirTabEntry(0, currentPos)); | |||
} | |||
writeString("maxp"); | |||
maxpDirOffset = currentPos; | |||
private void writeTableName(TTFTableName tableName) { | |||
writeString(tableName.getName()); | |||
offsets.put(tableName, currentPos); | |||
currentPos += 12; | |||
realSize += 16; | |||
if (hasPrep()) { | |||
writeString("prep"); | |||
offsets.put("prep", currentPos); | |||
currentPos += 12; | |||
realSize += 16; | |||
} | |||
if (operatingMode == OperatingMode.POSTSCRIPT_GLYPH_DIRECTORY) { | |||
//"gdir" indicates to the PostScript interpreter that the GlyphDirectory approach | |||
//is in use. | |||
writeString("gdir"); | |||
currentPos += 12; | |||
realSize += 16; | |||
} | |||
} | |||
private boolean hasCvt() { | |||
return dirTabs.containsKey("cvt "); | |||
return dirTabs.containsKey(TTFTableName.CVT); | |||
} | |||
private boolean hasFpgm() { | |||
return dirTabs.containsKey("fpgm"); | |||
return dirTabs.containsKey(TTFTableName.FPGM); | |||
} | |||
private boolean hasPrep() { | |||
return dirTabs.containsKey("prep"); | |||
return dirTabs.containsKey(TTFTableName.PREP); | |||
} | |||
/** | |||
@@ -191,26 +151,24 @@ public class TTFSubSetFile extends TTFFile { | |||
private void createLoca(int size) throws IOException { | |||
pad4(); | |||
locaOffset = currentPos; | |||
writeULong(locaDirOffset + 4, currentPos); | |||
writeULong(locaDirOffset + 8, size * 4 + 4); | |||
int dirTableOffset = offsets.get(TTFTableName.LOCA); | |||
writeULong(dirTableOffset + 4, currentPos); | |||
writeULong(dirTableOffset + 8, size * 4 + 4); | |||
currentPos += size * 4 + 4; | |||
realSize += size * 4 + 4; | |||
} | |||
private boolean copyTable(FontFileReader in, String tableName) throws IOException { | |||
TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get(tableName); | |||
private boolean copyTable(FontFileReader in, TTFTableName tableName) throws IOException { | |||
TTFDirTabEntry entry = dirTabs.get(tableName); | |||
if (entry != null) { | |||
pad4(); | |||
seekTab(in, tableName, 0); | |||
System.arraycopy(in.getBytes((int)entry.getOffset(), (int)entry.getLength()), | |||
0, output, currentPos, (int)entry.getLength()); | |||
int checksum = getCheckSum(currentPos, (int)entry.getLength()); | |||
int offset = offsets.get(tableName); | |||
writeULong(offset, checksum); | |||
writeULong(offset + 4, currentPos); | |||
writeULong(offset + 8, (int)entry.getLength()); | |||
currentPos += (int)entry.getLength(); | |||
realSize += (int)entry.getLength(); | |||
updateCheckSum(currentPos, (int) entry.getLength(), tableName); | |||
currentPos += (int) entry.getLength(); | |||
realSize += (int) entry.getLength(); | |||
return true; | |||
} else { | |||
return false; | |||
@@ -221,14 +179,34 @@ public class TTFSubSetFile extends TTFFile { | |||
* Copy the cvt table as is from original font to subset font | |||
*/ | |||
private boolean createCvt(FontFileReader in) throws IOException { | |||
return copyTable(in, "cvt "); | |||
return copyTable(in, TTFTableName.CVT); | |||
} | |||
/** | |||
* Copy the fpgm table as is from original font to subset font | |||
*/ | |||
private boolean createFpgm(FontFileReader in) throws IOException { | |||
return copyTable(in, "fpgm"); | |||
return copyTable(in, TTFTableName.FPGM); | |||
} | |||
/** | |||
* Copy the name table as is from the original. | |||
* @param in FontFileReader | |||
* @return boolean | |||
* @throws IOException exception | |||
*/ | |||
private boolean createName(FontFileReader in) throws IOException { | |||
return copyTable(in, TTFTableName.NAME); | |||
} | |||
/** | |||
* Copy the OS/2 table as is from the original. | |||
* @param in | |||
* @return | |||
* @throws IOException | |||
*/ | |||
private boolean createOS2(FontFileReader in) throws IOException { | |||
return copyTable(in, TTFTableName.OS2); | |||
} | |||
/** | |||
@@ -236,18 +214,16 @@ public class TTFSubSetFile extends TTFFile { | |||
* and set num glyphs to size | |||
*/ | |||
private void createMaxp(FontFileReader in, int size) throws IOException { | |||
TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("maxp"); | |||
TTFTableName maxp = TTFTableName.MAXP; | |||
TTFDirTabEntry entry = dirTabs.get(maxp); | |||
if (entry != null) { | |||
pad4(); | |||
seekTab(in, "maxp", 0); | |||
seekTab(in, maxp, 0); | |||
System.arraycopy(in.getBytes((int)entry.getOffset(), (int)entry.getLength()), | |||
0, output, currentPos, (int)entry.getLength()); | |||
writeUShort(currentPos + 4, size); | |||
int checksum = getCheckSum(currentPos, (int)entry.getLength()); | |||
writeULong(maxpDirOffset, checksum); | |||
writeULong(maxpDirOffset + 4, currentPos); | |||
writeULong(maxpDirOffset + 8, (int)entry.getLength()); | |||
updateCheckSum(currentPos, (int)entry.getLength(), maxp); | |||
currentPos += (int)entry.getLength(); | |||
realSize += (int)entry.getLength(); | |||
} else { | |||
@@ -255,12 +231,34 @@ public class TTFSubSetFile extends TTFFile { | |||
} | |||
} | |||
private void createPost(FontFileReader in) throws IOException { | |||
TTFTableName post = TTFTableName.POST; | |||
TTFDirTabEntry entry = dirTabs.get(post); | |||
if (entry != null) { | |||
pad4(); | |||
seekTab(in, post, 0); | |||
int newTableSize = 32; // This is the post table size with glyphs truncated | |||
byte[] newPostTable = new byte[newTableSize]; | |||
// We only want the first 28 bytes (truncate the glyph names); | |||
System.arraycopy(in.getBytes((int) entry.getOffset(), newTableSize), | |||
0, newPostTable, 0, newTableSize); | |||
// set the post table to Format 3.0 | |||
newPostTable[1] = 0x03; | |||
System.arraycopy(newPostTable, 0, output, currentPos, newTableSize); | |||
updateCheckSum(currentPos, newTableSize, post); | |||
currentPos += newTableSize; | |||
realSize += newTableSize; | |||
} else { | |||
throw new IOException("Can't find post table"); | |||
} | |||
} | |||
/** | |||
* Copy the prep table as is from original font to subset font | |||
*/ | |||
private boolean createPrep(FontFileReader in) throws IOException { | |||
return copyTable(in, "prep"); | |||
return copyTable(in, TTFTableName.PREP); | |||
} | |||
@@ -269,8 +267,18 @@ public class TTFSubSetFile extends TTFFile { | |||
* and fill in size of hmtx table | |||
*/ | |||
private void createHhea(FontFileReader in, int size) throws IOException { | |||
boolean copied = copyTable(in, "hhea"); | |||
if (!copied) { | |||
TTFDirTabEntry entry = dirTabs.get(TTFTableName.HHEA); | |||
if (entry != null) { | |||
pad4(); | |||
seekTab(in, TTFTableName.HHEA, 0); | |||
System.arraycopy(in.getBytes((int) entry.getOffset(), (int) entry.getLength()), 0, | |||
output, currentPos, (int) entry.getLength()); | |||
writeUShort((int) entry.getLength() + currentPos - 2, size); | |||
updateCheckSum(currentPos, (int) entry.getLength(), TTFTableName.HHEA); | |||
currentPos += (int) entry.getLength(); | |||
realSize += (int) entry.getLength(); | |||
} else { | |||
throw new IOException("Can't find hhea table"); | |||
} | |||
} | |||
@@ -283,10 +291,11 @@ public class TTFSubSetFile extends TTFFile { | |||
* in checkSumAdjustmentOffset | |||
*/ | |||
private void createHead(FontFileReader in) throws IOException { | |||
TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("head"); | |||
TTFTableName head = TTFTableName.HEAD; | |||
TTFDirTabEntry entry = dirTabs.get(head); | |||
if (entry != null) { | |||
pad4(); | |||
seekTab(in, "head", 0); | |||
seekTab(in, head, 0); | |||
System.arraycopy(in.getBytes((int)entry.getOffset(), (int)entry.getLength()), | |||
0, output, currentPos, (int)entry.getLength()); | |||
@@ -298,11 +307,7 @@ public class TTFSubSetFile extends TTFFile { | |||
output[currentPos + 50] = 0; // long locaformat | |||
output[currentPos + 51] = 1; // long locaformat | |||
int checksum = getCheckSum(currentPos, (int)entry.getLength()); | |||
writeULong(headDirOffset, checksum); | |||
writeULong(headDirOffset + 4, currentPos); | |||
writeULong(headDirOffset + 8, (int)entry.getLength()); | |||
updateCheckSum(currentPos, (int)entry.getLength(), head); | |||
currentPos += (int)entry.getLength(); | |||
realSize += (int)entry.getLength(); | |||
} else { | |||
@@ -315,8 +320,9 @@ public class TTFSubSetFile extends TTFFile { | |||
* Create the glyf table and fill in loca table | |||
*/ | |||
private void createGlyf(FontFileReader in, | |||
Map<Integer, Integer> glyphs) throws IOException { | |||
TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("glyf"); | |||
Map<Integer, Integer> glyphs) throws IOException { | |||
TTFTableName glyf = TTFTableName.GLYF; | |||
TTFDirTabEntry entry = dirTabs.get(glyf); | |||
int size = 0; | |||
int startPos = 0; | |||
int endOffset = 0; // Store this as the last loca | |||
@@ -329,6 +335,7 @@ public class TTFSubSetFile extends TTFFile { | |||
* location offset. | |||
*/ | |||
int[] origIndexes = buildSubsetIndexToOrigIndexMap(glyphs); | |||
glyphOffsets = new int[origIndexes.length]; | |||
for (int i = 0; i < origIndexes.length; i++) { | |||
int nextOffset = 0; | |||
@@ -358,27 +365,30 @@ public class TTFSubSetFile extends TTFFile { | |||
endOffset1 = (currentPos - startPos + glyphLength); | |||
} | |||
// Store the glyph boundary positions relative to the start the font | |||
glyphOffsets[i] = currentPos; | |||
currentPos += glyphLength; | |||
realSize += glyphLength; | |||
endOffset = endOffset1; | |||
endOffset = endOffset1; | |||
} | |||
size = currentPos - startPos; | |||
int checksum = getCheckSum(startPos, size); | |||
writeULong(glyfDirOffset, checksum); | |||
writeULong(glyfDirOffset + 4, startPos); | |||
writeULong(glyfDirOffset + 8, size); | |||
currentPos += 12; | |||
realSize += 12; | |||
updateCheckSum(startPos, size + 12, glyf); | |||
// Update loca checksum and last loca index | |||
writeULong(locaOffset + glyphs.size() * 4, endOffset); | |||
checksum = getCheckSum(locaOffset, glyphs.size() * 4 + 4); | |||
writeULong(locaDirOffset, checksum); | |||
int locaSize = glyphs.size() * 4 + 4; | |||
int checksum = getCheckSum(output, locaOffset, locaSize); | |||
writeULong(offsets.get(TTFTableName.LOCA), checksum); | |||
int padSize = (locaOffset + locaSize) % 4; | |||
newDirTabs.put(TTFTableName.LOCA, | |||
new TTFDirTabEntry(locaOffset, locaSize + padSize)); | |||
} else { | |||
throw new IOException("Can't find glyf table"); | |||
} | |||
@@ -402,7 +412,8 @@ public class TTFSubSetFile extends TTFFile { | |||
*/ | |||
private void createHmtx(FontFileReader in, | |||
Map<Integer, Integer> glyphs) throws IOException { | |||
TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("hmtx"); | |||
TTFTableName hmtx = TTFTableName.HMTX; | |||
TTFDirTabEntry entry = dirTabs.get(hmtx); | |||
int longHorMetricSize = glyphs.size() * 2; | |||
int leftSideBearingSize = glyphs.size() * 2; | |||
@@ -421,10 +432,7 @@ public class TTFSubSetFile extends TTFFile { | |||
mtxTab[origIndex.intValue()].getLsb()); | |||
} | |||
int checksum = getCheckSum(currentPos, hmtxSize); | |||
writeULong(hmtxDirOffset, checksum); | |||
writeULong(hmtxDirOffset + 4, currentPos); | |||
writeULong(hmtxDirOffset + 8, hmtxSize); | |||
updateCheckSum(currentPos, hmtxSize, hmtx); | |||
currentPos += hmtxSize; | |||
realSize += hmtxSize; | |||
} else { | |||
@@ -540,16 +548,16 @@ public class TTFSubSetFile extends TTFFile { | |||
*/ | |||
private void scanGlyphs(FontFileReader in, | |||
Map<Integer, Integer> glyphs) throws IOException { | |||
TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("glyf"); | |||
TTFDirTabEntry entry = dirTabs.get(TTFTableName.GLYF); | |||
Map<Integer, Integer> newComposites = null; | |||
Map<Integer, Integer> allComposites = new java.util.HashMap<Integer, Integer>(); | |||
Map<Integer, Integer> allComposites = new HashMap<Integer, Integer>(); | |||
int newIndex = glyphs.size(); | |||
if (entry != null) { | |||
while (newComposites == null || newComposites.size() > 0) { | |||
// Inefficient to iterate through all glyphs | |||
newComposites = new java.util.HashMap<Integer, Integer>(); | |||
newComposites = new HashMap<Integer, Integer>(); | |||
for (Map.Entry<Integer, Integer> glyph : glyphs.entrySet()) { | |||
int origIndex = glyph.getKey(); | |||
@@ -591,46 +599,38 @@ public class TTFSubSetFile extends TTFFile { | |||
} | |||
} | |||
/** | |||
* Returns a subset of the original font. | |||
* Reads a font and creates a subset of the font. | |||
* | |||
* @param in FontFileReader to read from | |||
* @param name Name to be checked for in the font file | |||
* @param glyphs Map of glyphs (glyphs has old index as (Integer) key and | |||
* new index as (Integer) value) | |||
* @return A subset of the original font | |||
* @throws IOException in case of an I/O problem | |||
*/ | |||
public byte[] readFont(FontFileReader in, String name, | |||
public void readFont(FontFileReader in, String name, | |||
Map<Integer, Integer> glyphs) throws IOException { | |||
fontFile = in; | |||
//Check if TrueType collection, and that the name exists in the collection | |||
if (!checkTTC(in, name)) { | |||
if (!checkTTC(name)) { | |||
throw new IOException("Failed to read font"); | |||
} | |||
//Copy the Map as we're going to modify it | |||
Map<Integer, Integer> subsetGlyphs = new java.util.HashMap<Integer, Integer>(glyphs); | |||
Map<Integer, Integer> subsetGlyphs = new HashMap<Integer, Integer>(glyphs); | |||
output = new byte[in.getFileSize()]; | |||
readDirTabs(in); | |||
readFontHeader(in); | |||
getNumGlyphs(in); | |||
readHorizontalHeader(in); | |||
readHorizontalMetrics(in); | |||
readIndexToLocation(in); | |||
readDirTabs(); | |||
readFontHeader(); | |||
getNumGlyphs(); | |||
readHorizontalHeader(); | |||
readHorizontalMetrics(); | |||
readIndexToLocation(); | |||
scanGlyphs(in, subsetGlyphs); | |||
createDirectory(OperatingMode.PDF); // Create the TrueType header and directory | |||
createHead(in); | |||
createHhea(in, subsetGlyphs.size()); // Create the hhea table | |||
createHmtx(in, subsetGlyphs); // Create hmtx table | |||
createMaxp(in, subsetGlyphs.size()); // copy the maxp table | |||
createDirectory(); // Create the TrueType header and directory | |||
boolean optionalTableFound; | |||
optionalTableFound = createCvt(in); // copy the cvt table | |||
@@ -644,78 +644,16 @@ public class TTFSubSetFile extends TTFFile { | |||
// fpgm is optional (used in TrueType fonts only) | |||
log.debug("TrueType: fpgm table not present. Skipped."); | |||
} | |||
optionalTableFound = createPrep(in); // copy prep table | |||
if (!optionalTableFound) { | |||
// prep is optional (used in TrueType fonts only) | |||
log.debug("TrueType: prep table not present. Skipped."); | |||
} | |||
createLoca(subsetGlyphs.size()); // create empty loca table | |||
createGlyf(in, subsetGlyphs); //create glyf table and update loca table | |||
pad4(); | |||
createCheckSumAdjustment(); | |||
byte[] ret = new byte[realSize]; | |||
System.arraycopy(output, 0, ret, 0, realSize); | |||
return ret; | |||
} | |||
/** | |||
* Returns a subset of the original font suitable for use in PostScript programs. | |||
* | |||
* @param in FontFileReader to read from | |||
* @param name Name to be checked for in the font file | |||
* @param glyphs Map of glyphs (glyphs has old index as (Integer) key and | |||
* new index as (Integer) value) | |||
* @param glyphHandler the handler to receive all glyphs of the subset | |||
* @return A subset of the original font | |||
* @throws IOException in case of an I/O problem | |||
*/ | |||
public byte[] toPostScriptSubset(FontFileReader in, String name, | |||
Map glyphs, GlyphHandler glyphHandler) throws IOException { | |||
//Check if TrueType collection, and that the name exists in the collection | |||
if (!checkTTC(in, name)) { | |||
throw new IOException("Failed to read font"); | |||
} | |||
//Copy the Map as we're going to modify it | |||
Map<Integer, Integer> subsetGlyphs = new java.util.HashMap(glyphs); | |||
output = new byte[in.getFileSize()]; | |||
readDirTabs(in); | |||
readFontHeader(in); | |||
getNumGlyphs(in); | |||
readHorizontalHeader(in); | |||
readHorizontalMetrics(in); | |||
readIndexToLocation(in); | |||
scanGlyphs(in, subsetGlyphs); | |||
// Create the TrueType header and directory | |||
createDirectory(OperatingMode.POSTSCRIPT_GLYPH_DIRECTORY); | |||
createGlyf(in, subsetGlyphs); //create glyf table and update loca table | |||
createOS2(in); // copy the OS/2 table | |||
createHead(in); | |||
createHhea(in, subsetGlyphs.size()); // Create the hhea table | |||
createHmtx(in, subsetGlyphs); // Create hmtx table | |||
createMaxp(in, subsetGlyphs.size()); // copy the maxp table | |||
boolean optionalTableFound; | |||
optionalTableFound = createCvt(in); // copy the cvt table | |||
if (!optionalTableFound) { | |||
// cvt is optional (used in TrueType fonts only) | |||
log.debug("TrueType: ctv table not present. Skipped."); | |||
} | |||
optionalTableFound = createFpgm(in); // copy fpgm table | |||
if (!optionalTableFound) { | |||
// fpgm is optional (used in TrueType fonts only) | |||
log.debug("TrueType: fpgm table not present. Skipped."); | |||
} | |||
createName(in); // copy the name table | |||
createPost(in); // copy the post table | |||
optionalTableFound = createPrep(in); // copy prep table | |||
if (!optionalTableFound) { | |||
@@ -723,59 +661,54 @@ public class TTFSubSetFile extends TTFFile { | |||
log.debug("TrueType: prep table not present. Skipped."); | |||
} | |||
//Send all the glyphs from the subset | |||
handleGlyphSubset(in, subsetGlyphs, glyphHandler); | |||
pad4(); | |||
createCheckSumAdjustment(); | |||
} | |||
/** | |||
* Returns a subset of the fonts (readFont() MUST be called first in order to create the | |||
* subset). | |||
* @return byte array | |||
*/ | |||
public byte[] getFontSubset() { | |||
byte[] ret = new byte[realSize]; | |||
System.arraycopy(output, 0, ret, 0, realSize); | |||
return ret; | |||
} | |||
private void handleGlyphSubset(FontFileReader in, Map<Integer, Integer> glyphs, | |||
GlyphHandler glyphHandler) throws IOException { | |||
TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("glyf"); | |||
if (entry != null) { | |||
int[] origIndexes = buildSubsetIndexToOrigIndexMap(glyphs); | |||
for (int i = 0; i < origIndexes.length; i++) { | |||
int nextOffset = 0; | |||
int origGlyphIndex = origIndexes[i]; | |||
if (origGlyphIndex >= (mtxTab.length - 1)) { | |||
nextOffset = (int)lastLoca; | |||
} else { | |||
nextOffset = (int)mtxTab[origGlyphIndex + 1].getOffset(); | |||
} | |||
int glyphOffset = (int)mtxTab[origGlyphIndex].getOffset(); | |||
int glyphLength = nextOffset - glyphOffset; | |||
byte[] glyphData = in.getBytes( | |||
(int)entry.getOffset() + glyphOffset, | |||
glyphLength); | |||
glyphHandler.addGlyph(glyphData); | |||
private void handleGlyphSubset(TTFGlyphOutputStream glyphOut) throws IOException { | |||
glyphOut.startGlyphStream(); | |||
// Stream all but the last glyph | |||
for (int i = 0; i < glyphOffsets.length - 1; i++) { | |||
glyphOut.streamGlyph(output, glyphOffsets[i], | |||
glyphOffsets[i + 1] - glyphOffsets[i]); | |||
} | |||
// Stream the last glyph | |||
TTFDirTabEntry glyf = newDirTabs.get(TTFTableName.GLYF); | |||
long lastGlyphLength = glyf.getLength() | |||
- (glyphOffsets[glyphOffsets.length - 1] - glyf.getOffset()); | |||
glyphOut.streamGlyph(output, glyphOffsets[glyphOffsets.length - 1], | |||
(int) lastGlyphLength); | |||
glyphOut.endGlyphStream(); | |||
} | |||
@Override | |||
public void stream(TTFOutputStream ttfOut) throws IOException { | |||
SortedSet<Map.Entry<TTFTableName, TTFDirTabEntry>> sortedDirTabs | |||
= sortDirTabMap(newDirTabs); | |||
TTFTableOutputStream tableOut = ttfOut.getTableOutputStream(); | |||
TTFGlyphOutputStream glyphOut = ttfOut.getGlyphOutputStream(); | |||
ttfOut.startFontStream(); | |||
for (Map.Entry<TTFTableName, TTFDirTabEntry> entry : sortedDirTabs) { | |||
if (entry.getKey().equals(TTFTableName.GLYF)) { | |||
handleGlyphSubset(glyphOut); | |||
} else { | |||
tableOut.streamTable(output, (int) entry.getValue().getOffset(), | |||
(int) entry.getValue().getLength()); | |||
} | |||
} else { | |||
throw new IOException("Can't find glyf table"); | |||
} | |||
} | |||
/** | |||
* Used as callback to handle a number of glyphs. | |||
*/ | |||
public static interface GlyphHandler { | |||
/** | |||
* Adds a glyph. | |||
* @param glyphData the glyph data | |||
* @throws IOException if an I/O error occurs | |||
*/ | |||
void addGlyph(byte[] glyphData) throws IOException; | |||
ttfOut.endFontStream(); | |||
} | |||
/** | |||
@@ -827,20 +760,6 @@ public class TTFSubSetFile extends TTFFile { | |||
output[pos + 1] = b2; | |||
} | |||
/** | |||
* Appends a ULONG to the output array, | |||
* updates currentPos but not realSize | |||
*/ | |||
private void writeULong(int s) { | |||
byte b1 = (byte)((s >> 24) & 0xff); | |||
byte b2 = (byte)((s >> 16) & 0xff); | |||
byte b3 = (byte)((s >> 8) & 0xff); | |||
byte b4 = (byte)(s & 0xff); | |||
writeByte(b1); | |||
writeByte(b2); | |||
writeByte(b3); | |||
writeByte(b4); | |||
} | |||
/** | |||
* Appends a ULONG to the output array, | |||
@@ -857,41 +776,17 @@ public class TTFSubSetFile extends TTFFile { | |||
output[pos + 3] = b4; | |||
} | |||
/** | |||
* Read a signed short value at given position | |||
*/ | |||
private short readShort(int pos) { | |||
int ret = readUShort(pos); | |||
return (short)ret; | |||
} | |||
/** | |||
* Read a unsigned short value at given position | |||
*/ | |||
private int readUShort(int pos) { | |||
int ret = output[pos]; | |||
if (ret < 0) { | |||
ret += 256; | |||
} | |||
ret = ret << 8; | |||
if (output[pos + 1] < 0) { | |||
ret |= output[pos + 1] + 256; | |||
} else { | |||
ret |= output[pos + 1]; | |||
} | |||
return ret; | |||
} | |||
/** | |||
* Create a padding in the fontfile to align | |||
* on a 4-byte boundary | |||
*/ | |||
private void pad4() { | |||
int padSize = currentPos % 4; | |||
for (int i = 0; i < padSize; i++) { | |||
output[currentPos++] = 0; | |||
realSize++; | |||
int padSize = getPadSize(currentPos); | |||
if (padSize < 4) { | |||
for (int i = 0; i < padSize; i++) { | |||
output[currentPos++] = 0; | |||
realSize++; | |||
} | |||
} | |||
} | |||
@@ -900,23 +795,25 @@ public class TTFSubSetFile extends TTFFile { | |||
*/ | |||
private int maxPow2(int max) { | |||
int i = 0; | |||
while (Math.pow(2, i) < max) { | |||
while (Math.pow(2, i) <= max) { | |||
i++; | |||
} | |||
return (i - 1); | |||
} | |||
private int log2(int num) { | |||
return (int)(Math.log(num) / Math.log(2)); | |||
} | |||
private int getCheckSum(int start, int size) { | |||
return (int)getLongCheckSum(output, start, size); | |||
private void updateCheckSum(int tableStart, int tableSize, TTFTableName tableName) { | |||
int checksum = getCheckSum(output, tableStart, tableSize); | |||
int offset = offsets.get(tableName); | |||
int padSize = getPadSize(tableStart + tableSize); | |||
newDirTabs.put(tableName, new TTFDirTabEntry(tableStart, tableSize + padSize)); | |||
writeULong(offset, checksum); | |||
writeULong(offset + 4, tableStart); | |||
writeULong(offset + 8, tableSize); | |||
} | |||
private static long getLongCheckSum(byte[] data, int start, int size) { | |||
private 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; | |||
@@ -927,26 +824,19 @@ public class TTFSubSetFile extends TTFFile { | |||
long sum = 0; | |||
for (int i = 0; i < size; i += 4) { | |||
int l = (data[start + i] << 24); | |||
l += (data[start + i + 1] << 16); | |||
l += (data[start + i + 2] << 16); | |||
l += (data[start + i + 3] << 16); | |||
sum += l; | |||
if (sum > 0xffffffff) { | |||
sum = sum - 0xffffffff; | |||
long l = 0; | |||
for (int j = 0; j < 4; j++) { | |||
l <<= 8; | |||
l |= data[start + i + j] & 0xff; | |||
} | |||
sum += l; | |||
} | |||
return sum; | |||
return (int) sum; | |||
} | |||
private void createCheckSumAdjustment() { | |||
long sum = getLongCheckSum(output, 0, realSize); | |||
long sum = getCheckSum(output, 0, realSize); | |||
int checksum = (int)(0xb1b0afba - sum); | |||
writeULong(checkSumAdjustmentOffset, checksum); | |||
} | |||
} | |||
@@ -0,0 +1,158 @@ | |||
/* | |||
* 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.fonts.truetype; | |||
/** | |||
* This class holds the True Type Format table names as in the Directory Table of a TTF font file. | |||
* This class must also support custom tables found in fonts (thus an enum wasn't used). | |||
*/ | |||
public final class TTFTableName { | |||
/** The first table in a True Type font file containing metadata about other tables. */ | |||
public static final TTFTableName DIRECTORY_TABLE = new TTFTableName("dirTable"); | |||
/** Embedded bitmap data */ | |||
public static final TTFTableName EBDT = new TTFTableName("EBDT"); | |||
/** Embedded bitmap location data */ | |||
public static final TTFTableName EBLC = new TTFTableName("EBLC"); | |||
/** Embedded bitmap scaling data */ | |||
public static final TTFTableName EBSC = new TTFTableName("EBSC"); | |||
/** A font forge specific table */ | |||
public static final TTFTableName FFTM = new TTFTableName("FFTM"); | |||
/** Divides glyphs into various classes that make using the GPOS/GSUB tables easier. */ | |||
public static final TTFTableName GDEF = new TTFTableName("GDEF"); | |||
/** Provides kerning information, mark-to-base, etc. for opentype fonts */ | |||
public static final TTFTableName GPOS = new TTFTableName("GPOS"); | |||
/** Provides ligature information, swash, etc. for opentype fonts */ | |||
public static final TTFTableName GSUB = new TTFTableName("GSUB"); | |||
/** Linear threshold table */ | |||
public static final TTFTableName LTSH = new TTFTableName("LTSH"); | |||
/** OS/2 and Windows specific metrics */ | |||
public static final TTFTableName OS2 = new TTFTableName("OS/2"); | |||
/** PCL 5 data*/ | |||
public static final TTFTableName PCLT = new TTFTableName("PCLT"); | |||
/** Vertical Device Metrics table */ | |||
public static final TTFTableName VDMX = new TTFTableName("VDMX"); | |||
/** character to glyph mapping */ | |||
public static final TTFTableName CMAP = new TTFTableName("cmap"); | |||
/** Control Value Table */ | |||
public static final TTFTableName CVT = new TTFTableName("cvt "); | |||
/** font program */ | |||
public static final TTFTableName FPGM = new TTFTableName("fpgm"); | |||
/** grid-fitting and scan conversion procedure (grayscale) */ | |||
public static final TTFTableName GASP = new TTFTableName("gasp"); | |||
/** glyph data */ | |||
public static final TTFTableName GLYF = new TTFTableName("glyf"); | |||
/** horizontal device metrics */ | |||
public static final TTFTableName HDMX = new TTFTableName("hdmx"); | |||
/** font header */ | |||
public static final TTFTableName HEAD = new TTFTableName("head"); | |||
/** horizontal header */ | |||
public static final TTFTableName HHEA = new TTFTableName("hhea"); | |||
/** horizontal metrics */ | |||
public static final TTFTableName HMTX = new TTFTableName("hmtx"); | |||
/** kerning */ | |||
public static final TTFTableName KERN = new TTFTableName("kern"); | |||
/** index to location */ | |||
public static final TTFTableName LOCA = new TTFTableName("loca"); | |||
/** maximum profile */ | |||
public static final TTFTableName MAXP = new TTFTableName("maxp"); | |||
/** naming table */ | |||
public static final TTFTableName NAME = new TTFTableName("name"); | |||
/** PostScript information */ | |||
public static final TTFTableName POST = new TTFTableName("post"); | |||
/** CVT Program */ | |||
public static final TTFTableName PREP = new TTFTableName("prep"); | |||
/** Vertical Metrics header */ | |||
public static final TTFTableName VHEA = new TTFTableName("vhea"); | |||
/** Vertical Metrics */ | |||
public static final TTFTableName VMTX = new TTFTableName("vmtx"); | |||
private final String name; | |||
private TTFTableName(String name) { | |||
this.name = name; | |||
} | |||
/** | |||
* Returns the name of the table as it should be in the Table Directory. | |||
* @return String | |||
*/ | |||
public String getName() { | |||
return name; | |||
} | |||
/** | |||
* Returns the appropriate TTFTableName object when given the string representation. | |||
* @param tableName table name as in the Directory Table. | |||
* @return TTFTableName | |||
*/ | |||
public static TTFTableName getValue(String tableName) { | |||
if (tableName != null) { | |||
return new TTFTableName(tableName); | |||
} | |||
throw new IllegalArgumentException("A TrueType font table name must not be null"); | |||
} | |||
@Override | |||
public int hashCode() { | |||
return name.hashCode(); | |||
} | |||
@Override | |||
public boolean equals(Object o) { | |||
if (o == this) { | |||
return true; | |||
} | |||
if (!(o instanceof TTFTableName)) { | |||
return false; | |||
} | |||
TTFTableName to = (TTFTableName) o; | |||
return this.name.equals(to.getName()); | |||
} | |||
} |
@@ -0,0 +1,37 @@ | |||
/* | |||
* 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.fonts.truetype; | |||
import java.io.IOException; | |||
/** | |||
* An interface for streaming full True Type tables from a TTF file. | |||
*/ | |||
public interface TTFTableOutputStream { | |||
/** | |||
* Streams a table defined in byteArray at offset of length bytes. | |||
* @param byteArray The source of the table to stream from. | |||
* @param offset The position in byteArray to begin streaming from. | |||
* @param length The number of bytes to stream. | |||
* @throws IOException write error. | |||
*/ | |||
void streamTable(byte[] byteArray, int offset, int length) throws IOException; | |||
} |
@@ -39,9 +39,6 @@ import org.apache.commons.io.IOUtils; | |||
import org.apache.commons.io.output.ByteArrayOutputStream; | |||
import org.apache.commons.logging.Log; | |||
import org.apache.commons.logging.LogFactory; | |||
import org.apache.xmlgraphics.xmp.Metadata; | |||
import org.apache.fop.fonts.CIDFont; | |||
import org.apache.fop.fonts.CIDSubset; | |||
import org.apache.fop.fonts.CodePointMapping; | |||
@@ -59,6 +56,7 @@ import org.apache.fop.fonts.truetype.FontFileReader; | |||
import org.apache.fop.fonts.truetype.TTFSubSetFile; | |||
import org.apache.fop.fonts.type1.PFBData; | |||
import org.apache.fop.fonts.type1.PFBParser; | |||
import org.apache.xmlgraphics.xmp.Metadata; | |||
/** | |||
* This class provides method to create and register PDF objects. | |||
@@ -1663,8 +1661,8 @@ public class PDFFactory { | |||
FontFileReader reader = new FontFileReader(in); | |||
TTFSubSetFile subset = new TTFSubSetFile(); | |||
byte[] subsetFont = subset.readFont(reader, | |||
mbfont.getTTCName(), mbfont.getUsedGlyphs()); | |||
subset.readFont(reader, mbfont.getTTCName(), mbfont.getUsedGlyphs()); | |||
byte[] subsetFont = subset.getFontSubset(); | |||
// Only TrueType CID fonts are supported now | |||
embeddedFont = new PDFTTFStream(subsetFont.length); |
@@ -25,7 +25,6 @@ import javax.xml.transform.Source; | |||
import org.apache.commons.logging.Log; | |||
import org.apache.commons.logging.LogFactory; | |||
import org.apache.fop.fonts.CustomFont; | |||
import org.apache.fop.fonts.EmbedFontInfo; | |||
import org.apache.fop.fonts.EncodingMode; | |||
@@ -88,8 +87,8 @@ public class ConfiguredFontCollection implements FontCollection { | |||
font = new CustomFontMetricsMapper(fontMetrics, fontSource); | |||
} else { | |||
CustomFont fontMetrics = FontLoader.loadFont( | |||
fontFile, null, true, EncodingMode.AUTO, | |||
configFontInfo.getKerning(), fontResolver); | |||
fontFile, null, true, configFontInfo.getEmbeddingMode(), | |||
EncodingMode.AUTO, configFontInfo.getKerning(), fontResolver); | |||
font = new CustomFontMetricsMapper(fontMetrics); | |||
} | |||
@@ -23,7 +23,10 @@ import java.io.FileNotFoundException; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.net.MalformedURLException; | |||
import java.util.HashMap; | |||
import java.util.HashSet; | |||
import java.util.Map; | |||
import java.util.Set; | |||
import javax.xml.transform.Source; | |||
import javax.xml.transform.stream.StreamSource; | |||
@@ -36,13 +39,13 @@ import org.apache.xmlgraphics.ps.DSCConstants; | |||
import org.apache.xmlgraphics.ps.PSGenerator; | |||
import org.apache.xmlgraphics.ps.PSResource; | |||
import org.apache.xmlgraphics.ps.dsc.ResourceTracker; | |||
import org.apache.xmlgraphics.util.io.ASCIIHexOutputStream; | |||
import org.apache.fop.fonts.BFEntry; | |||
import org.apache.fop.fonts.Base14Font; | |||
import org.apache.fop.fonts.CIDFontType; | |||
import org.apache.fop.fonts.CIDSubset; | |||
import org.apache.fop.fonts.CustomFont; | |||
import org.apache.fop.fonts.EmbeddingMode; | |||
import org.apache.fop.fonts.Font; | |||
import org.apache.fop.fonts.FontInfo; | |||
import org.apache.fop.fonts.FontType; | |||
@@ -52,8 +55,11 @@ import org.apache.fop.fonts.SingleByteEncoding; | |||
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.TTFFile; | |||
import org.apache.fop.fonts.truetype.TTFOutputStream; | |||
import org.apache.fop.fonts.truetype.TTFSubSetFile; | |||
import org.apache.fop.fonts.truetype.TTFFile.PostScriptVersion; | |||
import org.apache.fop.render.ps.fonts.PSTTFOutputStream; | |||
import org.apache.fop.util.HexEncoder; | |||
/** | |||
@@ -63,7 +69,6 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils { | |||
/** logging instance */ | |||
protected static final Log log = LogFactory.getLog(PSFontUtils.class); | |||
/** | |||
* Generates the PostScript code for the font dictionary. This method should only be | |||
* used if no "resource optimization" is performed, i.e. when the fonts are not embedded | |||
@@ -119,11 +124,12 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils { | |||
* @return a Map of PSResource instances representing all defined fonts (key: font key) | |||
* @throws IOException in case of an I/O problem | |||
*/ | |||
private static Map writeFontDict(PSGenerator gen, FontInfo fontInfo, Map<String, Typeface> fonts, | |||
boolean encodeAllCharacters, PSEventProducer eventProducer) throws IOException { | |||
private static Map writeFontDict(PSGenerator gen, FontInfo fontInfo, | |||
Map<String, Typeface> fonts, boolean encodeAllCharacters, PSEventProducer eventProducer) | |||
throws IOException { | |||
gen.commentln("%FOPBeginFontDict"); | |||
Map fontResources = new java.util.HashMap(); | |||
Map fontResources = new HashMap(); | |||
for (String key : fonts.keySet()) { | |||
Typeface tf = getTypeFace(fontInfo, fonts, key); | |||
PSResource fontRes = new PSResource(PSResource.TYPE_FONT, tf.getEmbedFontName()); | |||
@@ -219,57 +225,52 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils { | |||
private static PSFontResource embedFont(PSGenerator gen, Typeface tf, PSResource fontRes, | |||
PSEventProducer eventProducer) throws IOException { | |||
boolean embeddedFont = false; | |||
FontType fontType = tf.getFontType(); | |||
PSFontResource fontResource = null; | |||
if (fontType == FontType.TYPE1 || fontType == FontType.TRUETYPE | |||
|| fontType == FontType.TYPE0) { | |||
if (tf instanceof CustomFont) { | |||
CustomFont cf = (CustomFont)tf; | |||
if (isEmbeddable(cf)) { | |||
InputStream in = getInputStreamOnFont(gen, cf); | |||
if (in != null) { | |||
if (fontType == FontType.TYPE0) { | |||
if (gen.embedIdentityH()) { | |||
checkPostScriptLevel3(gen, eventProducer); | |||
/* | |||
* First CID-keyed font to be embedded; add | |||
* %%IncludeResource: comment for ProcSet CIDInit. | |||
*/ | |||
gen.includeProcsetCIDInitResource(); | |||
} | |||
PSResource cidFontResource = embedType2CIDFont(gen, | |||
(MultiByteFont) tf, in); | |||
fontResource = PSFontResource.createFontResource(fontRes, | |||
gen.getProcsetCIDInitResource(), | |||
gen.getIdentityHCMapResource(), | |||
cidFontResource); | |||
} | |||
gen.writeDSCComment(DSCConstants.BEGIN_RESOURCE, | |||
fontRes); | |||
if (fontType == FontType.TYPE1) { | |||
embedType1Font(gen, in); | |||
fontResource = PSFontResource.createFontResource(fontRes); | |||
} else if (fontType == FontType.TRUETYPE) { | |||
embedTrueTypeFont(gen, (SingleByteFont) tf, in); | |||
fontResource = PSFontResource.createFontResource(fontRes); | |||
} else { | |||
composeType0Font(gen, (MultiByteFont) tf, in); | |||
} | |||
gen.writeDSCComment(DSCConstants.END_RESOURCE); | |||
gen.getResourceTracker().registerSuppliedResource(fontRes); | |||
embeddedFont = true; | |||
} else { | |||
gen.commentln("%WARNING: Could not embed font: " + cf.getEmbedFontName()); | |||
log.warn("Font " + cf.getEmbedFontName() + " is marked as supplied in the" | |||
+ " PostScript file but could not be embedded!"); | |||
} | |||
} | |||
} | |||
} | |||
if (!embeddedFont) { | |||
if (!(fontType == FontType.TYPE1 || fontType == FontType.TRUETYPE | |||
|| fontType == FontType.TYPE0) || !(tf instanceof CustomFont)) { | |||
gen.writeDSCComment(DSCConstants.INCLUDE_RESOURCE, fontRes); | |||
fontResource = PSFontResource.createFontResource(fontRes); | |||
return fontResource; | |||
} | |||
CustomFont cf = (CustomFont)tf; | |||
if (isEmbeddable(cf)) { | |||
InputStream in = getInputStreamOnFont(gen, cf); | |||
if (in == null) { | |||
gen.commentln("%WARNING: Could not embed font: " + cf.getEmbedFontName()); | |||
log.warn("Font " + cf.getEmbedFontName() + " is marked as supplied in the" | |||
+ " PostScript file but could not be embedded!"); | |||
gen.writeDSCComment(DSCConstants.INCLUDE_RESOURCE, fontRes); | |||
fontResource = PSFontResource.createFontResource(fontRes); | |||
return fontResource; | |||
} | |||
if (fontType == FontType.TYPE0) { | |||
if (gen.embedIdentityH()) { | |||
checkPostScriptLevel3(gen, eventProducer); | |||
/* | |||
* First CID-keyed font to be embedded; add | |||
* %%IncludeResource: comment for ProcSet CIDInit. | |||
*/ | |||
gen.includeProcsetCIDInitResource(); | |||
} | |||
PSResource cidFontResource = embedType2CIDFont(gen, | |||
(MultiByteFont) tf, in); | |||
fontResource = PSFontResource.createFontResource(fontRes, | |||
gen.getProcsetCIDInitResource(), gen.getIdentityHCMapResource(), | |||
cidFontResource); | |||
} | |||
gen.writeDSCComment(DSCConstants.BEGIN_RESOURCE, fontRes); | |||
if (fontType == FontType.TYPE1) { | |||
embedType1Font(gen, in); | |||
fontResource = PSFontResource.createFontResource(fontRes); | |||
} else if (fontType == FontType.TRUETYPE) { | |||
embedTrueTypeFont(gen, (SingleByteFont) tf, in); | |||
fontResource = PSFontResource.createFontResource(fontRes); | |||
} else { | |||
composeType0Font(gen, (MultiByteFont) tf, in); | |||
} | |||
gen.writeDSCComment(DSCConstants.END_RESOURCE); | |||
gen.getResourceTracker().registerSuppliedResource(fontRes); | |||
} | |||
return fontResource; | |||
} | |||
@@ -292,12 +293,28 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils { | |||
/* See Adobe Technical Note #5012, "The Type 42 Font Format Specification" */ | |||
gen.commentln("%!PS-TrueTypeFont-65536-65536-1"); // TODO TrueType & font versions | |||
gen.writeln("11 dict begin"); | |||
createType42DictionaryEntries(gen, font, fontStream, font.getCMap()); | |||
if (font.getEmbeddingMode() == EmbeddingMode.AUTO) { | |||
font.setEmbeddingMode(EmbeddingMode.SUBSET); | |||
} | |||
FontFileReader reader = new FontFileReader(fontStream); | |||
// TODO is subset-embedding working? In which case the following can be factorized | |||
// with what is in composeType0Font | |||
// TTFFile ttfFile; | |||
// if (font.getEmbeddingMode() != EmbeddingMode.FULL) { | |||
// ttfFile = new TTFSubSetFile(); | |||
// ttfFile.readFont(reader, font.getFullName()(), font.getUsedGlyphs()); | |||
// } else { | |||
// ttfFile = new TTFFile(); | |||
// ttfFile.readFont(reader, font.getFullName()); | |||
// } | |||
TTFFile ttfFile = new TTFFile(); | |||
ttfFile.readFont(reader, font.getFullName()); | |||
createType42DictionaryEntries(gen, font, font.getCMap(), ttfFile); | |||
gen.writeln("FontName currentdict end definefont pop"); | |||
} | |||
private static void createType42DictionaryEntries(PSGenerator gen, CustomFont font, | |||
InputStream fontStream, BFEntry[] cmap) throws IOException { | |||
BFEntry[] cmap, TTFFile ttfFile) throws IOException { | |||
gen.write("/FontName /"); | |||
gen.write(font.getEmbedFontName()); | |||
gen.writeln(" def"); | |||
@@ -308,7 +325,8 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils { | |||
gen.writeln("/Encoding 256 array"); | |||
gen.writeln("0 1 255{1 index exch/.notdef put}for"); | |||
boolean buildCharStrings; | |||
if (font.getFontType() == FontType.TYPE0) { | |||
Set<String> glyphNames = new HashSet<String>(); | |||
if (font.getFontType() == FontType.TYPE0 && font.getEmbeddingMode() != EmbeddingMode.FULL) { | |||
//"/Encoding" is required but ignored for CID fonts | |||
//so we keep it minimal to save space | |||
buildCharStrings = false; | |||
@@ -323,72 +341,75 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils { | |||
gen.write(Glyphs.NOTDEF); | |||
} else { | |||
gen.write(glyphName); | |||
glyphNames.add(glyphName); | |||
} | |||
gen.writeln(" put"); | |||
} | |||
} | |||
gen.writeln("readonly def"); | |||
gen.write("/sfnts["); | |||
/* | |||
* Store the font file in an array of hex-encoded strings. Strings are limited to | |||
* 65535 characters, string will start with a newline, 2 characters are needed to | |||
* hex-encode each byte, one newline character will be added every 40 bytes, each | |||
* string should start at a 4-byte boundary | |||
* => buffer size = floor((65535 - 1) * 40 / 81 / 4) * 4 | |||
* TODO this is not robust: depends on how often ASCIIHexOutputStream adds a newline | |||
*/ | |||
// TODO does not follow Technical Note #5012's requirements: | |||
// "strings must begin at TrueType table boundaries, or at individual glyph | |||
// boundaries within the glyf table." | |||
// There may be compatibility issues with older PostScript interpreters | |||
byte[] buffer = new byte[32360]; | |||
int readCount; | |||
while ((readCount = fontStream.read(buffer)) > 0) { | |||
ASCIIHexOutputStream hexOut = new ASCIIHexOutputStream(gen.getOutputStream()); | |||
gen.writeln("<"); | |||
hexOut.write(buffer, 0, readCount); | |||
gen.write("> "); | |||
} | |||
gen.writeln("]def"); | |||
TTFOutputStream ttfOut = new PSTTFOutputStream(gen); | |||
ttfFile.stream(ttfOut); | |||
buildCharStrings(gen, buildCharStrings, cmap, glyphNames, font); | |||
} | |||
private static void buildCharStrings(PSGenerator gen, boolean buildCharStrings, | |||
BFEntry[] cmap, Set<String> glyphNames, CustomFont font) throws IOException { | |||
gen.write("/CharStrings "); | |||
if (buildCharStrings) { | |||
if (!buildCharStrings) { | |||
gen.write(1); | |||
} else if (font.getEmbeddingMode() != EmbeddingMode.FULL) { | |||
int charCount = 1; //1 for .notdef | |||
for (BFEntry entry : cmap) { | |||
charCount += entry.getUnicodeEnd() - entry.getUnicodeStart() + 1; | |||
} | |||
gen.write(charCount); | |||
} else { | |||
gen.write(1); | |||
gen.write(font.getCMap().length); | |||
} | |||
gen.writeln(" dict dup begin"); | |||
gen.write("/"); | |||
gen.write(Glyphs.NOTDEF); | |||
gen.writeln(" 0 def"); // .notdef always has to be at index 0 | |||
if (buildCharStrings) { | |||
//Only performed in singly-byte mode, ignored for CID fonts | |||
if (!buildCharStrings) { | |||
// If we're not building the full CharStrings we can end here | |||
gen.writeln("end readonly def"); | |||
return; | |||
} | |||
if (font.getEmbeddingMode() != EmbeddingMode.FULL) { | |||
//Only performed in singly-byte mode, ignored for CID fonts | |||
for (BFEntry entry : cmap) { | |||
int glyphIndex = entry.getGlyphStartIndex(); | |||
for (int ch = entry.getUnicodeStart(); ch <= entry.getUnicodeEnd(); ch++) { | |||
char ch16 = (char)ch; //TODO Handle Unicode characters beyond 16bit | |||
String glyphName = Glyphs.charToGlyphName(ch16); | |||
if ("".equals(glyphName)) { | |||
glyphName = "u" + Integer.toHexString(ch).toUpperCase(); | |||
} | |||
gen.write("/"); | |||
gen.write(glyphName); | |||
gen.write(" "); | |||
gen.write(glyphIndex); | |||
gen.writeln(" def"); | |||
writeGlyphDefs(gen, glyphName, glyphIndex); | |||
glyphIndex++; | |||
} | |||
} | |||
} else { | |||
for (String name : glyphNames) { | |||
writeGlyphDefs(gen, name, | |||
getGlyphIndex(Glyphs.getUnicodeSequenceForGlyphName(name).charAt(0), | |||
font.getCMap())); | |||
} | |||
} | |||
gen.writeln("end readonly def"); | |||
} | |||
private static void writeGlyphDefs(PSGenerator gen, String glyphName, int glyphIndex) | |||
throws IOException { | |||
gen.write("/"); | |||
gen.write(glyphName); | |||
gen.write(" "); | |||
gen.write(glyphIndex); | |||
gen.writeln(" def"); | |||
} | |||
private static int getGlyphIndex(char c, BFEntry[] cmap) { | |||
for (BFEntry entry : cmap) { | |||
if (entry.getUnicodeStart() <= c && c <= entry.getUnicodeEnd()) { | |||
@@ -408,7 +429,7 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils { | |||
gen.writeln("] composefont pop"); | |||
} | |||
private static PSResource embedType2CIDFont(final PSGenerator gen, | |||
private static PSResource embedType2CIDFont(PSGenerator gen, | |||
MultiByteFont font, InputStream fontStream) throws IOException { | |||
assert font.getCIDType() == CIDFontType.CIDTYPE2; | |||
@@ -467,30 +488,28 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils { | |||
lineCount = 1; | |||
} | |||
} | |||
String gid = HexEncoder.encode(cid, 4); | |||
String gid; | |||
if (font.getEmbeddingMode() != EmbeddingMode.FULL) { | |||
gid = HexEncoder.encode(cid, 4); | |||
} else { | |||
gid = HexEncoder.encode(cidSubset.getGlyphIndexForSubsetIndex(cid), 4); | |||
} | |||
gen.write(gid); | |||
} | |||
gen.writeln(">] def"); | |||
FontFileReader reader = new FontFileReader(fontStream); | |||
//Create tables for subset | |||
TTFSubSetFile subset = new TTFSubSetFile(); | |||
TTFSubSetFile.GlyphHandler glyphHandler = new TTFSubSetFile.GlyphHandler() { | |||
TTFFile ttfFile; | |||
if (font.getEmbeddingMode() != EmbeddingMode.FULL) { | |||
ttfFile = new TTFSubSetFile(); | |||
ttfFile.readFont(reader, font.getTTCName(), font.getUsedGlyphs()); | |||
} else { | |||
ttfFile = new TTFFile(); | |||
ttfFile.readFont(reader, font.getTTCName()); | |||
} | |||
public void addGlyph(byte[] glyphData) throws IOException { | |||
ASCIIHexOutputStream hexOut = new ASCIIHexOutputStream(gen.getOutputStream()); | |||
gen.writeln("<"); | |||
hexOut.write(glyphData); | |||
gen.writeln(">"); | |||
} | |||
}; | |||
gen.writeln("/GlyphDirectory ["); | |||
FontFileReader reader = new FontFileReader(fontStream); | |||
byte[] subsetFont = subset.toPostScriptSubset(reader, | |||
font.getTTCName(), font.getUsedGlyphs(), glyphHandler); | |||
gen.writeln("] def"); | |||
InputStream subsetInput = new java.io.ByteArrayInputStream(subsetFont); | |||
createType42DictionaryEntries(gen, font, subsetInput, new BFEntry[0]); | |||
createType42DictionaryEntries(gen, font, new BFEntry[0], ttfFile); | |||
gen.writeln("CIDFontName currentdict end /CIDFont defineresource pop"); | |||
gen.writeln("end"); | |||
gen.writeln("%%EndResource"); | |||
@@ -702,5 +721,4 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils { | |||
gen.getResourceTracker().registerSuppliedResource(res); | |||
return res; | |||
} | |||
} |
@@ -404,8 +404,7 @@ public class PSPainter extends AbstractIFPainter { | |||
} | |||
} | |||
private void writeText( // CSOK: ParameterNumber | |||
String text, int start, int len, | |||
private void writeText(String text, int start, int len, | |||
int letterSpacing, int wordSpacing, int[] dx, | |||
Font font, Typeface tf, boolean multiByte) throws IOException { | |||
PSGenerator generator = getGenerator(); | |||
@@ -416,16 +415,6 @@ public class PSPainter extends AbstractIFPainter { | |||
boolean hasLetterSpacing = (letterSpacing != 0); | |||
boolean needTJ = false; | |||
char strOpen; | |||
char strClose; | |||
if (multiByte) { | |||
strOpen = '<'; | |||
strClose = '>'; | |||
} else { | |||
strOpen = '('; | |||
strClose = ')'; | |||
} | |||
int lineStart = 0; | |||
StringBuffer accText = new StringBuffer(initialSize); | |||
StringBuffer sb = new StringBuffer(initialSize); | |||
@@ -467,9 +456,7 @@ public class PSPainter extends AbstractIFPainter { | |||
sb.append(PSGenerator.LF); | |||
lineStart = sb.length(); | |||
} | |||
sb.append(strOpen); | |||
sb.append(accText); | |||
sb.append(strClose); | |||
lineStart = writePostScriptString(sb, accText, multiByte, lineStart); | |||
sb.append(' '); | |||
accText.setLength(0); //reset accumulated text | |||
} | |||
@@ -478,9 +465,10 @@ public class PSPainter extends AbstractIFPainter { | |||
} | |||
if (needTJ) { | |||
if (accText.length() > 0) { | |||
sb.append(strOpen); | |||
sb.append(accText); | |||
sb.append(strClose); | |||
if ((sb.length() - lineStart + accText.length()) > 200) { | |||
sb.append(PSGenerator.LF); | |||
} | |||
writePostScriptString(sb, accText, multiByte); | |||
} | |||
if (hasLetterSpacing) { | |||
sb.append("] " + formatMptAsPt(generator, letterSpacing) + " ATJ"); | |||
@@ -488,7 +476,7 @@ public class PSPainter extends AbstractIFPainter { | |||
sb.append("] TJ"); | |||
} | |||
} else { | |||
sb.append(strOpen).append(accText).append(strClose); | |||
writePostScriptString(sb, accText, multiByte); | |||
if (hasLetterSpacing) { | |||
StringBuffer spb = new StringBuffer(); | |||
spb.append(formatMptAsPt(generator, letterSpacing)) | |||
@@ -502,6 +490,32 @@ public class PSPainter extends AbstractIFPainter { | |||
generator.writeln(sb.toString()); | |||
} | |||
private void writePostScriptString(StringBuffer buffer, StringBuffer string, | |||
boolean multiByte) { | |||
writePostScriptString(buffer, string, multiByte, 0); | |||
} | |||
private int writePostScriptString(StringBuffer buffer, StringBuffer string, boolean multiByte, | |||
int lineStart) { | |||
buffer.append(multiByte ? '<' : '('); | |||
int l = string.length(); | |||
int index = 0; | |||
int maxCol = 200; | |||
buffer.append(string.substring(index, Math.min(index + maxCol, l))); | |||
index += maxCol; | |||
while (index < l) { | |||
if (!multiByte) { | |||
buffer.append('\\'); | |||
} | |||
buffer.append(PSGenerator.LF); | |||
lineStart = buffer.length(); | |||
buffer.append(string.substring(index, Math.min(index + maxCol, l))); | |||
index += maxCol; | |||
} | |||
buffer.append(multiByte ? '>' : ')'); | |||
return lineStart; | |||
} | |||
private void useFont(String key, int size) throws IOException { | |||
PSFontResource res = this.documentHandler.getPSResourceForFontKey(key); | |||
PSGenerator generator = getGenerator(); | |||
@@ -509,5 +523,4 @@ public class PSPainter extends AbstractIFPainter { | |||
res.notifyResourceUsageOnPage(generator.getResourceTracker()); | |||
} | |||
} |
@@ -0,0 +1,104 @@ | |||
/* | |||
* 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.ps.fonts; | |||
import java.io.IOException; | |||
import org.apache.xmlgraphics.ps.PSGenerator; | |||
import org.apache.xmlgraphics.util.io.ASCIIHexOutputStream; | |||
/** | |||
* This is a wrapper for {@link PSGenerator} that contains some members specific for streaming | |||
* True Type fonts to a PostScript document. | |||
*/ | |||
public class PSTTFGenerator { | |||
private PSGenerator gen; | |||
private ASCIIHexOutputStream hexOut; | |||
/** | |||
* The buffer is used to store the font file in an array of hex-encoded strings. Strings are | |||
* limited to 65535 characters, string will start with a newline, 2 characters are needed to | |||
* hex-encode each byte. | |||
*/ | |||
public static final int MAX_BUFFER_SIZE = 32764; | |||
/** | |||
* Constructor - initialises the PSGenerator in this wrapper class. | |||
* @param gen PSGenerator | |||
*/ | |||
public PSTTFGenerator(PSGenerator gen) { | |||
this.gen = gen; | |||
hexOut = new ASCIIHexOutputStream(gen.getOutputStream()); | |||
} | |||
/** | |||
* Begins writing a string by writing '<' to the begin. | |||
* @throws IOException file write exception. | |||
*/ | |||
public void startString() throws IOException { | |||
// We need to reset the streamer so that it starts a new line in the PS document | |||
hexOut = new ASCIIHexOutputStream(gen.getOutputStream()); | |||
gen.writeln("<"); | |||
} | |||
/** | |||
* Streams a string to a PostScript document (wraps PSGenerator.write(String)). | |||
* @param cmd String | |||
* @throws IOException file write exception | |||
*/ | |||
public void write(String cmd) throws IOException { | |||
gen.write(cmd); | |||
} | |||
/** | |||
* Streams a string followed by a new line char to a PostScript document (wraps | |||
* PSGenerator.writeln(String)). | |||
* @param cmd String | |||
* @throws IOException file write exception | |||
*/ | |||
public void writeln(String cmd) throws IOException { | |||
gen.writeln(cmd); | |||
} | |||
/** | |||
* Streams the bytes. | |||
* @param byteArray byte[] the byte array to stream to file. | |||
* @param offset int the starting position in the byte array to stream to file. | |||
* @param length the number of bytes to stream to file. This MUST be less than | |||
* MAX_BUFFER_SIZE - 1 since strings are suffixed by '00' (as in spec). | |||
* @throws IOException file write exception | |||
*/ | |||
public void streamBytes(byte[] byteArray, int offset, int length) throws IOException { | |||
if (length > MAX_BUFFER_SIZE) { | |||
throw new UnsupportedOperationException("Attempting to write a string to a PostScript" | |||
+ " file that is greater than the buffer size."); | |||
} | |||
hexOut.write(byteArray, offset, length); | |||
} | |||
/** | |||
* Finishes writing a string by appending '00' and '>' to the end. | |||
* @throws IOException file write exception | |||
*/ | |||
public void endString() throws IOException { | |||
/* Appends a '00' to the end of the string as specified in the spec */ | |||
gen.write("00\n> "); | |||
} | |||
} |
@@ -0,0 +1,72 @@ | |||
/* | |||
* 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.ps.fonts; | |||
import java.io.IOException; | |||
import org.apache.fop.fonts.truetype.TTFGlyphOutputStream; | |||
/** | |||
* This class streams glyphs from the "glyf" table in a True Type font. | |||
*/ | |||
public class PSTTFGlyphOutputStream implements TTFGlyphOutputStream { | |||
/** This counts the total number of bytes written that have been streamed. */ | |||
private int byteCounter = 0; | |||
/** This is a place-holder for the offset of the last string boundary. */ | |||
private int lastStringBoundary = 0; | |||
private PSTTFGenerator ttfGen; | |||
/** | |||
* Constructor | |||
* @param ttfGen PSTTFGenerator | |||
*/ | |||
public PSTTFGlyphOutputStream(PSTTFGenerator ttfGen) { | |||
this.ttfGen = ttfGen; | |||
} | |||
/** {@inheritDoc} */ | |||
public void startGlyphStream() throws IOException { | |||
ttfGen.startString(); | |||
} | |||
/** {@inheritDoc} */ | |||
public void streamGlyph(byte[] byteArray, int offset, int length) throws IOException { | |||
if (length > PSTTFGenerator.MAX_BUFFER_SIZE) { | |||
throw new UnsupportedOperationException("The glyph is " + length + " there may be an " | |||
+ "error in the font file."); | |||
} | |||
if (length + (byteCounter - lastStringBoundary) < PSTTFGenerator.MAX_BUFFER_SIZE) { | |||
ttfGen.streamBytes(byteArray, offset, length); | |||
} else { | |||
ttfGen.endString(); | |||
lastStringBoundary = byteCounter; | |||
ttfGen.startString(); | |||
ttfGen.streamBytes(byteArray, offset, length); | |||
} | |||
byteCounter += length; | |||
} | |||
/** {@inheritDoc} */ | |||
public void endGlyphStream() throws IOException { | |||
ttfGen.endString(); | |||
} | |||
} |
@@ -0,0 +1,64 @@ | |||
/* | |||
* 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.ps.fonts; | |||
import java.io.IOException; | |||
import org.apache.fop.fonts.truetype.TTFGlyphOutputStream; | |||
import org.apache.fop.fonts.truetype.TTFOutputStream; | |||
import org.apache.fop.fonts.truetype.TTFTableOutputStream; | |||
import org.apache.xmlgraphics.ps.PSGenerator; | |||
/** | |||
* Implements TTFOutputStream and streams font tables to a PostScript file. | |||
*/ | |||
public class PSTTFOutputStream implements TTFOutputStream { | |||
/** The wrapper class for PSGenerator */ | |||
private final PSTTFGenerator ttfGen; | |||
/** | |||
* Constructor - assigns a PSGenerator to stream the font. | |||
* @param gen PSGenerator. | |||
*/ | |||
public PSTTFOutputStream(PSGenerator gen) { | |||
this.ttfGen = new PSTTFGenerator(gen); | |||
} | |||
/** {@inheritDoc} */ | |||
public void startFontStream() throws IOException { | |||
ttfGen.write("/sfnts["); | |||
} | |||
/** {@inheritDoc} */ | |||
public TTFTableOutputStream getTableOutputStream() { | |||
return new PSTTFTableOutputStream(ttfGen); | |||
} | |||
/** {@inheritDoc} */ | |||
public TTFGlyphOutputStream getGlyphOutputStream() { | |||
return new PSTTFGlyphOutputStream(ttfGen); | |||
} | |||
/** {@inheritDoc} */ | |||
public void endFontStream() throws IOException { | |||
ttfGen.writeln("] def"); | |||
} | |||
} |
@@ -0,0 +1,58 @@ | |||
/* | |||
* 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.ps.fonts; | |||
import java.io.IOException; | |||
import org.apache.fop.fonts.truetype.TTFTableOutputStream; | |||
/** | |||
* This class streams a truetype table to a PostScript file. | |||
* | |||
*/ | |||
public class PSTTFTableOutputStream implements TTFTableOutputStream { | |||
private PSTTFGenerator ttfGen; | |||
/** | |||
* Constructor. | |||
* @param ttfGen PSGenerator the streamer class used for streaming bytes. | |||
*/ | |||
public PSTTFTableOutputStream(PSTTFGenerator ttfGen) { | |||
this.ttfGen = ttfGen; | |||
} | |||
/** {@inheritDoc} */ | |||
public void streamTable(byte[] byteArray, int offset, int length) throws IOException { | |||
int offsetPosition = offset; | |||
// Need to split the table into MAX_BUFFER_SIZE chunks | |||
for (int i = 0; i < length / PSTTFGenerator.MAX_BUFFER_SIZE; i++) { | |||
streamString(byteArray, offsetPosition, PSTTFGenerator.MAX_BUFFER_SIZE); | |||
offsetPosition += PSTTFGenerator.MAX_BUFFER_SIZE; | |||
} | |||
if (length % PSTTFGenerator.MAX_BUFFER_SIZE > 0) { | |||
streamString(byteArray, offsetPosition, length % PSTTFGenerator.MAX_BUFFER_SIZE); | |||
} | |||
} | |||
private void streamString(byte[] byteArray, int offset, int length) throws IOException { | |||
ttfGen.startString(); | |||
ttfGen.streamBytes(byteArray, offset, length); | |||
ttfGen.endString(); | |||
} | |||
} |
@@ -33,13 +33,13 @@ public class DejaVuLGCSerifTest extends TestCase { | |||
/** | |||
* sets up the testcase by loading the DejaVu Font. | |||
* | |||
* | |||
* @throws Exception | |||
* if the test fails. | |||
*/ | |||
public void setUp() throws Exception { | |||
File file = new File("test/resources/fonts/DejaVuLGCSerif.ttf"); | |||
font = FontLoader.loadFont(file, "", true, EncodingMode.AUTO, | |||
font = FontLoader.loadFont(file, "", true, EmbeddingMode.AUTO, EncodingMode.AUTO, | |||
fontResolver); | |||
} | |||
@@ -21,16 +21,33 @@ package org.apache.fop.fonts; | |||
import junit.framework.TestCase; | |||
/** | |||
* Tests the enum org.apache.fop.fonts.EncodingMode. | |||
*/ | |||
public class EncodingModeTest extends TestCase { | |||
/** | |||
* Test getName() - tests the getName() method returns the expected String. | |||
*/ | |||
public void testGetName() { | |||
assertEquals("auto", EncodingMode.AUTO.getName()); | |||
assertEquals("single-byte", EncodingMode.SINGLE_BYTE.getName()); | |||
assertEquals("cid", EncodingMode.CID.getName()); | |||
} | |||
/** | |||
* Test getValue() - test that getValue() method returns the expected enum value when given | |||
* an appropriate String. | |||
*/ | |||
public void testGetValue() { | |||
assertEquals(EncodingMode.AUTO, EncodingMode.getEncodingMode("auto")); | |||
assertEquals(EncodingMode.SINGLE_BYTE, EncodingMode.getEncodingMode("single-byte")); | |||
assertEquals(EncodingMode.CID, EncodingMode.getEncodingMode("cid")); | |||
assertEquals(EncodingMode.AUTO, EncodingMode.getValue("auto")); | |||
assertEquals(EncodingMode.SINGLE_BYTE, EncodingMode.getValue("single-byte")); | |||
assertEquals(EncodingMode.CID, EncodingMode.getValue("cid")); | |||
try { | |||
// We expect this to fail | |||
assertEquals(EncodingMode.AUTO, EncodingMode.getValue("fail")); | |||
fail("Encoding mode fails to throw an appropriate exception"); | |||
} catch (IllegalArgumentException e) { | |||
// PASS | |||
} | |||
} | |||
} |
@@ -0,0 +1,54 @@ | |||
/* | |||
* 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.fonts; | |||
import junit.framework.Test; | |||
import junit.framework.TestSuite; | |||
import org.apache.fop.fonts.truetype.FontFileReaderTest; | |||
import org.apache.fop.fonts.truetype.TTFFileTest; | |||
import org.apache.fop.fonts.truetype.TTFSubSetFileTest; | |||
import org.apache.fop.fonts.truetype.TTFTableNameTest; | |||
/** | |||
* A test suite designed for org.apache.fop.fonts.* | |||
*/ | |||
public final class FOPFontsTestSuite { | |||
/** | |||
* Constructor | |||
*/ | |||
private FOPFontsTestSuite() { | |||
} | |||
/** | |||
* Testing org.apache.fop.fonts.* | |||
* @return test | |||
*/ | |||
public static Test suite() { | |||
TestSuite testSuite = new TestSuite("Test suite for FOPs fonts classes"); | |||
//$JUnit-BEGIN$ | |||
testSuite.addTest(new TestSuite(EncodingModeTest.class)); | |||
testSuite.addTest(new TestSuite(FontFileReaderTest.class)); | |||
testSuite.addTest(new TestSuite(TTFFileTest.class)); | |||
testSuite.addTest(new TestSuite(TTFSubSetFileTest.class)); | |||
testSuite.addTest(new TestSuite(TTFTableNameTest.class)); | |||
//$JUnit-END$ | |||
return testSuite; | |||
} | |||
} |
@@ -0,0 +1,283 @@ | |||
/* | |||
* 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.fonts.truetype; | |||
import java.io.ByteArrayInputStream; | |||
import java.io.EOFException; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.util.Arrays; | |||
import junit.framework.TestCase; | |||
/** | |||
* A test class for org.apache.fop.truetype.FontFileReader | |||
*/ | |||
public class FontFileReaderTest extends TestCase { | |||
private FontFileReader fontReader; | |||
private final InputStream in; | |||
private final byte[] byteArray; | |||
/** | |||
* Constructor - initialises an array that only needs to be created once. It creates a byte[] | |||
* of form { 0x00, 0x01, 0x02, 0x03..., 0xff}; | |||
*/ | |||
public FontFileReaderTest() { | |||
byteArray = new byte[256]; | |||
for (int i = 0; i < 256; i++) { | |||
byteArray[i] = (byte) i; | |||
} | |||
in = new ByteArrayInputStream(byteArray); | |||
} | |||
/** | |||
* sets up the test subject object for testing. | |||
*/ | |||
public void setUp() { | |||
try { | |||
fontReader = new FontFileReader(in); | |||
} catch (Exception e) { | |||
fail("Error: " + e.getMessage()); | |||
} | |||
} | |||
/** | |||
* the "destructor" method. | |||
* | |||
*/ | |||
public void tearDown() { | |||
fontReader = null; | |||
} | |||
/** | |||
* Test readTTFByte() | |||
* @throws IOException exception | |||
*/ | |||
public void testReadTTFByte() throws IOException { | |||
for (int i = 0; i < 256; i++) { | |||
assertEquals((byte) i, fontReader.readTTFByte()); | |||
} | |||
} | |||
/** | |||
* Test seekSet() - check that it moves to the correct position and enforce a failure case. | |||
* @throws IOException exception | |||
*/ | |||
public void testSeekSet() throws IOException { | |||
fontReader.seekSet(10); | |||
assertEquals(10, fontReader.readTTFByte()); | |||
try { | |||
fontReader.seekSet(257); | |||
fail("FileFontReaderTest Failed testSeekSet"); | |||
} catch (IOException e) { | |||
// Passed | |||
} | |||
} | |||
/** | |||
* Test skip() - check that it moves to the correct position and enforce a failure case. | |||
* @throws IOException exception | |||
*/ | |||
public void testSkip() throws IOException { | |||
fontReader.skip(100); | |||
assertEquals(100, fontReader.readTTFByte()); | |||
try { | |||
// 100 (seekAdd) + 1 (read() = 1 byte) + 156 = 257 | |||
fontReader.skip(156); | |||
fail("FileFontReaderTest Failed testSkip"); | |||
} catch (IOException e) { | |||
// Passed | |||
} | |||
} | |||
/** | |||
* Test getCurrentPos() - 3 checks: | |||
* 1) test with seekSet(int) | |||
* 2) test with skip(int) | |||
* 3) test with a readTTFByte() (this moves the position by the size of the data being read) | |||
* @throws IOException exception | |||
*/ | |||
public void testGetCurrentPos() throws IOException { | |||
fontReader.seekSet(10); | |||
fontReader.skip(100); | |||
assertEquals(110, fontReader.getCurrentPos()); | |||
fontReader.readTTFByte(); | |||
assertEquals(111, fontReader.getCurrentPos()); | |||
} | |||
/** | |||
* Test getFileSize() | |||
*/ | |||
public void testGetFileSize() { | |||
assertEquals(256, fontReader.getFileSize()); | |||
} | |||
/** | |||
* Test readTTFUByte() | |||
* @throws IOException exception | |||
*/ | |||
public void testReadTTFUByte() throws IOException { | |||
for (int i = 0; i < 256; i++) { | |||
assertEquals(i, fontReader.readTTFUByte()); | |||
} | |||
} | |||
/** | |||
* Test readTTFShort() - Test positive and negative numbers (two's compliment). | |||
* @throws IOException exception | |||
*/ | |||
public void testReadTTFShort() throws IOException { | |||
// 0x0001 = 1 | |||
assertEquals("Should have been 1 (0x0001)", 1, fontReader.readTTFShort()); | |||
// 0x0203 = 515 | |||
assertEquals(515, fontReader.readTTFShort()); | |||
// now test negative numbers | |||
fontReader.seekSet(250); | |||
// 0xfafb | |||
assertEquals(-1285, fontReader.readTTFShort()); | |||
} | |||
/** | |||
* Test readTTFUShort() - Test positive and potentially negative numbers (two's compliment). | |||
* @throws IOException exception | |||
*/ | |||
public void testReadTTFUShort() throws IOException { | |||
// 0x0001 | |||
assertEquals(1, fontReader.readTTFUShort()); | |||
// 0x0203 | |||
assertEquals(515, fontReader.readTTFUShort()); | |||
// test potential negatives | |||
fontReader.seekSet(250); | |||
// 0xfafb | |||
assertEquals((250 << 8) + 251, fontReader.readTTFUShort()); | |||
} | |||
/** | |||
* Test readTTFShort(int) - test reading ahead of current position and behind current position | |||
* and in both cases ensure that our current position isn't changed. | |||
* @throws IOException exception | |||
*/ | |||
public void testReadTTFShortWithArg() throws IOException { | |||
// 0x6465 | |||
assertEquals(25701, fontReader.readTTFShort(100)); | |||
assertEquals(0, fontReader.getCurrentPos()); | |||
// read behind current position (and negative) | |||
fontReader.seekSet(255); | |||
// 0xfafb | |||
assertEquals(-1285, fontReader.readTTFShort(250)); | |||
assertEquals(255, fontReader.getCurrentPos()); | |||
} | |||
/** | |||
* Test readTTFUShort(int arg) - test reading ahead of current position and behind current | |||
* position and in both cases ensure that our current position isn't changed. | |||
* @throws IOException exception | |||
*/ | |||
public void testReadTTFUShortWithArg() throws IOException { | |||
// 0x6465 | |||
assertEquals(25701, fontReader.readTTFUShort(100)); | |||
assertEquals(0, fontReader.getCurrentPos()); | |||
// read behind current position (and potential negative) | |||
fontReader.seekSet(255); | |||
// 0xfafb | |||
assertEquals(64251, fontReader.readTTFUShort(250)); | |||
assertEquals(255, fontReader.getCurrentPos()); | |||
} | |||
/** | |||
* Test readTTFLong() | |||
* @throws IOException exception | |||
*/ | |||
public void testReadTTFLong() throws IOException { | |||
// 0x00010203 | |||
assertEquals(66051, fontReader.readTTFLong()); | |||
// test negative numbers | |||
fontReader.seekSet(250); | |||
// 0xf0f1f2f3 | |||
assertEquals(-84148995, fontReader.readTTFLong()); | |||
} | |||
/** | |||
* Test readTTFULong() | |||
* @throws IOException exception | |||
*/ | |||
public void testReadTTFULong() throws IOException { | |||
// 0x00010203 | |||
assertEquals(66051, fontReader.readTTFULong()); | |||
// test negative numbers | |||
fontReader.seekSet(250); | |||
// 0xfafbfcfd | |||
assertEquals(4210818301L, fontReader.readTTFULong()); | |||
} | |||
/** | |||
* Test readTTFString() - there are two paths to test here: | |||
* 1) A null terminated string | |||
* 2) A string not terminated with a null (we expect this to throw an EOFException) | |||
* @throws IOException exception | |||
*/ | |||
public void testReadTTFString() throws IOException { | |||
byte[] strByte = {(byte)'t', (byte)'e', (byte)'s', (byte)'t', 0x00}; | |||
fontReader = new FontFileReader(new ByteArrayInputStream(strByte)); | |||
assertEquals("test", fontReader.readTTFString()); | |||
try { | |||
// not NUL terminated | |||
byte[] strByteNoNull = {(byte)'t', (byte)'e', (byte)'s', (byte)'t'}; | |||
fontReader = new FontFileReader(new ByteArrayInputStream(strByteNoNull)); | |||
assertEquals("test", fontReader.readTTFString()); | |||
fail("FontFileReaderTest testReadTTFString Fails."); | |||
} catch (EOFException e) { | |||
// Pass | |||
} | |||
} | |||
/** | |||
* Test readTTFString(int arg) | |||
* @throws IOException exception | |||
*/ | |||
public void testReadTTFStringIntArg() throws IOException { | |||
byte[] strByte = {(byte)'t', (byte)'e', (byte)'s', (byte)'t'}; | |||
fontReader = new FontFileReader(new ByteArrayInputStream(strByte)); | |||
assertEquals("test", fontReader.readTTFString(4)); | |||
try { | |||
fontReader = new FontFileReader(new ByteArrayInputStream(strByte)); | |||
assertEquals("test", fontReader.readTTFString(5)); | |||
fail("FontFileReaderTest testReadTTFStringIntArg Fails."); | |||
} catch (EOFException e) { | |||
// Pass | |||
} | |||
} | |||
/** | |||
* Test readTTFString(int arg1, int arg2) | |||
*/ | |||
public void testReadTTFString2IntArgs() { | |||
// currently the same as above | |||
} | |||
/** | |||
* Test getBytes() | |||
* @throws IOException exception | |||
*/ | |||
public void testGetBytes() throws IOException { | |||
byte[] retrievedBytes = fontReader.getBytes(0, 256); | |||
assertTrue(Arrays.equals(byteArray, retrievedBytes)); | |||
} | |||
} |
@@ -0,0 +1,399 @@ | |||
/* | |||
* 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.fonts.truetype; | |||
import java.io.IOException; | |||
import java.util.Map; | |||
import junit.framework.TestCase; | |||
import org.apache.fop.fonts.truetype.TTFFile.PostScriptVersion; | |||
/** | |||
* Class for testing org.apache.fop.fonts.truetype.TTFFile | |||
*/ | |||
public class TTFFileTest extends TestCase { | |||
// We only want to initialize the FontFileReader once (for performance reasons) | |||
/** The truetype font file (DejaVuLGCSerif) */ | |||
protected final TTFFile dejavuTTFFile; | |||
/** The FontFileReader for ttfFile (DejaVuLGCSerif) */ | |||
protected final FontFileReader dejavuReader; | |||
/** The truetype font file (DroidSansMono) */ | |||
protected final TTFFile droidmonoTTFFile; | |||
/** The FontFileReader for ttfFile (DroidSansMono) */ | |||
protected final FontFileReader droidmonoReader; | |||
/** | |||
* Constructor initialises FileFontReader to | |||
* @throws IOException exception | |||
*/ | |||
public TTFFileTest() throws IOException { | |||
dejavuTTFFile = new TTFFile(); | |||
dejavuReader = new FontFileReader("test/resources/fonts/DejaVuLGCSerif.ttf"); | |||
dejavuTTFFile.readFont(dejavuReader); | |||
droidmonoTTFFile = new TTFFile(); | |||
droidmonoReader = new FontFileReader("test/resources/fonts/DroidSansMono.ttf"); | |||
droidmonoTTFFile.readFont(droidmonoReader); | |||
} | |||
/** | |||
* Test convertTTFUnit2PDFUnit() - The units per em retrieved reading the HEAD table from | |||
* the font file. (DroidSansMono has the same units per em as DejaVu so no point testing it) | |||
*/ | |||
public void testConvertTTFUnit2PDFUnit() { | |||
// DejaVu has 2048 units per em (PDF works in millipts, thus the 1000) | |||
// test rational number | |||
assertEquals(1000, dejavuTTFFile.convertTTFUnit2PDFUnit(2048)); | |||
// test smallest case, this should = 0.488 (round down to 0) | |||
assertEquals(0, dejavuTTFFile.convertTTFUnit2PDFUnit(1)); | |||
// this should round up, but since it's millipts... | |||
assertEquals(0, dejavuTTFFile.convertTTFUnit2PDFUnit(2)); | |||
// ensure behaviour is the same for negative numbers | |||
assertEquals(0, dejavuTTFFile.convertTTFUnit2PDFUnit(-0)); | |||
assertEquals(-1000, dejavuTTFFile.convertTTFUnit2PDFUnit(-2048)); | |||
assertEquals(0, dejavuTTFFile.convertTTFUnit2PDFUnit(-1)); | |||
assertEquals(0, dejavuTTFFile.convertTTFUnit2PDFUnit(-2)); | |||
} | |||
/** | |||
* Test checkTTC() | |||
* @throws IOException exception | |||
*/ | |||
public void testCheckTTC() throws IOException { | |||
// DejaVu is not a TTC, thus this returns true | |||
assertTrue(dejavuTTFFile.checkTTC("")); | |||
assertTrue(droidmonoTTFFile.checkTTC("")); | |||
/* | |||
* Cannot reasonably test the rest of this method without an actual truetype collection | |||
* because all methods in FontFileReader are "final" and thus mocking isn't possible. | |||
*/ | |||
} | |||
/** | |||
* Test getAnsiKerning() - Tests values retrieved from the kern table in the font file. | |||
*/ | |||
public void testGetAnsiKerning() { | |||
Map<Integer, Map<Integer, Integer>> ansiKerning = dejavuTTFFile.getKerning(); | |||
if (ansiKerning.isEmpty()) { | |||
fail(); | |||
} | |||
Integer k1 = ansiKerning.get(Integer.valueOf('A')).get( | |||
Integer.valueOf('T')); | |||
assertEquals(dejavuTTFFile.convertTTFUnit2PDFUnit(-112), k1.intValue()); | |||
Integer k2 = ansiKerning.get(Integer.valueOf('Y')).get(Integer.valueOf('u')); | |||
assertEquals(dejavuTTFFile.convertTTFUnit2PDFUnit(-178), k2.intValue()); | |||
// DroidSansMono doens't have kerning (it's mono-spaced) | |||
ansiKerning = droidmonoTTFFile.getAnsiKerning(); | |||
if (!ansiKerning.isEmpty()) { | |||
fail("DroidSansMono shouldn't have any kerning data."); | |||
} | |||
} | |||
/** | |||
* Test getCapHeight - there are several paths to test: | |||
* 1) The PCLT table (if present) | |||
* 2) The yMax (3rd) value, for the bounding box, for 'H' in the glyf table. | |||
* if not the above: | |||
* 3) The caps height in the OS/2 table | |||
* Tests values retrieved from analysing the font file. | |||
*/ | |||
public void testGetCapHeight() { | |||
// DejaVu doesn't have the PCLT table and so these have to be guessed | |||
// The height is approximated to be the height of the "H" which for | |||
// Deja = 1493 TTFunits | |||
assertEquals(dejavuTTFFile.convertTTFUnit2PDFUnit(1493), dejavuTTFFile.getCapHeight()); | |||
// DroidSansMono doesn't have a PCLT table either | |||
// height of "H" = 1462 | |||
assertEquals(droidmonoTTFFile.convertTTFUnit2PDFUnit(1462), | |||
droidmonoTTFFile.getCapHeight()); | |||
} | |||
/** | |||
* Test getCharSetName() - check that it returns "WinAnsiEncoding". | |||
*/ | |||
public void testGetCharSetName() { | |||
assertTrue("WinAnsiEncoding".equals(dejavuTTFFile.getCharSetName())); | |||
assertTrue("WinAnsiEncoding".equals(droidmonoTTFFile.getCharSetName())); | |||
} | |||
/** | |||
* Test getCharWidth() - Test values retrieved from the metrics in the glyf table in | |||
* the font file. | |||
*/ | |||
public void testGetCharWidth() { | |||
// Arbitrarily test a few values: | |||
// The width of "H" (Unicode index 0x0048) is 1786 | |||
assertEquals(dejavuTTFFile.convertTTFUnit2PDFUnit(1786), dejavuTTFFile.getCharWidth(0x48)); | |||
// The width of "i" (unicode index 0x0069) is 655 TTFunits | |||
assertEquals(dejavuTTFFile.convertTTFUnit2PDFUnit(655), dejavuTTFFile.getCharWidth(0x69)); | |||
// final check, "!" (unicode index 0x0021) is 823 TTFunits | |||
assertEquals(dejavuTTFFile.convertTTFUnit2PDFUnit(823), dejavuTTFFile.getCharWidth(0x21)); | |||
// All the glyphs should be the same width in DroidSansMono (mono-spaced) | |||
int charWidth = droidmonoTTFFile.convertTTFUnit2PDFUnit(1229); | |||
for (int i = 0; i < 255; i++) { | |||
assertEquals(charWidth, droidmonoTTFFile.getCharWidth(i)); | |||
} | |||
} | |||
/** | |||
* TODO: add implementation to this test | |||
*/ | |||
public void testGetCMaps() { | |||
} | |||
/** | |||
* Test getFamilyNames() - Test value retrieved from the name table in the font file. | |||
*/ | |||
public void testGetFamilyNames() { | |||
assertEquals(1, dejavuTTFFile.getFamilyNames().size()); | |||
for (String name : dejavuTTFFile.getFamilyNames()) { | |||
assertEquals("DejaVu LGC Serif", name); | |||
} | |||
assertEquals(1, droidmonoTTFFile.getFamilyNames().size()); | |||
for (String name : droidmonoTTFFile.getFamilyNames()) { | |||
assertEquals("Droid Sans Mono", name); | |||
} | |||
} | |||
/** | |||
* Test getFirstChar() - TODO: implement a more intelligent test here. | |||
*/ | |||
public void testGetFirstChar() { | |||
// Not really sure how to test this intelligently | |||
assertEquals(0, dejavuTTFFile.getFirstChar()); | |||
assertEquals(0, droidmonoTTFFile.getFirstChar()); | |||
} | |||
/** | |||
* Test getFlags() - Test values retrieved from the POST table in the font file. | |||
*/ | |||
public void testGetFlags() { | |||
/* DejaVu flags are: | |||
* italic angle = 0 | |||
* fixed pitch = 0 | |||
* has serifs = true (default value; this font doesn't have a PCLT table) | |||
*/ | |||
int flags = dejavuTTFFile.getFlags(); | |||
assertEquals(0, flags & 64); // Italics angle = 0 | |||
assertEquals(32, flags & 32); // Adobe standard charset | |||
assertEquals(0, flags & 2); // fixed pitch = 0 | |||
assertEquals(1, flags & 1); // has serifs = 1 (true) | |||
/* | |||
* Droid flags are: | |||
* italic angle = 0 | |||
* fixed pitch = 1 | |||
* has serifs = true (default value; this font doesn't have a PCLT table) | |||
*/ | |||
flags = droidmonoTTFFile.getFlags(); | |||
assertEquals(0, flags & 64); | |||
assertEquals(32, flags & 32); | |||
assertEquals(2, flags & 2); | |||
assertEquals(1, flags & 1); | |||
} | |||
/** | |||
* Test getFontBBox() - Test values retrieved from values in the HEAD table in the font file. | |||
*/ | |||
public void testGetFontBBox() { | |||
int[] bBox = dejavuTTFFile.getFontBBox(); | |||
/* | |||
* The head table has the following values(DejaVu): | |||
* xmin = -1576, ymin = -710, xmax = 3439, ymax = 2544 | |||
*/ | |||
assertEquals(dejavuTTFFile.convertTTFUnit2PDFUnit(-1576), bBox[0]); | |||
assertEquals(dejavuTTFFile.convertTTFUnit2PDFUnit(-710), bBox[1]); | |||
assertEquals(dejavuTTFFile.convertTTFUnit2PDFUnit(3439), bBox[2]); | |||
assertEquals(dejavuTTFFile.convertTTFUnit2PDFUnit(2544), bBox[3]); | |||
/* | |||
* The head table has the following values (DroidSansMono): | |||
* xmin = -312, ymin= -555, xmax = 1315, ymax = 2163 | |||
*/ | |||
bBox = droidmonoTTFFile.getFontBBox(); | |||
assertEquals(droidmonoTTFFile.convertTTFUnit2PDFUnit(-312), bBox[0]); | |||
assertEquals(droidmonoTTFFile.convertTTFUnit2PDFUnit(-555), bBox[1]); | |||
assertEquals(droidmonoTTFFile.convertTTFUnit2PDFUnit(1315), bBox[2]); | |||
assertEquals(droidmonoTTFFile.convertTTFUnit2PDFUnit(2163), bBox[3]); | |||
} | |||
/** | |||
* Test getFullName() - Test value retrieved from the name table in the font file. | |||
*/ | |||
public void testGetFullName() { | |||
assertEquals("DejaVu LGC Serif", dejavuTTFFile.getFullName()); | |||
assertEquals("Droid Sans Mono", droidmonoTTFFile.getFullName()); | |||
} | |||
/** | |||
* Test getGlyphName - Test value retrieved from the POST table in the font file. | |||
*/ | |||
public void testGetGlyphName() { | |||
assertEquals("H", dejavuTTFFile.getGlyphName(43)); | |||
assertEquals("H", droidmonoTTFFile.getGlyphName(43)); | |||
} | |||
/** | |||
* Test getItalicAngle() - Test value retrieved from the POST table in the font file. | |||
*/ | |||
public void testGetItalicAngle() { | |||
assertEquals("0", dejavuTTFFile.getItalicAngle()); | |||
assertEquals("0", droidmonoTTFFile.getItalicAngle()); | |||
} | |||
/** | |||
* Test getKerning() - Test values retrieved from the kern table in the font file. | |||
*/ | |||
public void testGetKerning() { | |||
Map<Integer, Map<Integer, Integer>> kerning = dejavuTTFFile.getKerning(); | |||
if (kerning.isEmpty()) { | |||
fail(); | |||
} | |||
Integer k1 = kerning.get(Integer.valueOf('A')).get(Integer.valueOf('T')); | |||
assertEquals(dejavuTTFFile.convertTTFUnit2PDFUnit(-112), k1.intValue()); | |||
Integer k2 = kerning.get(Integer.valueOf('K')).get(Integer.valueOf('u')); | |||
assertEquals(dejavuTTFFile.convertTTFUnit2PDFUnit(-45), k2.intValue()); | |||
// DroidSansMono has no kerning data (mono-spaced) | |||
kerning = droidmonoTTFFile.getKerning(); | |||
if (!kerning.isEmpty()) { | |||
fail("DroidSansMono shouldn't have any kerning data"); | |||
} | |||
} | |||
/** | |||
* Test lastChar() - TODO: implement a more intelligent test | |||
*/ | |||
public void testLastChar() { | |||
assertEquals(0xff, dejavuTTFFile.getLastChar()); | |||
assertEquals(0xff, droidmonoTTFFile.getLastChar()); | |||
} | |||
/** | |||
* Test getLowerCaseAscent() - There are several paths to test: | |||
* 1) The values in the HHEA table (see code) | |||
* 2) Fall back to values from the OS/2 table | |||
* Test values retrieved from the font file. | |||
*/ | |||
public void testGetLowerCaseAscent() { | |||
assertEquals(dejavuTTFFile.convertTTFUnit2PDFUnit(1556), | |||
dejavuTTFFile.getLowerCaseAscent()); | |||
// Curiously the same value | |||
assertEquals(droidmonoTTFFile.convertTTFUnit2PDFUnit(1556), | |||
droidmonoTTFFile.getLowerCaseAscent()); | |||
} | |||
/** | |||
* Test getPostScriptName() - Test values retrieved from the post table in the font file. | |||
*/ | |||
public void testGetPostScriptName() { | |||
assertEquals(PostScriptVersion.V2, dejavuTTFFile.getPostScriptVersion()); | |||
assertEquals(PostScriptVersion.V2, droidmonoTTFFile.getPostScriptVersion()); | |||
} | |||
/** | |||
* Test getStemV() - Undefined. | |||
*/ | |||
public void testGetStemV() { | |||
// Undefined | |||
assertEquals("0", dejavuTTFFile.getStemV()); | |||
assertEquals("0", droidmonoTTFFile.getStemV()); | |||
} | |||
/** | |||
* Test getSubFamilyName() - Test values retrieved from the name table in the font file. | |||
*/ | |||
public void testGetSubFamilyName() { | |||
assertEquals("Book", dejavuTTFFile.getSubFamilyName()); | |||
assertEquals("Regular", droidmonoTTFFile.getSubFamilyName()); | |||
} | |||
/** | |||
* Test getTTCnames() - TODO: add implementation with TTC font. | |||
*/ | |||
public void testGetTTCnames() { | |||
// Can't test with with DejaVu since it's not a TrueType Collection | |||
} | |||
/** | |||
* Test getWeightClass() - Test value retrieved from the OS/2 table in the font file. | |||
*/ | |||
public void testGetWeightClass() { | |||
// Retrieved from OS/2 table | |||
assertEquals(400, dejavuTTFFile.getWeightClass()); | |||
assertEquals(400, droidmonoTTFFile.getWeightClass()); | |||
} | |||
/** | |||
* Test getWidths() - Test values retrieved from the hmtx table in the font file. | |||
*/ | |||
public void testGetWidths() { | |||
int[] widths = dejavuTTFFile.getWidths(); | |||
// using the width of 'A' index = 36 | |||
assertEquals(dejavuTTFFile.convertTTFUnit2PDFUnit(1479), widths[36]); | |||
// using the width of '|' index = 95 | |||
assertEquals(dejavuTTFFile.convertTTFUnit2PDFUnit(690), widths[95]); | |||
widths = droidmonoTTFFile.getWidths(); | |||
// DroidSansMono should have all widths the same size (mono-spaced) | |||
int width = droidmonoTTFFile.convertTTFUnit2PDFUnit(1229); | |||
for (int i = 0; i < 255; i++) { | |||
assertEquals(width, widths[i]); | |||
} | |||
} | |||
/** | |||
* Test getXHeight() - There are several paths to test: | |||
* 1) The PCLT table (if available) | |||
* 2) The yMax for the bounding box for 'x' in the glyf table. | |||
* Fall back: | |||
* 3) The xheight in the OS/2 table. | |||
*/ | |||
public void testGetXHeight() { | |||
// Since there's no PCLT table, the height of 'x' is used for both DejaVu and DroidSansMono | |||
assertEquals(dejavuTTFFile.convertTTFUnit2PDFUnit(1064), dejavuTTFFile.getXHeight()); | |||
assertEquals(droidmonoTTFFile.convertTTFUnit2PDFUnit(1098), droidmonoTTFFile.getXHeight()); | |||
} | |||
/** | |||
* Test isCFF() - TODO: add test for a CFF font. | |||
*/ | |||
public void testIsCFF() { | |||
// Neither DejaVu nor DroidSansMono are a compact format font | |||
assertEquals(false, dejavuTTFFile.isCFF()); | |||
assertEquals(false, droidmonoTTFFile.isCFF()); | |||
} | |||
/** | |||
* Test isEmbeddable() - Test value retrieved from the OS/2 table in the font file. | |||
*/ | |||
public void testIsEmbeddable() { | |||
// Dejavu and DroidSansMono are both embeddable | |||
assertEquals(true, dejavuTTFFile.isEmbeddable()); | |||
assertEquals(true, droidmonoTTFFile.isEmbeddable()); | |||
} | |||
/** | |||
* Test readFont() - Add implementation if necessary. | |||
*/ | |||
public void testReadFont() { | |||
// I'm pretty sure we've tested this with all the other tests | |||
} | |||
} |
@@ -0,0 +1,69 @@ | |||
/* | |||
* 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.fonts.truetype; | |||
import java.io.ByteArrayInputStream; | |||
import java.io.IOException; | |||
import java.util.HashMap; | |||
import java.util.Map; | |||
/** | |||
* This class tests TTFSubSetFile | |||
* TODO: Test with more than just a single font | |||
*/ | |||
public class TTFSubSetFileTest extends TTFFileTest { | |||
private TTFSubSetFile ttfSubset; | |||
private byte[] subset; | |||
/** | |||
* Constructor | |||
* @throws IOException exception | |||
*/ | |||
public TTFSubSetFileTest() throws IOException { | |||
super(); | |||
} | |||
/** | |||
* setUp() | |||
* @exception IOException file read error | |||
*/ | |||
public void setUp() throws IOException { | |||
ttfSubset = new TTFSubSetFile(); | |||
Map<Integer, Integer> glyphs = new HashMap<Integer, Integer>(); | |||
for (int i = 0; i < 255; i++) { | |||
glyphs.put(i, i); | |||
} | |||
ttfSubset.readFont(dejavuReader, "DejaVu", glyphs); | |||
subset = ttfSubset.getFontSubset(); | |||
} | |||
/** | |||
* Test readFont(FontFileReader, String, Map) - Reads the font and tests the output by injecting | |||
* it into a TTFFile object to check the validity of the file as a font. This currently doesn't | |||
* create a cmap table, and so the font doesn't contain ALL of the mandatory tables. | |||
* @throws IOException exception | |||
*/ | |||
public void testReadFont3Args() throws IOException { | |||
ByteArrayInputStream byteArray = new ByteArrayInputStream(subset); | |||
dejavuTTFFile.readFont(new FontFileReader(byteArray)); | |||
// Test a couple arbitrary values | |||
assertEquals(dejavuTTFFile.convertTTFUnit2PDFUnit(-1576), dejavuTTFFile.getFontBBox()[0]); | |||
assertEquals(dejavuTTFFile.getFullName(), "DejaVu LGC Serif"); | |||
} | |||
} |
@@ -0,0 +1,145 @@ | |||
/* | |||
* 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.fonts.truetype; | |||
import junit.framework.TestCase; | |||
/** | |||
* This class tests the enum org.apache.fop.fonts.truetype.TTFTableName | |||
* | |||
*/ | |||
public class TTFTableNameTest extends TestCase { | |||
/** | |||
* Test getName() - tests that the getName() method returns the expected String as expected in | |||
* the Directory Table. | |||
* @exception IllegalAccessException error | |||
*/ | |||
public void testGetName() throws IllegalAccessException { | |||
assertEquals("dirTable", TTFTableName.DIRECTORY_TABLE.getName()); | |||
assertEquals("EBDT", TTFTableName.EBDT.getName()); | |||
assertEquals("EBLC", TTFTableName.EBLC.getName()); | |||
assertEquals("EBSC", TTFTableName.EBSC.getName()); | |||
assertEquals("FFTM", TTFTableName.FFTM.getName()); | |||
assertEquals("GDEF", TTFTableName.GDEF.getName()); | |||
assertEquals("GPOS", TTFTableName.GPOS.getName()); | |||
assertEquals("GSUB", TTFTableName.GSUB.getName()); | |||
assertEquals("LTSH", TTFTableName.LTSH.getName()); | |||
assertEquals("OS/2", TTFTableName.OS2.getName()); | |||
assertEquals("PCLT", TTFTableName.PCLT.getName()); | |||
assertEquals("VDMX", TTFTableName.VDMX.getName()); | |||
assertEquals("cmap", TTFTableName.CMAP.getName()); | |||
assertEquals("cvt ", TTFTableName.CVT.getName()); | |||
assertEquals("fpgm", TTFTableName.FPGM.getName()); | |||
assertEquals("gasp", TTFTableName.GASP.getName()); | |||
assertEquals("glyf", TTFTableName.GLYF.getName()); | |||
assertEquals("hdmx", TTFTableName.HDMX.getName()); | |||
assertEquals("head", TTFTableName.HEAD.getName()); | |||
assertEquals("hhea", TTFTableName.HHEA.getName()); | |||
assertEquals("hmtx", TTFTableName.HMTX.getName()); | |||
assertEquals("kern", TTFTableName.KERN.getName()); | |||
assertEquals("loca", TTFTableName.LOCA.getName()); | |||
assertEquals("maxp", TTFTableName.MAXP.getName()); | |||
assertEquals("name", TTFTableName.NAME.getName()); | |||
assertEquals("post", TTFTableName.POST.getName()); | |||
assertEquals("prep", TTFTableName.PREP.getName()); | |||
assertEquals("vhea", TTFTableName.VHEA.getName()); | |||
assertEquals("vmtx", TTFTableName.VMTX.getName()); | |||
// make sure it works with other table names | |||
TTFTableName test = TTFTableName.getValue("test"); | |||
assertEquals("test", test.getName()); | |||
} | |||
/** | |||
* Test getValue(String) - tests that the getValue(String) method returns the expected | |||
* TTFTableNames value when it is given a String (name of a table). | |||
* @exception IllegalAccessException error | |||
*/ | |||
public void testGetValue() throws IllegalAccessException { | |||
assertEquals(TTFTableName.EBDT, TTFTableName.getValue("EBDT")); | |||
assertEquals(TTFTableName.EBLC, TTFTableName.getValue("EBLC")); | |||
assertEquals(TTFTableName.EBSC, TTFTableName.getValue("EBSC")); | |||
assertEquals(TTFTableName.FFTM, TTFTableName.getValue("FFTM")); | |||
assertEquals(TTFTableName.LTSH, TTFTableName.getValue("LTSH")); | |||
assertEquals(TTFTableName.OS2, TTFTableName.getValue("OS/2")); | |||
assertEquals(TTFTableName.PCLT, TTFTableName.getValue("PCLT")); | |||
assertEquals(TTFTableName.VDMX, TTFTableName.getValue("VDMX")); | |||
assertEquals(TTFTableName.CMAP, TTFTableName.getValue("cmap")); | |||
assertEquals(TTFTableName.CVT, TTFTableName.getValue("cvt ")); | |||
assertEquals(TTFTableName.FPGM, TTFTableName.getValue("fpgm")); | |||
assertEquals(TTFTableName.GASP, TTFTableName.getValue("gasp")); | |||
assertEquals(TTFTableName.GLYF, TTFTableName.getValue("glyf")); | |||
assertEquals(TTFTableName.HDMX, TTFTableName.getValue("hdmx")); | |||
assertEquals(TTFTableName.HEAD, TTFTableName.getValue("head")); | |||
assertEquals(TTFTableName.HHEA, TTFTableName.getValue("hhea")); | |||
assertEquals(TTFTableName.HMTX, TTFTableName.getValue("hmtx")); | |||
assertEquals(TTFTableName.KERN, TTFTableName.getValue("kern")); | |||
assertEquals(TTFTableName.LOCA, TTFTableName.getValue("loca")); | |||
assertEquals(TTFTableName.MAXP, TTFTableName.getValue("maxp")); | |||
assertEquals(TTFTableName.NAME, TTFTableName.getValue("name")); | |||
assertEquals(TTFTableName.POST, TTFTableName.getValue("post")); | |||
assertEquals(TTFTableName.PREP, TTFTableName.getValue("prep")); | |||
assertEquals(TTFTableName.VHEA, TTFTableName.getValue("vhea")); | |||
assertEquals(TTFTableName.VMTX, TTFTableName.getValue("vmtx")); | |||
// Test that we can store a random table name and it will not fail or throw an error. | |||
TTFTableName test = TTFTableName.getValue("random"); | |||
assertTrue(test instanceof TTFTableName); | |||
} | |||
/** | |||
* This class overrides hashCode() - we need to ensure it works properly by instantiating two | |||
* objects and comparing their hash-codes. | |||
* @exception IllegalAccessException error | |||
*/ | |||
public void testHashCode() throws IllegalAccessException { | |||
TTFTableName a = TTFTableName.getValue("testObject"); | |||
TTFTableName b = TTFTableName.getValue("testObject"); | |||
assertTrue(a.hashCode() == b.hashCode()); | |||
TTFTableName c = TTFTableName.getValue("fail"); | |||
assertFalse(a.hashCode() == c.hashCode()); | |||
} | |||
/** | |||
* This class overrides equals(object) - we need to test: | |||
* 1) Reflexivity | |||
* 2) Symmetry | |||
* 3) Transitivity | |||
* 4) Consistency | |||
* 5) check it fails if you put in a null value | |||
* @throws IllegalAccessException error | |||
*/ | |||
public void testEquals() throws IllegalAccessException { | |||
// Reflexivity | |||
TTFTableName a = TTFTableName.getValue("test"); | |||
assertTrue(a.equals(a)); | |||
// Symmetry | |||
TTFTableName b = TTFTableName.getValue("test"); | |||
assertTrue(a.equals(b)); | |||
assertTrue(b.equals(a)); | |||
// Transitivity (tested with symmetry) | |||
// Consistency (test that a == b is true and that a == c fails) | |||
TTFTableName c = TTFTableName.getValue("fail"); | |||
for (int i = 0; i < 100; i++) { | |||
assertTrue(a.equals(b)); | |||
assertFalse(a.equals(c)); | |||
} | |||
// check with null value | |||
assertFalse(a.equals(null)); | |||
} | |||
} |
@@ -0,0 +1,55 @@ | |||
/* | |||
* 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.ps; | |||
import junit.framework.Test; | |||
import junit.framework.TestSuite; | |||
import org.apache.fop.render.ps.fonts.PSTTFGeneratorTest; | |||
import org.apache.fop.render.ps.fonts.PSTTFGlyphOutputStreamTest; | |||
import org.apache.fop.render.ps.fonts.PSTTFOutputStreamTest; | |||
import org.apache.fop.render.ps.fonts.PSTTFTableOutputStreamTest; | |||
/** | |||
* A test Suite for org.apache.fop.render.ps.* | |||
*/ | |||
public final class RenderPSTestSuite { | |||
/** | |||
* Constructor. | |||
*/ | |||
private RenderPSTestSuite() { | |||
} | |||
/** | |||
* Testing org.apache.fop.render.ps.* | |||
* @return test | |||
*/ | |||
public static Test suite() { | |||
TestSuite suite = new TestSuite(); | |||
//$JUnit-BEGIN$ | |||
suite.addTest(new TestSuite(PSTTFGeneratorTest.class)); | |||
suite.addTest(new TestSuite(PSTTFOutputStreamTest.class)); | |||
suite.addTest(new TestSuite(PSTTFGlyphOutputStreamTest.class)); | |||
suite.addTest(new TestSuite(PSTTFTableOutputStreamTest.class)); | |||
//$JUnit-END$ | |||
return suite; | |||
} | |||
} |
@@ -0,0 +1,111 @@ | |||
/* | |||
* 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.ps.fonts; | |||
import java.io.ByteArrayOutputStream; | |||
import java.io.IOException; | |||
import junit.framework.TestCase; | |||
import org.apache.xmlgraphics.ps.PSGenerator; | |||
/** | |||
* The test class for org.apache.fop.render.ps.fonts.PSGenerator | |||
*/ | |||
public class PSTTFGeneratorTest extends TestCase { | |||
private PSTTFGenerator ttfGen; | |||
private ByteArrayOutputStream out = new ByteArrayOutputStream(); | |||
private PSGenerator gen = new PSGenerator(out); | |||
private byte[] byteArray; | |||
/** | |||
* Constructor | |||
*/ | |||
public PSTTFGeneratorTest() { | |||
byteArray = new byte[65536]; | |||
for (int i = 0; i < 65536; i++) { | |||
byteArray[i] = (byte) i; | |||
} | |||
} | |||
@Override | |||
public void setUp() { | |||
ttfGen = new PSTTFGenerator(gen); | |||
} | |||
/** | |||
* Tests startString() - starts the string in an appropriate way for a PostScript file. | |||
* @exception IOException write error | |||
*/ | |||
public void testStartString() throws IOException { | |||
ttfGen.startString(); | |||
assertEquals("<\n", out.toString()); | |||
} | |||
/** | |||
* Test streamBytes() - tests that strings are written to file in the proper format. | |||
* @throws IOException write error. | |||
*/ | |||
public void testStreamBytes() throws IOException { | |||
ttfGen.streamBytes(byteArray, 0, 16); | |||
assertEquals("000102030405060708090A0B0C0D0E0F", out.toString()); | |||
/* | |||
* 65520 is the closes multiple of 80 to 65535 (max string size in PS document) and since | |||
* one byte takes up two characters, 65520 / 2 - 16 (16 bytes already written)= 32744. | |||
*/ | |||
ttfGen.streamBytes(byteArray, 0, 32744); | |||
// Using a regex to ensure that the format is correct | |||
assertTrue(out.toString().matches("([0-9A-F]{80}\n){819}")); | |||
try { | |||
ttfGen.streamBytes(byteArray, 0, PSTTFGenerator.MAX_BUFFER_SIZE + 1); | |||
fail("Shouldn't be able to write more than MAX_BUFFER_SIZE to a PS document"); | |||
} catch (UnsupportedOperationException e) { | |||
// PASS | |||
} | |||
} | |||
/** | |||
* Test reset() - reset should reset the line counter such that when reset() is invoked the | |||
* following string streamed to the PS document should be 80 chars long. | |||
* @throws IOException file write error. | |||
*/ | |||
public void testReset() throws IOException { | |||
ttfGen.streamBytes(byteArray, 0, 40); | |||
assertTrue(out.toString().matches("([0-9A-F]{80}\n)")); | |||
ttfGen.streamBytes(byteArray, 0, 40); | |||
assertTrue(out.toString().matches("([0-9A-F]{80}\n){2}")); | |||
} | |||
/** | |||
* Test endString() - ensures strings are ended in the PostScript document in the correct | |||
* format, a "00" needs to be appended to the end of a string. | |||
* @throws IOException file write error | |||
*/ | |||
public void testEndString() throws IOException { | |||
ttfGen.endString(); | |||
assertEquals("00\n> ", out.toString()); | |||
out.reset(); | |||
// we need to check that this doesn't write more than 80 chars per line | |||
ttfGen.streamBytes(byteArray, 0, 40); | |||
ttfGen.endString(); | |||
assertTrue(out.toString().matches("([0-9A-F]{80}\n)00\n> ")); | |||
} | |||
} |
@@ -0,0 +1,105 @@ | |||
/* | |||
* 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.ps.fonts; | |||
import static org.mockito.Mockito.inOrder; | |||
import static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.times; | |||
import static org.mockito.Mockito.verify; | |||
import java.io.IOException; | |||
import junit.framework.TestCase; | |||
import org.mockito.InOrder; | |||
/** | |||
* Test class for PSTTFGlyphOutputStream | |||
*/ | |||
public class PSTTFGlyphOutputStreamTest extends TestCase { | |||
private PSTTFGenerator mockGen; | |||
private PSTTFGlyphOutputStream glyphOut; | |||
@Override | |||
public void setUp() { | |||
mockGen = mock(PSTTFGenerator.class); | |||
glyphOut = new PSTTFGlyphOutputStream(mockGen); | |||
} | |||
/** | |||
* Test startGlyphStream() - test that startGlyphStream() invokes reset() and startString() in | |||
* PSTTFGenerator. | |||
* @exception IOException file write error | |||
*/ | |||
public void testStartGlyphStream() throws IOException { | |||
glyphOut.startGlyphStream(); | |||
verify(mockGen).startString(); | |||
} | |||
/** | |||
* Test streamGlyph(byte[],int,int) - tests several paths: | |||
* 1) strings are properly appended | |||
* 2) when total strings size > PSTTFGenerator.MAX_BUFFER_SIZE, the strings is closed and a new | |||
* strings is started. | |||
* 3) if a glyph of size > PSTTFGenerator.MAX_BUFFER_SIZE is attempted, an exception is thrown. | |||
* @throws IOException file write error. | |||
*/ | |||
public void testStreamGlyph() throws IOException { | |||
int byteArraySize = 10; | |||
byte[] byteArray = new byte[byteArraySize]; | |||
int runs = 100; | |||
for (int i = 0; i < runs; i++) { | |||
glyphOut.streamGlyph(byteArray, 0, byteArraySize); | |||
} | |||
verify(mockGen, times(runs)).streamBytes(byteArray, 0, byteArraySize); | |||
/* | |||
* We want to run this for MAX_BUFFER_SIZE / byteArraySize so that go over the string | |||
* boundary and enforce the ending and starting of a new string. Using mockito to ensure | |||
* that this behaviour is performed in order (since this is an integral behavioural aspect) | |||
*/ | |||
int stringLimit = PSTTFGenerator.MAX_BUFFER_SIZE / byteArraySize; | |||
for (int i = 0; i < stringLimit; i++) { | |||
glyphOut.streamGlyph(byteArray, 0, byteArraySize); | |||
} | |||
InOrder inOrder = inOrder(mockGen); | |||
inOrder.verify(mockGen, times(stringLimit)).streamBytes(byteArray, 0, byteArraySize); | |||
inOrder.verify(mockGen).endString(); | |||
inOrder.verify(mockGen).startString(); | |||
inOrder.verify(mockGen, times(runs)).streamBytes(byteArray, 0, byteArraySize); | |||
try { | |||
glyphOut.streamGlyph(byteArray, 0, PSTTFGenerator.MAX_BUFFER_SIZE + 1); | |||
fail("Shouldn't allow a length > PSTTFGenerator.MAX_BUFFER_SIZE"); | |||
} catch (UnsupportedOperationException e) { | |||
// PASS | |||
} | |||
} | |||
/** | |||
* Test endGlyphStream() - tests that PSTTFGenerator.endString() is invoked when this method | |||
* is called. | |||
* @throws IOException file write exception | |||
*/ | |||
public void testEndGlyphStream() throws IOException { | |||
glyphOut.endGlyphStream(); | |||
verify(mockGen).endString(); | |||
} | |||
} |
@@ -0,0 +1,82 @@ | |||
/* | |||
* 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.ps.fonts; | |||
import static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.verify; | |||
import java.io.IOException; | |||
import junit.framework.TestCase; | |||
import org.apache.fop.fonts.truetype.TTFGlyphOutputStream; | |||
import org.apache.fop.fonts.truetype.TTFTableOutputStream; | |||
import org.apache.xmlgraphics.ps.PSGenerator; | |||
/** | |||
* Tests PSTTFOuputStream | |||
*/ | |||
public class PSTTFOutputStreamTest extends TestCase { | |||
private PSGenerator gen; | |||
private PSTTFOutputStream out; | |||
/** | |||
* Assigns an OutputStream to the PSGenerator. | |||
*/ | |||
public void setUp() { | |||
gen = mock(PSGenerator.class); | |||
out = new PSTTFOutputStream(gen); | |||
} | |||
/** | |||
* Test startFontStream() - Just tests that the font is properly initiated in the PostScript | |||
* document (in this case with "/sfnts[") | |||
* @throws IOException write exception. | |||
*/ | |||
public void testStartFontStream() throws IOException { | |||
out.startFontStream(); | |||
verify(gen).write("/sfnts["); | |||
} | |||
/** | |||
* Test getTableOutputStream() - we need to test that the inheritance model is properly obeyed. | |||
*/ | |||
public void testGetTableOutputStream() { | |||
TTFTableOutputStream tableOut = out.getTableOutputStream(); | |||
assertTrue(tableOut instanceof PSTTFTableOutputStream); | |||
} | |||
/** | |||
* Test getGlyphOutputStream() - we need to test that the inheritance model is properly obeyed. | |||
*/ | |||
public void testGetGlyphOutputStream() { | |||
TTFGlyphOutputStream glyphOut = out.getGlyphOutputStream(); | |||
assertTrue(glyphOut instanceof PSTTFGlyphOutputStream); | |||
} | |||
/** | |||
* Test endFontStream() | |||
* @exception IOException write error. | |||
*/ | |||
public void testEndFontStream() throws IOException { | |||
out.endFontStream(); | |||
verify(gen).writeln("] def"); | |||
} | |||
} |
@@ -0,0 +1,86 @@ | |||
/* | |||
* 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.ps.fonts; | |||
import static org.mockito.Mockito.inOrder; | |||
import static org.mockito.Mockito.mock; | |||
import java.io.IOException; | |||
import junit.framework.TestCase; | |||
import org.mockito.InOrder; | |||
/** | |||
* Test class for unit testing PSTTFTableOutputStream | |||
*/ | |||
public class PSTTFTableOutputStreamTest extends TestCase { | |||
private PSTTFGenerator mockGen; | |||
private PSTTFTableOutputStream tableOut; | |||
@Override | |||
public void setUp() { | |||
mockGen = mock(PSTTFGenerator.class); | |||
tableOut = new PSTTFTableOutputStream(mockGen); | |||
} | |||
/** | |||
* Test streamTable() - several paths to test (2. and 3. test corner cases): | |||
* 1) that a table of length < PSTTFGenerator.MAX_BUFFER_SIZE invokes the correct methods in | |||
* PSTTFGenerator. | |||
* 2) that a table of length > PSTTFGenerator.MAX_BUFFER_SIZE and | |||
* length == n * PSTTFGenerator.MAX_BUFFER_SIZE is split up and the methods in PSTTFGenerator | |||
* are invoked. | |||
* 3) that a table of length > PSTTFGenerator.MAX_BUFFER_SIZE but | |||
* length != n * PSTTFGenerator.MAX_BUFFER_SIZE is split up and the methods in PSTTFGenerator | |||
* are invoked. | |||
* @throws IOException file write error. | |||
*/ | |||
public void testStreamTable() throws IOException { | |||
byte[] byteArray = new byte[PSTTFGenerator.MAX_BUFFER_SIZE * 3]; | |||
tableOut.streamTable(byteArray, 0, 10); | |||
InOrder inOrder = inOrder(mockGen); | |||
inOrder.verify(mockGen).startString(); | |||
inOrder.verify(mockGen).streamBytes(byteArray, 0, 10); | |||
inOrder.verify(mockGen).endString(); | |||
setUp(); // reset all all the method calls | |||
/* We're going to run this 3 times to ensure the proper method calls are invoked and all | |||
* the bytes are streamed */ | |||
tableOut.streamTable(byteArray, 0, byteArray.length); | |||
inOrder = inOrder(mockGen); | |||
for (int i = 0; i < 3; i++) { | |||
int offset = PSTTFGenerator.MAX_BUFFER_SIZE * i; | |||
inOrder.verify(mockGen).startString(); | |||
inOrder.verify(mockGen).streamBytes(byteArray, offset, PSTTFGenerator.MAX_BUFFER_SIZE); | |||
inOrder.verify(mockGen).endString(); | |||
} | |||
setUp(); // reset all the method calls | |||
tableOut.streamTable(byteArray, 0, PSTTFGenerator.MAX_BUFFER_SIZE + 1); | |||
inOrder = inOrder(mockGen); | |||
inOrder.verify(mockGen).startString(); | |||
inOrder.verify(mockGen).streamBytes(byteArray, 0, PSTTFGenerator.MAX_BUFFER_SIZE); | |||
inOrder.verify(mockGen).endString(); | |||
inOrder.verify(mockGen).startString(); | |||
inOrder.verify(mockGen).streamBytes(byteArray, PSTTFGenerator.MAX_BUFFER_SIZE, 1); | |||
inOrder.verify(mockGen).endString(); | |||
} | |||
} |
@@ -0,0 +1,18 @@ | |||
Copyright (C) 2008 The Android Open Source Project | |||
Licensed 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. | |||
########## | |||
This directory contains the fonts for the platform. They are licensed | |||
under the Apache 2 license. |