From: Simon Steiner Date: Thu, 6 Aug 2015 10:59:51 +0000 (+0000) Subject: FOP-2504: Truetype support for AFP X-Git-Tag: fop-2_1~45 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=2e1089dd671b8d2283d245972a1bfd93e9d0daa0;p=xmlgraphics-fop.git FOP-2504: Truetype support for AFP git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1694450 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/lib/xmlgraphics-commons-svn-trunk.jar b/lib/xmlgraphics-commons-svn-trunk.jar index 45f593708..551295eb2 100644 Binary files a/lib/xmlgraphics-commons-svn-trunk.jar and b/lib/xmlgraphics-commons-svn-trunk.jar differ diff --git a/src/java/org/apache/fop/afp/AFPResourceManager.java b/src/java/org/apache/fop/afp/AFPResourceManager.java index fc34925d2..d4df8daab 100644 --- a/src/java/org/apache/fop/afp/AFPResourceManager.java +++ b/src/java/org/apache/fop/afp/AFPResourceManager.java @@ -36,15 +36,21 @@ import org.apache.fop.afp.fonts.AFPFont; import org.apache.fop.afp.fonts.CharacterSet; import org.apache.fop.afp.modca.AbstractNamedAFPObject; import org.apache.fop.afp.modca.AbstractPageObject; +import org.apache.fop.afp.modca.ActiveEnvironmentGroup; import org.apache.fop.afp.modca.IncludeObject; import org.apache.fop.afp.modca.IncludedResourceObject; +import org.apache.fop.afp.modca.ObjectContainer; import org.apache.fop.afp.modca.PageSegment; import org.apache.fop.afp.modca.Registry; import org.apache.fop.afp.modca.ResourceGroup; import org.apache.fop.afp.modca.ResourceObject; +import org.apache.fop.afp.modca.triplets.EncodingTriplet; +import org.apache.fop.afp.modca.triplets.FullyQualifiedNameTriplet; import org.apache.fop.afp.util.AFPResourceAccessor; import org.apache.fop.afp.util.AFPResourceUtil; import org.apache.fop.apps.io.InternalResourceResolver; +import org.apache.fop.fonts.FontType; +import org.apache.fop.render.afp.AFPFontConfig; /** * Manages the creation and storage of document resources @@ -330,12 +336,19 @@ public class AFPResourceManager { //Embed fonts (char sets and code pages) if (charSet.getResourceAccessor() != null) { AFPResourceAccessor accessor = charSet.getResourceAccessor(); - createIncludedResource( - charSet.getName(), accessor, - ResourceObject.TYPE_FONT_CHARACTER_SET); - createIncludedResource( - charSet.getCodePage(), accessor, - ResourceObject.TYPE_CODE_PAGE); + if (afpFont.getFontType() == FontType.TRUETYPE) { + + createIncludedResource(afpFont.getFontName(), accessor.resolveURI("."), accessor, + ResourceObject.TYPE_OBJECT_CONTAINER, true, + ((AFPFontConfig.AFPTrueTypeFont) afpFont).getTTC()); + } else { + createIncludedResource( + charSet.getName(), accessor, + ResourceObject.TYPE_FONT_CHARACTER_SET); + createIncludedResource( + charSet.getCodePage(), accessor, + ResourceObject.TYPE_CODE_PAGE); + } } } } @@ -366,7 +379,7 @@ public class AFPResourceManager { + " (" + e.getMessage() + ")"); } - createIncludedResource(resourceName, uri, accessor, resourceObjectType); + createIncludedResource(resourceName, uri, accessor, resourceObjectType, false, null); } /** @@ -378,7 +391,7 @@ public class AFPResourceManager { * @throws IOException if an I/O error occurs while loading the resource */ public void createIncludedResource(String resourceName, URI uri, AFPResourceAccessor accessor, - byte resourceObjectType) throws IOException { + byte resourceObjectType, boolean truetype, String ttc) throws IOException { AFPResourceLevel resourceLevel = new AFPResourceLevel(ResourceType.PRINT_FILE); AFPResourceInfo resourceInfo = new AFPResourceInfo(); @@ -391,15 +404,46 @@ public class AFPResourceManager { if (log.isDebugEnabled()) { log.debug("Adding included resource: " + resourceName); } - IncludedResourceObject resourceContent = new IncludedResourceObject( - resourceName, accessor, uri); - - ResourceObject resourceObject = factory.createResource(resourceName); - resourceObject.setDataObject(resourceContent); - resourceObject.setType(resourceObjectType); ResourceGroup resourceGroup = streamer.getResourceGroup(resourceLevel); - resourceGroup.addObject(resourceObject); + + if (truetype) { + ResourceObject res = factory.createResource(); + res.setType(ResourceObject.TYPE_OBJECT_CONTAINER); + + ActiveEnvironmentGroup.setupTruetypeMDR(res, false); + + ObjectContainer oc = factory.createObjectContainer(); + InputStream is; + try { + is = accessor.createInputStream(new URI(".")); + } catch (URISyntaxException e) { + throw new IOException(e); + } + + if (ttc != null) { + oc.setData(extractTTC(ttc, is)); + } else { + oc.setData(IOUtils.toByteArray(is)); + } + + ActiveEnvironmentGroup.setupTruetypeMDR(oc, true); + + res.addTriplet(new EncodingTriplet(1200)); + + res.setFullyQualifiedName(FullyQualifiedNameTriplet.TYPE_REPLACE_FIRST_GID_NAME, + FullyQualifiedNameTriplet.FORMAT_CHARSTR, resourceName, true); + + res.setDataObject(oc); + resourceGroup.addObject(res); + } else { + ResourceObject resourceObject = factory.createResource(resourceName); + IncludedResourceObject resourceContent = new IncludedResourceObject( + resourceName, accessor, uri); + resourceObject.setDataObject(resourceContent); + resourceObject.setType(resourceObjectType); + resourceGroup.addObject(resourceObject); + } //TODO what is the data object? cachedObject = new CachedObject(resourceName, null); @@ -411,6 +455,23 @@ public class AFPResourceManager { } } + private byte[] extractTTC(String ttc, InputStream is) throws IOException { +// TrueTypeCollection trueTypeCollection = new TrueTypeCollection(is); +// for (TrueTypeFont ttf : trueTypeCollection.getFonts()) { +// String name = ttf.getNaming().getFontFamily(); +// if (name.equals(ttc)) { +// ByteArrayOutputStream bos = new ByteArrayOutputStream(); +// TTFSubsetter s = new TTFSubsetter(ttf, null); +// for (int i = 0; i < 256 * 256; i++) { +// s.addCharCode(i); +// } +// s.writeToStream(bos); +// return bos.toByteArray(); +// } +// } + throw new IOException(ttc + " not supported"); + } + /** * Creates an included resource extracting the named resource from an external source. * @param resourceName the name of the resource diff --git a/src/java/org/apache/fop/afp/fonts/CharacterSet.java b/src/java/org/apache/fop/afp/fonts/CharacterSet.java index d57b14ed7..593b40592 100644 --- a/src/java/org/apache/fop/afp/fonts/CharacterSet.java +++ b/src/java/org/apache/fop/afp/fonts/CharacterSet.java @@ -108,8 +108,12 @@ public class CharacterSet { // the character set name must be 8 chars long this.name = padName(name); - // the code page name must be 8 chars long - this.codePage = padName(codePage); + if (codePage == null) { + this.codePage = null; + } else { + // the code page name must be 8 chars long + this.codePage = padName(codePage); + } this.encoding = encoding; this.encoder = charsetType.getEncoder(encoding); diff --git a/src/java/org/apache/fop/afp/fonts/CharacterSetBuilder.java b/src/java/org/apache/fop/afp/fonts/CharacterSetBuilder.java index e7b9041ba..39d2be845 100644 --- a/src/java/org/apache/fop/afp/fonts/CharacterSetBuilder.java +++ b/src/java/org/apache/fop/afp/fonts/CharacterSetBuilder.java @@ -232,6 +232,12 @@ public abstract class CharacterSetBuilder { eventProducer); } + public CharacterSet build(String characterSetName, String codePageName, String encoding, + Typeface typeface, AFPResourceAccessor accessor, AFPEventProducer eventProducer) + throws IOException { + return new FopCharacterSet(codePageName, encoding, characterSetName, typeface, accessor, eventProducer); + } + private CharacterSet processFont(String characterSetName, String codePageName, String encoding, CharacterSetType charsetType, AFPResourceAccessor accessor, AFPEventProducer eventProducer) throws IOException { diff --git a/src/java/org/apache/fop/afp/fonts/FopCharacterSet.java b/src/java/org/apache/fop/afp/fonts/FopCharacterSet.java index d3af78f7b..9d53cc7ea 100644 --- a/src/java/org/apache/fop/afp/fonts/FopCharacterSet.java +++ b/src/java/org/apache/fop/afp/fonts/FopCharacterSet.java @@ -49,6 +49,12 @@ public class FopCharacterSet extends CharacterSet { this.charSet = charSet; } + public FopCharacterSet(String codePage, String encoding, String name, Typeface charSet, + AFPResourceAccessor accessor, AFPEventProducer eventProducer) { + super(codePage, encoding, CharacterSetType.SINGLE_BYTE, name, accessor, eventProducer); + this.charSet = charSet; + } + /** * Ascender height is the distance from the character baseline to the * top of the character box. A negative ascender height signifies that diff --git a/src/java/org/apache/fop/afp/modca/AbstractTripletStructuredObject.java b/src/java/org/apache/fop/afp/modca/AbstractTripletStructuredObject.java index 662b344c1..d840e2077 100644 --- a/src/java/org/apache/fop/afp/modca/AbstractTripletStructuredObject.java +++ b/src/java/org/apache/fop/afp/modca/AbstractTripletStructuredObject.java @@ -103,7 +103,7 @@ public abstract class AbstractTripletStructuredObject extends AbstractStructured * * @param triplet the triplet to add */ - protected void addTriplet(AbstractTriplet triplet) { + public void addTriplet(AbstractTriplet triplet) { triplets.add(triplet); } @@ -131,7 +131,11 @@ public abstract class AbstractTripletStructuredObject extends AbstractStructured * @param fqName the fully qualified name of this resource */ public void setFullyQualifiedName(byte fqnType, byte fqnFormat, String fqName) { - addTriplet(new FullyQualifiedNameTriplet(fqnType, fqnFormat, fqName)); + addTriplet(new FullyQualifiedNameTriplet(fqnType, fqnFormat, fqName, false)); + } + + public void setFullyQualifiedName(byte fqnType, byte fqnFormat, String fqName, boolean utf16be) { + addTriplet(new FullyQualifiedNameTriplet(fqnType, fqnFormat, fqName, utf16be)); } /** @return the fully qualified name of this triplet or null if it does not exist */ diff --git a/src/java/org/apache/fop/afp/modca/ActiveEnvironmentGroup.java b/src/java/org/apache/fop/afp/modca/ActiveEnvironmentGroup.java index 05f48b47c..48eaa0a40 100644 --- a/src/java/org/apache/fop/afp/modca/ActiveEnvironmentGroup.java +++ b/src/java/org/apache/fop/afp/modca/ActiveEnvironmentGroup.java @@ -23,8 +23,17 @@ import java.io.IOException; import java.io.OutputStream; import java.util.List; +import org.apache.fop.afp.AFPDataObjectInfo; import org.apache.fop.afp.Factory; import org.apache.fop.afp.fonts.AFPFont; +import org.apache.fop.afp.modca.triplets.AbstractTriplet; +import org.apache.fop.afp.modca.triplets.EncodingTriplet; +import org.apache.fop.afp.modca.triplets.FullyQualifiedNameTriplet; +import org.apache.fop.afp.modca.triplets.ObjectClassificationTriplet; +import org.apache.fop.afp.util.BinaryUtils; +import org.apache.fop.apps.MimeConstants; +import org.apache.fop.fonts.FontType; +import org.apache.fop.render.afp.AFPFontConfig; /** * An Active Environment Group (AEG) is associated with each page, @@ -63,6 +72,8 @@ public final class ActiveEnvironmentGroup extends AbstractEnvironmentGroup { /** the resource manager */ private final Factory factory; + private MapDataResource mdr; + /** * Constructor for the ActiveEnvironmentGroup, this takes a * name parameter which must be 8 characters long. @@ -169,27 +180,100 @@ public final class ActiveEnvironmentGroup extends AbstractEnvironmentGroup { * @param orientation the orientation of the font (e.g. 0, 90, 180, 270) */ public void createFont(int fontRef, AFPFont font, int size, int orientation) { - MapCodedFont mapCodedFont = getCurrentMapCodedFont(); - if (mapCodedFont == null) { - mapCodedFont = factory.createMapCodedFont(); - mapCodedFonts.add(mapCodedFont); - } + if (font.getFontType() == FontType.TRUETYPE) { + if (mdr == null) { + mdr = factory.createMapDataResource(); + mapCodedFonts.add(mdr); + } + mdr.addTriplet(new EncodingTriplet(1200)); + String name = font.getFontName(); + if (((AFPFontConfig.AFPTrueTypeFont)font).getTTC() != null) { + name = ((AFPFontConfig.AFPTrueTypeFont)font).getTTC(); + } + mdr.setFullyQualifiedName(FullyQualifiedNameTriplet.TYPE_DATA_OBJECT_EXTERNAL_RESOURCE_REF, + FullyQualifiedNameTriplet.FORMAT_CHARSTR, name, true); + mdr.addTriplet(new FontFullyQualifiedNameTriplet((byte) fontRef)); - try { - mapCodedFont.addFont(fontRef, font, size, orientation); - } catch (MaximumSizeExceededException msee) { - mapCodedFont = factory.createMapCodedFont(); - mapCodedFonts.add(mapCodedFont); + setupTruetypeMDR(mdr, false); + mdr.addTriplet(new DataObjectFontTriplet(size / 1000)); + mdr.finishElement(); + } else { + MapCodedFont mapCodedFont = getCurrentMapCodedFont(); + if (mapCodedFont == null) { + mapCodedFont = factory.createMapCodedFont(); + mapCodedFonts.add(mapCodedFont); + } try { mapCodedFont.addFont(fontRef, font, size, orientation); - } catch (MaximumSizeExceededException ex) { - // Should never happen (but log just in case) - LOG.error("createFont():: resulted in a MaximumSizeExceededException"); + } catch (MaximumSizeExceededException msee) { + mapCodedFont = factory.createMapCodedFont(); + mapCodedFonts.add(mapCodedFont); + + try { + mapCodedFont.addFont(fontRef, font, size, orientation); + } catch (MaximumSizeExceededException ex) { + // Should never happen (but log just in case) + LOG.error("createFont():: resulted in a MaximumSizeExceededException"); + } } } } + public static void setupTruetypeMDR(AbstractTripletStructuredObject mdr, boolean res) { + AFPDataObjectInfo dataInfo = new AFPDataObjectInfo(); + dataInfo.setMimeType(MimeConstants.MIME_AFP_TRUETYPE); + mdr.setObjectClassification(ObjectClassificationTriplet.CLASS_DATA_OBJECT_FONT, + dataInfo.getObjectType(), res, false, res); + } + + public static class FontFullyQualifiedNameTriplet extends AbstractTriplet { + private byte fqName; + public FontFullyQualifiedNameTriplet(byte fqName) { + super(FULLY_QUALIFIED_NAME); + this.fqName = fqName; + } + + public int getDataLength() { + return 5; + } + + public void writeToStream(OutputStream os) throws IOException { + byte[] data = getData(); + data[2] = FullyQualifiedNameTriplet.TYPE_DATA_OBJECT_INTERNAL_RESOURCE_REF; + data[3] = FullyQualifiedNameTriplet.FORMAT_CHARSTR; + data[4] = fqName; + os.write(data); + } + } + + static class DataObjectFontTriplet extends AbstractTriplet { + private int pointSize; + + public DataObjectFontTriplet(int size) { + super(DATA_OBJECT_FONT_DESCRIPTOR); + pointSize = size; + } + + public int getDataLength() { + return 16; + } + + public void writeToStream(OutputStream os) throws IOException { + byte[] data = getData(); + data[3] = 0x20; + byte[] pointSizeBytes = BinaryUtils.convert(pointSize * 20, 2); + data[4] = pointSizeBytes[0]; //vfs + data[5] = pointSizeBytes[1]; +// data[6] = pointSizeBytes[0]; //hsf +// data[7] = pointSizeBytes[1]; + //charrot + data[11] = 0x03; //encenv + data[13] = 0x01; //encid + os.write(data); + } + } + /** * Getter method for the most recent MapCodedFont added to the * Active Environment Group (returns null if no MapCodedFonts exist) diff --git a/src/java/org/apache/fop/afp/modca/MapDataResource.java b/src/java/org/apache/fop/afp/modca/MapDataResource.java index 0bac920bd..c7aa758cf 100644 --- a/src/java/org/apache/fop/afp/modca/MapDataResource.java +++ b/src/java/org/apache/fop/afp/modca/MapDataResource.java @@ -21,7 +21,11 @@ package org.apache.fop.afp.modca; import java.io.IOException; import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; +import org.apache.fop.afp.modca.triplets.AbstractTriplet; +import org.apache.fop.afp.modca.triplets.Triplet; import org.apache.fop.afp.util.BinaryUtils; /** @@ -29,6 +33,7 @@ import org.apache.fop.afp.util.BinaryUtils; * required for presentation. */ public class MapDataResource extends AbstractTripletStructuredObject { + private List> tripletsList = new ArrayList>(); /** * Main constructor @@ -36,23 +41,45 @@ public class MapDataResource extends AbstractTripletStructuredObject { public MapDataResource() { } + public void finishElement() { + tripletsList.add(triplets); + triplets = new ArrayList(); + } + + @Override + protected int getTripletDataLength() { + int dataLength = 0; + for (List l : tripletsList) { + dataLength += getTripletDataLength(l) + 2; + } + return dataLength; + } + + private int getTripletDataLength(List l) { + int dataLength = 0; + for (Triplet triplet : l) { + dataLength += triplet.getDataLength(); + } + return dataLength; + } + /** {@inheritDoc} */ public void writeToStream(OutputStream os) throws IOException { super.writeStart(os); - byte[] data = new byte[11]; + byte[] data = new byte[9]; copySF(data, Type.MAP, Category.DATA_RESOURCE); int tripletDataLen = getTripletDataLength(); - byte[] len = BinaryUtils.convert(10 + tripletDataLen, 2); + byte[] len = BinaryUtils.convert(8 + tripletDataLen, 2); data[1] = len[0]; data[2] = len[1]; - - len = BinaryUtils.convert(2 + tripletDataLen, 2); - data[9] = len[0]; - data[10] = len[1]; - os.write(data); - writeTriplets(os); + + for (List l : tripletsList) { + len = BinaryUtils.convert(2 + getTripletDataLength(l), 2); + os.write(len); + writeObjects(l, os); + } } } diff --git a/src/java/org/apache/fop/afp/modca/ObjectContainer.java b/src/java/org/apache/fop/afp/modca/ObjectContainer.java index 50aabb03b..288e5aad0 100644 --- a/src/java/org/apache/fop/afp/modca/ObjectContainer.java +++ b/src/java/org/apache/fop/afp/modca/ObjectContainer.java @@ -36,7 +36,7 @@ import org.apache.fop.afp.util.BinaryUtils; public class ObjectContainer extends AbstractDataObject { /** the object container data maximum length */ - private static final int MAX_DATA_LEN = 32759; + private static final int MAX_DATA_LEN = 8192; private byte[] data; diff --git a/src/java/org/apache/fop/afp/modca/Registry.java b/src/java/org/apache/fop/afp/modca/Registry.java index 50aaca32c..fe0a42790 100644 --- a/src/java/org/apache/fop/afp/modca/Registry.java +++ b/src/java/org/apache/fop/afp/modca/Registry.java @@ -174,24 +174,24 @@ public final class Registry { MimeConstants.MIME_PCL ) ); + mimeObjectTypeMap.put( + MimeConstants.MIME_AFP_TRUETYPE, + new ObjectType( + COMPID_TRUETYPE_OPENTYPE_FONT_RESOURCE_OBJECT, + new byte[] {0x06, 0x07, 0x2B, 0x12, 0x00, 0x04, 0x01, 0x01, 0x33}, + "TrueType/OpenType Font Resource Object", + true, + MimeConstants.MIME_AFP_TRUETYPE + ) + ); // mimeObjectTypeMap.put( -// null, -// new ObjectType( -// COMPID_TRUETYPE_OPENTYPE_FONT_RESOURCE_OBJECT, -// new byte[] {0x06, 0x07, 0x2B, 0x12, 0x00, 0x04, 0x01, 0x01, 0x33}, -// "TrueType/OpenType Font Resource Object", -// true, -// null -// ) -// ); -// mimeObjectTypeMap.put( -// null, +// MimeConstants.MIME_AFP_TTC, // new ObjectType( // COMPID_TRUETYPE_OPENTYPE_FONT_COLLECTION_RESOURCE_OBJECT, // new byte[] {0x06, 0x07, 0x2B, 0x12, 0x00, 0x04, 0x01, 0x01, 0x35}, // "TrueType/OpenType Font Collection Resource Object", // true, -// null +// MimeConstants.MIME_AFP_TTC // ) // ); } diff --git a/src/java/org/apache/fop/afp/modca/triplets/FullyQualifiedNameTriplet.java b/src/java/org/apache/fop/afp/modca/triplets/FullyQualifiedNameTriplet.java index db6d8add8..581d215b8 100644 --- a/src/java/org/apache/fop/afp/modca/triplets/FullyQualifiedNameTriplet.java +++ b/src/java/org/apache/fop/afp/modca/triplets/FullyQualifiedNameTriplet.java @@ -150,6 +150,10 @@ public class FullyQualifiedNameTriplet extends AbstractTriplet { /** the actual fully qualified name */ private final String fqName; + private String encoding = AFPConstants.EBCIDIC_ENCODING; + + private int charlen = 1; + /** * Main constructor * @@ -157,11 +161,15 @@ public class FullyQualifiedNameTriplet extends AbstractTriplet { * @param format the fully qualified name format * @param fqName the fully qualified name */ - public FullyQualifiedNameTriplet(byte type, byte format, String fqName) { + public FullyQualifiedNameTriplet(byte type, byte format, String fqName, boolean utf16be) { super(FULLY_QUALIFIED_NAME); this.type = type; this.format = format; this.fqName = fqName; + if (utf16be) { + encoding = "UTF-16BE"; + charlen = 2; + } } /** @@ -180,7 +188,7 @@ public class FullyQualifiedNameTriplet extends AbstractTriplet { /** {@inheritDoc} */ public int getDataLength() { - return 4 + fqName.length(); + return 4 + (fqName.length() * charlen); } /** {@inheritDoc} */ @@ -191,7 +199,6 @@ public class FullyQualifiedNameTriplet extends AbstractTriplet { // FQName byte[] fqNameBytes; - String encoding = AFPConstants.EBCIDIC_ENCODING; if (format == FORMAT_URL) { encoding = AFPConstants.US_ASCII_ENCODING; } diff --git a/src/java/org/apache/fop/render/afp/AFPDocumentHandler.java b/src/java/org/apache/fop/render/afp/AFPDocumentHandler.java index 092e2a425..a2c1389d7 100644 --- a/src/java/org/apache/fop/render/afp/AFPDocumentHandler.java +++ b/src/java/org/apache/fop/render/afp/AFPDocumentHandler.java @@ -399,7 +399,7 @@ public class AFPDocumentHandler extends AbstractBinaryWritingIFDocumentHandler try { getResourceManager().createIncludedResource(formMap.getName(), formMap.getSrc(), accessor, - ResourceObject.TYPE_FORMDEF); + ResourceObject.TYPE_FORMDEF, false, null); } catch (IOException ioe) { throw new IFException( "I/O error while embedding form map resource: " + formMap.getName(), ioe); diff --git a/src/java/org/apache/fop/render/afp/AFPFontConfig.java b/src/java/org/apache/fop/render/afp/AFPFontConfig.java index 149973edd..341123331 100644 --- a/src/java/org/apache/fop/render/afp/AFPFontConfig.java +++ b/src/java/org/apache/fop/render/afp/AFPFontConfig.java @@ -20,6 +20,8 @@ package org.apache.fop.render.afp; import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -42,12 +44,16 @@ import org.apache.fop.afp.util.AFPResourceAccessor; import org.apache.fop.apps.FOPException; import org.apache.fop.apps.io.InternalResourceResolver; import org.apache.fop.events.EventProducer; +import org.apache.fop.fonts.EmbedFontInfo; import org.apache.fop.fonts.FontConfig; import org.apache.fop.fonts.FontManager; import org.apache.fop.fonts.FontManagerConfigurator; import org.apache.fop.fonts.FontTriplet; import org.apache.fop.fonts.FontTriplet.Matcher; +import org.apache.fop.fonts.FontType; +import org.apache.fop.fonts.FontUris; import org.apache.fop.fonts.FontUtil; +import org.apache.fop.fonts.LazyFont; import org.apache.fop.fonts.Typeface; /** @@ -154,6 +160,13 @@ public final class AFPFontConfig implements FontConfig { triplet.getAttribute("style"), weight); tripletList.add(fontTriplet); } + + String tturi = fontCfg.getAttribute("embed-url", null); + if (tturi != null) { + fontFromType(tripletList, "truetype", null, "UTF-16BE", fontCfg, eventProducer, tturi); + return; + } + //build the fonts Configuration[] config = fontCfg.getChildren("afp-font"); if (config.length == 0) { @@ -199,8 +212,9 @@ public final class AFPFontConfig implements FontConfig { embedURI); } else if ("CIDKeyed".equalsIgnoreCase(type)) { config = getCIDKeyedFont(fontTriplets, type, codepage, encoding, cfg, - eventProducer, - embedURI); + eventProducer, embedURI); + } else if ("truetype".equalsIgnoreCase(type)) { + config = getTruetypeFont(fontTriplets, type, codepage, encoding, cfg, eventProducer, embedURI); } else { LOG.error("No or incorrect type attribute: " + type); } @@ -240,6 +254,19 @@ public final class AFPFontConfig implements FontConfig { name, base14, isEmbbedable(fontTriplets), uri); } + private TrueTypeFontConfig getTruetypeFont(List fontTriplets, String type, String codepage, + String encoding, Configuration cfg, AFPEventProducer eventProducer, + String uri) throws ConfigurationException { + String name = cfg.getAttribute("name", null); + if (name == null) { + eventProducer.fontConfigMissing(this, "font name attribute", cfg.getLocation()); + return null; + } + String subfont = cfg.getAttribute("sub-font", null); + return new TrueTypeFontConfig(fontTriplets, type, codepage, encoding, "", + name, subfont, isEmbbedable(fontTriplets), uri); + } + private RasterFontConfig getRasterFont(List triplets, String type, String codepage, String encoding, Configuration cfg, AFPEventProducer eventProducer, String uri) @@ -281,12 +308,12 @@ public final class AFPFontConfig implements FontConfig { } abstract static class AFPFontConfigData { - private final List triplets; + protected final List triplets; private final String codePage; private final String encoding; private final String name; private final boolean embeddable; - private final String uri; + protected final String uri; AFPFontConfigData(List triplets, String type, String codePage, String encoding, String name, boolean embeddable, String uri) { @@ -334,6 +361,55 @@ public final class AFPFontConfig implements FontConfig { } } + static final class TrueTypeFontConfig extends AFPFontConfigData { + private String characterset; + private String subfont; + + private TrueTypeFontConfig(List triplets, String type, String codePage, + String encoding, String characterset, String name, String subfont, + boolean embeddable, String uri) { + super(triplets, type, codePage, encoding, name, embeddable, uri); + this.characterset = characterset; + this.subfont = subfont; + } + + @Override + AFPFontInfo getFontInfo(InternalResourceResolver resourceResolver, AFPEventProducer eventProducer) + throws IOException { + Typeface tf; + try { + tf = new LazyFont(new EmbedFontInfo( + new FontUris(new URI(uri), null) + , false, true, null, subfont), resourceResolver, false).getRealFont(); + } catch (URISyntaxException e) { + throw new IOException(e); + } + AFPResourceAccessor accessor = getAccessor(resourceResolver); + CharacterSet characterSet = CharacterSetBuilder.getDoubleByteInstance().build(characterset, super.codePage, + super.encoding, tf, accessor, eventProducer); + OutlineFont font = new AFPTrueTypeFont(super.name, super.embeddable, characterSet, + eventProducer, subfont); + return getFontInfo(font, this); + } + } + + public static class AFPTrueTypeFont extends OutlineFont { + private String ttc; + public AFPTrueTypeFont(String name, boolean embeddable, CharacterSet charSet, AFPEventProducer eventProducer, + String ttc) { + super(name, embeddable, charSet, eventProducer); + this.ttc = ttc; + } + + public FontType getFontType() { + return FontType.TRUETYPE; + } + + public String getTTC() { + return ttc; + } + } + static final class OutlineFontConfig extends AFPFontConfigData { private final String base14; private final String characterset; diff --git a/src/java/org/apache/fop/render/afp/AFPPainter.java b/src/java/org/apache/fop/render/afp/AFPPainter.java index aaa702afa..bfaa5db74 100644 --- a/src/java/org/apache/fop/render/afp/AFPPainter.java +++ b/src/java/org/apache/fop/render/afp/AFPPainter.java @@ -68,6 +68,7 @@ import org.apache.fop.afp.ptoca.PtocaProducer; import org.apache.fop.afp.util.AFPResourceAccessor; import org.apache.fop.fonts.Font; import org.apache.fop.fonts.FontTriplet; +import org.apache.fop.fonts.FontType; import org.apache.fop.fonts.Typeface; import org.apache.fop.render.ImageHandlerUtil; import org.apache.fop.render.RenderingContext; @@ -973,11 +974,18 @@ public class AFPPainter extends AbstractIFPainter { builder.setVariableSpaceCharacterIncrement(varSpaceCharacterIncrement); boolean fixedSpaceMode = false; + int ttPos = p.x; for (int i = 0; i < l; i++) { char orgChar = text.charAt(i); float glyphAdjust = 0; - if (CharUtilities.isFixedWidthSpace(orgChar)) { + if (afpFont.getFontType() == FontType.TRUETYPE) { + flushText(builder, sb, charSet); + fixedSpaceMode = true; + int charWidth = font.getCharWidth(orgChar); + sb.append(orgChar); + glyphAdjust += charWidth; + } else if (CharUtilities.isFixedWidthSpace(orgChar)) { flushText(builder, sb, charSet); builder.setVariableSpaceCharacterIncrement( fixedSpaceCharacterIncrement); @@ -1005,7 +1013,11 @@ public class AFPPainter extends AbstractIFPainter { glyphAdjust += dx[i + 1]; } - if (glyphAdjust != 0) { + if (afpFont.getFontType() == FontType.TRUETYPE) { + flushText(builder, sb, charSet); + ttPos += Math.round(unitConv.mpt2units(glyphAdjust)); + builder.absoluteMoveInline(ttPos); + } else if (glyphAdjust != 0) { flushText(builder, sb, charSet); int increment = Math.round(unitConv.mpt2units(glyphAdjust)); builder.relativeMoveInline(increment); diff --git a/test/java/org/apache/fop/render/afp/AFPParser.java b/test/java/org/apache/fop/render/afp/AFPParser.java new file mode 100644 index 000000000..9f0f92322 --- /dev/null +++ b/test/java/org/apache/fop/render/afp/AFPParser.java @@ -0,0 +1,235 @@ +/* + * 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.afp; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; + +import org.apache.fop.afp.AFPConstants; +import org.apache.fop.afp.ptoca.PtocaBuilder; + +import junit.framework.Assert; + +public class AFPParser { + private boolean readText; + public AFPParser(boolean readText) { + this.readText = readText; + } + + public void read(InputStream bis, StringBuilder sb) throws IOException { + while (bis.available() > 0) { + readField(bis, sb); + } + } + + private void readField(InputStream bis, StringBuilder sb) throws IOException { + bis.read(); + int len = getLength(bis.read(), bis.read()); + byte[] field = new byte[len - 2]; + bis.read(field); + InputStream fieldStream = new ByteArrayInputStream(field); + fieldStream.read(); + byte type = (byte) fieldStream.read(); + byte category = (byte) fieldStream.read(); + fieldStream.skip(3); + String typeStr = TYPE_MAP.get(type & 0xFF); + String catStr = CATEGORY_MAP.get(category & 0xFF); + if (typeStr != null && catStr != null) { + sb.append(typeStr + " " + catStr); + if (typeStr.equals("BEGIN") || typeStr.equals("END")) { + byte[] name = new byte[8]; + fieldStream.read(name); + sb.append(" " + new String(name, AFPConstants.EBCIDIC_ENCODING)); + fieldStream.skip(2); + readTriplet(fieldStream, sb); + } else if (typeStr.equals("MAP")) { + fieldStream.skip(2); + readTriplet(fieldStream, sb); + } else if (typeStr.equals("DESCRIPTOR") && catStr.equals("OBJECT_AREA")) { + readTriplet(fieldStream, sb); + } else if (typeStr.equals("DATA") && catStr.equals("PRESENTATION_TEXT") && readText) { + readData(fieldStream, sb); + } + sb.append("\n"); + } + } + + private void readData(InputStream bis, StringBuilder sb) throws IOException { + Assert.assertEquals(bis.read(), 0x2B); + Assert.assertEquals(bis.read(), 0xD3); + while (bis.available() > 0) { + int len = bis.read(); + int functionType = bis.read(); + + sb.append(" " + PTOCA_MAP.get(functionType)); + + if ("TRN".equals(PTOCA_MAP.get(functionType))) { + byte[] data = new byte[len - 2]; + bis.read(data); + sb.append(" " + new String(data, "UTF-16BE")); + } else { + bis.skip(len - 2); + } + } + } + + private void readTriplet(InputStream des, StringBuilder sb) throws IOException { + if (des.available() > 0) { + sb.append(" Triplets: "); + } + while (des.available() > 0) { + int len2 = des.read(); + int id = des.read(); + int b = id & 0xFF; + if (TRIPLET_MAP.containsKey(b)) { + sb.append(TRIPLET_MAP.get(b) + ","); + } else { + sb.append(String.format("0x%02X,", b)); + } + des.skip(len2 - 2); + } + } + + private int getLength(int a, int b) { + return (a * 256) + b; + } + + private static final Map TYPE_MAP = new HashMap(); + private static final Map CATEGORY_MAP = new HashMap(); + private static final Map TRIPLET_MAP = new HashMap(); + private static final Map PTOCA_MAP = new HashMap(); + static { + PTOCA_MAP.put(0xC2 | PtocaBuilder.CHAIN_BIT, "SIA"); + PTOCA_MAP.put(0xC4 | PtocaBuilder.CHAIN_BIT, "SVI"); + PTOCA_MAP.put(0xC6 | PtocaBuilder.CHAIN_BIT, "AMI"); + PTOCA_MAP.put(0xC8 | PtocaBuilder.CHAIN_BIT, "RMI"); + PTOCA_MAP.put(0xD2 | PtocaBuilder.CHAIN_BIT, "AMB"); + PTOCA_MAP.put(0xDA | PtocaBuilder.CHAIN_BIT, "TRN"); + PTOCA_MAP.put(0xE4 | PtocaBuilder.CHAIN_BIT, "DIR"); + PTOCA_MAP.put(0xE6 | PtocaBuilder.CHAIN_BIT, "DBR"); + PTOCA_MAP.put(0x80 | PtocaBuilder.CHAIN_BIT, "SEC"); + PTOCA_MAP.put(0xF0 | PtocaBuilder.CHAIN_BIT, "SCFL"); + PTOCA_MAP.put(0xF6 | PtocaBuilder.CHAIN_BIT, "STO"); + PTOCA_MAP.put(0xF8 | PtocaBuilder.CHAIN_BIT, "NOP"); + + TYPE_MAP.put(0xA0, "ATTRIBUTE"); + TYPE_MAP.put(0xA2, "COPY_COUNT"); + TYPE_MAP.put(0xA6, "DESCRIPTOR"); + TYPE_MAP.put(0xA7, "CONTROL"); + TYPE_MAP.put(0xA8, "BEGIN"); + TYPE_MAP.put(0xA9, "END"); + TYPE_MAP.put(0xAB, "MAP"); + TYPE_MAP.put(0xAC, "POSITION"); + TYPE_MAP.put(0xAD, "PROCESS"); + TYPE_MAP.put(0xAF, "INCLUDE"); + TYPE_MAP.put(0xB0, "TABLE"); + TYPE_MAP.put(0xB1, "MIGRATION"); + TYPE_MAP.put(0xB2, "VARIABLE"); + TYPE_MAP.put(0xB4, "LINK"); + TYPE_MAP.put(0xEE, "DATA"); + + CATEGORY_MAP.put(0x5F, "PAGE_SEGMENT"); + CATEGORY_MAP.put(0x6B, "OBJECT_AREA"); + CATEGORY_MAP.put(0x77, "COLOR_ATTRIBUTE_TABLE"); + CATEGORY_MAP.put(0x7B, "IM_IMAGE"); + CATEGORY_MAP.put(0x88, "MEDIUM"); + CATEGORY_MAP.put(0x8A, "CODED_FONT"); + CATEGORY_MAP.put(0x90, "PROCESS_ELEMENT"); + CATEGORY_MAP.put(0x92, "OBJECT_CONTAINER"); + CATEGORY_MAP.put(0x9B, "PRESENTATION_TEXT"); + CATEGORY_MAP.put(0xA7, "INDEX"); + CATEGORY_MAP.put(0xA8, "DOCUMENT"); + CATEGORY_MAP.put(0xAD, "PAGE_GROUP"); + CATEGORY_MAP.put(0xAF, "PAGE"); + CATEGORY_MAP.put(0xBB, "GRAPHICS"); + CATEGORY_MAP.put(0xC3, "DATA_RESOURCE"); + CATEGORY_MAP.put(0xC4, "DOCUMENT_ENVIRONMENT_GROUP"); + CATEGORY_MAP.put(0xC6, "RESOURCE_GROUP"); + CATEGORY_MAP.put(0xC7, "OBJECT_ENVIRONMENT_GROUP"); + CATEGORY_MAP.put(0xC9, "ACTIVE_ENVIRONMENT_GROUP"); + CATEGORY_MAP.put(0xCC, "MEDIUM_MAP"); + CATEGORY_MAP.put(0xCD, "FORM_MAP"); + CATEGORY_MAP.put(0xCE, "NAME_RESOURCE"); + CATEGORY_MAP.put(0xD8, "PAGE_OVERLAY"); + CATEGORY_MAP.put(0xD9, "RESOURCE_ENVIROMENT_GROUP"); + CATEGORY_MAP.put(0xDF, "OVERLAY"); + CATEGORY_MAP.put(0xEA, "DATA_SUPRESSION"); + CATEGORY_MAP.put(0xEB, "BARCODE"); + CATEGORY_MAP.put(0xEE, "NO_OPERATION"); + CATEGORY_MAP.put(0xFB, "IMAGE"); + + TRIPLET_MAP.put(0x02, "FULLY_QUALIFIED_NAME"); + TRIPLET_MAP.put(0x04, "MAPPING_OPTION"); + TRIPLET_MAP.put(0x10, "OBJECT_CLASSIFICATION"); + TRIPLET_MAP.put(0x18, "MODCA_INTERCHANGE_SET"); + TRIPLET_MAP.put(0x1F, "FONT_DESCRIPTOR_SPECIFICATION"); + TRIPLET_MAP.put(0x21, "OBJECT_FUNCTION_SET_SPECIFICATION"); + TRIPLET_MAP.put(0x22, "EXTENDED_RESOURCE_LOCAL_IDENTIFIER"); + TRIPLET_MAP.put(0x24, "RESOURCE_LOCAL_IDENTIFIER"); + TRIPLET_MAP.put(0x25, "RESOURCE_SECTION_NUMBER"); + TRIPLET_MAP.put(0x26, "CHARACTER_ROTATION"); + TRIPLET_MAP.put(0x2D, "OBJECT_BYTE_OFFSET"); + TRIPLET_MAP.put(0x36, "ATTRIBUTE_VALUE"); + TRIPLET_MAP.put(0x43, "DESCRIPTOR_POSITION"); + TRIPLET_MAP.put(0x45, "MEDIA_EJECT_CONTROL"); + TRIPLET_MAP.put(0x46, "PAGE_OVERLAY_CONDITIONAL_PROCESSING"); + TRIPLET_MAP.put(0x47, "RESOURCE_USAGE_ATTRIBUTE"); + TRIPLET_MAP.put(0x4B, "MEASUREMENT_UNITS"); + TRIPLET_MAP.put(0x4C, "OBJECT_AREA_SIZE"); + TRIPLET_MAP.put(0x4D, "AREA_DEFINITION"); + TRIPLET_MAP.put(0x4E, "COLOR_SPECIFICATION"); + TRIPLET_MAP.put(0x50, "ENCODING_SCHEME_ID"); + TRIPLET_MAP.put(0x56, "MEDIUM_MAP_PAGE_NUMBER"); + TRIPLET_MAP.put(0x57, "OBJECT_BYTE_EXTENT"); + TRIPLET_MAP.put(0x58, "OBJECT_STRUCTURED_FIELD_OFFSET"); + TRIPLET_MAP.put(0x59, "OBJECT_STRUCTURED_FIELD_EXTENT"); + TRIPLET_MAP.put(0x5A, "OBJECT_OFFSET"); + TRIPLET_MAP.put(0x5D, "FONT_HORIZONTAL_SCALE_FACTOR"); + TRIPLET_MAP.put(0x5E, "OBJECT_COUNT"); + TRIPLET_MAP.put(0x62, "OBJECT_DATE_AND_TIMESTAMP"); + TRIPLET_MAP.put(0x65, "COMMENT"); + TRIPLET_MAP.put(0x68, "MEDIUM_ORIENTATION"); + TRIPLET_MAP.put(0x6C, "RESOURCE_OBJECT_INCLUDE"); + TRIPLET_MAP.put(0x70, "PRESENTATION_SPACE_RESET_MIXING"); + TRIPLET_MAP.put(0x71, "PRESENTATION_SPACE_MIXING_RULE"); + TRIPLET_MAP.put(0x72, "UNIVERSAL_DATE_AND_TIMESTAMP"); + TRIPLET_MAP.put(0x74, "TONER_SAVER"); + TRIPLET_MAP.put(0x75, "COLOR_FIDELITY"); + TRIPLET_MAP.put(0x78, "FONT_FIDELITY"); + TRIPLET_MAP.put(0x80, "ATTRIBUTE_QUALIFIER"); + TRIPLET_MAP.put(0x81, "PAGE_POSITION_INFORMATION"); + TRIPLET_MAP.put(0x82, "PARAMETER_VALUE"); + TRIPLET_MAP.put(0x83, "PRESENTATION_CONTROL"); + TRIPLET_MAP.put(0x84, "FONT_RESOLUTION_AND_METRIC_TECHNOLOGY"); + TRIPLET_MAP.put(0x85, "FINISHING_OPERATION"); + TRIPLET_MAP.put(0x86, "TEXT_FIDELITY"); + TRIPLET_MAP.put(0x87, "MEDIA_FIDELITY"); + TRIPLET_MAP.put(0x88, "FINISHING_FIDELITY"); + TRIPLET_MAP.put(0x8B, "DATA_OBJECT_FONT_DESCRIPTOR"); + TRIPLET_MAP.put(0x8C, "LOCALE_SELECTOR"); + TRIPLET_MAP.put(0x8E, "UP3I_FINISHING_OPERATION"); + TRIPLET_MAP.put(0x91, "COLOR_MANAGEMENT_RESOURCE_DESCRIPTOR"); + TRIPLET_MAP.put(0x95, "RENDERING_INTENT"); + TRIPLET_MAP.put(0x96, "CMR_TAG_FIDELITY"); + TRIPLET_MAP.put(0x97, "DEVICE_APPEARANCE"); + } +} diff --git a/test/java/org/apache/fop/render/afp/AFPTrueTypeTestCase.java b/test/java/org/apache/fop/render/afp/AFPTrueTypeTestCase.java new file mode 100644 index 000000000..6e6bbfe94 --- /dev/null +++ b/test/java/org/apache/fop/render/afp/AFPTrueTypeTestCase.java @@ -0,0 +1,181 @@ +/* + * 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.afp; + +import java.awt.Color; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; + +import javax.xml.transform.Result; +import javax.xml.transform.Source; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.sax.SAXResult; +import javax.xml.transform.stream.StreamSource; + +import org.junit.Test; + +import org.xml.sax.SAXException; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import org.apache.fop.afp.AFPPaintingState; +import org.apache.fop.afp.AFPResourceManager; +import org.apache.fop.afp.DataStream; +import org.apache.fop.afp.Factory; +import org.apache.fop.afp.fonts.FopCharacterSet; +import org.apache.fop.afp.modca.PageObject; +import org.apache.fop.apps.FOUserAgent; +import org.apache.fop.apps.Fop; +import org.apache.fop.apps.FopConfParser; +import org.apache.fop.apps.FopFactory; +import org.apache.fop.apps.FopFactoryBuilder; +import org.apache.fop.fonts.EmbeddingMode; +import org.apache.fop.fonts.FontInfo; +import org.apache.fop.fonts.FontTriplet; +import org.apache.fop.fonts.MultiByteFont; +import org.apache.fop.render.intermediate.IFException; + +import junit.framework.Assert; + +public class AFPTrueTypeTestCase { + @Test + public void testAFPTrueType() throws IOException, SAXException, TransformerException { + String fopxconf = "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + ""; + String fo = "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " Univers\n" + + " \n" + + " \n" + + ""; + + FopFactoryBuilder confBuilder = new FopConfParser( + new ByteArrayInputStream(fopxconf.getBytes()), new File(".").toURI()).getFopFactoryBuilder(); + FopFactory fopFactory = confBuilder.build(); + FOUserAgent foUserAgent = fopFactory.newFOUserAgent(); + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + Fop fop = fopFactory.newFop("application/x-afp", foUserAgent, bos); + TransformerFactory factory = TransformerFactory.newInstance(); + Transformer transformer = factory.newTransformer(); + Source src = new StreamSource(new ByteArrayInputStream(fo.getBytes())); + Result res = new SAXResult(fop.getDefaultHandler()); + transformer.transform(src, res); + bos.close(); + + StringBuilder sb = new StringBuilder(); + InputStream bis = new ByteArrayInputStream(bos.toByteArray()); + new AFPParser(false).read(bis, sb); + + String format = "BEGIN RESOURCE_GROUP RG000001\n" + + "BEGIN NAME_RESOURCE RES00001 Triplets: OBJECT_FUNCTION_SET_SPECIFICATION" + + ",OBJECT_CLASSIFICATION,0x01,FULLY_QUALIFIED_NAME,\n" + + "BEGIN OBJECT_CONTAINER OC000001 Triplets: 0x41,0x00,0x00,\n"; + for (int i = 0; i < 29; i++) { + format += "DATA OBJECT_CONTAINER\n"; + } + format += "END OBJECT_CONTAINER OC000001\n" + + "END NAME_RESOURCE RES00001\n" + + "END RESOURCE_GROUP RG000001\n" + + "BEGIN DOCUMENT DOC00001\n" + + "BEGIN PAGE_GROUP PGP00001\n" + + "BEGIN PAGE PGN00001\n" + + "BEGIN ACTIVE_ENVIRONMENT_GROUP AEG00001\n" + + "MAP DATA_RESOURCE Triplets: 0x01,FULLY_QUALIFIED_NAME,FULLY_QUALIFIED_NAME" + + ",OBJECT_CLASSIFICATION,DATA_OBJECT_FONT_DESCRIPTOR,\n" + + "DESCRIPTOR PAGE\n" + + "MIGRATION PRESENTATION_TEXT\n" + + "END ACTIVE_ENVIRONMENT_GROUP AEG00001\n" + + "BEGIN PRESENTATION_TEXT PT000001\n" + + "DATA PRESENTATION_TEXT\n" + + "END PRESENTATION_TEXT PT000001\n" + + "END PAGE PGN00001\n" + + "END PAGE_GROUP PGP00001\n" + + "END DOCUMENT DOC00001\n"; + + Assert.assertEquals(sb.toString(), format); + } + + @Test + public void testAFPPainter() throws IFException, IOException { + AFPDocumentHandler afpDocumentHandler = mock(AFPDocumentHandler.class); + when(afpDocumentHandler.getPaintingState()).thenReturn(new AFPPaintingState()); + when(afpDocumentHandler.getResourceManager()).thenReturn(new AFPResourceManager(null)); + + DataStream ds = mock(DataStream.class); + when(afpDocumentHandler.getDataStream()).thenReturn(ds); + PageObject po = new PageObject(new Factory(), "PAGE0001", 0, 0, 0, 0, 0); + when(ds.getCurrentPage()).thenReturn(po); + + AFPPainter afpPainter = new MyAFPPainter(afpDocumentHandler); + afpPainter.setFont("any", "normal", 400, null, null, Color.BLACK); + afpPainter.drawText(0, 0, 0, 0, null, "test"); + + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + po.writeToStream(bos); + + InputStream bis = new ByteArrayInputStream(bos.toByteArray()); + StringBuilder sb = new StringBuilder(); + new AFPParser(true).read(bis, sb); + Assert.assertTrue(sb.toString(), + sb.toString().contains("DATA PRESENTATION_TEXT AMB AMI SCFL TRN t TRN e TRN s TRN t")); + } + + class MyAFPPainter extends AFPPainter { + public MyAFPPainter(AFPDocumentHandler documentHandler) { + super(documentHandler); + } + + protected FOUserAgent getUserAgent() { + FopFactory fopFactory = FopFactory.newInstance(new File(".").toURI()); + return fopFactory.newFOUserAgent(); + } + + protected FontInfo getFontInfo() { + FontInfo f = new FontInfo(); + f.addFontProperties("any", FontTriplet.DEFAULT_FONT_TRIPLET); + MultiByteFont font = new MultiByteFont(null, EmbeddingMode.AUTO); + font.setWidthArray(new int[100]); + f.addMetrics("any", new AFPFontConfig.AFPTrueTypeFont("", true, + new FopCharacterSet("", "UTF-16BE", "", font, null, null), null, null)); + return f; + } + } +}