Browse Source

Added full URI resolution support for AFP fonts.

git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@752133 13f79535-47bb-0310-9956-ffa450edef68
pull/37/head
Jeremias Maerki 15 years ago
parent
commit
d83d80471c

+ 13
- 4
src/documentation/content/xdocs/trunk/output.xml View File

@@ -536,8 +536,8 @@ out = proc.getOutputStream();]]></source>
<source><![CDATA[ <!-- This is an example of mapping actual IBM raster fonts / code pages to a FOP font -->
<font>
<!-- The afp-font element defines the IBM code page, the matching Java encoding and the
path to the font -->
<afp-font type="raster" codepage="T1V10500" encoding="Cp500" path="fonts/ibm">
base URI for the font -->
<afp-font type="raster" codepage="T1V10500" encoding="Cp500" base-uri="fonts/ibm/">
<!-- For a raster font a separate element for each font size is required providing
the font size and the corresponding IBM Character set name -->
<afp-raster-font size="7" characterset="C0N20070"/>
@@ -563,15 +563,24 @@ out = proc.getOutputStream();]]></source>
However, the characterset definition is now required within the afp-font element.</p>
<source><![CDATA[ <font>
<afp-font type="outline" codepage="T1V10500" encoding="Cp500" characterset="CZH200 "
path="fonts/ibm" />
base-uri="file:/fonts/ibm" />
<font-triplet name="sans-serif" style="normal" weight="normal"/>
<font-triplet name="Helvetica" style="normal" weight="normal"/>
<font-triplet name="any" style="normal" weight="normal"/>
</font>
]]></source>
<p>
If "base-uri" is missing or a relative URI, the fonts are resolved relative to
the font base URI specified in the configuration (or on the FopFactory).
</p>
<note>
Previously, the location of the font files was given by the "path" attribute. This is still
supported for the time being, but you should move to using the more flexible "base-uri"
attribute so you can profit from the power of URI resolvers.
</note>
<p>Experimentation has shown that the font metrics for the FOP built-in Base14 fonts are actually
very similar to some of the IBM outline and raster fonts. In cases were the IBM font files are not
available the path attribute in the afp-font element can be replaced by a base14-font attribute
available the base-uri attribute in the afp-font element can be replaced by a base14-font attribute
giving the name of the matching Base14 font. In this case the AFP Renderer will take the
font metrics from the built-in font.</p>
<source><![CDATA[ <!-- The following are examples of defining outline fonts based on FOP built-in

+ 2
- 6
src/java/org/apache/fop/afp/AFPResourceManager.java View File

@@ -37,7 +37,6 @@ 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.util.ResourceAccessor;
import org.apache.fop.afp.util.SimpleResourceAccessor;

/**
* Manages the creation and storage of document resources
@@ -236,11 +235,11 @@ public class AFPResourceManager {
/**
* Creates an included resource object by loading the contained object from a file.
* @param resourceName the name of the resource
* @param basePath the base path in which to look for the resource files
* @param accessor resource accessor to access the resource with
* @param resourceObjectType the resource object type ({@link ResourceObject}.*)
* @throws IOException if an I/O error occurs while loading the resource
*/
public void createIncludedResource(String resourceName, String basePath,
public void createIncludedResource(String resourceName, ResourceAccessor accessor,
byte resourceObjectType) throws IOException {
AFPResourceLevel resourceLevel = new AFPResourceLevel(AFPResourceLevel.PRINT_FILE);
URI uri;
@@ -261,9 +260,6 @@ public class AFPResourceManager {
if (log.isDebugEnabled()) {
log.debug("Adding included resource: " + resourceName);
}
//TODO This works with local filenames only. In the long term, this
//should work through FOP's URI resolver.
ResourceAccessor accessor = new SimpleResourceAccessor(basePath);
IncludedResourceObject resourceContent = new IncludedResourceObject(
resourceName, accessor, uri);


+ 15
- 12
src/java/org/apache/fop/afp/fonts/AFPFontCollection.java View File

@@ -67,18 +67,14 @@ public class AFPFontCollection implements FontCollection {
num++;
}
}
if (!fontInfo.hasFont("any", Font.STYLE_NORMAL, Font.WEIGHT_NORMAL)) {
eventProducer.warnMissingDefaultFont(this, Font.STYLE_NORMAL, Font.WEIGHT_NORMAL);
}
if (!fontInfo.hasFont("any", Font.STYLE_ITALIC, Font.WEIGHT_NORMAL)) {
eventProducer.warnMissingDefaultFont(this, Font.STYLE_ITALIC, Font.WEIGHT_NORMAL);
}
if (!fontInfo.hasFont("any", Font.STYLE_NORMAL, Font.WEIGHT_BOLD)) {
eventProducer.warnMissingDefaultFont(this, Font.STYLE_ITALIC, Font.WEIGHT_BOLD);
}
if (!fontInfo.hasFont("any", Font.STYLE_ITALIC, Font.WEIGHT_BOLD)) {
eventProducer.warnMissingDefaultFont(this, Font.STYLE_ITALIC, Font.WEIGHT_BOLD);
}
checkDefaultFontAvailable(fontInfo, eventProducer,
Font.STYLE_NORMAL, Font.WEIGHT_NORMAL);
checkDefaultFontAvailable(fontInfo, eventProducer,
Font.STYLE_ITALIC, Font.WEIGHT_NORMAL);
checkDefaultFontAvailable(fontInfo, eventProducer,
Font.STYLE_NORMAL, Font.WEIGHT_BOLD);
checkDefaultFontAvailable(fontInfo, eventProducer,
Font.STYLE_ITALIC, Font.WEIGHT_BOLD);
} else {
eventProducer.warnDefaultFontSetup(this);

@@ -89,4 +85,11 @@ public class AFPFontCollection implements FontCollection {
return num;
}

private void checkDefaultFontAvailable(FontInfo fontInfo, AFPEventProducer eventProducer,
String style, int weight) {
if (!fontInfo.hasFont("any", style, weight)) {
eventProducer.warnMissingDefaultFont(this, style, weight);
}
}

}

+ 25
- 73
src/java/org/apache/fop/afp/fonts/AFPFontReader.java View File

@@ -19,21 +19,19 @@

package org.apache.fop.afp.fonts;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.List;
import java.util.Map;

import org.apache.commons.io.FileUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.apache.fop.afp.AFPConstants;
import org.apache.fop.afp.util.ResourceAccessor;
import org.apache.fop.afp.util.StructuredFieldReader;

/**
@@ -58,7 +56,7 @@ public final class AFPFontReader {
/**
* Static logging instance
*/
protected static final Log log = LogFactory.getLog("org.apache.xmlgraphics.afp.fonts");
protected static final Log log = LogFactory.getLog(AFPFontReader.class);

/**
* Template used to convert lists to arrays.
@@ -96,7 +94,7 @@ public final class AFPFontReader {
/**
* The collection of code pages
*/
private final Map/*<String, Map<String, String>>*/ codePages
private final Map/*<String, Map<String, String>>*/ codePagesCache
= new java.util.HashMap/*<String, Map<String, String>>*/();

/**
@@ -108,65 +106,16 @@ public final class AFPFontReader {
*
* @throws IOException in the event that an I/O exception of some sort has occurred
*/
private InputStream openInputStream(String path, String filename) throws IOException {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
if (classLoader == null) {
classLoader = AFPFontReader.class.getClassLoader();
}

URL url = classLoader.getResource(path);

if (url == null) {
try {
File file = new File(path);
url = file.toURI().toURL();
if (url == null) {
String msg = "file not found " + filename + " in classpath: " + path;
log.error(msg);
throw new FileNotFoundException(msg);
}
} catch (MalformedURLException ex) {
String msg = "file not found " + filename + " in classpath: " + path;
log.error(msg);
throw new FileNotFoundException(msg);
}
}

File directory = FileUtils.toFile(url);
if (!directory.canRead()) {
String msg = "Failed to read directory " + url.getPath();
log.error(msg);
throw new FileNotFoundException(msg);
}

final String filterpattern = filename.trim();
FilenameFilter filter = new FilenameFilter() {
public boolean accept(File dir, String name) {
return name.startsWith(filterpattern);
}
};

File[] files = directory.listFiles(filter);

if (files.length < 1) {
String msg = "file search for " + filename + " located "
+ files.length + " files";
log.error(msg);
throw new FileNotFoundException(msg);
} else if (files.length > 1) {
String msg = "file search for " + filename + " located "
+ files.length + " files";
log.warn(msg);
}

InputStream inputStream = files[0].toURI().toURL().openStream();

if (inputStream == null) {
String msg = "AFPFontReader:: getInputStream():: file not found for " + filename;
log.error(msg);
throw new FileNotFoundException(msg);
private InputStream openInputStream(ResourceAccessor accessor, String filename)
throws IOException {
URI uri;
try {
uri = new URI(filename.trim());
} catch (URISyntaxException e) {
throw new FileNotFoundException("Invalid filename: "
+ filename + " (" + e.getMessage() + ")");
}
InputStream inputStream = accessor.createInputStream(uri);
return inputStream;
}

@@ -206,13 +155,14 @@ public final class AFPFontReader {
* chracter global identifier.
*/
String codePageId = new String(characterSet.getCodePage());
String path = characterSet.getPath();
ResourceAccessor accessor = characterSet.getResourceAccessor();

Map/*<String,String>*/ codePage = (Map/*<String,String>*/)codePages.get(codePageId);
Map/*<String,String>*/ codePage
= (Map/*<String,String>*/)codePagesCache.get(codePageId);

if (codePage == null) {
codePage = loadCodePage(codePageId, characterSet.getEncoding(), path);
codePages.put(codePageId, codePage);
codePage = loadCodePage(codePageId, characterSet.getEncoding(), accessor);
codePagesCache.put(codePageId, codePage);
}

/**
@@ -222,7 +172,7 @@ public final class AFPFontReader {
*/
final String characterSetName = characterSet.getName();

inputStream = openInputStream(path, characterSetName);
inputStream = openInputStream(accessor, characterSetName);

StructuredFieldReader structuredFieldReader = new StructuredFieldReader(inputStream);

@@ -246,7 +196,8 @@ public final class AFPFontReader {
}

//process D3AC89 Font Position
processFontPosition(structuredFieldReader, characterSetOrientations, metricNormalizationFactor);
processFontPosition(structuredFieldReader, characterSetOrientations,
metricNormalizationFactor);

//process D38C89 Font Index (per orientation)
for (int i = 0; i < characterSetOrientations.length; i++) {
@@ -274,17 +225,18 @@ public final class AFPFontReader {
* the code page identifier
* @param encoding
* the encoding to use for the character decoding
* @param accessor the resource accessor
* @returns a code page mapping
*/
private Map/*<String,String>*/ loadCodePage(String codePage, String encoding,
String path) throws IOException {
ResourceAccessor accessor) throws IOException {

// Create the HashMap to store code page information
Map/*<String,String>*/ codePages = new java.util.HashMap/*<String,String>*/();

InputStream inputStream = null;
try {
inputStream = openInputStream(path, codePage.trim());
inputStream = openInputStream(accessor, codePage.trim());

StructuredFieldReader structuredFieldReader = new StructuredFieldReader(inputStream);
byte[] data = structuredFieldReader.getNext(CHARACTER_TABLE_SF);

+ 25
- 7
src/java/org/apache/fop/afp/fonts/CharacterSet.java View File

@@ -19,8 +19,10 @@

package org.apache.fop.afp.fonts;

import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.CharacterCodingException;
@@ -34,6 +36,8 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.apache.fop.afp.AFPConstants;
import org.apache.fop.afp.util.ResourceAccessor;
import org.apache.fop.afp.util.SimpleResourceAccessor;
import org.apache.fop.afp.util.StringUtils;

/**
@@ -80,7 +84,7 @@ public class CharacterSet {
protected String name;

/** The path to the installed fonts */
protected String path;
private ResourceAccessor accessor;

/** Indicator as to whether to metrics have been loaded */
private boolean isMetricsLoaded = false;
@@ -99,8 +103,23 @@ public class CharacterSet {
* @param encoding the encoding of the font
* @param name the character set name
* @param path the path to the installed afp fonts
* @deprecated Please use {@link #CharacterSet(String, String, String, URI)} instead.
*/
public CharacterSet(String codePage, String encoding, String name, String path) {
this(codePage, encoding, name,
new SimpleResourceAccessor(path != null ? new File(path) : null));
}

/**
* Constructor for the CharacterSetMetric object, the character set is used
* to load the font information from the actual AFP font.
*
* @param codePage the code page identifier
* @param encoding the encoding of the font
* @param name the character set name
* @param accessor the resource accessor to load resource with
*/
public CharacterSet(String codePage, String encoding, String name, ResourceAccessor accessor) {
if (name.length() > MAX_NAME_LEN) {
String msg = "Character set name '" + name + "' must be a maximum of "
+ MAX_NAME_LEN + " characters";
@@ -123,7 +142,7 @@ public class CharacterSet {
//This may happen with "Cp500" on Sun Java 1.4.2
this.encoder = null;
}
this.path = path;
this.accessor = accessor;

this.characterSetOrientations = new java.util.HashMap(4);
}
@@ -202,12 +221,11 @@ public class CharacterSet {
}

/**
* Returns the path where the font resources are installed
*
* @return the path where the font resources are installed
* Returns the resource accessor to load the font resources with.
* @return the resource accessor to load the font resources with
*/
public String getPath() {
return path;
public ResourceAccessor getResourceAccessor() {
return this.accessor;
}

/**

+ 2
- 1
src/java/org/apache/fop/afp/fonts/FopCharacterSet.java View File

@@ -19,6 +19,7 @@

package org.apache.fop.afp.fonts;

import org.apache.fop.afp.util.ResourceAccessor;
import org.apache.fop.fonts.Typeface;

/**
@@ -43,7 +44,7 @@ public class FopCharacterSet extends CharacterSet {
String name,
Typeface charSet) {

super(codePage, encoding, name, null);
super(codePage, encoding, name, (ResourceAccessor)null);
this.charSet = charSet;
}


+ 34
- 8
src/java/org/apache/fop/afp/util/DefaultFOPResourceAccessor.java View File

@@ -19,42 +19,68 @@

package org.apache.fop.afp.util;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URL;

import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;

import org.apache.commons.io.IOUtils;

import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.apps.FopFactory;
import org.apache.fop.fonts.FontManager;

/**
* Default implementation of the {@link ResourceAccessor} interface for use inside FOP.
*/
public class DefaultFOPResourceAccessor implements ResourceAccessor {
public class DefaultFOPResourceAccessor extends SimpleResourceAccessor {

private FOUserAgent userAgent;
private String categoryBaseURI;

/**
* Main constructor.
* Constructor for resource to be accessed via the {@link FOUserAgent}. This contructor
* can take two base URIs: the category base URI is the one to use when differentiating between
* normal resources (ex. images) and font resources. So, if fonts need to be accessed, you can
* set the {@link FontManager}'s base URI instead of the one on the {@link FopFactory}.
* @param userAgent the FO user agent
* @param categoryBaseURI the category base URI (may be null)
* @param baseURI the custom base URI to resolve relative URIs against (may be null)
*/
public DefaultFOPResourceAccessor(FOUserAgent userAgent) {
public DefaultFOPResourceAccessor(FOUserAgent userAgent, String categoryBaseURI, URI baseURI) {
super(baseURI);
this.userAgent = userAgent;
this.categoryBaseURI = categoryBaseURI;
}

/** {@inheritDoc} */
public InputStream createInputStream(URI uri) throws IOException {
Source src = userAgent.resolveURI(uri.toASCIIString());
//Step 1: resolve against local base URI --> URI
URI resolved = resolveAgainstBase(uri);

//Step 2: resolve against the user agent --> stream
Source src;
src = userAgent.resolveURI(resolved.toASCIIString(), this.categoryBaseURI);

if (src == null) {
return null;
throw new FileNotFoundException("Resource not found: " + uri.toASCIIString());
} else if (src instanceof StreamSource) {
StreamSource ss = (StreamSource)src;
InputStream in = ss.getInputStream();
return in;
} else {
return null;
if (in != null) {
return in;
}
if (ss.getReader() != null) {
//Don't support reader, retry using system ID below
IOUtils.closeQuietly(ss.getReader());
}
}
URL url = new URL(src.getSystemId());
return url.openStream();
}

}

+ 26
- 8
src/java/org/apache/fop/afp/util/SimpleResourceAccessor.java View File

@@ -26,7 +26,8 @@ import java.net.URI;
import java.net.URL;

/**
* Simple implementation of the {@link ResourceAccessor} interface for access via files.
* Simple implementation of the {@link ResourceAccessor} interface for access relative to a
* base URI.
*/
public class SimpleResourceAccessor implements ResourceAccessor {

@@ -34,23 +35,40 @@ public class SimpleResourceAccessor implements ResourceAccessor {

/**
* Creates a new simple resource accessor.
* @param basePath the base path to resolve relative URIs to
* @param baseURI the base URI to resolve relative URIs against (may be null)
*/
public SimpleResourceAccessor(File basePath) {
this.baseURI = basePath.toURI();
public SimpleResourceAccessor(URI baseURI) {
this.baseURI = baseURI;
}

/**
* Creates a new simple resource accessor.
* @param basePath the base path to resolve relative URIs to
* @param baseDir the base directory to resolve relative filenames against (may be null)
*/
public SimpleResourceAccessor(String basePath) {
this(new File(basePath));
public SimpleResourceAccessor(File baseDir) {
this(baseDir != null ? baseDir.toURI() : null);
}

/**
* Returns the base URI.
* @return the base URI (or null if no base URI was set)
*/
public URI getBaseURI() {
return this.baseURI;
}

/**
* Resolve the given URI against the baseURI.
* @param uri the URI to resolve
* @return the resolved URI
*/
protected URI resolveAgainstBase(URI uri) {
return (getBaseURI() != null ? getBaseURI().resolve(uri) : uri);
}

/** {@inheritDoc} */
public InputStream createInputStream(URI uri) throws IOException {
URI resolved = this.baseURI.resolve(uri);
URI resolved = resolveAgainstBase(uri);
URL url = resolved.toURL();
return url.openStream();
}

+ 5
- 3
src/java/org/apache/fop/render/afp/AFPPainter.java View File

@@ -49,6 +49,7 @@ import org.apache.fop.afp.modca.PresentationTextObject;
import org.apache.fop.afp.modca.ResourceObject;
import org.apache.fop.afp.ptoca.PtocaBuilder;
import org.apache.fop.afp.ptoca.PtocaProducer;
import org.apache.fop.afp.util.ResourceAccessor;
import org.apache.fop.fonts.Font;
import org.apache.fop.fonts.FontInfo;
import org.apache.fop.fonts.FontTriplet;
@@ -342,12 +343,13 @@ public class AFPPainter extends AbstractIFPainter {
try {
//Embed fonts (char sets and code pages)
//TODO This should be moved to a place where it has less performance impact
if (charSet.getPath() != null) {
if (charSet.getResourceAccessor() != null) {
ResourceAccessor accessor = charSet.getResourceAccessor();
documentHandler.getResourceManager().createIncludedResource(
charSet.getName(), charSet.getPath(),
charSet.getName(), accessor,
ResourceObject.TYPE_FONT_CHARACTER_SET);
documentHandler.getResourceManager().createIncludedResource(
charSet.getCodePage(), charSet.getPath(),
charSet.getCodePage(), accessor,
ResourceObject.TYPE_CODE_PAGE);
}
} catch (IOException ioe) {

+ 29
- 3
src/java/org/apache/fop/render/afp/AFPRendererConfigurator.java View File

@@ -20,6 +20,8 @@
package org.apache.fop.render.afp;

import java.io.File;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.List;

import org.apache.avalon.framework.configuration.Configuration;
@@ -33,6 +35,8 @@ import org.apache.fop.afp.fonts.CharacterSet;
import org.apache.fop.afp.fonts.FopCharacterSet;
import org.apache.fop.afp.fonts.OutlineFont;
import org.apache.fop.afp.fonts.RasterFont;
import org.apache.fop.afp.util.DefaultFOPResourceAccessor;
import org.apache.fop.afp.util.ResourceAccessor;
import org.apache.fop.apps.FOPException;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.fonts.FontCollection;
@@ -85,7 +89,29 @@ public class AFPRendererConfigurator extends PrintRendererConfigurator
log.error("Mandatory font configuration element '<afp-font...' is missing");
return null;
}
String path = afpFontCfg.getAttribute("path", fontPath);

URI baseURI = null;
String uri = afpFontCfg.getAttribute("base-uri", fontPath);
if (uri == null) {
//Fallback for old attribute which only supports local filenames
String path = afpFontCfg.getAttribute("path", fontPath);
if (path != null) {
File f = new File(path);
baseURI = f.toURI();
}
} else {
try {
baseURI = new URI(uri);
} catch (URISyntaxException e) {
log.error("Invalid URI: " + e.getMessage());
return null;
}
}
ResourceAccessor accessor = new DefaultFOPResourceAccessor(
this.userAgent,
this.userAgent.getFactory().getFontManager().getFontBaseURL(),
baseURI);

String type = afpFontCfg.getAttribute("type");
if (type == null) {
log.error("Mandatory afp-font configuration attribute 'type=' is missing");
@@ -147,7 +173,7 @@ public class AFPRendererConfigurator extends PrintRendererConfigurator
}
} else {
font.addCharacterSet(size, new CharacterSet(
codepage, encoding, characterset, path));
codepage, encoding, characterset, accessor));
}
}
return new AFPFontInfo(font, tripletList);
@@ -180,7 +206,7 @@ public class AFPRendererConfigurator extends PrintRendererConfigurator
log.error(msg);
}
} else {
characterSet = new CharacterSet(codepage, encoding, characterset, path);
characterSet = new CharacterSet(codepage, encoding, characterset, accessor);
}
// Create a new font object
OutlineFont font = new OutlineFont(name, characterSet);

+ 3
- 0
status.xml View File

@@ -58,6 +58,9 @@
documents. Example: the fix of marks layering will be such a case when it's done.
-->
<release version="FOP Trunk" date="TBD">
<action context="Fonts" dev="JM" type="add">
AFP Fonts: Added support for full URI resolution on configured AFP fonts.
</action>
<action context="Renderers" dev="JM" type="add">
AFP Output: Tag Logical Element (TLE) is now also allowed on fo:page-sequence
(page group level).

Loading…
Cancel
Save