git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_TrueTypeInPostScript@959012 13f79535-47bb-0310-9956-ffa450edef68tags/fop-1_1rc1^2
@@ -34,7 +34,7 @@ public class CIDFontType extends ValuedEnum { | |||
/** | |||
* CID Font Type 2 (based on TrueType format) | |||
*/ | |||
public static final CIDFontType CIDTYPE2 = new CIDFontType("CIDFontType2", 1); | |||
public static final CIDFontType CIDTYPE2 = new CIDFontType("CIDFontType2", 2); | |||
/** |
@@ -42,19 +42,20 @@ class FontResourceCache { | |||
} | |||
/** | |||
* Returns the PSResource for the given font key. | |||
* Returns the PSFontResource for the given font key. | |||
* @param key the font key ("F*") | |||
* @return the matching PSResource | |||
* @return the matching PSFontResource instance | |||
*/ | |||
public PSResource getPSResourceForFontKey(String key) { | |||
PSResource res = null; | |||
public PSFontResource getFontResourceForFontKey(String key) { | |||
PSFontResource res = null; | |||
if (this.fontResources != null) { | |||
res = (PSResource)this.fontResources.get(key); | |||
res = (PSFontResource)this.fontResources.get(key); | |||
} else { | |||
this.fontResources = new java.util.HashMap(); | |||
} | |||
if (res == null) { | |||
res = new PSResource(PSResource.TYPE_FONT, getPostScriptNameForFontKey(key)); | |||
res = PSFontResource.createFontResource( | |||
new PSResource(PSResource.TYPE_FONT, getPostScriptNameForFontKey(key))); | |||
this.fontResources.put(key, res); | |||
} | |||
return res; |
@@ -0,0 +1,57 @@ | |||
/* | |||
* 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; | |||
/** | |||
* A helper class to hex-encoded representations of numbers. | |||
*/ | |||
final class HexEncoder { | |||
private HexEncoder() { } | |||
/** | |||
* Returns an hex encoding of the given number as a string of the given length, | |||
* left-padded with zeros if necessary. | |||
* | |||
* @param n a number | |||
* @param width required length of the string | |||
* @return an hex-encoded representation of the number | |||
*/ | |||
static String encode(int n, int width) { | |||
char[] digits = new char[width]; | |||
for (int i = width - 1; i >= 0; i--) { | |||
int digit = n & 0xF; | |||
digits[i] = (char) (digit < 10 ? '0' + digit : 'A' + digit - 10); | |||
n >>= 4; | |||
} | |||
return new String(digits); | |||
} | |||
/** | |||
* Returns an hex encoding of the given character as a four-character string. | |||
* | |||
* @param c a character | |||
* @return an hex-encoded representation of the character | |||
*/ | |||
static String encode(char c) { | |||
return encode(c, 4); | |||
} | |||
} |
@@ -200,7 +200,8 @@ public class PSDocumentHandler extends AbstractBinaryWritingIFDocumentHandler { | |||
gen.writeDSCComment(DSCConstants.BEGIN_SETUP); | |||
PSRenderingUtil.writeSetupCodeList(gen, setupCodeList, "SetupCode"); | |||
if (!psUtil.isOptimizeResources()) { | |||
this.fontResources.addAll(PSFontUtils.writeFontDict(gen, fontInfo)); | |||
this.fontResources.addAll(PSFontUtils.writeFontDict(gen, fontInfo, | |||
PSEventProducer.Provider.get(getUserAgent().getEventBroadcaster()))); | |||
} else { | |||
gen.commentln("%FOPFontSetup"); //Place-holder, will be replaced in the second pass | |||
} | |||
@@ -538,8 +539,8 @@ public class PSDocumentHandler extends AbstractBinaryWritingIFDocumentHandler { | |||
* @param key the font key ("F*") | |||
* @return the matching PSResource | |||
*/ | |||
protected PSResource getPSResourceForFontKey(String key) { | |||
return this.fontResources.getPSResourceForFontKey(key); | |||
protected PSFontResource getPSResourceForFontKey(String key) { | |||
return this.fontResources.getFontResourceForFontKey(key); | |||
} | |||
/** |
@@ -50,4 +50,11 @@ public interface PSEventProducer extends EventProducer { | |||
*/ | |||
void postscriptDictionaryParseError(Object source, String content, Exception e); | |||
/** | |||
* PostScript Level 3 features are being used. | |||
* | |||
* @param source the event source | |||
* @event.severity WARN | |||
*/ | |||
void postscriptLevel3Used(Object source); | |||
} |
@@ -1,4 +1,5 @@ | |||
<?xml version="1.0" encoding="UTF-8"?> | |||
<catalogue xml:lang="en"> | |||
<message key="postscriptDictionaryParseError">Failed to parse dictionary string. Reason: {e}, content = "{content}"</message> | |||
<message key="postscriptLevel3Used">PostScript Level 3 features are needed to handle this document. Please make sure that your printer supports PostScript Level 3.</message> | |||
</catalogue> |
@@ -0,0 +1,77 @@ | |||
/* | |||
* 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 org.apache.xmlgraphics.ps.PSResource; | |||
import org.apache.xmlgraphics.ps.dsc.ResourceTracker; | |||
/** | |||
* A DSC resource corresponding to a font. This class handles the possible other resources | |||
* that a font may depend on. For example, a CID-keyed font depends on a CIDFont resource, a | |||
* CMap resource, and the ProcSet CIDInit resource. | |||
*/ | |||
abstract class PSFontResource { | |||
static PSFontResource createFontResource(final PSResource fontResource) { | |||
return new PSFontResource() { | |||
String getName() { | |||
return fontResource.getName(); | |||
} | |||
void notifyResourceUsageOnPage(ResourceTracker resourceTracker) { | |||
resourceTracker.notifyResourceUsageOnPage(fontResource); | |||
} | |||
}; | |||
} | |||
static PSFontResource createFontResource(final PSResource fontResource, | |||
final PSResource procsetCIDInitResource, final PSResource cmapResource, | |||
final PSResource cidFontResource) { | |||
return new PSFontResource() { | |||
String getName() { | |||
return fontResource.getName(); | |||
} | |||
void notifyResourceUsageOnPage(ResourceTracker resourceTracker) { | |||
resourceTracker.notifyResourceUsageOnPage(fontResource); | |||
resourceTracker.notifyResourceUsageOnPage(procsetCIDInitResource); | |||
resourceTracker.notifyResourceUsageOnPage(cmapResource); | |||
resourceTracker.notifyResourceUsageOnPage(cidFontResource); | |||
} | |||
}; | |||
} | |||
/** | |||
* Returns the name of the font resource. | |||
* | |||
* @return the name of the font | |||
*/ | |||
abstract String getName(); | |||
/** | |||
* Notifies the given resource tracker of all the resources needed by this font. | |||
* | |||
* @param resourceTracker | |||
*/ | |||
abstract void notifyResourceUsageOnPage(ResourceTracker resourceTracker); | |||
} |
@@ -23,6 +23,7 @@ import java.io.FileNotFoundException; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.net.MalformedURLException; | |||
import java.util.Collections; | |||
import java.util.HashSet; | |||
import java.util.Iterator; | |||
import java.util.List; | |||
@@ -43,11 +44,13 @@ import org.apache.xmlgraphics.ps.dsc.ResourceTracker; | |||
import org.apache.xmlgraphics.util.io.ASCIIHexOutputStream; | |||
import org.apache.fop.fonts.Base14Font; | |||
import org.apache.fop.fonts.CIDSubset; | |||
import org.apache.fop.fonts.CustomFont; | |||
import org.apache.fop.fonts.Font; | |||
import org.apache.fop.fonts.FontInfo; | |||
import org.apache.fop.fonts.FontType; | |||
import org.apache.fop.fonts.LazyFont; | |||
import org.apache.fop.fonts.MultiByteFont; | |||
import org.apache.fop.fonts.SingleByteEncoding; | |||
import org.apache.fop.fonts.SingleByteFont; | |||
import org.apache.fop.fonts.Typeface; | |||
@@ -72,7 +75,12 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils { | |||
*/ | |||
public static Map writeFontDict(PSGenerator gen, FontInfo fontInfo) | |||
throws IOException { | |||
return writeFontDict(gen, fontInfo, fontInfo.getFonts(), true); | |||
return writeFontDict(gen, fontInfo, (PSEventProducer) null); | |||
} | |||
public static Map writeFontDict(PSGenerator gen, FontInfo fontInfo, | |||
PSEventProducer eventProducer) throws IOException { | |||
return writeFontDict(gen, fontInfo, fontInfo.getFonts(), true, eventProducer); | |||
} | |||
/** | |||
@@ -87,7 +95,7 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils { | |||
*/ | |||
public static Map writeFontDict(PSGenerator gen, FontInfo fontInfo, Map fonts) | |||
throws IOException { | |||
return writeFontDict(gen, fontInfo, fonts, false); | |||
return writeFontDict(gen, fontInfo, fonts, false, null); | |||
} | |||
/** | |||
@@ -101,7 +109,7 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils { | |||
* @throws IOException in case of an I/O problem | |||
*/ | |||
private static Map writeFontDict(PSGenerator gen, FontInfo fontInfo, Map fonts, | |||
boolean encodeAllCharacters) throws IOException { | |||
boolean encodeAllCharacters, PSEventProducer eventProducer) throws IOException { | |||
gen.commentln("%FOPBeginFontDict"); | |||
Map fontResources = new java.util.HashMap(); | |||
@@ -110,8 +118,8 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils { | |||
String key = (String)iter.next(); | |||
Typeface tf = getTypeFace(fontInfo, fonts, key); | |||
PSResource fontRes = new PSResource(PSResource.TYPE_FONT, tf.getFontName()); | |||
fontResources.put(key, fontRes); | |||
embedFont(gen, tf, fontRes); | |||
PSFontResource fontResource = embedFont(gen, tf, fontRes, eventProducer); | |||
fontResources.put(key, fontResource); | |||
if (tf instanceof SingleByteFont) { | |||
SingleByteFont sbf = (SingleByteFont)tf; | |||
@@ -191,29 +199,45 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils { | |||
return tf; | |||
} | |||
/** | |||
* Embeds a font in the PostScript file. | |||
* @param gen the PostScript generator | |||
* @param tf the font | |||
* @param fontRes the PSResource associated with the font | |||
* @throws IOException In case of an I/O error | |||
*/ | |||
public static void embedFont(PSGenerator gen, Typeface tf, PSResource fontRes) | |||
throws IOException { | |||
private static PSFontResource embedFont(PSGenerator gen, Typeface tf, PSResource fontRes, | |||
PSEventProducer eventProducer) throws IOException { | |||
boolean embeddedFont = false; | |||
FontType fontType = tf.getFontType(); | |||
if (fontType == FontType.TYPE1 || fontType == FontType.TRUETYPE) { | |||
PSFontResource fontResource = null; | |||
if (fontType == FontType.TYPE1 || fontType == FontType.TRUETYPE | |||
|| fontType == FontType.TYPE0) { | |||
if (tf instanceof CustomFont) { | |||
CustomFont cf = (CustomFont)tf; | |||
if (isEmbeddable(cf)) { | |||
InputStream in = getInputStreamOnFont(gen, cf); | |||
if (in != null) { | |||
if (fontType == FontType.TYPE0) { | |||
if (gen.embedIdentityH()) { | |||
/* | |||
* First CID-keyed font to be embedded; add | |||
* %%IncludeResource: comment for ProcSet CIDInit. | |||
*/ | |||
gen.includeProcsetCIDInitResource(); | |||
if (eventProducer != null) { | |||
eventProducer.postscriptLevel3Used(gen); | |||
} | |||
} | |||
PSResource cidFontResource = embedCIDFont(gen, (MultiByteFont) tf, in); | |||
fontResource = PSFontResource.createFontResource(fontRes, | |||
gen.getProcsetCIDInitResource(), | |||
gen.getIdentityHCMapResource(), | |||
cidFontResource); | |||
} | |||
gen.writeDSCComment(DSCConstants.BEGIN_RESOURCE, | |||
fontRes); | |||
if (fontType == FontType.TYPE1) { | |||
embedType1Font(gen, in); | |||
} else { | |||
fontResource = PSFontResource.createFontResource(fontRes); | |||
} else if (fontType == FontType.TRUETYPE) { | |||
embedTrueTypeFont(gen, (SingleByteFont) tf, in); | |||
fontResource = PSFontResource.createFontResource(fontRes); | |||
} else { | |||
embedType0Font(gen, (MultiByteFont) tf, in); | |||
} | |||
gen.writeDSCComment(DSCConstants.END_RESOURCE); | |||
gen.getResourceTracker().registerSuppliedResource(fontRes); | |||
@@ -229,6 +253,7 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils { | |||
if (!embeddedFont) { | |||
gen.writeDSCComment(DSCConstants.INCLUDE_RESOURCE, fontRes); | |||
} | |||
return fontResource; | |||
} | |||
private static void embedTrueTypeFont(PSGenerator gen, | |||
@@ -236,15 +261,25 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils { | |||
/* See Adobe Technical Note #5012, "The Type 42 Font Format Specification" */ | |||
gen.commentln("%!PS-TrueTypeFont-65536-65536-1"); // TODO TrueType & font versions | |||
gen.writeln("11 dict begin"); | |||
createType42DictionaryEntries(gen, font, fontStream, font.getCMaps()); | |||
gen.writeln("FontName currentdict end definefont pop"); | |||
} | |||
private static void createType42DictionaryEntries(PSGenerator gen, CustomFont font, | |||
InputStream fontStream, List cmaps) throws IOException { | |||
gen.write("/FontName /"); | |||
gen.write(font.getFontName()); | |||
gen.writeln(" def"); | |||
gen.writeln("/PaintType 0 def"); | |||
gen.writeln("/FontMatrix [1 0 0 1 0 0] def"); | |||
writeFontBBox(gen, font); | |||
gen.writeln("/FontType 42 def"); | |||
gen.writeln("/Encoding 256 array"); | |||
gen.writeln("0 1 255{1 index exch/.notdef put}for"); | |||
Set glyphs = new HashSet(); | |||
for (int i = 0; i < Glyphs.WINANSI_ENCODING.length; i++) { | |||
gen.write("dup "); | |||
gen.write(Integer.toString(i)); | |||
gen.write(i); | |||
gen.write(" /"); | |||
String glyphName = Glyphs.charToGlyphName(Glyphs.WINANSI_ENCODING[i]); | |||
if (glyphName.equals("")) { | |||
@@ -256,16 +291,6 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils { | |||
gen.writeln(" put"); | |||
} | |||
gen.writeln("readonly def"); | |||
gen.writeln("/PaintType 0 def"); | |||
gen.writeln("/FontMatrix [1 0 0 1 0 0] def"); | |||
int[] bbox = font.getFontBBox(); | |||
gen.write("/FontBBox["); | |||
for (int i = 0; i < 4; i++) { | |||
gen.write(" "); | |||
gen.write(Integer.toString(bbox[i])); | |||
} | |||
gen.writeln(" ] def"); | |||
gen.writeln("/FontType 42 def"); | |||
gen.write("/sfnts["); | |||
/* | |||
* Store the font file in an array of hex-encoded strings. Strings are limited to | |||
@@ -289,23 +314,21 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils { | |||
} | |||
gen.writeln("]def"); | |||
gen.write("/CharStrings "); | |||
gen.write(Integer.toString(glyphs.size() + 1)); | |||
gen.write(glyphs.size() + 1); | |||
gen.writeln(" dict dup begin"); | |||
gen.write("/"); | |||
gen.write(Glyphs.NOTDEF); | |||
gen.writeln(" 0 def"); // TODO always glyph index 0? | |||
// TODO ugly and temporary, until CID is implemented | |||
List cmaps = font.getCMaps(); | |||
for (Iterator iter = glyphs.iterator(); iter.hasNext();) { | |||
String glyphName = (String) iter.next(); | |||
gen.write("/"); | |||
gen.write(glyphName); | |||
gen.write(" "); | |||
gen.write(Integer.toString(getGlyphIndex(glyphName, cmaps))); | |||
gen.write(getGlyphIndex(glyphName, cmaps)); | |||
gen.writeln(" def"); | |||
} | |||
gen.writeln("end readonly def"); | |||
gen.writeln("FontName currentdict end definefont pop"); | |||
} | |||
private static int getGlyphIndex(String glyphName, List cmaps) { | |||
@@ -319,6 +342,96 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils { | |||
return 0; | |||
} | |||
private static void embedType0Font(PSGenerator gen, MultiByteFont font, InputStream fontStream) | |||
throws IOException { | |||
String psName = font.getFontName(); | |||
gen.write("/"); | |||
gen.write(psName); | |||
gen.write(" /Identity-H [/"); | |||
gen.write(psName); | |||
gen.writeln("] composefont pop"); | |||
} | |||
private static PSResource embedCIDFont(PSGenerator gen, | |||
MultiByteFont font, InputStream fontStream) throws IOException { | |||
String psName = font.getFontName(); | |||
gen.write("%%BeginResource: CIDFont "); | |||
gen.writeln(psName); | |||
gen.write("%%Title: ("); | |||
gen.write(psName); | |||
gen.writeln(" Adobe Identity 0)"); | |||
gen.writeln("%%Version: 1"); // TODO use font revision? | |||
gen.writeln("/CIDInit /ProcSet findresource begin"); | |||
gen.writeln("20 dict begin"); | |||
gen.write("/CIDFontName /"); | |||
gen.write(psName); | |||
gen.writeln(" def"); | |||
gen.writeln("/CIDFontVersion 1 def"); // TODO same as %%Version above | |||
gen.write("/CIDFontType "); | |||
gen.write(font.getCIDType().getValue()); | |||
gen.writeln(" def"); | |||
gen.writeln("/CIDSystemInfo 3 dict dup begin"); | |||
gen.writeln(" /Registry (Adobe) def"); | |||
gen.writeln(" /Ordering (Identity) def"); | |||
gen.writeln(" /Supplement 0 def"); | |||
gen.writeln("end def"); | |||
// TODO UIDBase (and UIDOffset in CMap) necessary if PostScript Level 1 & 2 | |||
// interpreters are to be supported | |||
// (Level 1: with composite font extensions; Level 2: those that do not offer | |||
// native mode support for CID-keyed fonts) | |||
// TODO XUID (optional but strongly recommended) | |||
// TODO /FontInfo | |||
gen.write("/CIDCount "); | |||
CIDSubset cidSubset = font.getCIDSubset(); | |||
int subsetSize = cidSubset.getSubsetSize(); | |||
gen.write(subsetSize); | |||
gen.writeln(" def"); | |||
gen.writeln("/GDBytes 2 def"); // TODO always 2? | |||
gen.writeln("/CIDMap [<"); | |||
int colCount = 0; | |||
int lineCount = 1; | |||
for (int cid = 0; cid < subsetSize; cid++) { | |||
if (colCount++ == 20) { | |||
gen.newLine(); | |||
colCount = 1; | |||
if (lineCount++ == 800) { | |||
gen.writeln("> <"); | |||
lineCount = 1; | |||
} | |||
} | |||
String gid = HexEncoder.encode(cidSubset.getGlyphIndexForSubsetIndex(cid), 4); | |||
gen.write(gid); | |||
} | |||
gen.writeln(">] def"); | |||
createType42DictionaryEntries(gen, font, fontStream, Collections.EMPTY_LIST); | |||
gen.writeln("CIDFontName currentdict end /CIDFont defineresource pop"); | |||
gen.writeln("end"); | |||
gen.writeln("%%EndResource"); | |||
PSResource cidFontResource = new PSResource(PSResource.TYPE_CIDFONT, psName); | |||
gen.getResourceTracker().registerSuppliedResource(cidFontResource); | |||
return cidFontResource; | |||
} | |||
private static void writeFontBBox(PSGenerator gen, CustomFont font) throws IOException { | |||
int[] bbox = font.getFontBBox(); | |||
gen.write("/FontBBox["); | |||
for (int i = 0; i < 4; i++) { | |||
gen.write(" "); | |||
gen.write(bbox[i]); | |||
} | |||
gen.writeln(" ] def"); | |||
} | |||
private static boolean isEmbeddable(CustomFont font) { | |||
return font.isEmbeddable(); | |||
} |
@@ -44,6 +44,7 @@ import org.apache.fop.fonts.Font; | |||
import org.apache.fop.fonts.FontInfo; | |||
import org.apache.fop.fonts.FontTriplet; | |||
import org.apache.fop.fonts.LazyFont; | |||
import org.apache.fop.fonts.MultiByteFont; | |||
import org.apache.fop.fonts.SingleByteFont; | |||
import org.apache.fop.fonts.Typeface; | |||
import org.apache.fop.render.RenderingContext; | |||
@@ -379,7 +380,7 @@ public class PSPainter extends AbstractIFPainter { | |||
if (currentEncoding != encoding) { | |||
if (i > 0) { | |||
writeText(text, start, i - start, | |||
letterSpacing, wordSpacing, dx, font, tf); | |||
letterSpacing, wordSpacing, dx, font, tf, false); | |||
} | |||
if (encoding == 0) { | |||
useFont(fontKey, sizeMillipoints); | |||
@@ -391,12 +392,11 @@ public class PSPainter extends AbstractIFPainter { | |||
} | |||
} | |||
writeText(text, start, textLen - start, | |||
letterSpacing, wordSpacing, dx, font, tf); | |||
letterSpacing, wordSpacing, dx, font, tf, false); | |||
} else { | |||
//Simple single-font painting | |||
useFont(fontKey, sizeMillipoints); | |||
writeText(text, 0, textLen, | |||
letterSpacing, wordSpacing, dx, font, tf); | |||
letterSpacing, wordSpacing, dx, font, tf, tf instanceof MultiByteFont); | |||
} | |||
} catch (IOException ioe) { | |||
throw new IFException("I/O error in drawText()", ioe); | |||
@@ -405,7 +405,7 @@ public class PSPainter extends AbstractIFPainter { | |||
private void writeText(String text, int start, int len, | |||
int letterSpacing, int wordSpacing, int[] dx, | |||
Font font, Typeface tf) throws IOException { | |||
Font font, Typeface tf, boolean multiByte) throws IOException { | |||
PSGenerator generator = getGenerator(); | |||
int end = start + len; | |||
int initialSize = len; | |||
@@ -414,6 +414,16 @@ public class PSPainter extends AbstractIFPainter { | |||
boolean hasLetterSpacing = (letterSpacing != 0); | |||
boolean needTJ = false; | |||
char strOpen; | |||
char strClose; | |||
if (multiByte) { | |||
strOpen = '<'; | |||
strClose = '>'; | |||
} else { | |||
strOpen = '('; | |||
strClose = ')'; | |||
} | |||
int lineStart = 0; | |||
StringBuffer accText = new StringBuffer(initialSize); | |||
StringBuffer sb = new StringBuffer(initialSize); | |||
@@ -439,8 +449,12 @@ public class PSPainter extends AbstractIFPainter { | |||
if (dx != null && i < dxl - 1) { | |||
glyphAdjust -= dx[i + 1]; | |||
} | |||
char codepoint = (char)(ch % 256); | |||
PSGenerator.escapeChar(codepoint, accText); //add character to accumulated text | |||
if (multiByte) { | |||
accText.append(HexEncoder.encode(ch)); | |||
} else { | |||
char codepoint = (char)(ch % 256); | |||
PSGenerator.escapeChar(codepoint, accText); //add character to accumulated text | |||
} | |||
if (glyphAdjust != 0) { | |||
needTJ = true; | |||
if (sb.length() == 0) { | |||
@@ -451,9 +465,10 @@ public class PSPainter extends AbstractIFPainter { | |||
sb.append(PSGenerator.LF); | |||
lineStart = sb.length(); | |||
} | |||
sb.append('('); | |||
sb.append(strOpen); | |||
sb.append(accText); | |||
sb.append(") "); | |||
sb.append(strClose); | |||
sb.append(' '); | |||
accText.setLength(0); //reset accumulated text | |||
} | |||
sb.append(Integer.toString(glyphAdjust)).append(' '); | |||
@@ -461,9 +476,9 @@ public class PSPainter extends AbstractIFPainter { | |||
} | |||
if (needTJ) { | |||
if (accText.length() > 0) { | |||
sb.append('('); | |||
sb.append(strOpen); | |||
sb.append(accText); | |||
sb.append(')'); | |||
sb.append(strClose); | |||
} | |||
if (hasLetterSpacing) { | |||
sb.append("] " + formatMptAsPt(generator, letterSpacing) + " ATJ"); | |||
@@ -471,7 +486,7 @@ public class PSPainter extends AbstractIFPainter { | |||
sb.append("] TJ"); | |||
} | |||
} else { | |||
sb.append('(').append(accText).append(")"); | |||
sb.append(strOpen).append(accText).append(strClose); | |||
if (hasLetterSpacing) { | |||
StringBuffer spb = new StringBuffer(); | |||
spb.append(formatMptAsPt(generator, letterSpacing)) | |||
@@ -486,10 +501,10 @@ public class PSPainter extends AbstractIFPainter { | |||
} | |||
private void useFont(String key, int size) throws IOException { | |||
PSResource res = this.documentHandler.getPSResourceForFontKey(key); | |||
PSFontResource res = this.documentHandler.getPSResourceForFontKey(key); | |||
PSGenerator generator = getGenerator(); | |||
generator.useFont("/" + res.getName(), size / 1000f); | |||
generator.getResourceTracker().notifyResourceUsageOnPage(res); | |||
res.notifyResourceUsageOnPage(generator.getResourceTracker()); | |||
} | |||
@@ -35,13 +35,13 @@ import java.text.AttributedCharacterIterator; | |||
import java.util.Iterator; | |||
import java.util.List; | |||
import org.apache.batik.gvt.TextNode; | |||
import org.apache.batik.gvt.font.GVTGlyphVector; | |||
import org.apache.batik.gvt.text.TextPaintInfo; | |||
import org.apache.batik.gvt.text.TextSpanLayout; | |||
import org.apache.xmlgraphics.java2d.ps.PSGraphics2D; | |||
import org.apache.xmlgraphics.ps.PSGenerator; | |||
import org.apache.xmlgraphics.ps.PSResource; | |||
import org.apache.fop.fonts.Font; | |||
import org.apache.fop.fonts.FontInfo; | |||
@@ -240,9 +240,9 @@ public class PSTextPainter extends NativeTextPainter { | |||
} | |||
} | |||
private PSResource getResourceForFont(Font f, String postfix) { | |||
private PSFontResource getResourceForFont(Font f, String postfix) { | |||
String key = (postfix != null ? f.getFontName() + '_' + postfix : f.getFontName()); | |||
return this.fontResources.getPSResourceForFontKey(key); | |||
return this.fontResources.getFontResourceForFontKey(key); | |||
} | |||
private void clip(PSGraphics2D ps, Shape shape) throws IOException { | |||
@@ -299,9 +299,9 @@ public class PSTextPainter extends NativeTextPainter { | |||
public void selectFont(Font f, char mapped) throws IOException { | |||
int encoding = mapped / 256; | |||
String postfix = (encoding == 0 ? null : Integer.toString(encoding)); | |||
PSResource res = getResourceForFont(f, postfix); | |||
PSFontResource res = getResourceForFont(f, postfix); | |||
gen.useFont("/" + res.getName(), f.getFontSize() / 1000f); | |||
gen.getResourceTracker().notifyResourceUsageOnPage(res); | |||
res.notifyResourceUsageOnPage(gen.getResourceTracker()); | |||
} | |||
public Font getCurrentFont() { |
@@ -24,6 +24,7 @@ import junit.framework.TestSuite; | |||
import org.apache.fop.events.BasicEventTestCase; | |||
import org.apache.fop.pdf.PDFObjectTestCase; | |||
import org.apache.fop.render.ps.HexEncoderTestCase; | |||
import org.apache.fop.traits.BorderPropsTestCase; | |||
import org.apache.fop.util.ColorUtilTestCase; | |||
import org.apache.fop.util.ElementListUtilsTestCase; | |||
@@ -51,6 +52,7 @@ public class UtilityCodeTestSuite { | |||
suite.addTest(new TestSuite(BasicEventTestCase.class)); | |||
suite.addTest(new TestSuite(XMLResourceBundleTestCase.class)); | |||
suite.addTest(new TestSuite(URIResolutionTestCase.class)); | |||
suite.addTest(new TestSuite(HexEncoderTestCase.class)); | |||
//$JUnit-END$ | |||
return suite; | |||
} |
@@ -0,0 +1,58 @@ | |||
/* | |||
* Licensed to the Apache Software Foundation (ASF) under one or more | |||
* contributor license agreements. See the NOTICE file distributed with | |||
* this work for additional information regarding copyright ownership. | |||
* The ASF licenses this file to You under the Apache License, Version 2.0 | |||
* (the "License"); you may not use this file except in compliance with | |||
* the License. You may obtain a copy of the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | |||
* limitations under the License. | |||
*/ | |||
/* $Id$ */ | |||
package org.apache.fop.render.ps; | |||
import junit.framework.TestCase; | |||
/** | |||
* Test case for the conversion of characters into hex-encoded strings. | |||
*/ | |||
public class HexEncoderTestCase extends TestCase { | |||
private static char successor(char d) { | |||
if (d == '9') { | |||
return 'A'; | |||
} else if (d == 'F') { | |||
return '0'; | |||
} else { | |||
return (char) (d + 1); | |||
} | |||
} | |||
private static void increment(char[] digits) { | |||
int d = 4; | |||
do { | |||
d--; | |||
digits[d] = successor(digits[d]); | |||
} while (digits[d] == '0' && d > 0); | |||
} | |||
/** | |||
* Tests that characters are properly encoded into hex strings. | |||
*/ | |||
public void testEncodeChar() { | |||
char[] digits = new char[] {'0', '0', '0', '0'}; | |||
for (int c = 0; c <= 0xFFFF; c++) { | |||
assertEquals(new String(digits), HexEncoder.encode((char) c)); | |||
increment(digits); | |||
} | |||
} | |||
} |