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
//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);
+ }
}
}
}
+ " (" + e.getMessage() + ")");
}
- createIncludedResource(resourceName, uri, accessor, resourceObjectType);
+ createIncludedResource(resourceName, uri, accessor, resourceObjectType, false, null);
}
/**
* @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();
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);
}
}
+ 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
// 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);
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 {
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
*
* @param triplet the triplet to add
*/
- protected void addTriplet(AbstractTriplet triplet) {
+ public void addTriplet(AbstractTriplet triplet) {
triplets.add(triplet);
}
* @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 */
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,
/** 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.
* @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)
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;
/**
* required for presentation.
*/
public class MapDataResource extends AbstractTripletStructuredObject {
+ private List<List<AbstractTriplet>> tripletsList = new ArrayList<List<AbstractTriplet>>();
/**
* Main constructor
public MapDataResource() {
}
+ public void finishElement() {
+ tripletsList.add(triplets);
+ triplets = new ArrayList<AbstractTriplet>();
+ }
+
+ @Override
+ protected int getTripletDataLength() {
+ int dataLength = 0;
+ for (List<AbstractTriplet> l : tripletsList) {
+ dataLength += getTripletDataLength(l) + 2;
+ }
+ return dataLength;
+ }
+
+ private int getTripletDataLength(List<AbstractTriplet> 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<AbstractTriplet> l : tripletsList) {
+ len = BinaryUtils.convert(2 + getTripletDataLength(l), 2);
+ os.write(len);
+ writeObjects(l, os);
+ }
}
}
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;
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
// )
// );
}
/** the actual fully qualified name */
private final String fqName;
+ private String encoding = AFPConstants.EBCIDIC_ENCODING;
+
+ private int charlen = 1;
+
/**
* Main constructor
*
* @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;
+ }
}
/**
/** {@inheritDoc} */
public int getDataLength() {
- return 4 + fqName.length();
+ return 4 + (fqName.length() * charlen);
}
/** {@inheritDoc} */
// FQName
byte[] fqNameBytes;
- String encoding = AFPConstants.EBCIDIC_ENCODING;
if (format == FORMAT_URL) {
encoding = AFPConstants.US_ASCII_ENCODING;
}
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);
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;
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;
/**
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) {
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);
}
name, base14, isEmbbedable(fontTriplets), uri);
}
+ private TrueTypeFontConfig getTruetypeFont(List<FontTriplet> 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<FontTriplet> triplets, String type,
String codepage, String encoding, Configuration cfg,
AFPEventProducer eventProducer, String uri)
}
abstract static class AFPFontConfigData {
- private final List<FontTriplet> triplets;
+ protected final List<FontTriplet> 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<FontTriplet> triplets, String type, String codePage,
String encoding, String name, boolean embeddable, String uri) {
}
}
+ static final class TrueTypeFontConfig extends AFPFontConfigData {
+ private String characterset;
+ private String subfont;
+
+ private TrueTypeFontConfig(List<FontTriplet> 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;
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;
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);
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);
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+package org.apache.fop.render.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<Integer, String> TYPE_MAP = new HashMap<Integer, String>();
+ private static final Map<Integer, String> CATEGORY_MAP = new HashMap<Integer, String>();
+ private static final Map<Integer, String> TRIPLET_MAP = new HashMap<Integer, String>();
+ private static final Map<Integer, String> PTOCA_MAP = new HashMap<Integer, String>();
+ 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");
+ }
+}
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+package org.apache.fop.render.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 = "<fop version=\"1.0\">\n"
+ + " <renderers>\n"
+ + " <renderer mime=\"application/x-afp\">\n"
+ + " <fonts>\n"
+ + " <font name=\"Univers\" embed-url=\"test/resources/fonts/ttf/DejaVuLGCSerif.ttf\">\n"
+ + " <font-triplet name=\"Univers\" style=\"normal\" weight=\"normal\"/>\n"
+ + " <font-triplet name=\"any\" style=\"normal\" weight=\"normal\"/>\n"
+ + " </font>\n"
+ + " </fonts>\n"
+ + " </renderer>\n"
+ + " </renderers>\n"
+ + "</fop>";
+ String fo = "<fo:root xmlns:fo=\"http://www.w3.org/1999/XSL/Format\">\n"
+ + " <fo:layout-master-set>\n"
+ + " <fo:simple-page-master master-name=\"simple\">\n"
+ + " <fo:region-body />\n"
+ + " </fo:simple-page-master>\n"
+ + " </fo:layout-master-set>\n"
+ + " <fo:page-sequence master-reference=\"simple\">\n"
+ + " <fo:flow flow-name=\"xsl-region-body\">\n"
+ + " <fo:block font-family=\"Univers\">Univers</fo:block>\n"
+ + " </fo:flow>\n"
+ + " </fo:page-sequence>\n"
+ + "</fo:root>";
+
+ 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;
+ }
+ }
+}