aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/documentation/intermediate-format-ng/fop-intermediate-format-ng-content.xsd2
-rw-r--r--src/java/org/apache/fop/afp/AFPResourceManager.java127
-rw-r--r--src/java/org/apache/fop/afp/fonts/AFPFont.java8
-rw-r--r--src/java/org/apache/fop/afp/modca/IncludedResourceObject.java63
-rw-r--r--src/java/org/apache/fop/afp/util/DefaultFOPResourceAccessor.java60
-rw-r--r--src/java/org/apache/fop/afp/util/ResourceAccessor.java40
-rw-r--r--src/java/org/apache/fop/afp/util/SimpleResourceAccessor.java58
-rw-r--r--src/java/org/apache/fop/fonts/FontLoader.java26
-rw-r--r--src/java/org/apache/fop/fonts/LazyFont.java2
-rw-r--r--src/java/org/apache/fop/fonts/autodetect/FontInfoFinder.java2
-rw-r--r--src/java/org/apache/fop/fonts/truetype/TTFFontLoader.java12
-rw-r--r--src/java/org/apache/fop/fonts/type1/Type1FontLoader.java15
-rw-r--r--src/java/org/apache/fop/pdf/PDFPaintingState.java75
-rw-r--r--src/java/org/apache/fop/render/afp/AFPPainter.java146
-rw-r--r--src/java/org/apache/fop/render/afp/AFPRendererConfigurator.java3
-rw-r--r--src/java/org/apache/fop/render/intermediate/AbstractIFPainter.java1
-rw-r--r--src/java/org/apache/fop/render/intermediate/IFPainter.java56
-rw-r--r--src/java/org/apache/fop/render/intermediate/IFParser.java7
-rw-r--r--src/java/org/apache/fop/render/intermediate/IFRenderer.java20
-rw-r--r--src/java/org/apache/fop/render/intermediate/IFSerializer.java12
-rw-r--r--src/java/org/apache/fop/render/java2d/ConfiguredFontCollection.java3
-rw-r--r--src/java/org/apache/fop/render/java2d/Java2DPainter.java10
-rw-r--r--src/java/org/apache/fop/render/pcl/PCLPainter.java34
-rw-r--r--src/java/org/apache/fop/render/pdf/PDFContentGenerator.java10
-rw-r--r--src/java/org/apache/fop/render/pdf/PDFPainter.java12
-rw-r--r--src/java/org/apache/fop/render/ps/FOPProcSet.java81
-rw-r--r--src/java/org/apache/fop/render/ps/PSDocumentHandler.java1
-rw-r--r--src/java/org/apache/fop/render/ps/PSPainter.java119
-rw-r--r--src/sandbox/org/apache/fop/render/svg/SVGPainter.java12
29 files changed, 844 insertions, 173 deletions
diff --git a/src/documentation/intermediate-format-ng/fop-intermediate-format-ng-content.xsd b/src/documentation/intermediate-format-ng/fop-intermediate-format-ng-content.xsd
index daedf9272..c0bdd281b 100644
--- a/src/documentation/intermediate-format-ng/fop-intermediate-format-ng-content.xsd
+++ b/src/documentation/intermediate-format-ng/fop-intermediate-format-ng-content.xsd
@@ -61,6 +61,8 @@
<xs:extension base="xs:string">
<xs:attribute name="x" use="required" type="mf:lengthType"/>
<xs:attribute name="y" use="required" type="mf:lengthType"/>
+ <xs:attribute name="letter-spacing" type="mf:lengthType"/>
+ <xs:attribute name="word-spacing" type="mf:lengthType"/>
<xs:attribute name="dx">
<xs:simpleType>
<xs:list itemType="mf:lengthType"/>
diff --git a/src/java/org/apache/fop/afp/AFPResourceManager.java b/src/java/org/apache/fop/afp/AFPResourceManager.java
index 9218ea8e4..78c06bedf 100644
--- a/src/java/org/apache/fop/afp/AFPResourceManager.java
+++ b/src/java/org/apache/fop/afp/AFPResourceManager.java
@@ -21,19 +21,32 @@ package org.apache.fop.afp;
import java.io.IOException;
import java.io.OutputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
import java.util.Map;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
import org.apache.fop.afp.modca.AbstractNamedAFPObject;
import org.apache.fop.afp.modca.AbstractPageObject;
import org.apache.fop.afp.modca.IncludeObject;
+import org.apache.fop.afp.modca.IncludedResourceObject;
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.util.ResourceAccessor;
+import org.apache.fop.afp.util.SimpleResourceAccessor;
/**
* Manages the creation and storage of document resources
*/
public class AFPResourceManager {
+
+ /** logging instance */
+ private static Log log = LogFactory.getLog(AFPResourceManager.class);
+
/** The AFP datastream (document tree) */
private DataStream dataStream;
@@ -47,8 +60,8 @@ public class AFPResourceManager {
/** Maintain a reference count of instream objects for referencing purposes */
private int instreamObjectCount = 0;
- /** a mapping of resourceInfo --> names of includable objects */
- private final Map/*<AFPResourceInfo,String>*/ includableObjectsMap
+ /** a mapping of resourceInfo --> include name */
+ private final Map/*<AFPResourceInfo,String>*/ includeNameMap
= new java.util.HashMap()/*<AFPResourceInfo,String>*/;
private Map pageSegmentMap = new java.util.HashMap();
@@ -120,7 +133,7 @@ public class AFPResourceManager {
AFPResourceInfo resourceInfo = dataObjectInfo.getResourceInfo();
updateResourceInfoUri(resourceInfo);
- String objectName = (String)includableObjectsMap.get(resourceInfo);
+ String objectName = (String)includeNameMap.get(resourceInfo);
if (objectName != null) {
// an existing data resource so reference it by adding an include to the current page
includeObject(dataObjectInfo, objectName);
@@ -156,35 +169,35 @@ public class AFPResourceManager {
useInclude &= resourceGroup != null;
if (useInclude) {
- boolean usePageSegment = dataObjectInfo.isCreatePageSegment();
-
- // if it is to reside within a resource group at print-file or external level
- if (resourceLevel.isPrintFile() || resourceLevel.isExternal()) {
- if (usePageSegment) {
- String pageSegmentName = "S10" + namedObj.getName().substring(3);
- namedObj.setName(pageSegmentName);
- PageSegment seg = new PageSegment(pageSegmentName);
- seg.addObject(namedObj);
- namedObj = seg;
- }
+ boolean usePageSegment = dataObjectInfo.isCreatePageSegment();
- // wrap newly created data object in a resource object
- namedObj = dataObjectFactory.createResource(namedObj, resourceInfo, objectType);
+ // if it is to reside within a resource group at print-file or external level
+ if (resourceLevel.isPrintFile() || resourceLevel.isExternal()) {
+ if (usePageSegment) {
+ String pageSegmentName = "S10" + namedObj.getName().substring(3);
+ namedObj.setName(pageSegmentName);
+ PageSegment seg = new PageSegment(pageSegmentName);
+ seg.addObject(namedObj);
+ namedObj = seg;
}
- // add data object into its resource group destination
- resourceGroup.addObject(namedObj);
+ // wrap newly created data object in a resource object
+ namedObj = dataObjectFactory.createResource(namedObj, resourceInfo, objectType);
+ }
- // create the include object
- objectName = namedObj.getName();
- if (usePageSegment) {
- includePageSegment(dataObjectInfo, objectName);
- pageSegmentMap.put(resourceInfo, objectName);
- } else {
- includeObject(dataObjectInfo, objectName);
- // record mapping of resource info to data object resource name
- includableObjectsMap.put(resourceInfo, objectName);
- }
+ // add data object into its resource group destination
+ resourceGroup.addObject(namedObj);
+
+ // create the include object
+ objectName = namedObj.getName();
+ if (usePageSegment) {
+ includePageSegment(dataObjectInfo, objectName);
+ pageSegmentMap.put(resourceInfo, objectName);
+ } else {
+ includeObject(dataObjectInfo, objectName);
+ // record mapping of resource info to data object resource name
+ includeNameMap.put(resourceInfo, objectName);
+ }
} else {
// not to be included so inline data object directly into the current page
@@ -206,10 +219,10 @@ public class AFPResourceManager {
private void includeObject(AFPDataObjectInfo dataObjectInfo,
String objectName) {
- IncludeObject includeObject
- = dataObjectFactory.createInclude(objectName, dataObjectInfo);
- dataStream.getCurrentPage().addObject(includeObject);
- }
+ IncludeObject includeObject
+ = dataObjectFactory.createInclude(objectName, dataObjectInfo);
+ dataStream.getCurrentPage().addObject(includeObject);
+ }
private void includePageSegment(AFPDataObjectInfo dataObjectInfo,
String pageSegmentName) {
@@ -221,6 +234,53 @@ 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 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,
+ byte resourceObjectType) throws IOException {
+ AFPResourceLevel resourceLevel = new AFPResourceLevel(AFPResourceLevel.PRINT_FILE);
+ URI uri;
+ try {
+ uri = new URI(resourceName.trim());
+ } catch (URISyntaxException e) {
+ throw new IOException("Could not create URI from resource name: " + resourceName
+ + " (" + e.getMessage() + ")");
+ }
+
+ AFPResourceInfo resourceInfo = new AFPResourceInfo();
+ resourceInfo.setLevel(resourceLevel);
+ resourceInfo.setName(resourceName);
+ resourceInfo.setUri(uri.toASCIIString());
+
+ String objectName = (String)includeNameMap.get(resourceInfo);
+ if (objectName == null) {
+ 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);
+
+ ResourceObject resourceObject = factory.createResource(resourceName);
+ resourceObject.setDataObject(resourceContent);
+ resourceObject.setType(resourceObjectType);
+
+ ResourceGroup resourceGroup = streamer.getResourceGroup(resourceLevel);
+ resourceGroup.addObject(resourceObject);
+ // record mapping of resource info to data object resource name
+ includeNameMap.put(resourceInfo, resourceName);
+ } else {
+ //skip, already created
+ }
+ }
+
+ /**
* Sets resource level defaults. The existing defaults over merged with the ones passed in
* as parameter.
* @param defaults the new defaults
@@ -236,4 +296,5 @@ public class AFPResourceManager {
public AFPResourceLevelDefaults getResourceLevelDefaults() {
return this.resourceLevelDefaults;
}
-} \ No newline at end of file
+
+}
diff --git a/src/java/org/apache/fop/afp/fonts/AFPFont.java b/src/java/org/apache/fop/afp/fonts/AFPFont.java
index 4801737b3..f56611087 100644
--- a/src/java/org/apache/fop/afp/fonts/AFPFont.java
+++ b/src/java/org/apache/fop/afp/fonts/AFPFont.java
@@ -97,6 +97,14 @@ public abstract class AFPFont extends Typeface {
*/
public abstract CharacterSet getCharacterSet(int size);
+ /**
+ * Indicates if this font may be embedded.
+ * @return True, if embedding is possible/permitted
+ */
+ public boolean isEmbeddable() {
+ return false; //TODO Complete AFP font embedding
+ }
+
/** {@inheritDoc} */
public String toString() {
return "name=" + name;
diff --git a/src/java/org/apache/fop/afp/modca/IncludedResourceObject.java b/src/java/org/apache/fop/afp/modca/IncludedResourceObject.java
new file mode 100644
index 000000000..296ab2d9d
--- /dev/null
+++ b/src/java/org/apache/fop/afp/modca/IncludedResourceObject.java
@@ -0,0 +1,63 @@
+/*
+ * 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.afp.modca;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URI;
+
+import org.apache.commons.io.IOUtils;
+
+import org.apache.fop.afp.util.ResourceAccessor;
+
+
+/**
+ * Encapsulates an included resource object that is loaded from an external file.
+ */
+public class IncludedResourceObject extends AbstractNamedAFPObject {
+
+ private ResourceAccessor resourceAccessor;
+ private URI uri;
+
+ /**
+ * Main constructor.
+ * @param name the name of the included resource
+ * @param resourceAccessor the resource accessor to load the external file with
+ * @param uri the URI of the external file
+ */
+ public IncludedResourceObject(String name,
+ ResourceAccessor resourceAccessor, URI uri) {
+ super(name);
+ this.resourceAccessor = resourceAccessor;
+ this.uri = uri;
+ }
+
+ /** {@inheritDoc} */
+ public void writeToStream(OutputStream os) throws IOException {
+ InputStream in = resourceAccessor.createInputStream(this.uri);
+ try {
+ IOUtils.copy(in, os);
+ } finally {
+ IOUtils.closeQuietly(in);
+ }
+ }
+
+}
diff --git a/src/java/org/apache/fop/afp/util/DefaultFOPResourceAccessor.java b/src/java/org/apache/fop/afp/util/DefaultFOPResourceAccessor.java
new file mode 100644
index 000000000..55c8eab7d
--- /dev/null
+++ b/src/java/org/apache/fop/afp/util/DefaultFOPResourceAccessor.java
@@ -0,0 +1,60 @@
+/*
+ * 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.afp.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+
+import javax.xml.transform.Source;
+import javax.xml.transform.stream.StreamSource;
+
+import org.apache.fop.apps.FOUserAgent;
+
+/**
+ * Default implementation of the {@link ResourceAccessor} interface for use inside FOP.
+ */
+public class DefaultFOPResourceAccessor implements ResourceAccessor {
+
+ private FOUserAgent userAgent;
+
+ /**
+ * Main constructor.
+ * @param userAgent the FO user agent
+ */
+ public DefaultFOPResourceAccessor(FOUserAgent userAgent) {
+ this.userAgent = userAgent;
+ }
+
+ /** {@inheritDoc} */
+ public InputStream createInputStream(URI uri) throws IOException {
+ Source src = userAgent.resolveURI(uri.toASCIIString());
+ if (src == null) {
+ return null;
+ } else if (src instanceof StreamSource) {
+ StreamSource ss = (StreamSource)src;
+ InputStream in = ss.getInputStream();
+ return in;
+ } else {
+ return null;
+ }
+ }
+
+}
diff --git a/src/java/org/apache/fop/afp/util/ResourceAccessor.java b/src/java/org/apache/fop/afp/util/ResourceAccessor.java
new file mode 100644
index 000000000..6b9995c44
--- /dev/null
+++ b/src/java/org/apache/fop/afp/util/ResourceAccessor.java
@@ -0,0 +1,40 @@
+/*
+ * 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.afp.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+
+/**
+ * Defines an interface through which external resource objects can be accessed.
+ */
+public interface ResourceAccessor {
+
+ /**
+ * Creates a new {@link InputStream} for the given URI that allows read access to an external
+ * resource.
+ * @param uri the URI of an external resource.
+ * @return the new input stream
+ * @throws IOException if an I/O error occurs while opening the resource
+ */
+ InputStream createInputStream(URI uri) throws IOException;
+
+}
diff --git a/src/java/org/apache/fop/afp/util/SimpleResourceAccessor.java b/src/java/org/apache/fop/afp/util/SimpleResourceAccessor.java
new file mode 100644
index 000000000..51772c253
--- /dev/null
+++ b/src/java/org/apache/fop/afp/util/SimpleResourceAccessor.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.afp.util;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URL;
+
+/**
+ * Simple implementation of the {@link ResourceAccessor} interface for access via files.
+ */
+public class SimpleResourceAccessor implements ResourceAccessor {
+
+ private URI baseURI;
+
+ /**
+ * Creates a new simple resource accessor.
+ * @param basePath the base path to resolve relative URIs to
+ */
+ public SimpleResourceAccessor(File basePath) {
+ this.baseURI = basePath.toURI();
+ }
+
+ /**
+ * Creates a new simple resource accessor.
+ * @param basePath the base path to resolve relative URIs to
+ */
+ public SimpleResourceAccessor(String basePath) {
+ this(new File(basePath));
+ }
+
+ /** {@inheritDoc} */
+ public InputStream createInputStream(URI uri) throws IOException {
+ URI resolved = this.baseURI.resolve(uri);
+ URL url = resolved.toURL();
+ return url.openStream();
+ }
+
+}
diff --git a/src/java/org/apache/fop/fonts/FontLoader.java b/src/java/org/apache/fop/fonts/FontLoader.java
index b3d32e38d..aaca5b3fd 100644
--- a/src/java/org/apache/fop/fonts/FontLoader.java
+++ b/src/java/org/apache/fop/fonts/FontLoader.java
@@ -53,16 +53,21 @@ public abstract class FontLoader {
protected boolean loaded = false;
/** true if the font will be embedded, false if it will be referenced only. */
protected boolean embedded = true;
+ /** true if kerning information shall be loaded if available. */
+ protected boolean useKerning = true;
/**
* Default constructor.
* @param fontFileURI the URI to the PFB file of a Type 1 font
* @param embedded indicates whether the font is embedded or referenced
+ * @param useKerning indicates whether kerning information shall be loaded if available
* @param resolver the font resolver used to resolve URIs
*/
- public FontLoader(String fontFileURI, boolean embedded, FontResolver resolver) {
+ public FontLoader(String fontFileURI, boolean embedded, boolean useKerning,
+ FontResolver resolver) {
this.fontFileURI = fontFileURI;
this.embedded = embedded;
+ this.useKerning = useKerning;
this.resolver = resolver;
}
@@ -82,7 +87,8 @@ public abstract class FontLoader {
*/
public static CustomFont loadFont(File fontFile, String subFontName,
boolean embedded, EncodingMode encodingMode, FontResolver resolver) throws IOException {
- return loadFont(fontFile.getAbsolutePath(), subFontName, embedded, encodingMode, resolver);
+ return loadFont(fontFile.getAbsolutePath(), subFontName,
+ embedded, encodingMode, true, resolver);
}
/**
@@ -96,8 +102,11 @@ public abstract class FontLoader {
* @throws IOException In case of an I/O error
*/
public static CustomFont loadFont(URL fontUrl, String subFontName,
- boolean embedded, EncodingMode encodingMode, FontResolver resolver) throws IOException {
- return loadFont(fontUrl.toExternalForm(), subFontName, embedded, encodingMode, resolver);
+ boolean embedded, EncodingMode encodingMode,
+ FontResolver resolver) throws IOException {
+ return loadFont(fontUrl.toExternalForm(), subFontName,
+ embedded, encodingMode, true,
+ resolver);
}
/**
@@ -106,12 +115,14 @@ public abstract class FontLoader {
* @param subFontName the sub-fontname of a font (for TrueType Collections, null otherwise)
* @param embedded indicates whether the font is embedded or referenced
* @param encodingMode the requested encoding mode
+ * @param useKerning indicates whether kerning information should be loaded if available
* @param resolver the font resolver to use when resolving URIs
* @return the newly loaded font
* @throws IOException In case of an I/O error
*/
public static CustomFont loadFont(String fontFileURI, String subFontName,
- boolean embedded, EncodingMode encodingMode, FontResolver resolver) throws IOException {
+ boolean embedded, EncodingMode encodingMode, boolean useKerning,
+ FontResolver resolver) throws IOException {
fontFileURI = fontFileURI.trim();
boolean type1 = isType1(fontFileURI);
FontLoader loader;
@@ -120,9 +131,10 @@ public abstract class FontLoader {
throw new IllegalArgumentException(
"CID encoding mode not supported for Type 1 fonts");
}
- loader = new Type1FontLoader(fontFileURI, embedded, resolver);
+ loader = new Type1FontLoader(fontFileURI, embedded, useKerning, resolver);
} else {
- loader = new TTFFontLoader(fontFileURI, subFontName, embedded, encodingMode, resolver);
+ loader = new TTFFontLoader(fontFileURI, subFontName,
+ embedded, encodingMode, useKerning, resolver);
}
return loader.getFont();
}
diff --git a/src/java/org/apache/fop/fonts/LazyFont.java b/src/java/org/apache/fop/fonts/LazyFont.java
index c18ed6965..e5d111d38 100644
--- a/src/java/org/apache/fop/fonts/LazyFont.java
+++ b/src/java/org/apache/fop/fonts/LazyFont.java
@@ -132,7 +132,7 @@ public class LazyFont extends Typeface implements FontDescriptor {
throw new RuntimeException("Cannot load font. No font URIs available.");
}
realFont = FontLoader.loadFont(fontEmbedPath, this.subFontName,
- this.embedded, this.encodingMode, resolver);
+ this.embedded, this.encodingMode, useKerning, resolver);
}
if (realFont instanceof FontDescriptor) {
realFontDescriptor = (FontDescriptor) realFont;
diff --git a/src/java/org/apache/fop/fonts/autodetect/FontInfoFinder.java b/src/java/org/apache/fop/fonts/autodetect/FontInfoFinder.java
index 951ebdcff..03a3e1018 100644
--- a/src/java/org/apache/fop/fonts/autodetect/FontInfoFinder.java
+++ b/src/java/org/apache/fop/fonts/autodetect/FontInfoFinder.java
@@ -225,7 +225,7 @@ public class FontInfoFinder {
}
try {
TTFFontLoader ttfLoader = new TTFFontLoader(
- fontFileURI, fontName, true, EncodingMode.AUTO, resolver);
+ fontFileURI, fontName, true, EncodingMode.AUTO, true, resolver);
customFont = ttfLoader.getFont();
if (this.eventListener != null) {
customFont.setEventListener(this.eventListener);
diff --git a/src/java/org/apache/fop/fonts/truetype/TTFFontLoader.java b/src/java/org/apache/fop/fonts/truetype/TTFFontLoader.java
index cbf9c9d17..405a25f9e 100644
--- a/src/java/org/apache/fop/fonts/truetype/TTFFontLoader.java
+++ b/src/java/org/apache/fop/fonts/truetype/TTFFontLoader.java
@@ -55,7 +55,7 @@ public class TTFFontLoader extends FontLoader {
* @param resolver the FontResolver for font URI resolution
*/
public TTFFontLoader(String fontFileURI, FontResolver resolver) {
- this(fontFileURI, null, true, EncodingMode.AUTO, resolver);
+ this(fontFileURI, null, true, EncodingMode.AUTO, true, resolver);
}
/**
@@ -65,11 +65,13 @@ public class TTFFontLoader extends FontLoader {
* TrueType fonts)
* @param embedded indicates whether the font is embedded or referenced
* @param encodingMode the requested encoding mode
+ * @param useKerning true to enable loading kerning info if available, false to disable
* @param resolver the FontResolver for font URI resolution
*/
public TTFFontLoader(String fontFileURI, String subFontName,
- boolean embedded, EncodingMode encodingMode, FontResolver resolver) {
- super(fontFileURI, embedded, resolver);
+ boolean embedded, EncodingMode encodingMode, boolean useKerning,
+ FontResolver resolver) {
+ super(fontFileURI, embedded, true, resolver);
this.subFontName = subFontName;
this.encodingMode = encodingMode;
if (this.encodingMode == EncodingMode.AUTO) {
@@ -164,7 +166,9 @@ public class TTFFontLoader extends FontLoader {
copyWidthsSingleByte(ttf);
}
- copyKerning(ttf, isCid);
+ if (useKerning) {
+ copyKerning(ttf, isCid);
+ }
if (this.embedded && ttf.isEmbeddable()) {
returnFont.setEmbedFileName(this.fontFileURI);
}
diff --git a/src/java/org/apache/fop/fonts/type1/Type1FontLoader.java b/src/java/org/apache/fop/fonts/type1/Type1FontLoader.java
index 981f3ad69..1d0c75605 100644
--- a/src/java/org/apache/fop/fonts/type1/Type1FontLoader.java
+++ b/src/java/org/apache/fop/fonts/type1/Type1FontLoader.java
@@ -45,12 +45,13 @@ public class Type1FontLoader extends FontLoader {
* Constructs a new Type 1 font loader.
* @param fontFileURI the URI to the PFB file of a Type 1 font
* @param embedded indicates whether the font is embedded or referenced
+ * @param useKerning indicates whether to load kerning information if available
* @param resolver the font resolver used to resolve URIs
* @throws IOException In case of an I/O error
*/
- public Type1FontLoader(String fontFileURI, boolean embedded, FontResolver resolver)
- throws IOException {
- super(fontFileURI, embedded, resolver);
+ public Type1FontLoader(String fontFileURI, boolean embedded, boolean useKerning,
+ FontResolver resolver) throws IOException {
+ super(fontFileURI, embedded, useKerning, resolver);
}
private String getPFMURI(String pfbURI) {
@@ -321,7 +322,9 @@ public class Type1FontLoader extends FontLoader {
singleFont.setWidth(chm.getCharCode(), (int)Math.round(chm.getWidthX()));
}
}
- returnFont.replaceKerningMap(afm.createXKerningMapEncoded());
+ if (useKerning) {
+ returnFont.replaceKerningMap(afm.createXKerningMapEncoded());
+ }
} else {
returnFont.setFlags(pfm.getFlags());
returnFont.setFirstChar(pfm.getFirstChar());
@@ -329,7 +332,9 @@ public class Type1FontLoader extends FontLoader {
for (short i = pfm.getFirstChar(); i <= pfm.getLastChar(); i++) {
singleFont.setWidth(i, pfm.getCharWidth(i));
}
- returnFont.replaceKerningMap(pfm.getKerning());
+ if (useKerning) {
+ returnFont.replaceKerningMap(pfm.getKerning());
+ }
}
}
diff --git a/src/java/org/apache/fop/pdf/PDFPaintingState.java b/src/java/org/apache/fop/pdf/PDFPaintingState.java
index 11dfc635a..ebe2b383b 100644
--- a/src/java/org/apache/fop/pdf/PDFPaintingState.java
+++ b/src/java/org/apache/fop/pdf/PDFPaintingState.java
@@ -62,14 +62,15 @@ public class PDFPaintingState extends org.apache.fop.util.AbstractPaintingState
* @return true if the new paint changes the current paint
*/
public boolean setPaint(Paint p) {
- Paint paint = ((PDFData)getData()).paint;
+ PDFData data = getPDFData();
+ Paint paint = data.paint;
if (paint == null) {
if (p != null) {
- ((PDFData)getData()).paint = p;
+ data.paint = p;
return true;
}
} else if (!paint.equals(p)) {
- ((PDFData)getData()).paint = p;
+ data.paint = p;
return true;
}
return false;
@@ -88,7 +89,7 @@ public class PDFPaintingState extends org.apache.fop.util.AbstractPaintingState
* @return true if the clip will change the current clip.
*/
public boolean checkClip(Shape cl) {
- Shape clip = ((PDFData)getData()).clip;
+ Shape clip = getPDFData().clip;
if (clip == null) {
if (cl != null) {
return true;
@@ -108,17 +109,40 @@ public class PDFPaintingState extends org.apache.fop.util.AbstractPaintingState
* @param cl the new clip in the current state
*/
public void setClip(Shape cl) {
- Shape clip = ((PDFData)getData()).clip;
+ PDFData data = getPDFData();
+ Shape clip = data.clip;
if (clip != null) {
Area newClip = new Area(clip);
newClip.intersect(new Area(cl));
- ((PDFData)getData()).clip = new GeneralPath(newClip);
+ data.clip = new GeneralPath(newClip);
} else {
- ((PDFData)getData()).clip = cl;
+ data.clip = cl;
}
}
/**
+ * Sets the character spacing (Tc).
+ * @param value the new value
+ * @return true if the value was changed with respect to the previous value
+ */
+ public boolean setCharacterSpacing(float value) {
+ PDFData data = getPDFData();
+ if (value != data.characterSpacing) {
+ data.characterSpacing = value;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Returns the current character spacing (Tc) value.
+ * @return the Tc value
+ */
+ public float getCharacterSpacing() {
+ return getPDFData().characterSpacing;
+ }
+
+ /**
* Get the current stack level.
*
* @return the current stack level
@@ -149,8 +173,8 @@ public class PDFPaintingState extends org.apache.fop.util.AbstractPaintingState
newState.addValues(state);
}
}
- if (((PDFData)getData()).gstate != null) {
- newState.addValues(((PDFData)getData()).gstate);
+ if (getPDFData().gstate != null) {
+ newState.addValues(getPDFData().gstate);
}
return newState;
}
@@ -177,32 +201,38 @@ public class PDFPaintingState extends org.apache.fop.util.AbstractPaintingState
getStateStack().add(copy);
}
+ private PDFData getPDFData() {
+ return (PDFData)getData();
+ }
+
private class PDFData extends org.apache.fop.util.AbstractPaintingState.AbstractData {
private static final long serialVersionUID = 3527950647293177764L;
private Paint paint = null;
private Paint backPaint = null;
- private int lineCap = 0;
- private int lineJoin = 0;
- private float miterLimit = 0;
- private boolean text = false;
- private int dashOffset = 0;
+ //private int lineCap = 0; //Disabled the ones that are not used, yet
+ //private int lineJoin = 0;
+ //private float miterLimit = 0;
+ //private int dashOffset = 0;
private Shape clip = null;
private PDFGState gstate = null;
+ //text state
+ private float characterSpacing = 0f;
+
/** {@inheritDoc} */
public Object clone() {
PDFData obj = (PDFData)super.clone();
obj.paint = this.paint;
obj.backPaint = this.paint;
- obj.lineCap = this.lineCap;
- obj.lineJoin = this.lineJoin;
- obj.miterLimit = this.miterLimit;
- obj.text = this.text;
- obj.dashOffset = this.dashOffset;
+ //obj.lineCap = this.lineCap;
+ //obj.lineJoin = this.lineJoin;
+ //obj.miterLimit = this.miterLimit;
+ //obj.dashOffset = this.dashOffset;
obj.clip = this.clip;
obj.gstate = this.gstate;
+ obj.characterSpacing = this.characterSpacing;
return obj;
}
@@ -211,10 +241,9 @@ public class PDFPaintingState extends org.apache.fop.util.AbstractPaintingState
return super.toString()
+ ", paint=" + paint
+ ", backPaint=" + backPaint
- + ", lineCap=" + lineCap
- + ", miterLimit=" + miterLimit
- + ", text=" + text
- + ", dashOffset=" + dashOffset
+ //+ ", lineCap=" + lineCap
+ //+ ", miterLimit=" + miterLimit
+ //+ ", dashOffset=" + dashOffset
+ ", clip=" + clip
+ ", gstate=" + gstate;
}
diff --git a/src/java/org/apache/fop/render/afp/AFPPainter.java b/src/java/org/apache/fop/render/afp/AFPPainter.java
index d22ea8326..0c2501b87 100644
--- a/src/java/org/apache/fop/render/afp/AFPPainter.java
+++ b/src/java/org/apache/fop/render/afp/AFPPainter.java
@@ -30,9 +30,6 @@ import java.util.Map;
import org.w3c.dom.Document;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
import org.apache.xmlgraphics.image.loader.ImageProcessingHints;
import org.apache.xmlgraphics.image.loader.ImageSessionContext;
@@ -49,6 +46,7 @@ import org.apache.fop.afp.fonts.AFPPageFonts;
import org.apache.fop.afp.fonts.CharacterSet;
import org.apache.fop.afp.modca.AbstractPageObject;
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.fonts.Font;
@@ -69,8 +67,8 @@ import org.apache.fop.util.CharUtilities;
*/
public class AFPPainter extends AbstractIFPainter {
- /** logging instance */
- private static Log log = LogFactory.getLog(AFPPainter.class);
+ //** logging instance */
+ //private static Log log = LogFactory.getLog(AFPPainter.class);
private static final int X = 0;
private static final int Y = 1;
@@ -241,7 +239,7 @@ public class AFPPainter extends AbstractIFPainter {
//TODO Try to resolve the name-clash between the AFPBorderPainter in the afp package
//and this one. Not done for now to avoid a lot of re-implementation and code duplication.
- private class AFPBorderPainterAdapter extends BorderPainter {
+ private static class AFPBorderPainterAdapter extends BorderPainter {
private AFPBorderPainter delegate;
@@ -312,8 +310,9 @@ public class AFPPainter extends AbstractIFPainter {
}
/** {@inheritDoc} */
- public void drawText(int x, int y, final int[] dx, int[] dy, final String text)
- throws IFException {
+ public void drawText(int x, int y,
+ final int letterSpacing, final int wordSpacing, final int[] dx,
+ final String text) throws IFException {
final int fontSize = this.state.getFontSize();
getPaintingState().setFontSize(fontSize);
@@ -322,8 +321,8 @@ public class AFPPainter extends AbstractIFPainter {
//TODO Ignored: state.getFontVariant()
String fontKey = getFontInfo().getInternalFontKey(triplet);
if (fontKey == null) {
- fontKey = getFontInfo().getInternalFontKey(
- new FontTriplet("any", Font.STYLE_NORMAL, Font.WEIGHT_NORMAL));
+ triplet = new FontTriplet("any", Font.STYLE_NORMAL, Font.WEIGHT_NORMAL);
+ fontKey = getFontInfo().getInternalFontKey(triplet);
}
// register font as necessary
@@ -339,6 +338,23 @@ public class AFPPainter extends AbstractIFPainter {
final CharacterSet charSet = afpFont.getCharacterSet(fontSize);
+ if (afpFont.isEmbeddable()) {
+ 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) {
+ documentHandler.getResourceManager().createIncludedResource(
+ charSet.getName(), charSet.getPath(),
+ ResourceObject.TYPE_FONT_CHARACTER_SET);
+ documentHandler.getResourceManager().createIncludedResource(
+ charSet.getCodePage(), charSet.getPath(),
+ ResourceObject.TYPE_CODE_PAGE);
+ }
+ } catch (IOException ioe) {
+ throw new IFException("Error while embedding font resources", ioe);
+ }
+ }
+
AbstractPageObject page = getDataStream().getCurrentPage();
PresentationTextObject pto = page.getPresentationTextObject();
try {
@@ -350,8 +366,6 @@ public class AFPPainter extends AbstractIFPainter {
builder.absoluteMoveBaseline(p.y);
builder.absoluteMoveInline(p.x);
- builder.setVariableSpaceCharacterIncrement(0);
- builder.setInterCharacterAdjustment(0);
builder.setExtendedTextColor(state.getTextColor());
builder.setCodedFont((byte)fontReference);
@@ -363,33 +377,105 @@ public class AFPPainter extends AbstractIFPainter {
int dxu = Math.round(unitConv.mpt2units(dx[0]));
builder.relativeMoveInline(-dxu);
}
- for (int i = 0; i < l; i++) {
- char orgChar = text.charAt(i);
- float glyphAdjust = 0;
- if (CharUtilities.isFixedWidthSpace(orgChar)) {
- sb.append(CharUtilities.SPACE);
- int spaceWidth = font.getCharWidth(CharUtilities.SPACE);
- int charWidth = font.getCharWidth(orgChar);
- glyphAdjust += (charWidth - spaceWidth);
- } else {
- sb.append(orgChar);
+
+ //Following are two variants for glyph placement.
+ //SVI does not seem to be implemented in the same way everywhere, so
+ //a fallback alternative is preserved here.
+ final boolean usePTOCAWordSpacing = true;
+ if (usePTOCAWordSpacing) {
+
+ int interCharacterAdjustment = 0;
+ if (letterSpacing != 0) {
+ interCharacterAdjustment = Math.round(unitConv.mpt2units(
+ letterSpacing));
+ }
+ builder.setInterCharacterAdjustment(interCharacterAdjustment);
+
+ int spaceWidth = font.getCharWidth(CharUtilities.SPACE);
+ int fixedSpaceCharacterIncrement = Math.round(unitConv.mpt2units(
+ spaceWidth + letterSpacing));
+ int varSpaceCharacterIncrement = fixedSpaceCharacterIncrement;
+ if (wordSpacing != 0) {
+ varSpaceCharacterIncrement = Math.round(unitConv.mpt2units(
+ spaceWidth + wordSpacing + letterSpacing));
}
+ builder.setVariableSpaceCharacterIncrement(varSpaceCharacterIncrement);
+
+ boolean fixedSpaceMode = false;
+
+ for (int i = 0; i < l; i++) {
+ char orgChar = text.charAt(i);
+ float glyphAdjust = 0;
+ if (CharUtilities.isFixedWidthSpace(orgChar)) {
+ flushText(builder, sb, charSet);
+ builder.setVariableSpaceCharacterIncrement(
+ fixedSpaceCharacterIncrement);
+ fixedSpaceMode = true;
+ sb.append(CharUtilities.SPACE);
+ int charWidth = font.getCharWidth(orgChar);
+ glyphAdjust += (charWidth - spaceWidth);
+ } else {
+ if (fixedSpaceMode) {
+ flushText(builder, sb, charSet);
+ builder.setVariableSpaceCharacterIncrement(
+ varSpaceCharacterIncrement);
+ fixedSpaceMode = false;
+ }
+ char ch;
+ if (orgChar == CharUtilities.NBSPACE) {
+ ch = ' '; //converted to normal space to allow word spacing
+ } else {
+ ch = orgChar;
+ }
+ sb.append(ch);
+ }
+
+ if (i < dxl - 1) {
+ glyphAdjust += dx[i + 1];
+ }
- if (i < dxl - 1) {
- glyphAdjust += dx[i + 1];
+ if (glyphAdjust != 0) {
+ flushText(builder, sb, charSet);
+ int increment = Math.round(unitConv.mpt2units(glyphAdjust));
+ builder.relativeMoveInline(increment);
+ }
}
+ } else {
+ for (int i = 0; i < l; i++) {
+ char orgChar = text.charAt(i);
+ float glyphAdjust = 0;
+ if (CharUtilities.isFixedWidthSpace(orgChar)) {
+ sb.append(CharUtilities.SPACE);
+ int spaceWidth = font.getCharWidth(CharUtilities.SPACE);
+ int charWidth = font.getCharWidth(orgChar);
+ glyphAdjust += (charWidth - spaceWidth);
+ } else {
+ sb.append(orgChar);
+ }
- if (glyphAdjust != 0) {
- if (sb.length() > 0) {
- builder.addTransparentData(charSet.encodeChars(sb));
- sb.setLength(0);
+ if ((wordSpacing != 0) && CharUtilities.isAdjustableSpace(orgChar)) {
+ glyphAdjust += wordSpacing;
+ }
+ glyphAdjust += letterSpacing;
+ if (i < dxl - 1) {
+ glyphAdjust += dx[i + 1];
+ }
+
+ if (glyphAdjust != 0) {
+ flushText(builder, sb, charSet);
+ int increment = Math.round(unitConv.mpt2units(glyphAdjust));
+ builder.relativeMoveInline(increment);
}
- int increment = Math.round(unitConv.mpt2units(glyphAdjust));
- builder.relativeMoveInline(increment);
}
}
+ flushText(builder, sb, charSet);
+ }
+
+ private void flushText(PtocaBuilder builder, StringBuffer sb,
+ final CharacterSet charSet) throws IOException {
if (sb.length() > 0) {
builder.addTransparentData(charSet.encodeChars(sb));
+ sb.setLength(0);
}
}
diff --git a/src/java/org/apache/fop/render/afp/AFPRendererConfigurator.java b/src/java/org/apache/fop/render/afp/AFPRendererConfigurator.java
index fffba85a1..bd6df6136 100644
--- a/src/java/org/apache/fop/render/afp/AFPRendererConfigurator.java
+++ b/src/java/org/apache/fop/render/afp/AFPRendererConfigurator.java
@@ -345,7 +345,8 @@ public class AFPRendererConfigurator extends PrintRendererConfigurator
LogUtil.handleException(log, e,
userAgent.getFactory().validateUserConfigStrictly());
}
-
+ } else {
+ fontCollections.add(new AFPFontCollection(userAgent.getEventBroadcaster(), null));
}
fontManager.setup(fontInfo,
diff --git a/src/java/org/apache/fop/render/intermediate/AbstractIFPainter.java b/src/java/org/apache/fop/render/intermediate/AbstractIFPainter.java
index 82ea18d00..7f6cce05a 100644
--- a/src/java/org/apache/fop/render/intermediate/AbstractIFPainter.java
+++ b/src/java/org/apache/fop/render/intermediate/AbstractIFPainter.java
@@ -411,5 +411,4 @@ public abstract class AbstractIFPainter implements IFPainter {
return new AffineTransform(matrix);
}
-
}
diff --git a/src/java/org/apache/fop/render/intermediate/IFPainter.java b/src/java/org/apache/fop/render/intermediate/IFPainter.java
index d60cd24d2..6369b0251 100644
--- a/src/java/org/apache/fop/render/intermediate/IFPainter.java
+++ b/src/java/org/apache/fop/render/intermediate/IFPainter.java
@@ -81,13 +81,53 @@ import org.apache.fop.traits.RuleStyle;
*/
public interface IFPainter {
- void startViewport(AffineTransform transform, Dimension size, Rectangle clipRect) throws IFException;
- void startViewport(AffineTransform[] transforms, Dimension size, Rectangle clipRect) throws IFException;
+ /**
+ * Starts a new viewport, establishing a new coordinate system. A viewport has a size and
+ * can optionally be clipped. Corresponds to SVG's svg element.
+ * @param transform the transformation matrix establishing the new coordinate system
+ * @param size the size of the viewport
+ * @param clipRect the clipping rectangle (may be null)
+ * @throws IFException if an error occurs while handling this element
+ */
+ void startViewport(AffineTransform transform, Dimension size, Rectangle clipRect)
+ throws IFException;
+
+ /**
+ * Starts a new viewport, establishing a new coordinate system. A viewport has a size and
+ * can optionally be clipped. Corresponds to SVG's svg element.
+ * @param transforms a series of transformation matrices establishing the new coordinate system
+ * @param size the size of the viewport
+ * @param clipRect the clipping rectangle (may be null)
+ * @throws IFException if an error occurs while handling this element
+ */
+ void startViewport(AffineTransform[] transforms, Dimension size, Rectangle clipRect)
+ throws IFException;
//For transform, Batik's org.apache.batik.parser.TransformListHandler/Parser can be used
+
+ /**
+ * Ends the current viewport and restores the previous coordinate system.
+ * @throws IFException if an error occurs while handling this element
+ */
void endViewport() throws IFException;
+ /**
+ * Starts a new group of graphical elements. Corresponds to SVG's g element.
+ * @param transforms a series of transformation matrices establishing the new coordinate system
+ * @throws IFException if an error occurs while handling this element
+ */
void startGroup(AffineTransform[] transforms) throws IFException;
+
+ /**
+ * Starts a new group of graphical elements. Corresponds to SVG's g element.
+ * @param transform the transformation matrix establishing the new coordinate system
+ * @throws IFException if an error occurs while handling this element
+ */
void startGroup(AffineTransform transform) throws IFException;
+
+ /**
+ * Ends the current group and restores the previous coordinate system.
+ * @throws IFException if an error occurs while handling this element
+ */
void endGroup() throws IFException;
/**
@@ -105,16 +145,18 @@ public interface IFPainter {
/**
* Draws text. The initial coordinates (x and y) point to the starting point at the normal
- * baseline of the font. The arrays (dx and dy) are optional and can be used to achieve
- * effects like kerning.
+ * baseline of the font. The parameters letterSpacing, wordSpacing and the array dx are
+ * optional and can be used to influence character positioning (for example, for kerning).
* @param x X-coordinate of the starting point of the text
* @param y Y-coordinate of the starting point of the text
- * @param dx an array of adjustment values for each character in X-direction
- * @param dy an array of adjustment values for each character in Y-direction
+ * @param letterSpacing additional spacing between characters (may be 0)
+ * @param wordSpacing additional spacing between words (may be 0)
+ * @param dx an array of adjustment values for each character in X-direction (may be null)
* @param text the text
* @throws IFException if an error occurs while handling this event
*/
- void drawText(int x, int y, int[] dx, int[] dy, String text) throws IFException;
+ void drawText(int x, int y, int letterSpacing, int wordSpacing,
+ int[] dx, String text) throws IFException;
/**
* Restricts the current clipping region with the given rectangle.
diff --git a/src/java/org/apache/fop/render/intermediate/IFParser.java b/src/java/org/apache/fop/render/intermediate/IFParser.java
index c5c88ca78..61d1838bc 100644
--- a/src/java/org/apache/fop/render/intermediate/IFParser.java
+++ b/src/java/org/apache/fop/render/intermediate/IFParser.java
@@ -462,9 +462,12 @@ public class IFParser implements IFConstants {
public void endElement() throws IFException {
int x = Integer.parseInt(lastAttributes.getValue("x"));
int y = Integer.parseInt(lastAttributes.getValue("y"));
+ String s = lastAttributes.getValue("letter-spacing");
+ int letterSpacing = (s != null ? Integer.parseInt(s) : 0);
+ s = lastAttributes.getValue("word-spacing");
+ int wordSpacing = (s != null ? Integer.parseInt(s) : 0);
int[] dx = XMLUtil.getAttributeAsIntArray(lastAttributes, "dx");
- int[] dy = XMLUtil.getAttributeAsIntArray(lastAttributes, "dy");
- painter.drawText(x, y, dx, dy, content.toString());
+ painter.drawText(x, y, letterSpacing, wordSpacing, dx, content.toString());
}
public boolean ignoreCharacters() {
diff --git a/src/java/org/apache/fop/render/intermediate/IFRenderer.java b/src/java/org/apache/fop/render/intermediate/IFRenderer.java
index abef34e22..03ec6a4c0 100644
--- a/src/java/org/apache/fop/render/intermediate/IFRenderer.java
+++ b/src/java/org/apache/fop/render/intermediate/IFRenderer.java
@@ -984,6 +984,7 @@ public class IFRenderer extends AbstractPathOrientedRenderer {
int bl = currentBPPosition + text.getOffset() + text.getBaselineOffset();
textUtil.flush();
textUtil.setStartPosition(rx, bl);
+ textUtil.setSpacing(text.getTextLetterSpaceAdjust(), text.getTextWordSpaceAdjust());
super.renderText(text);
textUtil.flush();
@@ -1009,9 +1010,9 @@ public class IFRenderer extends AbstractPathOrientedRenderer {
AbstractTextArea textArea = (AbstractTextArea)space.getParentArea();
renderText(s, null, font, textArea);
- if (space.isAdjustable()) {
+ if (textUtil.combined && space.isAdjustable()) {
//Used for justified text, for example
- int tws = ((TextArea) space.getParentArea()).getTextWordSpaceAdjust()
+ int tws = textArea.getTextWordSpaceAdjust()
+ 2 * textArea.getTextLetterSpaceAdjust();
if (tws != 0) {
textUtil.adjust(tws);
@@ -1042,7 +1043,7 @@ public class IFRenderer extends AbstractPathOrientedRenderer {
char ch = s.charAt(i);
textUtil.addChar(ch);
int glyphAdjust = 0;
- if (font.hasChar(ch)) {
+ if (textUtil.combined && font.hasChar(ch)) {
int tls = (i < l - 1 ? parentArea.getTextLetterSpaceAdjust() : 0);
glyphAdjust += tls;
}
@@ -1060,6 +1061,8 @@ public class IFRenderer extends AbstractPathOrientedRenderer {
private int lastDXPos = 0;
private StringBuffer text = new StringBuffer();
private int startx, starty;
+ private int tls, tws;
+ private boolean combined = false;
void addChar(char ch) {
text.append(ch);
@@ -1092,6 +1095,11 @@ public class IFRenderer extends AbstractPathOrientedRenderer {
this.starty = y;
}
+ void setSpacing(int tls, int tws) {
+ this.tls = tls;
+ this.tws = tws;
+ }
+
void flush() {
if (text.length() > 0) {
try {
@@ -1101,7 +1109,11 @@ public class IFRenderer extends AbstractPathOrientedRenderer {
effDX = new int[size];
System.arraycopy(dx, 0, effDX, 0, size);
}
- painter.drawText(startx, starty, effDX, null, text.toString());
+ if (combined) {
+ painter.drawText(startx, starty, 0, 0, effDX, text.toString());
+ } else {
+ painter.drawText(startx, starty, tls, tws, effDX, text.toString());
+ }
} catch (IFException e) {
handleIFException(e);
}
diff --git a/src/java/org/apache/fop/render/intermediate/IFSerializer.java b/src/java/org/apache/fop/render/intermediate/IFSerializer.java
index 50bc5f7de..e4aa86e9e 100644
--- a/src/java/org/apache/fop/render/intermediate/IFSerializer.java
+++ b/src/java/org/apache/fop/render/intermediate/IFSerializer.java
@@ -508,18 +508,22 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler
}
/** {@inheritDoc} */
- public void drawText(int x, int y, int[] dx, int[] dy, String text) throws IFException {
+ public void drawText(int x, int y, int letterSpacing, int wordSpacing,
+ int[] dx, String text) throws IFException {
try {
AttributesImpl atts = new AttributesImpl();
XMLUtil.addAttribute(atts, XMLConstants.XML_SPACE, "preserve");
addAttribute(atts, "x", Integer.toString(x));
addAttribute(atts, "y", Integer.toString(y));
+ if (letterSpacing != 0) {
+ addAttribute(atts, "letter-spacing", Integer.toString(letterSpacing));
+ }
+ if (wordSpacing != 0) {
+ addAttribute(atts, "word-spacing", Integer.toString(wordSpacing));
+ }
if (dx != null) {
addAttribute(atts, "dx", IFUtil.toString(dx));
}
- if (dy != null) {
- addAttribute(atts, "dy", IFUtil.toString(dy));
- }
handler.startElement(EL_TEXT, atts);
char[] chars = text.toCharArray();
handler.characters(chars, 0, chars.length);
diff --git a/src/java/org/apache/fop/render/java2d/ConfiguredFontCollection.java b/src/java/org/apache/fop/render/java2d/ConfiguredFontCollection.java
index 9f96087b9..29570e69f 100644
--- a/src/java/org/apache/fop/render/java2d/ConfiguredFontCollection.java
+++ b/src/java/org/apache/fop/render/java2d/ConfiguredFontCollection.java
@@ -88,7 +88,8 @@ public class ConfiguredFontCollection implements FontCollection {
font = new CustomFontMetricsMapper(fontMetrics, fontSource);
} else {
CustomFont fontMetrics = FontLoader.loadFont(
- fontFile, null, true, EncodingMode.AUTO, fontResolver);
+ fontFile, null, true, EncodingMode.AUTO,
+ configFontInfo.getKerning(), fontResolver);
font = new CustomFontMetricsMapper(fontMetrics);
}
diff --git a/src/java/org/apache/fop/render/java2d/Java2DPainter.java b/src/java/org/apache/fop/render/java2d/Java2DPainter.java
index c32df4f38..55c5b8015 100644
--- a/src/java/org/apache/fop/render/java2d/Java2DPainter.java
+++ b/src/java/org/apache/fop/render/java2d/Java2DPainter.java
@@ -46,6 +46,7 @@ import org.apache.fop.render.intermediate.IFException;
import org.apache.fop.render.intermediate.IFState;
import org.apache.fop.traits.BorderProps;
import org.apache.fop.traits.RuleStyle;
+import org.apache.fop.util.CharUtilities;
/**
* {@code IFPainter} implementation that paints on a Graphics2D instance.
@@ -83,6 +84,7 @@ public class Java2DPainter extends AbstractIFPainter {
* @param g2d the target Graphics2D instance
* @param context the IF context
* @param fontInfo the font information
+ * @param state the IF state object
*/
public Java2DPainter(Graphics2D g2d, IFContext context, FontInfo fontInfo, IFState state) {
super();
@@ -206,8 +208,8 @@ public class Java2DPainter extends AbstractIFPainter {
}
/** {@inheritDoc} */
- public void drawText(int x, int y, int[] dx, int[] dy, String text) throws IFException {
- //Note: dy is currently ignored
+ public void drawText(int x, int y, int letterSpacing, int wordSpacing, int[] dx, String text)
+ throws IFException {
g2dState.updateColor(state.getTextColor());
FontTriplet triplet = new FontTriplet(
state.getFontFamily(), state.getFontStyle(), state.getFontWeight());
@@ -234,6 +236,10 @@ public class Java2DPainter extends AbstractIFPainter {
float glyphAdjust = 0;
int cw = font.getCharWidth(orgChar);
+ if ((wordSpacing != 0) && CharUtilities.isAdjustableSpace(orgChar)) {
+ glyphAdjust += wordSpacing;
+ }
+ glyphAdjust += letterSpacing;
if (dx != null && i < dxl - 1) {
glyphAdjust += dx[i + 1];
}
diff --git a/src/java/org/apache/fop/render/pcl/PCLPainter.java b/src/java/org/apache/fop/render/pcl/PCLPainter.java
index 03e40d549..da4f6a656 100644
--- a/src/java/org/apache/fop/render/pcl/PCLPainter.java
+++ b/src/java/org/apache/fop/render/pcl/PCLPainter.java
@@ -312,8 +312,8 @@ public class PCLPainter extends AbstractIFPainter implements PCLConstants {
}
/** {@inheritDoc} */
- public void drawText(int x, int y, int[] dx, int[] dy, String text) throws IFException {
- //Note: dy is currently ignored
+ public void drawText(int x, int y, int letterSpacing, int wordSpacing, int[] dx, String text)
+ throws IFException {
try {
FontTriplet triplet = new FontTriplet(
state.getFontFamily(), state.getFontStyle(), state.getFontWeight());
@@ -324,13 +324,13 @@ public class PCLPainter extends AbstractIFPainter implements PCLConstants {
? false
: HardcodedFonts.setFont(gen, fontKey, state.getFontSize(), text);
if (pclFont) {
- drawTextNative(x, y, dx, text, triplet);
+ drawTextNative(x, y, letterSpacing, wordSpacing, dx, text, triplet);
} else {
- drawTextAsBitmap(x, y, dx, dy, text, triplet);
+ drawTextAsBitmap(x, y, letterSpacing, wordSpacing, dx, text, triplet);
if (DEBUG) {
state.setTextColor(Color.GRAY);
HardcodedFonts.setFont(gen, "F1", state.getFontSize(), text);
- drawTextNative(x, y, dx, text, triplet);
+ drawTextNative(x, y, letterSpacing, wordSpacing, dx, text, triplet);
}
}
} catch (IOException ioe) {
@@ -338,8 +338,8 @@ public class PCLPainter extends AbstractIFPainter implements PCLConstants {
}
}
- private void drawTextNative(int x, int y, int[] dx, String text, FontTriplet triplet)
- throws IOException {
+ private void drawTextNative(int x, int y, int letterSpacing, int wordSpacing, int[] dx,
+ String text, FontTriplet triplet) throws IOException {
Color textColor = state.getTextColor();
if (textColor != null) {
gen.setTransparencyMode(true, false);
@@ -376,6 +376,10 @@ public class PCLPainter extends AbstractIFPainter implements PCLConstants {
}
sb.append(ch);
+ if ((wordSpacing != 0) && CharUtilities.isAdjustableSpace(orgChar)) {
+ glyphAdjust += wordSpacing;
+ }
+ glyphAdjust += letterSpacing;
if (dx != null && i < dxl - 1) {
glyphAdjust += dx[i + 1];
}
@@ -391,7 +395,9 @@ public class PCLPainter extends AbstractIFPainter implements PCLConstants {
private static final double SAFETY_MARGIN_FACTOR = 0.05;
- private Rectangle getTextBoundingBox(int x, int y, int[] dx, int[] dy, String text,
+ private Rectangle getTextBoundingBox(int x, int y,
+ int letterSpacing, int wordSpacing, int[] dx,
+ String text,
Font font, FontMetricsMapper metrics) {
int maxAscent = metrics.getMaxAscent(font.getFontSize()) / 1000;
int descent = metrics.getDescender(font.getFontSize()) / 1000; //is negative
@@ -412,6 +418,10 @@ public class PCLPainter extends AbstractIFPainter implements PCLConstants {
float glyphAdjust = 0;
int cw = font.getCharWidth(orgChar);
+ if ((wordSpacing != 0) && CharUtilities.isAdjustableSpace(orgChar)) {
+ glyphAdjust += wordSpacing;
+ }
+ glyphAdjust += letterSpacing;
if (dx != null && i < dxl - 1) {
glyphAdjust += dx[i + 1];
}
@@ -425,7 +435,8 @@ public class PCLPainter extends AbstractIFPainter implements PCLConstants {
return boundingRect;
}
- private void drawTextAsBitmap(final int x, final int y, final int[] dx, final int[] dy,
+ private void drawTextAsBitmap(final int x, final int y,
+ final int letterSpacing, final int wordSpacing, final int[] dx,
final String text, FontTriplet triplet) throws IFException {
//Use Java2D to paint different fonts via bitmap
final Font font = parent.getFontInfo().getFontInstance(triplet, state.getFontSize());
@@ -439,7 +450,8 @@ public class PCLPainter extends AbstractIFPainter implements PCLConstants {
int safetyMargin = (int)(SAFETY_MARGIN_FACTOR * font.getFontSize());
final int baselineOffset = maxAscent + safetyMargin;
- final Rectangle boundingBox = getTextBoundingBox(x, y, dx, dy, text, font, mapper);
+ final Rectangle boundingBox = getTextBoundingBox(x, y,
+ letterSpacing, wordSpacing, dx, text, font, mapper);
final Dimension dim = boundingBox.getSize();
Graphics2DImagePainter painter = new Graphics2DImagePainter() {
@@ -462,7 +474,7 @@ public class PCLPainter extends AbstractIFPainter implements PCLConstants {
Java2DPainter painter = new Java2DPainter(g2d,
getContext(), parent.getFontInfo(), state);
try {
- painter.drawText(x, y, dx, dy, text);
+ painter.drawText(x, y, letterSpacing, wordSpacing, dx, text);
} catch (IFException e) {
//This should never happen with the Java2DPainter
throw new RuntimeException("Unexpected error while painting text", e);
diff --git a/src/java/org/apache/fop/render/pdf/PDFContentGenerator.java b/src/java/org/apache/fop/render/pdf/PDFContentGenerator.java
index 567ef0507..45fb7ec51 100644
--- a/src/java/org/apache/fop/render/pdf/PDFContentGenerator.java
+++ b/src/java/org/apache/fop/render/pdf/PDFContentGenerator.java
@@ -238,6 +238,16 @@ public class PDFContentGenerator {
}
/**
+ * Sets the current character spacing (Tc) value.
+ * @param value the Tc value (in unscaled text units)
+ */
+ public void updateCharacterSpacing(float value) {
+ if (getState().setCharacterSpacing(value)) {
+ currentStream.add(format(value) + " Tc\n");
+ }
+ }
+
+ /**
* Establishes a new foreground or fill color.
* @param col the color to apply
* @param fill true to set the fill color, false for the foreground color
diff --git a/src/java/org/apache/fop/render/pdf/PDFPainter.java b/src/java/org/apache/fop/render/pdf/PDFPainter.java
index 6386b8df6..fa00fd7b4 100644
--- a/src/java/org/apache/fop/render/pdf/PDFPainter.java
+++ b/src/java/org/apache/fop/render/pdf/PDFPainter.java
@@ -253,8 +253,8 @@ public class PDFPainter extends AbstractIFPainter {
}
/** {@inheritDoc} */
- public void drawText(int x, int y, int[] dx, int[] dy, String text) throws IFException {
- //Note: dy is currently ignored
+ public void drawText(int x, int y, int letterSpacing, int wordSpacing, int[] dx, String text)
+ throws IFException {
generator.updateColor(state.getTextColor(), true, null);
generator.beginTextObject();
FontTriplet triplet = new FontTriplet(
@@ -277,6 +277,8 @@ public class PDFPainter extends AbstractIFPainter {
PDFTextUtil textutil = generator.getTextUtil();
textutil.updateTf(fontKey, fontSize, tf.isMultiByte());
+ generator.updateCharacterSpacing((float)letterSpacing / 1000f);
+
textutil.writeTextMatrix(new AffineTransform(1, 0, 0, -1, x / 1000f, y / 1000f));
int l = text.length();
int dxl = (dx != null ? dx.length : 0);
@@ -300,6 +302,9 @@ public class PDFPainter extends AbstractIFPainter {
ch = (char)(ch % 256);
}
}
+ if ((wordSpacing != 0) && CharUtilities.isAdjustableSpace(orgChar)) {
+ glyphAdjust += wordSpacing;
+ }
} else {
if (CharUtilities.isFixedWidthSpace(orgChar)) {
//Fixed width space are rendered as spaces so copy/paste works in a reader
@@ -308,6 +313,9 @@ public class PDFPainter extends AbstractIFPainter {
glyphAdjust = -spaceDiff;
} else {
ch = font.mapChar(orgChar);
+ if ((wordSpacing != 0) && CharUtilities.isAdjustableSpace(orgChar)) {
+ glyphAdjust += wordSpacing;
+ }
}
}
textutil.writeTJMappedChar(ch);
diff --git a/src/java/org/apache/fop/render/ps/FOPProcSet.java b/src/java/org/apache/fop/render/ps/FOPProcSet.java
new file mode 100644
index 000000000..99b62a64d
--- /dev/null
+++ b/src/java/org/apache/fop/render/ps/FOPProcSet.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.ps;
+
+import java.io.IOException;
+
+import org.apache.xmlgraphics.ps.DSCConstants;
+import org.apache.xmlgraphics.ps.PSGenerator;
+import org.apache.xmlgraphics.ps.PSProcSet;
+
+/**
+ * Proc Set with FOP-specific procs.
+ */
+public final class FOPProcSet extends PSProcSet {
+
+ /** Singleton instance of the FOP procset */
+ public static final FOPProcSet INSTANCE = new FOPProcSet();
+
+ private FOPProcSet() {
+ super("Apache FOP Std ProcSet", 1.0f, 0);
+ }
+
+ /**
+ * Writes the procset to the PostScript file.
+ * @param gen the PS generator
+ * @throws IOException if an I/O error occurs
+ */
+ public void writeTo(PSGenerator gen) throws IOException {
+ gen.writeDSCComment(DSCConstants.BEGIN_RESOURCE,
+ new Object[] {TYPE_PROCSET, getName(),
+ Float.toString(getVersion()), Integer.toString(getRevision())});
+ gen.writeDSCComment(DSCConstants.VERSION,
+ new Object[] {Float.toString(getVersion()), Integer.toString(getRevision())});
+ gen.writeDSCComment(DSCConstants.COPYRIGHT, "Copyright 2009 "
+ + "The Apache Software Foundation. "
+ + "License terms: http://www.apache.org/licenses/LICENSE-2.0");
+ gen.writeDSCComment(DSCConstants.TITLE,
+ "Basic set of procedures used by Apache FOP");
+
+
+ gen.writeln("/TJ { % Similar but not equal to PDF's TJ operator");
+ gen.writeln(" {");
+ gen.writeln(" dup type /stringtype eq");
+ gen.writeln(" { show }"); //normal text show
+ gen.writeln(" { neg 1000 div 0 rmoveto }"); //negative X movement
+ gen.writeln(" ifelse");
+ gen.writeln(" } forall");
+ gen.writeln("} bd");
+
+ gen.writeln("/ATJ { % As TJ but adds letter-spacing");
+ gen.writeln(" /ATJls exch def");
+ gen.writeln(" {");
+ gen.writeln(" dup type /stringtype eq");
+ gen.writeln(" { ATJls 0 3 2 roll ashow }"); //normal text show
+ gen.writeln(" { neg 1000 div 0 rmoveto }"); //negative X movement
+ gen.writeln(" ifelse");
+ gen.writeln(" } forall");
+ gen.writeln("} bd");
+
+ gen.writeDSCComment(DSCConstants.END_RESOURCE);
+ gen.getResourceTracker().registerSuppliedResource(this);
+ }
+
+}
diff --git a/src/java/org/apache/fop/render/ps/PSDocumentHandler.java b/src/java/org/apache/fop/render/ps/PSDocumentHandler.java
index 7f86017c1..c3aac8cd8 100644
--- a/src/java/org/apache/fop/render/ps/PSDocumentHandler.java
+++ b/src/java/org/apache/fop/render/ps/PSDocumentHandler.java
@@ -199,6 +199,7 @@ public class PSDocumentHandler extends AbstractBinaryWritingIFDocumentHandler {
gen.writeDSCComment(DSCConstants.BEGIN_PROLOG);
PSProcSets.writeStdProcSet(gen);
PSProcSets.writeEPSProcSet(gen);
+ FOPProcSet.INSTANCE.writeTo(gen);
gen.writeDSCComment(DSCConstants.END_PROLOG);
//Setup
diff --git a/src/java/org/apache/fop/render/ps/PSPainter.java b/src/java/org/apache/fop/render/ps/PSPainter.java
index 02fb7292c..cb88f4670 100644
--- a/src/java/org/apache/fop/render/ps/PSPainter.java
+++ b/src/java/org/apache/fop/render/ps/PSPainter.java
@@ -312,8 +312,33 @@ public class PSPainter extends AbstractIFPainter {
}
}
+ private String formatMptAsPt(PSGenerator gen, int value) {
+ return gen.formatDouble(value / 1000.0);
+ }
+
+ /* Disabled: performance experiment (incomplete)
+
+ private static final String ZEROS = "0.00";
+
+ private String formatMptAsPt1(int value) {
+ String s = Integer.toString(value);
+ int len = s.length();
+ StringBuffer sb = new StringBuffer();
+ if (len < 4) {
+ sb.append(ZEROS.substring(0, 5 - len));
+ sb.append(s);
+ } else {
+ int dec = len - 3;
+ sb.append(s.substring(0, dec));
+ sb.append('.');
+ sb.append(s.substring(dec));
+ }
+ return sb.toString();
+ }*/
+
/** {@inheritDoc} */
- public void drawText(int x, int y, int[] dx, int[] dy, String text) throws IFException {
+ public void drawText(int x, int y, int letterSpacing, int wordSpacing,
+ int[] dx, String text) throws IFException {
try {
//Note: dy is currently ignored
PSGenerator generator = getGenerator();
@@ -334,16 +359,13 @@ public class PSPainter extends AbstractIFPainter {
singleByteFont = (SingleByteFont)tf;
}
Font font = getFontInfo().getFontInstance(triplet, sizeMillipoints);
- //String fontName = font.getFontName();
PSResource res = this.documentHandler.getPSResourceForFontKey(fontKey);
generator.useFont("/" + res.getName(), fontSize);
generator.getResourceTracker().notifyResourceUsageOnPage(res);
- //textutil.updateTf(fontKey, fontSize, tf.isMultiByte());
- generator.writeln("1 0 0 -1 " + generator.formatDouble(x / 1000.0)
- + " " + generator.formatDouble(y / 1000.0) + " Tm");
- //textutil.writeTextMatrix(new AffineTransform(1, 0, 0, -1, x / 1000f, y / 1000f));
+ generator.writeln("1 0 0 -1 " + formatMptAsPt(generator, x)
+ + " " + formatMptAsPt(generator, y) + " Tm");
int textLen = text.length();
if (singleByteFont != null && singleByteFont.hasAdditionalEncodings()) {
@@ -356,7 +378,8 @@ public class PSPainter extends AbstractIFPainter {
int encoding = mapped / 256;
if (currentEncoding != encoding) {
if (i > 0) {
- writeText(text, start, i - start, dx, dy, font, tf);
+ writeText(text, start, i - start,
+ letterSpacing, wordSpacing, dx, font, tf);
}
if (encoding == 0) {
useFont(fontKey, sizeMillipoints);
@@ -367,62 +390,98 @@ public class PSPainter extends AbstractIFPainter {
start = i;
}
}
- writeText(text, start, textLen - start, dx, dy, font, tf);
+ writeText(text, start, textLen - start,
+ letterSpacing, wordSpacing, dx, font, tf);
} else {
//Simple single-font painting
useFont(fontKey, sizeMillipoints);
- writeText(text, 0, textLen, dx, dy, font, tf);
+ writeText(text, 0, textLen,
+ letterSpacing, wordSpacing, dx, font, tf);
}
} catch (IOException ioe) {
throw new IFException("I/O error in drawText()", ioe);
}
}
- private void writeText(String text, int start, int len, int[] dx, int[] dy,
+ private void writeText(String text, int start, int len,
+ int letterSpacing, int wordSpacing, int[] dx,
Font font, Typeface tf) throws IOException {
PSGenerator generator = getGenerator();
int end = start + len;
int initialSize = len;
initialSize += initialSize / 2;
+
+ boolean hasLetterSpacing = (letterSpacing != 0);
+ boolean needTJ = false;
+
+ int lineStart = 0;
+ StringBuffer accText = new StringBuffer(initialSize);
StringBuffer sb = new StringBuffer(initialSize);
- sb.append("(");
- int[] offsets = new int[len];
int dxl = (dx != null ? dx.length : 0);
for (int i = start; i < end; i++) {
char orgChar = text.charAt(i);
char ch;
int cw;
+ int glyphAdjust = 0;
if (CharUtilities.isFixedWidthSpace(orgChar)) {
//Fixed width space are rendered as spaces so copy/paste works in a reader
ch = font.mapChar(CharUtilities.SPACE);
- //int spaceDiff = font.getCharWidth(ch) - font.getCharWidth(orgChar);
- //glyphAdjust = -(spaceDiff);
+ cw = font.getCharWidth(orgChar);
+ glyphAdjust = font.getCharWidth(ch) - cw;
} else {
+ if ((wordSpacing != 0) && CharUtilities.isAdjustableSpace(orgChar)) {
+ glyphAdjust -= wordSpacing;
+ }
ch = font.mapChar(orgChar);
- //cw = tf.getWidth(ch, font.getFontSize()) / 1000;
+ cw = font.getCharWidth(orgChar);
}
- cw = font.getCharWidth(orgChar);
- int glyphAdjust = 0;
if (dx != null && i < dxl - 1) {
- glyphAdjust += dx[i + 1];
+ glyphAdjust -= dx[i + 1];
}
- offsets[i - start] = cw + glyphAdjust;
char codepoint = (char)(ch % 256);
- PSGenerator.escapeChar(codepoint, sb);
- }
- sb.append(")" + PSGenerator.LF + "[");
- for (int i = 0; i < len; i++) {
- if (i > 0) {
- if (i % 8 == 0) {
- sb.append(PSGenerator.LF);
- } else {
- sb.append(" ");
+ PSGenerator.escapeChar(codepoint, accText); //add character to accumulated text
+ if (glyphAdjust != 0) {
+ needTJ = true;
+ if (sb.length() == 0) {
+ sb.append('['); //Need to start TJ
+ }
+ if (accText.length() > 0) {
+ if ((sb.length() - lineStart + accText.length()) > 200) {
+ sb.append(PSGenerator.LF);
+ lineStart = sb.length();
+ }
+ sb.append('(');
+ sb.append(accText);
+ sb.append(") ");
+ accText.setLength(0); //reset accumulated text
}
+ sb.append(Integer.toString(glyphAdjust)).append(' ');
+ }
+ }
+ if (needTJ) {
+ if (accText.length() > 0) {
+ sb.append('(');
+ sb.append(accText);
+ sb.append(')');
+ }
+ if (hasLetterSpacing) {
+ sb.append("] " + formatMptAsPt(generator, letterSpacing) + " ATJ");
+ } else {
+ sb.append("] TJ");
+ }
+ } else {
+ sb.append('(').append(accText).append(")");
+ if (hasLetterSpacing) {
+ StringBuffer spb = new StringBuffer();
+ spb.append(formatMptAsPt(generator, letterSpacing))
+ .append(" 0 ");
+ sb.insert(0, spb.toString());
+ sb.append(" ashow");
+ } else {
+ sb.append(" show");
}
- sb.append(generator.formatDouble(offsets[i] / 1000f));
}
- sb.append("]" + PSGenerator.LF + "xshow");
generator.writeln(sb.toString());
}
diff --git a/src/sandbox/org/apache/fop/render/svg/SVGPainter.java b/src/sandbox/org/apache/fop/render/svg/SVGPainter.java
index d427096be..0ee13c021 100644
--- a/src/sandbox/org/apache/fop/render/svg/SVGPainter.java
+++ b/src/sandbox/org/apache/fop/render/svg/SVGPainter.java
@@ -325,19 +325,23 @@ public class SVGPainter extends AbstractIFPainter implements SVGConstants {
}
/** {@inheritDoc} */
- public void drawText(int x, int y, int[] dx, int[] dy, String text) throws IFException {
+ public void drawText(int x, int y, int letterSpacing, int wordSpacing, int[] dx, String text)
+ throws IFException {
try {
establish(MODE_TEXT);
AttributesImpl atts = new AttributesImpl();
XMLUtil.addAttribute(atts, XMLConstants.XML_SPACE, "preserve");
XMLUtil.addAttribute(atts, "x", Integer.toString(x));
XMLUtil.addAttribute(atts, "y", Integer.toString(y));
+ if (letterSpacing != 0) {
+ XMLUtil.addAttribute(atts, "letter-spacing", Integer.toString(letterSpacing));
+ }
+ if (wordSpacing != 0) {
+ XMLUtil.addAttribute(atts, "word-spacing", Integer.toString(wordSpacing));
+ }
if (dx != null) {
XMLUtil.addAttribute(atts, "dx", IFUtil.toString(dx));
}
- if (dy != null) {
- XMLUtil.addAttribute(atts, "dy", IFUtil.toString(dy));
- }
handler.startElement("text", atts);
char[] chars = text.toCharArray();
handler.characters(chars, 0, chars.length);