Browse Source

Merged revisions 638842-642143 via svnmerge from

https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk

........
  r640089 | clay | 2008-03-22 22:54:27 +0100 (Sa, 22 Mrz 2008) | 1 line
  
  updates to Forrest web site (expand menu structure).
........
  r640242 | adelmelle | 2008-03-23 20:36:36 +0100 (So, 23 Mrz 2008) | 11 lines
  
  Cleanup and minor refactoring:
  * consolidate addId() in AbstractLayoutManager
  * replace getPSLM().addIdToPage(getXXX().getId()) in various LMs
  
  Smaller changes/cleanup in the affected LMs include:
  * removal of some redundant casts
  * simplified conditionals
  * avoid duplicating the reference to the FObj as much as possible:
    the reference is already stored in AbstractLM, yet every subclass seems to
    add another reference...
........
  r641742 | jeremias | 2008-03-27 09:49:41 +0100 (Do, 27 Mrz 2008) | 6 lines
  
  Added support for addressing all glyphs available in a Type 1 font, not just the ones in the font's primary encoding.
  Typeface: getEncoding() changed to getEncodingName() to make clearer what is held here.
  Some cleanup in the font classes to put the various things in more appropriate places.
  Created a common base class for all Base 14 fonts (makes the hierarchy clearer).
  Made PDFTextUtil more universally useful and made use of it in PDFRenderer, too.
  Made PDFStream.add(String) more efficient. The encoding converter is not called for each invocation anymore as the whole thing get buffered by a BufferedWriter (as suggested by the javadoc of OutputStreamWriter).
........
  r641764 | maxberger | 2008-03-27 11:02:25 +0100 (Do, 27 Mrz 2008) | 1 line
  
  Added my key
........
  r641827 | jeremias | 2008-03-27 15:29:44 +0100 (Do, 27 Mrz 2008) | 2 lines
  
  When a JPEG image is embedded, an optionally embedded color profile is filtered out as it's already embedded separately in the PDF file.
  Worked around a problem (PDF renderer) with JPEG image containing RGB color profiles which are not sRGB. The images drifted into yellow. The color profile is simply disabled in this case. Please let us know if you know what the problem could be.
........


git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_ProcessingFeedback@642155 13f79535-47bb-0310-9956-ffa450edef68
Temp_ProcessingFeedback
Jeremias Maerki 16 years ago
parent
commit
90ffcf688e
65 changed files with 1861 additions and 884 deletions
  1. 23
    1
      KEYS
  2. 1
    1
      fop.bat
  3. 1
    1
      forrest.properties
  4. BIN
      lib/xmlgraphics-commons-1.4svn.jar
  5. 3
    3
      src/codegen/fonts/font-file.xsl
  6. 1
    0
      src/documentation/skinconf.xml
  7. 3
    2
      src/java/org/apache/fop/fo/properties/CommonHyphenation.java
  8. 6
    17
      src/java/org/apache/fop/fonts/AbstractCodePointMapping.java
  9. 27
    0
      src/java/org/apache/fop/fonts/Base14Font.java
  10. 7
    24
      src/java/org/apache/fop/fonts/CIDFont.java
  11. 177
    0
      src/java/org/apache/fop/fonts/CIDSubset.java
  12. 1
    1
      src/java/org/apache/fop/fonts/Font.java
  13. 11
    13
      src/java/org/apache/fop/fonts/LazyFont.java
  14. 38
    118
      src/java/org/apache/fop/fonts/MultiByteFont.java
  15. 142
    0
      src/java/org/apache/fop/fonts/NamedCharacter.java
  16. 145
    0
      src/java/org/apache/fop/fonts/SimpleSingleByteEncoding.java
  17. 50
    0
      src/java/org/apache/fop/fonts/SingleByteEncoding.java
  18. 188
    26
      src/java/org/apache/fop/fonts/SingleByteFont.java
  19. 2
    5
      src/java/org/apache/fop/fonts/Typeface.java
  20. 11
    8
      src/java/org/apache/fop/fonts/truetype/TTFSubSetFile.java
  21. 28
    18
      src/java/org/apache/fop/fonts/type1/AFMCharMetrics.java
  22. 2
    10
      src/java/org/apache/fop/fonts/type1/AFMFile.java
  23. 16
    1
      src/java/org/apache/fop/fonts/type1/AFMParser.java
  24. 24
    9
      src/java/org/apache/fop/fonts/type1/Type1FontLoader.java
  25. 14
    2
      src/java/org/apache/fop/layoutmgr/AbstractLayoutManager.java
  26. 5
    5
      src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java
  27. 4
    4
      src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java
  28. 0
    7
      src/java/org/apache/fop/layoutmgr/inline/AbstractGraphicsLayoutManager.java
  29. 8
    9
      src/java/org/apache/fop/layoutmgr/inline/AbstractPageNumberCitationLayoutManager.java
  30. 6
    17
      src/java/org/apache/fop/layoutmgr/inline/CharacterLayoutManager.java
  31. 0
    5
      src/java/org/apache/fop/layoutmgr/inline/ICLayoutManager.java
  32. 2
    7
      src/java/org/apache/fop/layoutmgr/inline/InlineLayoutManager.java
  33. 6
    20
      src/java/org/apache/fop/layoutmgr/inline/InlineStackingLayoutManager.java
  34. 0
    5
      src/java/org/apache/fop/layoutmgr/inline/LeaderLayoutManager.java
  35. 0
    10
      src/java/org/apache/fop/layoutmgr/inline/LeafNodeLayoutManager.java
  36. 1
    5
      src/java/org/apache/fop/layoutmgr/inline/PageNumberLayoutManager.java
  37. 6
    6
      src/java/org/apache/fop/layoutmgr/list/ListBlockLayoutManager.java
  38. 5
    5
      src/java/org/apache/fop/layoutmgr/list/ListItemContentLayoutManager.java
  39. 10
    10
      src/java/org/apache/fop/layoutmgr/list/ListItemLayoutManager.java
  40. 2
    5
      src/java/org/apache/fop/layoutmgr/table/TableAndCaptionLayoutManager.java
  41. 1
    4
      src/java/org/apache/fop/layoutmgr/table/TableCaptionLayoutManager.java
  42. 5
    5
      src/java/org/apache/fop/layoutmgr/table/TableCellLayoutManager.java
  43. 3
    3
      src/java/org/apache/fop/layoutmgr/table/TableLayoutManager.java
  44. 1
    4
      src/java/org/apache/fop/pdf/PDFCMap.java
  45. 8
    0
      src/java/org/apache/fop/pdf/PDFEncoding.java
  46. 92
    41
      src/java/org/apache/fop/pdf/PDFFactory.java
  47. 1
    1
      src/java/org/apache/fop/pdf/PDFResources.java
  48. 29
    9
      src/java/org/apache/fop/pdf/PDFStream.java
  49. 1
    1
      src/java/org/apache/fop/pdf/PDFTTFStream.java
  50. 295
    0
      src/java/org/apache/fop/pdf/PDFTextUtil.java
  51. 2
    5
      src/java/org/apache/fop/render/afp/fonts/OutlineFont.java
  52. 3
    5
      src/java/org/apache/fop/render/afp/fonts/RasterFont.java
  53. 1
    1
      src/java/org/apache/fop/render/java2d/CustomFontMetricsMapper.java
  54. 1
    1
      src/java/org/apache/fop/render/java2d/SystemFontMetricsMapper.java
  55. 9
    1
      src/java/org/apache/fop/render/pdf/AbstractImageAdapter.java
  56. 100
    1
      src/java/org/apache/fop/render/pdf/ImageRawJPEGAdapter.java
  57. 2
    2
      src/java/org/apache/fop/render/pdf/PDFGraphics2DAdapter.java
  58. 92
    164
      src/java/org/apache/fop/render/pdf/PDFRenderer.java
  59. 114
    14
      src/java/org/apache/fop/render/ps/PSFontUtils.java
  60. 72
    19
      src/java/org/apache/fop/render/ps/PSRenderer.java
  61. 1
    10
      src/java/org/apache/fop/svg/PDFGraphics2D.java
  62. 8
    3
      src/java/org/apache/fop/svg/PDFTextPainter.java
  63. 25
    206
      src/java/org/apache/fop/svg/PDFTextUtil.java
  64. 15
    2
      status.xml
  65. 4
    2
      test/java/org/apache/fop/render/pdf/PDFEncodingTestCase.java

+ 23
- 1
KEYS View File

@@ -19,7 +19,8 @@ pub 1024D/5F298824 2006-09-30 Simon Pepping <spepping@leverkruid.eu>
sub 2048g/40F32100 2006-09-30
pub 1024D/4358C584 2006-12-08 Vincent Hennebert <vhennebert@apache.org>
sub 2048g/0BD6AC9B 2006-12-08

pub 1024D/CC31AE97 2008-03-27 [expires: 2011-01-01]
uid Maximilian Berger <maxberger@apache.org>

-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v1.0.6 (GNU/Linux)
@@ -167,3 +168,24 @@ SQQYEQIACQUCRXmZwgIbDAAKCRCgctTQQ1jFhBc3AKCQ1X7oIVR8g7GvSGEUw6DE
HgEaUgCgkl30lcl9gGa9hqk4cuGYn1OTyks=
=XphF
-----END PGP PUBLIC KEY BLOCK-----

-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v1.4.7 (OpenBSD)

mQGiBEfrX/ARBADulGs9V7StbfK8CFfClihdh4lRpw2tktMuYiQJ/rIsNq4zghkT
UGxFU0eUC2ZVPYvwG6sXgZ5SSm6i8Ii79YdIIxUG7oNwXaY/e9rK+xX193xqmDgl
Vh5vm4LUtncHi9TZDSF+g1YU1DVHbSjTd2oQEHxbBppL0CLfaLv3U1qr2wCgxeGw
ZIwb6KD6yesRLx6vGDkDzAMEAL99p0m4sNnjZdKC25Rrt6NZ7CROXWs89/+dkewg
JCZiVEoAcdSa0z5d8+XaSzmqR8BJWM1PBPjG48eEdqOevwypd0F/U1mwZ42MvQEw
oBayUY+7/pptW7C5L3Fjk18drE0a8lIzt9VBGX5fe3GoXtZKuOzDv0hEuHyzR9q9
JLXsA/sELFfrHf0tBfXAdE7pj/Mahalu17/GAyb0RHSvuOfoUvXSXZA91cFg8ycB
4x265NtYZk52M32wi5ePrYeSJIZ6vWRvPuWVAXDg5S6HCMjcXc6ElkgLcUt1NoPI
DmRXe4FIDZkGSYSXdLTUByu7+8fCuWQHCFG2sALdOyVlucij2LQoTWF4aW1pbGlh
biBCZXJnZXIgPG1heGJlcmdlckBhcGFjaGUub3JnPohmBBMRAgAmBQJH61/wAhsD
BQkFM4sABgsJCAcDAgQVAggDBBYCAwECHgECF4AACgkQ4+RAT8wxrpcs1wCeIAoI
B0MwZfWLV+wD3pt1wDkPXbsAnjuOsyZ7EeNOcpoqSteB17aPRGeLiEYEEBECAAYF
Akfrb00ACgkQByq3OugVkrx76ACgqJ8W64IhBYgBvp3dTDLS825gCQQAnjB6X5+4
eW/0pQUpJcvolxbT9xvF
=bbQO
-----END PGP PUBLIC KEY BLOCK-----


+ 1
- 1
fop.bat View File

@@ -61,7 +61,7 @@ set LOCALCLASSPATH=%LOCALCLASSPATH%;%LIBDIR%\xercesImpl-2.7.1.jar
set LOCALCLASSPATH=%LOCALCLASSPATH%;%LIBDIR%\xalan-2.7.0.jar
set LOCALCLASSPATH=%LOCALCLASSPATH%;%LIBDIR%\serializer-2.7.0.jar
set LOCALCLASSPATH=%LOCALCLASSPATH%;%LIBDIR%\batik-all-1.7.jar
set LOCALCLASSPATH=%LOCALCLASSPATH%;%LIBDIR%\xmlgraphics-commons-1.3.jar
set LOCALCLASSPATH=%LOCALCLASSPATH%;%LIBDIR%\xmlgraphics-commons-1.4svn.jar
set LOCALCLASSPATH=%LOCALCLASSPATH%;%LIBDIR%\avalon-framework-4.2.0.jar
set LOCALCLASSPATH=%LOCALCLASSPATH%;%LIBDIR%\commons-io-1.3.1.jar
set LOCALCLASSPATH=%LOCALCLASSPATH%;%LIBDIR%\commons-logging-1.0.4.jar

+ 1
- 1
forrest.properties View File

@@ -56,7 +56,7 @@ project.skin=pelt
#project.images-dir=${project.resources-dir}/images
#project.schema-dir=${project.resources-dir}/schema
#project.skins-dir=${project.content-dir}/skins
#project.skinconf=${project.content-dir}/skinconf.xml
project.skinconf=${project.content-dir}/skinconf.xml
#project.lib-dir=${project.content-dir}/lib
#project.classes-dir=${project.content-dir}/classes
#project.translations-dir=${project.content-dir}/translations

BIN
lib/xmlgraphics-commons-1.3.jar → lib/xmlgraphics-commons-1.4svn.jar View File


+ 3
- 3
src/codegen/fonts/font-file.xsl View File

@@ -40,10 +40,10 @@ import java.util.Map;
</xsl:if>
import java.util.Set;
import org.apache.fop.fonts.FontType;
import org.apache.fop.fonts.Typeface;
import org.apache.fop.fonts.Base14Font;
import org.apache.fop.fonts.CodePointMapping;

public class <xsl:value-of select="class-name"/> extends Typeface {
public class <xsl:value-of select="class-name"/> extends Base14Font {
private final static String fontName = "<xsl:value-of select="font-name"/>";
private final static String fullName = "<xsl:value-of select="full-name"/>";
private final static Set familyNames;
@@ -84,7 +84,7 @@ public class <xsl:value-of select="class-name"/> extends Typeface {
this.enableKerning = enableKerning;
}

public String getEncoding() {
public String getEncodingName() {
return encoding;
}


+ 1
- 0
src/documentation/skinconf.xml View File

@@ -164,6 +164,7 @@ which will be used to configure the chosen Forrest skin.
.ForrestTable td.partial { background-color: #FFFFCC; text-align: center; }
.ForrestTable td.category { /*background-color: #CFDCED;*/
font-size: 1.2em }
.menuitemgroup{ display: block;}

</extra-css>


+ 3
- 2
src/java/org/apache/fop/fo/properties/CommonHyphenation.java View File

@@ -21,6 +21,7 @@ package org.apache.fop.fo.properties;

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

import org.apache.fop.fo.Constants;
import org.apache.fop.fo.PropertyList;
import org.apache.fop.fo.expr.PropertyException;
@@ -142,7 +143,7 @@ public final class CommonHyphenation {
FontMetrics metrics = font.getFontMetrics();
if (metrics instanceof Typeface) {
Typeface typeface = (Typeface)metrics;
if ("SymbolEncoding".equals(typeface.getEncoding())) {
if ("SymbolEncoding".equals(typeface.getEncodingName())) {
//SymbolEncoding doesn't have HYPHEN_MINUS, so replace by MINUS_SIGN
} else {
//only warn if the encoding is not SymbolEncoding
@@ -154,7 +155,7 @@ public final class CommonHyphenation {
FontMetrics metrics = font.getFontMetrics();
if (metrics instanceof Typeface) {
Typeface typeface = (Typeface)metrics;
if ("ZapfDingbatsEncoding".equals(typeface.getEncoding())) {
if ("ZapfDingbatsEncoding".equals(typeface.getEncodingName())) {
//ZapfDingbatsEncoding doesn't have HYPHEN_MINUS, so replace by ' '
} else {
//only warn if the encoding is not ZapfDingbatsEncoding

+ 6
- 17
src/java/org/apache/fop/fonts/AbstractCodePointMapping.java View File

@@ -29,7 +29,7 @@ import org.apache.fop.util.CharUtilities;
/**
* Abstract base class for code point mapping classes (1-byte character encodings).
*/
public class AbstractCodePointMapping {
public class AbstractCodePointMapping implements SingleByteEncoding {

private String name;
private char[] latin1Map;
@@ -114,19 +114,12 @@ public class AbstractCodePointMapping {
}
}

/**
* Returns the encoding's name.
* @return the name of the encoding
*/
/** {@inheritDoc} */
public String getName() {
return this.name;
}

/**
* Maps a Unicode character to a code point in the encoding.
* @param c the Unicode character to map
* @return the coid point in the encoding or 0 (=.notdef) if not found
*/
/** {@inheritDoc} */
public final char mapChar(char c) {
if (c < 256) {
char latin1 = latin1Map[c];
@@ -172,8 +165,8 @@ public class AbstractCodePointMapping {
}
}
putFallbackCharacter(c, '\0');
return 0;
putFallbackCharacter(c, NOT_FOUND_CODE_POINT);
return NOT_FOUND_CODE_POINT;
}

private void putFallbackCharacter(char c, char mapTo) {
@@ -227,11 +220,7 @@ public class AbstractCodePointMapping {
return -1;
}
/**
* Returns the array of character names for this encoding.
* @return the array of character names
* (unmapped code points are represented by a ".notdef" value)
*/
/** {@inheritDoc} */
public String[] getCharNameMap() {
if (this.charNameMap != null) {
String[] copy = new String[this.charNameMap.length];

+ 27
- 0
src/java/org/apache/fop/fonts/Base14Font.java View File

@@ -0,0 +1,27 @@
/*
* 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.fonts;

/**
* Base class for all Base 14 fonts.
*/
public abstract class Base14Font extends Typeface {

}

+ 7
- 24
src/java/org/apache/fop/fonts/CIDFont.java View File

@@ -20,31 +20,14 @@
package org.apache.fop.fonts;

//Java
import java.util.Map;

/**
* Abstract base class for CID fonts.
*/
public abstract class CIDFont extends CustomFont {

/**
* usedGlyphs contains orginal, new glyph index
*/
public Map usedGlyphs = new java.util.HashMap();

/**
* usedGlyphsIndex contains new glyph, original index
*/
public Map usedGlyphsIndex = new java.util.HashMap();
public int usedGlyphsCount = 0;

/**
* usedCharsIndex contains new glyph, original char
*/
public Map usedCharsIndex = new java.util.HashMap();

//private PDFWArray warray = new PDFWArray();
public int width[] = null;
/** Contains the character widths for all characters in the font */
protected int[] width = null;

// ---- Required ----
/**
@@ -73,6 +56,11 @@ public abstract class CIDFont extends CustomFont {
*/
public abstract int getSupplement();

/**
* Returns the subset information for this font.
* @return the subset information
*/
public abstract CIDSubset getCIDSubset();

// ---- Optional ----
/**
@@ -88,9 +76,4 @@ public abstract class CIDFont extends CustomFont {
return true;
}

/**
* Returns a char array containing all Unicode characters that have been accessed.
* @return a char array with all used Unicode characters
*/
public abstract char[] getCharsUsed();
}

+ 177
- 0
src/java/org/apache/fop/fonts/CIDSubset.java View File

@@ -0,0 +1,177 @@
/*
* 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.fonts;

import java.util.BitSet;
import java.util.Collections;
import java.util.Iterator;
import java.util.Map;

import org.apache.fop.util.CharUtilities;

//Naming:
//glyph index: original index of the glyph in the non-subset font (!= unicode index)
//character selector: index into a set of glyphs. For subset CID fonts, this starts at 0. For
// non-subset fonts, this is the same as the glyph index.
//Unicode index: The Unicode codepoint of a character.
//Glyph name: the Adobe glyph name (as found in Glyphs.java)

/**
* Keeps track of the glyphs used in a document. This information is later used to build
* a subset of a font.
*/
public class CIDSubset {

/**
* usedGlyphs contains orginal, new glyph index (glyph index -> char selector)
*/
private Map/*<Integer, Integer>*/ usedGlyphs = new java.util.HashMap();

/**
* usedGlyphsIndex contains new glyph, original index (char selector -> glyph index)
*/
private Map/*<Integer, Integer>*/ usedGlyphsIndex = new java.util.HashMap();
private int usedGlyphsCount = 0;

/**
* usedCharsIndex contains new glyph, original char (char selector -> Unicode)
*/
private Map/*<Integer, Character>*/ usedCharsIndex = new java.util.HashMap();
public CIDSubset() {
}
/**
* Adds the initial 3 glyphs which are the same for all CID subsets.
*/
public void setupFirstThreeGlyphs() {
// Make sure that the 3 first glyphs are included
usedGlyphs.put(new Integer(0), new Integer(0));
usedGlyphsIndex.put(new Integer(0), new Integer(0));
usedGlyphsCount++;
usedGlyphs.put(new Integer(1), new Integer(1));
usedGlyphsIndex.put(new Integer(1), new Integer(1));
usedGlyphsCount++;
usedGlyphs.put(new Integer(2), new Integer(2));
usedGlyphsIndex.put(new Integer(2), new Integer(2));
usedGlyphsCount++;
}
/**
* Returns the original index of the glyph inside the (non-subset) font's glyph list. This
* index can be used to access the character width information, for example.
* @param subsetIndex the subset index (character selector) to access the glyph
* @return the original index (or -1 if no glyph index is available for the subset index)
*/
public int getGlyphIndexForSubsetIndex(int subsetIndex) {
Integer glyphIndex = (Integer)usedGlyphsIndex.get(new Integer(subsetIndex));
if (glyphIndex != null) {
return glyphIndex.intValue();
} else {
return -1;
}
}
/**
* Returns the Unicode value for a subset index (character selector). If there's no such
* Unicode value, the "NOT A CHARACTER" (0xFFFF) is returned.
* @param subsetIndex the subset index (character selector)
* @return the Unicode value or "NOT A CHARACTER" (0xFFFF)
*/
public char getUnicodeForSubsetIndex(int subsetIndex) {
Character mapValue = (Character)usedCharsIndex.get(new Integer(subsetIndex));
if (mapValue != null) {
return mapValue.charValue();
} else {
return CharUtilities.NOT_A_CHARACTER;
}
}
/**
* Maps a character to a character selector for a font subset. If the character isn't in the
* subset, yet, it is added and a new character selector returned. Otherwise, the already
* allocated character selector is returned from the existing map/subset.
* @param glyphIndex the glyph index of the character
* @param unicode the Unicode index of the character
* @return the subset index
*/
public int mapSubsetChar(int glyphIndex, char unicode) {
// Reencode to a new subset font or get the reencoded value
// IOW, accumulate the accessed characters and build a character map for them
Integer subsetCharSelector = (Integer)usedGlyphs.get(new Integer(glyphIndex));
if (subsetCharSelector == null) {
int selector = usedGlyphsCount;
usedGlyphs.put(new Integer(glyphIndex),
new Integer(selector));
usedGlyphsIndex.put(new Integer(selector),
new Integer(glyphIndex));
usedCharsIndex.put(new Integer(selector),
new Character(unicode));
usedGlyphsCount++;
return selector;
} else {
return subsetCharSelector.intValue();
}
}

/**
* Returns an unmodifiable Map of the font subset. It maps from glyph index to
* character selector (i.e. the subset index in this case).
* @return Map Map&lt;Integer, Integer&gt; of the font subset
*/
public Map/*<Integer, Integer>*/ getSubsetGlyphs() {
return Collections.unmodifiableMap(this.usedGlyphs);
}
/**
* Returns a char array containing all Unicode characters that are in the subset.
* @return a char array with all used Unicode characters
*/
public char[] getSubsetChars() {
char[] charArray = new char[usedGlyphsCount];
for (int i = 0; i < usedGlyphsCount; i++) {
charArray[i] = getUnicodeForSubsetIndex(i);
}
return charArray;
}

/**
* Returns the number of glyphs in the subset.
* @return the number of glyphs in the subset
*/
public int getSubsetSize() {
return this.usedGlyphsCount;
}
/**
* Returns a BitSet with bits set for each available glyph index.
* @return a BitSet indicating available glyph indices
*/
public BitSet getGlyphIndexBitSet() {
BitSet bitset = new BitSet();
Iterator iter = usedGlyphs.keySet().iterator();
while (iter.hasNext()) {
Integer cid = (Integer)iter.next();
bitset.set(cid.intValue());
}
return bitset;
}
}

+ 1
- 1
src/java/org/apache/fop/fonts/Font.java View File

@@ -199,7 +199,7 @@ public class Font {

// Use default CodePointMapping
char d = CodePointMapping.getMapping("WinAnsiEncoding").mapChar(c);
if (d != 0) {
if (d != SingleByteEncoding.NOT_FOUND_CODE_POINT) {
c = d;
} else {
log.warn("Glyph " + (int) c + " not available in font " + fontName);

+ 11
- 13
src/java/org/apache/fop/fonts/LazyFont.java View File

@@ -64,11 +64,10 @@ public class LazyFont extends Typeface implements FontDescriptor {
this.resolver = resolver;
}

/**
* String representation of LazyFont
*/
/** {@inheritDoc} */
public String toString() {
return ( "metrics-url=" + metricsFileName + ", embed-url=" + fontEmbedPath + ", kerning=" + useKerning );
return ( "metrics-url=" + metricsFileName + ", embed-url=" + fontEmbedPath
+ ", kerning=" + useKerning );
}
private void load(boolean fail) {
@@ -80,8 +79,9 @@ public class LazyFont extends Typeface implements FontDescriptor {
if (resolver != null) {
Source source = resolver.resolve(metricsFileName);
if (source == null) {
String err = "Cannot load font: failed to create Source from metrics file "
+ metricsFileName;
String err
= "Cannot load font: failed to create Source from metrics file "
+ metricsFileName;
if (fail) {
throw new RuntimeException(err);
} else {
@@ -112,8 +112,8 @@ public class LazyFont extends Typeface implements FontDescriptor {
src.setSystemId(source.getSystemId());
reader = new FontReader(src);
} else {
reader
= new FontReader(new InputSource(new URL(metricsFileName).openStream()));
reader = new FontReader(new InputSource(
new URL(metricsFileName).openStream()));
}
reader.setKerningEnabled(useKerning);
reader.setFontEmbedPath(fontEmbedPath);
@@ -153,12 +153,10 @@ public class LazyFont extends Typeface implements FontDescriptor {
}

// ---- Font ----
/**
* {@inheritDoc}
*/
public String getEncoding() {
/** {@inheritDoc} */
public String getEncodingName() {
load(true);
return realFont.getEncoding();
return realFont.getEncodingName();
}

/**

+ 38
- 118
src/java/org/apache/fop/fonts/MultiByteFont.java View File

@@ -39,6 +39,9 @@ public class MultiByteFont extends CIDFont {

private String namePrefix = null; // Quasi unique prefix

private CIDSubset subset = new CIDSubset();
/** A map from Unicode indices to glyph indices */
private BFEntry[] bfentries = null;

/**
@@ -46,15 +49,7 @@ public class MultiByteFont extends CIDFont {
*/
public MultiByteFont() {
// Make sure that the 3 first glyphs are included
usedGlyphs.put(new Integer(0), new Integer(0));
usedGlyphsIndex.put(new Integer(0), new Integer(0));
usedGlyphsCount++;
usedGlyphs.put(new Integer(1), new Integer(1));
usedGlyphsIndex.put(new Integer(1), new Integer(1));
usedGlyphsCount++;
usedGlyphs.put(new Integer(2), new Integer(2));
usedGlyphsIndex.put(new Integer(2), new Integer(2));
usedGlyphsCount++;
subset.setupFirstThreeGlyphs();
// Create a quasiunique prefix for fontname
synchronized (this.getClass()) {
@@ -77,37 +72,27 @@ public class MultiByteFont extends CIDFont {
setFontType(FontType.TYPE0);
}

/**
* {@inheritDoc}
*/
/** {@inheritDoc} */
public int getDefaultWidth() {
return defaultWidth;
}

/**
* {@inheritDoc}
*/
/** {@inheritDoc} */
public String getRegistry() {
return "Adobe";
}

/**
* {@inheritDoc}
*/
/** {@inheritDoc} */
public String getOrdering() {
return "UCS";
}

/**
* {@inheritDoc}
*/
/** {@inheritDoc} */
public int getSupplement() {
return 0;
}

/**
* {@inheritDoc}
*/
/** {@inheritDoc} */
public CIDFontType getCIDType() {
return cidType;
}
@@ -133,68 +118,47 @@ public class MultiByteFont extends CIDFont {
}
}

/**
* {@inheritDoc}
*/
/** {@inheritDoc} */
public boolean isEmbeddable() {
return !(getEmbedFileName() == null && getEmbedResourceName() == null);
}

/**
* {@inheritDoc}
*/
public String getEncoding() {
/** {@inheritDoc} */
public CIDSubset getCIDSubset() {
return this.subset;
}
/** {@inheritDoc} */
public String getEncodingName() {
return encoding;
}

/**
* {@inheritDoc}
*/
/** {@inheritDoc} */
public int getWidth(int i, int size) {
if (isEmbeddable()) {
Integer idx = (Integer)usedGlyphsIndex.get(new Integer(i));
return size * width[idx.intValue()];
int glyphIndex = subset.getGlyphIndexForSubsetIndex(i);
return size * width[glyphIndex];
} else {
return size * width[i];
}
}

/**
* {@inheritDoc}
*/
/** {@inheritDoc} */
public int[] getWidths() {
int[] arr = new int[width.length];
System.arraycopy(width, 0, arr, 0, width.length - 1);
/*
for (int i = 0; i < arr.length; i++)
arr[i] *= size;
*/
return arr;
}

/**
* Remaps a codepoint based.
* @param i codepoint to remap
* @return new codepoint
* Returns the glyph index for a Unicode character. The method returns 0 if there's no
* such glyph in the character map.
* @param c the Unicode character index
* @return the glyph index (or 0 if the glyph is not available)
*/
/* unused
public Integer reMap(Integer i) {
if (isEmbeddable()) {
Integer ret = (Integer)usedGlyphsIndex.get(i);
if (ret == null) {
ret = i;
}
return ret;
} else {
return i;
}

}
*/

private int findGlyphIndex(char c) {
int idx = (int)c;
int retIdx = 0;
int retIdx = 0; //.notdef

for (int i = 0; (i < bfentries.length) && retIdx == 0; i++) {
if (bfentries[i].getUnicodeStart() <= idx
@@ -208,48 +172,30 @@ public class MultiByteFont extends CIDFont {
return retIdx;
}

/**
* {@inheritDoc}
*/
/** {@inheritDoc} */
public char mapChar(char c) {
notifyMapOperation();
int retIdx = findGlyphIndex(c);
int glyphIndex = findGlyphIndex(c);

if (isEmbeddable()) {
// Reencode to a new subset font or get
// the reencoded value
Integer newIdx = (Integer)usedGlyphs.get(new Integer(retIdx));
if (newIdx == null) {
usedGlyphs.put(new Integer(retIdx),
new Integer(usedGlyphsCount));
usedGlyphsIndex.put(new Integer(usedGlyphsCount),
new Integer(retIdx));
usedCharsIndex.put(new Integer(usedGlyphsCount),
new Integer((int) c));
retIdx = usedGlyphsCount;
usedGlyphsCount++;
} else {
retIdx = newIdx.intValue();
}
glyphIndex = subset.mapSubsetChar(glyphIndex, c);
}

return (char)retIdx;
return (char)glyphIndex;
}

/**
* {@inheritDoc}
*/
/** {@inheritDoc} */
public boolean hasChar(char c) {
return (findGlyphIndex(c) > 0);
}


/**
* Sets the bfentries.
* @param bfentries The bfentries to set
* Sets the array of BFEntry instances which constitutes the Unicode to glyph index map for
* a font. ("BF" means "base font")
* @param entries the Unicode to glyph index map
*/
public void setBFEntries(BFEntry[] bfentries) {
this.bfentries = bfentries;
public void setBFEntries(BFEntry[] entries) {
this.bfentries = entries;
}

/**
@@ -276,17 +222,6 @@ public class MultiByteFont extends CIDFont {
this.ttcName = ttcName;
}

/**
* Adds a new CID width entry to the font.
* @param cidWidthIndex index
* @param wds array of widths
*/
/*
public void addCIDWidthEntry(int cidWidthIndex, int[] wds) {
this.warray.addEntry(cidWidthIndex, wds);
}*/


/**
* Sets the width array.
* @param wds array of widths.
@@ -300,30 +235,15 @@ public class MultiByteFont extends CIDFont {
* @return Map Map of used Glyphs
*/
public Map getUsedGlyphs() {
return usedGlyphs;
return subset.getSubsetGlyphs();
}

/** The invalid Unicode character, suitable as a return value in methods
* that need to return an invalid character. */
public static final char INVALID_UNICODE_CHAR = 0xFFFF;

/** {@inheritDoc} */
public char[] getCharsUsed() {
if (!isEmbeddable()) {
return null;
}
char[] charArray = new char[usedGlyphsCount];
for (int i = 0; i < usedGlyphsCount; i++) {
Integer mapValue = (Integer)usedCharsIndex.get(new Integer(i));
if (mapValue != null) {
char arrayItem = (char) mapValue.intValue();
charArray[i] = arrayItem;
}
else {
charArray[i] = INVALID_UNICODE_CHAR;
}
}
return charArray;
return subset.getSubsetChars();
}
}


+ 142
- 0
src/java/org/apache/fop/fonts/NamedCharacter.java View File

@@ -0,0 +1,142 @@
/*
* 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.fonts;

import org.apache.xmlgraphics.fonts.Glyphs;

import org.apache.fop.util.CharUtilities;

/**
* Represents an named character with character name (from the Adobe glyph list) and a Unicode
* sequence that this character represents.
*/
public class NamedCharacter {
private String charName;
private String unicodeSequence;
/**
* Main constructor.
* @param charName the character name
* @param unicodeSequence the Unicode sequence associated with this character
*/
public NamedCharacter(String charName, String unicodeSequence) {
if (charName == null) {
throw new NullPointerException("charName must not be null");
}
this.charName = charName;
if (unicodeSequence != null) {
this.unicodeSequence = unicodeSequence;
} else {
this.unicodeSequence = Glyphs.getUnicodeSequenceForGlyphName(charName);
}
}
/**
* Simple constructor.
* @param charName the character name
*/
public NamedCharacter(String charName) {
this(charName, null);
}
/** {@inheritDoc} */
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((charName == null) ? 0 : charName.hashCode());
return result;
}

/** {@inheritDoc} */
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final NamedCharacter other = (NamedCharacter)obj;
return charName.equals(other.charName);
}

/**
* Returns the character name (as defined by the Adobe glyph list).
* @return the character name
*/
public String getName() {
return this.charName;
}
/**
* Returns the Unicode sequence associated with this character.
* @return the Unicode sequence (or null if no Unicode sequence is associated)
*/
public String getUnicodeSequence() {
return this.unicodeSequence;
}
/**
* Indicates whether a single Unicode value is associated with this character.
* @return true if exactly one Unicode value is associated with this character, false otherwise
*/
public boolean hasSingleUnicodeValue() {
return (this.unicodeSequence != null && this.unicodeSequence.length() == 1);
}
/**
* Returns the single Unicode value associated with this named character. Check
* {@link #hasSingleUnicodeValue()} before you call this method because an
* IllegalStateException is thrown is a Unicode sequence with more than one character is
* associated with this character.
* @return the single Unicode value (or FFFF ("NOT A CHARACTER") if no Unicode value is
* available)
* @throws IllegalStateException if a Unicode sequence with more than one value is associated
* with the named character
*/
public char getSingleUnicodeValue() throws IllegalStateException {
if (this.unicodeSequence == null) {
return CharUtilities.NOT_A_CHARACTER;
}
if (this.unicodeSequence.length() > 1) {
throw new IllegalStateException("getSingleUnicodeValue() may not be called for a"
+ " named character that has more than one Unicode value (a sequence)"
+ " associated with the named character!");
}
return this.unicodeSequence.charAt(0);
}
/** {@inheritDoc} */
public String toString() {
StringBuffer sb = new StringBuffer(this.unicodeSequence);
sb.append(" (");
if (this.unicodeSequence != null) {
for (int i = 0, c = this.unicodeSequence.length(); i < c; i++) {
sb.append("0x").append(Integer.toHexString(this.unicodeSequence.charAt(0)));
}
sb.append(", ");
}
sb.append(getName()).append(')');
return sb.toString();
}
}

+ 145
- 0
src/java/org/apache/fop/fonts/SimpleSingleByteEncoding.java View File

@@ -0,0 +1,145 @@
/*
* 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.fonts;

import java.util.Arrays;
import java.util.List;
import java.util.Map;

import org.apache.xmlgraphics.fonts.Glyphs;

/**
* A simple implementation of the OneByteEncoding mostly used for encodings that are constructed
* on-the-fly.
*/
public class SimpleSingleByteEncoding implements SingleByteEncoding {

private String name;
private List mapping = new java.util.ArrayList();
//List<NamedCharacter>
private Map charMap = new java.util.HashMap();
//Map<Character(Unicode), Character(code point)>
/**
* Main constructor.
* @param name the encoding's name
*/
public SimpleSingleByteEncoding(String name) {
this.name = name;
}
/** {@inheritDoc} */
public String getName() {
return this.name;
}

/** {@inheritDoc} */
public char mapChar(char c) {
Character nc = (Character)charMap.get(new Character(c));
if (nc != null) {
return nc.charValue();
}
return NOT_FOUND_CODE_POINT;
}

/** {@inheritDoc} */
public String[] getCharNameMap() {
String[] map = new String[getSize()];
Arrays.fill(map, Glyphs.NOTDEF);
for (int i = getFirstChar(); i <= getLastChar(); i++) {
NamedCharacter ch = (NamedCharacter)this.mapping.get(i - 1);
map[i] = ch.getName();
}
return map;
}
/**
* Returns the index of the first defined character.
* @return the index of the first defined character (always 1 for this class)
*/
public int getFirstChar() {
return 1;
}
/**
* Returns the index of the last defined character.
* @return the index of the last defined character
*/
public int getLastChar() {
return this.mapping.size();
}
/**
* Returns the number of characters defined by this encoding.
* @return the number of characters
*/
public int getSize() {
return this.mapping.size() + 1;
}
/**
* Indicates whether the encoding is full (with 256 code points).
* @return true if the encoding is full
*/
public boolean isFull() {
return (getSize() == 256);
}
/**
* Adds a new character to the encoding.
* @param ch the named character
* @return the code point assigned to the character
*/
public char addCharacter(NamedCharacter ch) {
if (!ch.hasSingleUnicodeValue()) {
throw new IllegalArgumentException("Only NamedCharacters with a single Unicode value"
+ " are currently supported!");
}
if (isFull()) {
throw new IllegalStateException("Encoding is full!");
}
char newSlot = (char)(getLastChar() + 1);
this.mapping.add(ch);
this.charMap.put(new Character(ch.getSingleUnicodeValue()), new Character(newSlot));
return newSlot;
}

/**
* Returns the named character at a given code point in the encoding.
* @param codePoint the code point of the character
* @return the NamedCharacter (or null if no character is at this position)
*/
public NamedCharacter getCharacterForIndex(int codePoint) {
if (codePoint < 0 || codePoint > 255) {
throw new IllegalArgumentException("codePoint must be between 0 and 255");
}
if (codePoint <= getLastChar()) {
return (NamedCharacter)this.mapping.get(codePoint - 1);
} else {
return null;
}
}
/** {@inheritDoc} */
public String toString() {
return getName() + " (" + getSize() + " chars)";
}
}

+ 50
- 0
src/java/org/apache/fop/fonts/SingleByteEncoding.java View File

@@ -0,0 +1,50 @@
/*
* 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.fonts;

/**
* The interface defines a 1-byte character encoding (with 256 characters).
*/
public interface SingleByteEncoding {

/** Code point that is used if no code point for a specific character has been found. */
char NOT_FOUND_CODE_POINT = '\0';

/**
* Returns the encoding's name.
* @return the name of the encoding
*/
String getName();

/**
* Maps a Unicode character to a code point in the encoding.
* @param c the Unicode character to map
* @return the code point in the encoding or 0 (=.notdef) if not found
*/
char mapChar(char c);

/**
* Returns the array of character names for this encoding.
* @return the array of character names
* (unmapped code points are represented by a ".notdef" value)
*/
String[] getCharNameMap();
}

+ 188
- 26
src/java/org/apache/fop/fonts/SingleByteFont.java View File

@@ -19,6 +19,8 @@

package org.apache.fop.fonts;

import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.logging.Log;
@@ -31,14 +33,21 @@ import org.apache.xmlgraphics.fonts.Glyphs;
*/
public class SingleByteFont extends CustomFont {

/** Code point that is used if no code point for a specific character has been found. */
public static final char NOT_FOUND = '#';

/** logger */
private static Log log = LogFactory.getLog(SingleByteFont.class);

private CodePointMapping mapping;
private SingleByteEncoding mapping;

private int[] width = null;

private Set warnedChars;

private Map unencodedCharacters;
//Map<Character, UnencodedCharacter>
private List additionalEncodings;
/**
* Main constructor.
@@ -54,7 +63,7 @@ public class SingleByteFont extends CustomFont {
}

/** {@inheritDoc} */
public String getEncoding() {
public String getEncodingName() {
return this.mapping.getName();
}

@@ -62,18 +71,28 @@ public class SingleByteFont extends CustomFont {
* Returns the code point mapping (encoding) of this font.
* @return the code point mapping
*/
public CodePointMapping getCodePointMapping() {
public SingleByteEncoding getEncoding() {
return this.mapping;
}
/** {@inheritDoc} */
public int getWidth(int i, int size) {
int idx = i - getFirstChar();
if (idx >= 0 && idx < width.length) {
return size * width[i - getFirstChar()];
} else {
return 0;
if (i < 256) {
int idx = i - getFirstChar();
if (idx >= 0 && idx < width.length) {
return size * width[i - getFirstChar()];
}
} else if (this.additionalEncodings != null) {
int encodingIndex = (i / 256) - 1;
SimpleSingleByteEncoding encoding = getAdditionalEncoding(encodingIndex);
int codePoint = i % 256;
NamedCharacter nc = encoding.getCharacterForIndex(codePoint);
UnencodedCharacter uc
= (UnencodedCharacter)this.unencodedCharacters.get(
new Character(nc.getSingleUnicodeValue()));
return size * uc.getWidth();
}
return 0;
}

/** {@inheritDoc} */
@@ -87,30 +106,80 @@ public class SingleByteFont extends CustomFont {
public char mapChar(char c) {
notifyMapOperation();
char d = mapping.mapChar(c);
if (d != 0) {
if (d != SingleByteEncoding.NOT_FOUND_CODE_POINT) {
return d;
} else {
Character ch = new Character(c);
if (warnedChars == null) {
warnedChars = new java.util.HashSet();
}
//Check unencoded characters which are available in the font by character name
d = mapUnencodedChar(c);
if (d != SingleByteEncoding.NOT_FOUND_CODE_POINT) {
return d;
}
//Give up, character is not available
Character ch = new Character(c);
if (warnedChars == null) {
warnedChars = new java.util.HashSet();
}
if (warnedChars.size() < 8 && !warnedChars.contains(ch)) {
warnedChars.add(ch);
if (warnedChars.size() == 8) {
log.warn("Many requested glyphs are not available in font " + getFontName());
} else {
log.warn("Glyph " + (int)c + " (0x" + Integer.toHexString(c)
+ ", " + Glyphs.charToGlyphName(c)
+ ") not available in font " + getFontName());
}
if (warnedChars.size() < 8 && !warnedChars.contains(ch)) {
warnedChars.add(ch);
if (warnedChars.size() == 8) {
log.warn("Many requested glyphs are not available in font " + getFontName());
} else {
log.warn("Glyph " + (int)c + " (0x" + Integer.toHexString(c)
+ ", " + Glyphs.charToGlyphName(c)
+ ") not available in font " + getFontName());
}
return NOT_FOUND;
}

private char mapUnencodedChar(char ch) {
if (this.unencodedCharacters != null) {
UnencodedCharacter unencoded
= (UnencodedCharacter)this.unencodedCharacters.get(new Character(ch));
if (unencoded != null) {
if (this.additionalEncodings == null) {
this.additionalEncodings = new java.util.ArrayList();
}
SimpleSingleByteEncoding encoding = null;
char mappedStart = 0;
int additionalsCount = this.additionalEncodings.size();
for (int i = 0; i < additionalsCount; i++) {
mappedStart += 256;
encoding = getAdditionalEncoding(i);
char alt = encoding.mapChar(ch);
if (alt != 0) {
return (char)(mappedStart + alt);
}
}
if (encoding != null && encoding.isFull()) {
encoding = null;
}
if (encoding == null) {
encoding = new SimpleSingleByteEncoding(
getFontName() + "EncodingSupp" + (additionalsCount + 1));
this.additionalEncodings.add(encoding);
mappedStart += 256;
}
return (char)(mappedStart + encoding.addCharacter(unencoded.getCharacter()));
}
return '#';
}
return 0;
}

/** {@inheritDoc} */
public boolean hasChar(char c) {
return (mapping.mapChar(c) > 0);
char d = mapping.mapChar(c);
if (d != SingleByteEncoding.NOT_FOUND_CODE_POINT) {
return true;
}
//Check unencoded characters which are available in the font by character name
d = mapUnencodedChar(c);
if (d != SingleByteEncoding.NOT_FOUND_CODE_POINT) {
return true;
}
return false;
}

/* ---- single byte font specific setters --- */
@@ -146,13 +215,106 @@ public class SingleByteFont extends CustomFont {
/**
* Sets a width for a character.
* @param index index of the character
* @param width the width of the character
* @param w the width of the character
*/
public void setWidth(int index, int width) {
public void setWidth(int index, int w) {
if (this.width == null) {
this.width = new int[getLastChar() - getFirstChar() + 1];
}
this.width[index - getFirstChar()] = width;
this.width[index - getFirstChar()] = w;
}

/**
* Adds an unencoded character (one that is not supported by the primary encoding).
* @param ch the named character
* @param width the width of the character
*/
public void addUnencodedCharacter(NamedCharacter ch, int width) {
if (this.unencodedCharacters == null) {
this.unencodedCharacters = new java.util.HashMap();
}
if (ch.hasSingleUnicodeValue()) {
UnencodedCharacter uc = new UnencodedCharacter(ch, width);
this.unencodedCharacters.put(new Character(ch.getSingleUnicodeValue()), uc);
} else {
//Cannot deal with unicode sequences, so ignore this character
}
}

/**
* Indicates whether the encoding has additional encodings besides the primary encoding.
* @return true if there are additional encodings.
*/
public boolean hasAdditionalEncodings() {
return (this.additionalEncodings != null) && (this.additionalEncodings.size() > 0);
}
/**
* Returns the number of additional encodings this single-byte font maintains.
* @return the number of additional encodings
*/
public int getAdditionalEncodingCount() {
if (hasAdditionalEncodings()) {
return this.additionalEncodings.size();
} else {
return 0;
}
}
/**
* Returns an additional encoding.
* @param index the index of the additional encoding
* @return the additional encoding
* @throws IndexOutOfBoundsException if the index is out of bounds
*/
public SimpleSingleByteEncoding getAdditionalEncoding(int index)
throws IndexOutOfBoundsException {
if (hasAdditionalEncodings()) {
return (SimpleSingleByteEncoding)this.additionalEncodings.get(index);
} else {
throw new IndexOutOfBoundsException("No additional encodings available");
}
}
/**
* Returns an array with the widths for an additional encoding.
* @param index the index of the additional encoding
* @return the width array
*/
public int[] getAdditionalWidths(int index) {
SimpleSingleByteEncoding enc = getAdditionalEncoding(index);
int[] arr = new int[enc.getLastChar() - enc.getFirstChar() + 1];
for (int i = 0, c = arr.length; i < c; i++) {
NamedCharacter nc = enc.getCharacterForIndex(enc.getFirstChar() + i);
UnencodedCharacter uc = (UnencodedCharacter)this.unencodedCharacters.get(
new Character(nc.getSingleUnicodeValue()));
arr[i] = uc.getWidth();
}
return arr;
}
private static final class UnencodedCharacter {
private NamedCharacter character;
private int width;
public UnencodedCharacter(NamedCharacter character, int width) {
this.character = character;
this.width = width;
}
public NamedCharacter getCharacter() {
return this.character;
}
public int getWidth() {
return this.width;
}
/** {@inheritDoc} */
public String toString() {
return getCharacter().toString();
}
}

}

+ 2
- 5
src/java/org/apache/fop/fonts/Typeface.java View File

@@ -19,11 +19,8 @@
package org.apache.fop.fonts;

// FOP


/**
* Base class for PDF font classes
* Base class for font classes
*/
public abstract class Typeface implements FontMetrics {

@@ -37,7 +34,7 @@ public abstract class Typeface implements FontMetrics {
* Get the encoding of the font.
* @return the encoding
*/
public abstract String getEncoding();
public abstract String getEncodingName();

/**
* Map a Unicode character to a code point in the font.

+ 11
- 8
src/java/org/apache/fop/fonts/truetype/TTFSubSetFile.java View File

@@ -592,8 +592,8 @@ public class TTFSubSetFile extends TTFFile {
+ mtxTab[origIndex.intValue()].getOffset()) < 0) {
// origIndex is a composite glyph
allComposites.put(origIndex, glyphs.get(origIndex));
List composites =
getIncludedGlyphs(in, (int)entry.getOffset(),
List composites
= getIncludedGlyphs(in, (int)entry.getOffset(),
origIndex);

// Iterate through all composites pointed to
@@ -651,6 +651,9 @@ public class TTFSubSetFile extends TTFFile {
if (!checkTTC(in, name)) {
throw new IOException("Failed to read font");
}
//Copy the Map as we're going to modify it
Map subsetGlyphs = new java.util.HashMap(glyphs);

output = new byte[in.getFileSize()];

@@ -661,14 +664,14 @@ public class TTFSubSetFile extends TTFFile {
readHorizontalMetrics(in);
readIndexToLocation(in);

scanGlyphs(in, glyphs);
scanGlyphs(in, subsetGlyphs);

createDirectory(); // Create the TrueType header and directory

createHead(in);
createHhea(in, glyphs.size()); // Create the hhea table
createHmtx(in, glyphs); // Create hmtx table
createMaxp(in, glyphs.size()); // copy the maxp table
createHhea(in, subsetGlyphs.size()); // Create the hhea table
createHmtx(in, subsetGlyphs); // Create hmtx table
createMaxp(in, subsetGlyphs.size()); // copy the maxp table

boolean optionalTableFound;
optionalTableFound = createCvt(in); // copy the cvt table
@@ -689,8 +692,8 @@ public class TTFSubSetFile extends TTFFile {
log.debug("TrueType: prep table not present. Skipped.");
}

createLoca(glyphs.size()); // create empty loca table
createGlyf(in, glyphs); //create glyf table and update loca table
createLoca(subsetGlyphs.size()); // create empty loca table
createGlyf(in, subsetGlyphs); //create glyf table and update loca table

pad4();
createCheckSumAdjustment();

+ 28
- 18
src/java/org/apache/fop/fonts/type1/AFMCharMetrics.java View File

@@ -21,6 +21,8 @@ package org.apache.fop.fonts.type1;

import java.awt.geom.RectangularShape;

import org.apache.fop.fonts.NamedCharacter;


/**
* Holds the metrics of a single character from an AFM file.
@@ -28,8 +30,7 @@ import java.awt.geom.RectangularShape;
public class AFMCharMetrics {

private int charCode = -1;
private String unicodeSequence;
private String charName;
private NamedCharacter character;
private double widthX;
private double widthY;
private RectangularShape bBox;
@@ -59,36 +60,45 @@ public class AFMCharMetrics {
}
/**
* Returns the Unicode sequence for this character.
* @return the Unicode characters
* (or null if no such Unicode sequence exists for this character)
* Returns the named character represented by this instance.
* @return the named character (or null if no named character is associated)
*/
public String getUnicodeSequence() {
return this.unicodeSequence;
public NamedCharacter getCharacter() {
return this.character;
}
/**
* Sets the named character represented by this instance.
* @param ch the named character
*/
public void setCharacter(NamedCharacter ch) {
this.character = ch;
}
/**
* Sets the Unicode sequence for this character.
* Sets the named character represented by this instance.
* @param charName the character name (as defined in the Adobe glyph list)
* @param unicodeSequence the Unicode sequence
*/
public void setUnicodeSequence(String unicodeSequence) {
this.unicodeSequence = unicodeSequence;
public void setCharacter(String charName, String unicodeSequence) {
setCharacter(new NamedCharacter(charName, unicodeSequence));
}
/**
* Returns the PostScript character name.
* @return the charName
* Returns the Unicode sequence for this character.
* @return the Unicode characters
* (or null if no such Unicode sequence exists for this character)
*/
public String getCharName() {
return charName;
public String getUnicodeSequence() {
return (getCharacter() != null ? getCharacter().getUnicodeSequence() : null);
}
/**
* Sets the PostScript character name.
* @param charName the charName to set
* Returns the PostScript character name.
* @return the charName (or null if no character name is associated)
*/
public void setCharName(String charName) {
this.charName = charName;
public String getCharName() {
return (getCharacter() != null ? getCharacter().getName() : null);
}
/**

+ 2
- 10
src/java/org/apache/fop/fonts/type1/AFMFile.java View File

@@ -26,7 +26,6 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.xmlgraphics.fonts.Glyphs;
import org.apache.xmlgraphics.java2d.Dimension2DDouble;

/**
@@ -315,15 +314,8 @@ public class AFMFile {
public void addCharMetrics(AFMCharMetrics metrics) {
String name = metrics.getCharName();
if (metrics.getUnicodeSequence() == null) {
if (name != null) {
String u = Glyphs.getUnicodeSequenceForGlyphName(metrics.getCharName());
if (u != null) {
metrics.setUnicodeSequence(u);
}
} else {
//Ignore as no Unicode assignment is possible
return;
}
//Ignore as no Unicode assignment is possible
return;
}
this.charMetrics.add(metrics);
if (name != null) {

+ 16
- 1
src/java/org/apache/fop/fonts/type1/AFMParser.java View File

@@ -31,6 +31,8 @@ import java.util.Stack;

import org.apache.commons.io.IOUtils;

import org.apache.fop.fonts.NamedCharacter;

/**
* Parses the contents of a Type 1 AFM font metrics file into an object structure ({@link AFMFile}).
*/
@@ -126,7 +128,7 @@ public class AFMParser {
VALUE_PARSERS.put(W, new NotImplementedYet(W));
VALUE_PARSERS.put(W0, new NotImplementedYet(W0));
VALUE_PARSERS.put(W1, new NotImplementedYet(W1));
VALUE_PARSERS.put(N, new StringSetter("CharName"));
VALUE_PARSERS.put(N, new NamedCharacterSetter("Character"));
VALUE_PARSERS.put(B, new CharBBox());
VALUE_PARSERS.put(START_TRACK_KERN, new NotImplementedYet(START_TRACK_KERN));
VALUE_PARSERS.put(END_TRACK_KERN, new NotImplementedYet(END_TRACK_KERN));
@@ -379,6 +381,19 @@ public class AFMParser {
}
}
private static class NamedCharacterSetter extends BeanSetter {
public NamedCharacterSetter(String variable) {
super(variable);
}
public void parse(String line, int startpos, Stack stack) throws IOException {
NamedCharacter ch = new NamedCharacter(getStringValue(line, startpos));
Object obj = stack.peek();
setValue(obj, ch);
}
}
private static class NumberSetter extends BeanSetter {
public NumberSetter(String variable) {
super(variable);

+ 24
- 9
src/java/org/apache/fop/fonts/type1/Type1FontLoader.java View File

@@ -127,12 +127,17 @@ public class Type1FontLoader extends FontLoader {
singleFont.setEmbedFileName(this.fontFileURI);
returnFont = singleFont;
handleEncoding(afm, pfm);
handleFontName(afm, pfm);
handleMetrics(afm, pfm);
}

private void handleEncoding(AFMFile afm, PFMFile pfm) {
//Encoding
if (afm != null) {
String encoding = afm.getEncodingScheme();
if ("AdobeStandardEncoding".equals(encoding)) {
//Use WinAnsi in this case as it better fits the usual character set people need
singleFont.setEncoding(CodePointMapping.WIN_ANSI_ENCODING);
singleFont.setEncoding(CodePointMapping.STANDARD_ENCODING);
} else {
String effEncodingName;
if ("FontSpecific".equals(encoding)) {
@@ -147,6 +152,14 @@ public class Type1FontLoader extends FontLoader {
CodePointMapping mapping = buildCustomEncoding(effEncodingName, afm);
singleFont.setEncoding(mapping);
}
List charMetrics = afm.getCharMetrics();
for (int i = 0, c = afm.getCharCount(); i < c; i++) {
AFMCharMetrics metrics = (AFMCharMetrics)charMetrics.get(i);
if (!metrics.hasCharCode() && metrics.getCharacter() != null) {
singleFont.addUnencodedCharacter(metrics.getCharacter(),
(int)Math.round(metrics.getWidthX()));
}
}
} else {
if (pfm.getCharSet() >= 0 && pfm.getCharSet() <= 2) {
singleFont.setEncoding(pfm.getCharSetName() + "Encoding");
@@ -156,7 +169,9 @@ public class Type1FontLoader extends FontLoader {
singleFont.setEncoding("WinAnsiEncoding"); //Try fallback, no guarantees!
}
}
}

private void handleFontName(AFMFile afm, PFMFile pfm) {
//Font name
if (afm != null) {
returnFont.setFontName(afm.getFontName()); //PostScript font name
@@ -173,7 +188,9 @@ public class Type1FontLoader extends FontLoader {
names.add(pfm.getWindowsName()); //emulate afm.getFamilyName()
returnFont.setFamilyNames(names);
}
}

private void handleMetrics(AFMFile afm, PFMFile pfm) {
//Basic metrics
if (afm != null) {
if (afm.getCapHeight() != null) {
@@ -271,8 +288,6 @@ public class Type1FontLoader extends FontLoader {
returnFont.setCapHeight(returnFont.getAscender());
}
/* DISABLED because of mismatch with our using WinAnsiEncoding and the AFM
delivering the font's default encoding.
if (afm != null) {
//TODO returnFont.setFlags(flags);
@@ -286,15 +301,15 @@ public class Type1FontLoader extends FontLoader {
}
}
returnFont.replaceKerningMap(afm.createXKerningMapEncoded());
} else {*/
returnFont.setFlags(pfm.getFlags());
} else {
returnFont.setFirstChar(pfm.getFirstChar());
returnFont.setLastChar(pfm.getLastChar());
for (short i = pfm.getFirstChar(); i <= pfm.getLastChar(); i++) {
singleFont.setWidth(i, pfm.getCharWidth(i));
}
returnFont.replaceKerningMap(pfm.getKerning());
//}
}
returnFont.setFlags(pfm.getFlags());
}

private CodePointMapping buildCustomEncoding(String encodingName, AFMFile afm) {

+ 14
- 2
src/java/org/apache/fop/layoutmgr/AbstractLayoutManager.java View File

@@ -113,7 +113,7 @@ public abstract class AbstractLayoutManager extends AbstractBaseLayoutManager
if (curChildLM != null && !curChildLM.isFinished()) {
return curChildLM;
}
while (childLMiter.hasNext()) {
if (childLMiter.hasNext()) {
curChildLM = (LayoutManager) childLMiter.next();
curChildLM.initialize();
return curChildLM;
@@ -350,6 +350,10 @@ public abstract class AbstractLayoutManager extends AbstractBaseLayoutManager
/**
* Registers the FO's markers on the current PageViewport
*
* @param isStarting boolean indicating whether the markers qualify as 'starting'
* @param isFirst boolean indicating whether the markers qualify as 'first'
* @param isLast boolean indicating whether the markers qualify as 'last'
*/
protected void addMarkersToPage(boolean isStarting, boolean isFirst, boolean isLast) {
if (this.markers != null) {
@@ -361,10 +365,18 @@ public abstract class AbstractLayoutManager extends AbstractBaseLayoutManager
}
}

/**
* Registers the FO's id on the current PageViewport
*/
protected void addId() {
if (fobj != null) {
getPSLM().addIDToPage(fobj.getId());
}
}
/** {@inheritDoc} */
public String toString() {
return (super.toString() + (fobj != null ? "[fobj=" + fobj.toString() + "]" : ""));
}
}

+ 5
- 5
src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java View File

@@ -247,7 +247,7 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager
MinOptMax stackLimit = new MinOptMax(relDims.bpd);

LinkedList returnedList = null;
LinkedList returnedList;
LinkedList contentList = new LinkedList();
LinkedList returnList = new LinkedList();
@@ -718,7 +718,7 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager
addBlockSpacing(0.0, new MinOptMax(layoutContext.getSpaceBefore()));
}

LayoutManager childLM = null;
LayoutManager childLM;
LayoutManager lastLM = null;
LayoutContext lc = new LayoutContext(0);
lc.setSpaceAdjust(layoutContext.getSpaceAdjust());
@@ -747,7 +747,7 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager
}
Position innerPosition = pos;
if (pos instanceof NonLeafPosition) {
innerPosition = ((NonLeafPosition)pos).getPosition();
innerPosition = pos.getPosition();
}
if (pos instanceof BlockContainerPosition) {
if (bcpos != null) {
@@ -783,7 +783,7 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager
}
}

getPSLM().addIDToPage(getBlockContainerFO().getId());
addId();
addMarkersToPage(true, isFirst(firstPos), isLast(lastPos));
@@ -884,7 +884,7 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager
referenceArea = null;
resetSpaces();
getPSLM().notifyEndOfLayout(((BlockContainer)getFObj()).getId());
getPSLM().notifyEndOfLayout(fobj.getId());
}
/**

+ 4
- 4
src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java View File

@@ -147,7 +147,7 @@ public class BlockLayoutManager extends BlockStackingLayoutManager
* @return true if there are more child lms
*/
public boolean hasNext() {
return (curPos < listLMs.size()) ? true : createNextChildLMs(curPos);
return (curPos < listLMs.size()) || createNextChildLMs(curPos);
}

/**
@@ -250,7 +250,7 @@ public class BlockLayoutManager extends BlockStackingLayoutManager
addBlockSpacing(0.0, new MinOptMax(layoutContext.getSpaceBefore()));
}

LayoutManager childLM = null;
LayoutManager childLM;
LayoutManager lastLM = null;
LayoutContext lc = new LayoutContext(0);
lc.setSpaceAdjust(layoutContext.getSpaceAdjust());
@@ -280,7 +280,7 @@ public class BlockLayoutManager extends BlockStackingLayoutManager
Position innerPosition = pos;
if (pos instanceof NonLeafPosition) {
//Not all elements are wrapped
innerPosition = ((NonLeafPosition) pos).getPosition();
innerPosition = pos.getPosition();
}
if (innerPosition == null) {
// pos was created by this BlockLM and was inside an element
@@ -309,7 +309,7 @@ public class BlockLayoutManager extends BlockStackingLayoutManager
}
}

getPSLM().addIDToPage(getBlockFO().getId());
addId();
addMarkersToPage(true, isFirst(firstPos), isLast(lastPos));


+ 0
- 7
src/java/org/apache/fop/layoutmgr/inline/AbstractGraphicsLayoutManager.java View File

@@ -123,13 +123,6 @@ public abstract class AbstractGraphicsLayoutManager extends LeafNodeLayoutManage
);
}

/**
* {@inheritDoc}
*/
protected void addId() {
getPSLM().addIDToPage(fobj.getId());
}

/**
* Returns the image of foreign object area to be put into
* the viewport.

+ 8
- 9
src/java/org/apache/fop/layoutmgr/inline/AbstractPageNumberCitationLayoutManager.java View File

@@ -51,7 +51,7 @@ public abstract class AbstractPageNumberCitationLayoutManager extends LeafNodeLa
* Constructor
*
* @param node the formatting object that creates this area
* @todo better retrieval of font info
* TODO better retrieval of font info
*/
public AbstractPageNumberCitationLayoutManager(AbstractPageNumberCitation node) {
super(node);
@@ -83,7 +83,7 @@ public abstract class AbstractPageNumberCitationLayoutManager extends LeafNodeLa

/** {@inheritDoc} */
public InlineArea get(LayoutContext context) {
curArea = getPageNumberCitationInlineArea(parentLM);
curArea = getPageNumberCitationInlineArea();
return curArea;
}
@@ -99,12 +99,15 @@ public abstract class AbstractPageNumberCitationLayoutManager extends LeafNodeLa
}
/**
* if id can be resolved then simply return a word, otherwise
* If id can be resolved then simply return a word, otherwise
* return a resolvable area
*
* @param parentLM the parent LayoutManager
* @return a corresponding InlineArea
*/
private InlineArea getPageNumberCitationInlineArea(LayoutManager parentLM) {
private InlineArea getPageNumberCitationInlineArea() {
PageViewport page = getPSLM().getFirstPVWithID(fobj.getRefId());
TextArea text = null;
TextArea text;
if (page != null) {
String str = page.getPageNumberString();
// get page string from parent, build area
@@ -150,9 +153,5 @@ public abstract class AbstractPageNumberCitationLayoutManager extends LeafNodeLa
return width;
}

/** {@inheritDoc} */
protected void addId() {
getPSLM().addIDToPage(fobj.getId());
}
}


+ 6
- 17
src/java/org/apache/fop/layoutmgr/inline/CharacterLayoutManager.java View File

@@ -45,7 +45,6 @@ import org.apache.fop.util.CharUtilities;
* LayoutManager for the fo:character formatting object
*/
public class CharacterLayoutManager extends LeafNodeLayoutManager {
private Character fobj;
private MinOptMax letterSpaceIPD;
private int hyphIPD;
private Font font;
@@ -57,13 +56,13 @@ public class CharacterLayoutManager extends LeafNodeLayoutManager {
* @param node the fo:character formatting object
*/
public CharacterLayoutManager(Character node) {
// @todo better null checking of node
super(node);
fobj = node;
}
/** {@inheritDoc} */
public void initialize() {
Character fobj = (Character)this.fobj;
FontInfo fi = fobj.getFOEventHandler().getFontInfo();
FontTriplet[] fontkeys = fobj.getCommonFont().getFontState(fi);
font = fi.getFontInstance(fontkeys[0], fobj.getCommonFont().fontSize.getValue(this));
@@ -90,7 +89,7 @@ public class CharacterLayoutManager extends LeafNodeLayoutManager {
text.addWord(String.valueOf(ch), 0);
}
TraitSetter.setProducerID(text, node.getId());
TraitSetter.addTextDecoration(text, fobj.getTextDecoration());
TraitSetter.addTextDecoration(text, node.getTextDecoration());
return text;
}

@@ -105,6 +104,8 @@ public class CharacterLayoutManager extends LeafNodeLayoutManager {
return null;
}

Character fobj = (Character)this.fobj;
ipd = new MinOptMax(font.getCharWidth(fobj.getCharacter()));

curArea.setIPD(ipd.opt);
@@ -178,14 +179,7 @@ public class CharacterLayoutManager extends LeafNodeLayoutManager {
/** {@inheritDoc} */
public boolean applyChanges(List oldList) {
setFinished(false);
if (isSomethingChanged) {
// there is nothing to do,
// possible changes have already been applied
// in the hyphenate() method
return true;
} else {
return false;
}
return isSomethingChanged;
}

/** {@inheritDoc} */
@@ -238,10 +232,5 @@ public class CharacterLayoutManager extends LeafNodeLayoutManager {
return returnList;
}

/** {@inheritDoc} */
protected void addId() {
getPSLM().addIDToPage(fobj.getId());
}

}


+ 0
- 5
src/java/org/apache/fop/layoutmgr/inline/ICLayoutManager.java View File

@@ -31,12 +31,10 @@ import org.apache.fop.fo.flow.InlineContainer;
* and id areas are maintained for later retrieval.
*/
public class ICLayoutManager extends LeafNodeLayoutManager {
private InlineContainer fobj;
private List childrenLM;

public ICLayoutManager(InlineContainer node, List childLM) {
super(node);
fobj = node;
childrenLM = childLM;
}

@@ -44,7 +42,4 @@ public class ICLayoutManager extends LeafNodeLayoutManager {
return null;
}

protected void addId() {
getPSLM().addIDToPage(fobj.getId());
}
}

+ 2
- 7
src/java/org/apache/fop/layoutmgr/inline/InlineLayoutManager.java View File

@@ -297,7 +297,7 @@ public class InlineLayoutManager extends InlineStackingLayoutManager {
);
}
while ((curLM = (LayoutManager) getChildLM()) != null) {
while ((curLM = getChildLM()) != null) {
if (!(curLM instanceof InlineLevelLayoutManager)) {
// A block LM
@@ -436,7 +436,7 @@ public class InlineLayoutManager extends InlineStackingLayoutManager {
// layout context given to lastLM, but must be cleared in the
// layout context given to the other LMs.
LinkedList positionList = new LinkedList();
NonLeafPosition pos = null;
NonLeafPosition pos;
LayoutManager lastLM = null;// last child LM in this iterator
Position lastPos = null;
while (parentIter.hasNext()) {
@@ -600,9 +600,4 @@ public class InlineLayoutManager extends InlineStackingLayoutManager {
return this.auxiliaryPosition;
}
/** {@inheritDoc} */
protected void addId() {
getPSLM().addIDToPage(fobj.getId());
}
}

+ 6
- 20
src/java/org/apache/fop/layoutmgr/inline/InlineStackingLayoutManager.java View File

@@ -75,15 +75,9 @@ public abstract class InlineStackingLayoutManager extends AbstractLayoutManager

private Area currentArea; // LineArea or InlineParent

//private BreakPoss prevBP;
/** The child layout context */
protected LayoutContext childLC;

private boolean bAreaCreated = false;

//private LayoutManager currentLM = null;

/** Used to store previous content IPD for each child LM. */
private HashMap hmPrevIPD = new HashMap();

@@ -170,14 +164,6 @@ public abstract class InlineStackingLayoutManager extends AbstractLayoutManager
hmPrevIPD.clear();
}

/**
* This method is called by addAreas() so IDs can be added to a page for FOs that
* support the 'id' property.
*/
protected void addId() {
// Do nothing here, overriden in subclasses that have an 'id' property.
}
/**
* Returns the current area.
* @return the current area
@@ -255,7 +241,7 @@ public abstract class InlineStackingLayoutManager extends AbstractLayoutManager
// "unwrap" the Position stored in each element of oldList
while (oldListIterator.hasNext()) {
element = (KnuthElement) oldListIterator.next();
element.setPosition(((NonLeafPosition)element.getPosition()).getPosition());
element.setPosition(element.getPosition().getPosition());
}

// The last element may not have a layout manager (its position == null);
@@ -288,7 +274,7 @@ public abstract class InlineStackingLayoutManager extends AbstractLayoutManager
// "unwrap" the Position stored in each element of oldList
while (oldListIterator.hasNext()) {
element = (KnuthElement) oldListIterator.next();
element.setPosition(((NonLeafPosition)element.getPosition()).getPosition());
element.setPosition(element.getPosition().getPosition());
}

((InlineLevelLayoutManager)
@@ -298,14 +284,14 @@ public abstract class InlineStackingLayoutManager extends AbstractLayoutManager

/** {@inheritDoc} */
public void getWordChars(StringBuffer sbChars, Position pos) {
Position newPos = ((NonLeafPosition) pos).getPosition();
Position newPos = pos.getPosition();
((InlineLevelLayoutManager)
newPos.getLM()).getWordChars(sbChars, newPos);
}

/** {@inheritDoc} */
public void hyphenate(Position pos, HyphContext hc) {
Position newPos = ((NonLeafPosition) pos).getPosition();
Position newPos = pos.getPosition();
((InlineLevelLayoutManager)
newPos.getLM()).hyphenate(newPos, hc);
}
@@ -318,7 +304,7 @@ public abstract class InlineStackingLayoutManager extends AbstractLayoutManager
while (oldListIterator.hasNext()) {
oldElement = (KnuthElement) oldListIterator.next();
oldElement.setPosition
(((NonLeafPosition) oldElement.getPosition()).getPosition());
(oldElement.getPosition().getPosition());
}
// reset the iterator
oldListIterator = oldList.listIterator();
@@ -385,7 +371,7 @@ public abstract class InlineStackingLayoutManager extends AbstractLayoutManager
while (oldListIterator.hasNext()) {
oldElement = (KnuthElement) oldListIterator.next();
oldElement.setPosition
(((NonLeafPosition) oldElement.getPosition()).getPosition());
(oldElement.getPosition().getPosition());
}
// reset the iterator
oldListIterator = oldList.listIterator();

+ 0
- 5
src/java/org/apache/fop/layoutmgr/inline/LeaderLayoutManager.java View File

@@ -341,11 +341,6 @@ public class LeaderLayoutManager extends LeafNodeLayoutManager {
return returnList;
}

/** {@inheritDoc} */
protected void addId() {
getPSLM().addIDToPage(fobj.getId());
}

/** {@inheritDoc} */
public int getBaseLength(int lengthBase, FObj fobj) {
return getParent().getBaseLength(lengthBase, getParent().getFObj());

+ 0
- 10
src/java/org/apache/fop/layoutmgr/inline/LeafNodeLayoutManager.java View File

@@ -65,8 +65,6 @@ public abstract class LeafNodeLayoutManager extends AbstractLayoutManager
/** The alignment context applying to this area */
protected AlignmentContext alignmentContext = null;
private MinOptMax ipd;

/** Flag to indicate if something was changed as part of the getChangeKnuthElements sequence */
protected boolean isSomethingChanged = false;
/** Our area info for the Knuth elements */
@@ -204,14 +202,6 @@ public abstract class LeafNodeLayoutManager extends AbstractLayoutManager
return curArea;
}
/**
* This method is called by addAreas() so IDs can be added to a page for FOs that
* support the 'id' property.
*/
protected void addId() {
// Do nothing here, overriden in subclasses that have an 'id' property.
}
/**
* Offset this area.
* Offset the inline area in the bpd direction when adding the

+ 1
- 5
src/java/org/apache/fop/layoutmgr/inline/PageNumberLayoutManager.java View File

@@ -42,7 +42,7 @@ public class PageNumberLayoutManager extends LeafNodeLayoutManager {
* Constructor
*
* @param node the fo:page-number formatting object that creates the area
* @todo better null checking of node, font
* TODO better null checking of node, font
*/
public PageNumberLayoutManager(PageNumber node) {
super(node);
@@ -131,9 +131,5 @@ public class PageNumberLayoutManager extends LeafNodeLayoutManager {
return width;
}
/** {@inheritDoc} */
protected void addId() {
getPSLM().addIDToPage(fobj.getId());
}
}


+ 6
- 6
src/java/org/apache/fop/layoutmgr/list/ListBlockLayoutManager.java View File

@@ -156,11 +156,11 @@ public class ListBlockLayoutManager extends BlockStackingLayoutManager
addBlockSpacing(0.0, new MinOptMax(layoutContext.getSpaceBefore()));
}

getPSLM().addIDToPage(getListBlockFO().getId());
addId();

// the list block contains areas stacked from each list item

LayoutManager childLM = null;
LayoutManager childLM;
LayoutContext lc = new LayoutContext(0);
LayoutManager firstLM = null;
LayoutManager lastLM = null;
@@ -181,10 +181,10 @@ public class ListBlockLayoutManager extends BlockStackingLayoutManager
}
if (pos instanceof NonLeafPosition
&& (pos.getPosition() != null)
&& ((NonLeafPosition) pos).getPosition().getLM() != this) {
&& pos.getPosition().getLM() != this) {
// pos was created by a child of this ListBlockLM
positionList.add(((NonLeafPosition) pos).getPosition());
lastLM = ((NonLeafPosition) pos).getPosition().getLM();
positionList.add(pos.getPosition());
lastLM = pos.getPosition().getLM();
if (firstLM == null) {
firstLM = lastLM;
}
@@ -218,7 +218,7 @@ public class ListBlockLayoutManager extends BlockStackingLayoutManager
curBlockArea = null;
resetSpaces();
getPSLM().notifyEndOfLayout(((ListBlock)getFObj()).getId());
getPSLM().notifyEndOfLayout(fobj.getId());
}

/**

+ 5
- 5
src/java/org/apache/fop/layoutmgr/list/ListItemContentLayoutManager.java View File

@@ -115,9 +115,9 @@ public class ListItemContentLayoutManager extends BlockStackingLayoutManager {
LayoutContext layoutContext) {
getParentArea(null);
getPSLM().addIDToPage(getPartFO().getId());
addId();

LayoutManager childLM = null;
LayoutManager childLM;
LayoutContext lc = new LayoutContext(0);
LayoutManager firstLM = null;
LayoutManager lastLM = null;
@@ -141,8 +141,8 @@ public class ListItemContentLayoutManager extends BlockStackingLayoutManager {
}
if (pos instanceof NonLeafPosition) {
// pos was created by a child of this ListBlockLM
positionList.add(((NonLeafPosition) pos).getPosition());
lastLM = ((NonLeafPosition) pos).getPosition().getLM();
positionList.add(pos.getPosition());
lastLM = pos.getPosition().getLM();
if (firstLM == null) {
firstLM = lastLM;
}
@@ -172,7 +172,7 @@ public class ListItemContentLayoutManager extends BlockStackingLayoutManager {

curBlockArea = null;
getPSLM().notifyEndOfLayout(((AbstractListItemPart)getFObj()).getId());
getPSLM().notifyEndOfLayout(fobj.getId());
}

/**

+ 10
- 10
src/java/org/apache/fop/layoutmgr/list/ListItemLayoutManager.java View File

@@ -73,8 +73,6 @@ public class ListItemLayoutManager extends BlockStackingLayoutManager
private LinkedList labelList = null;
private LinkedList bodyList = null;

private int listItemHeight;
private boolean discardBorderBefore;
private boolean discardBorderAfter;
private boolean discardPaddingBefore;
@@ -84,7 +82,9 @@ public class ListItemLayoutManager extends BlockStackingLayoutManager
private boolean keepWithNextPendingOnLabel;
private boolean keepWithNextPendingOnBody;

private int listItemHeight;
private class ListItemPosition extends Position {
private int iLabelFirstIndex;
private int iLabelLastIndex;
@@ -307,12 +307,12 @@ public class ListItemLayoutManager extends BlockStackingLayoutManager
int additionalPenaltyHeight = 0;
KnuthElement endEl = (KnuthElement)elementLists[0].get(end[0]);
if (endEl instanceof KnuthPenalty) {
additionalPenaltyHeight = ((KnuthPenalty)endEl).getW();
additionalPenaltyHeight = endEl.getW();
}
endEl = (KnuthElement)elementLists[1].get(end[1]);
if (endEl instanceof KnuthPenalty) {
additionalPenaltyHeight = Math.max(
additionalPenaltyHeight, ((KnuthPenalty)endEl).getW());
additionalPenaltyHeight, endEl.getW());
}
int boxHeight = step - addedBoxHeight - penaltyHeight;
@@ -419,10 +419,10 @@ public class ListItemLayoutManager extends BlockStackingLayoutManager
// body
// "unwrap" the Positions stored in the elements
ListIterator oldListIterator = oldList.listIterator();
KnuthElement oldElement = null;
KnuthElement oldElement;
while (oldListIterator.hasNext()) {
oldElement = (KnuthElement)oldListIterator.next();
Position innerPosition = ((NonLeafPosition) oldElement.getPosition()).getPosition();
Position innerPosition = oldElement.getPosition().getPosition();
//log.debug(" BLM> unwrapping: " + (oldElement.isBox()
// ? "box " : (oldElement.isGlue() ? "glue " : "penalty"))
// + " creato da " + oldElement.getLayoutManager().getClass().getName());
@@ -465,7 +465,7 @@ public class ListItemLayoutManager extends BlockStackingLayoutManager
LayoutContext layoutContext) {
getParentArea(null);

getPSLM().addIDToPage(getListItemFO().getId());
addId();

LayoutContext lc = new LayoutContext(0);
Position firstPos = null;
@@ -484,7 +484,7 @@ public class ListItemLayoutManager extends BlockStackingLayoutManager
}
if (pos instanceof NonLeafPosition && pos.getPosition() != null) {
// pos contains a ListItemPosition created by this ListBlockLM
positionList.add(((NonLeafPosition) pos).getPosition());
positionList.add(pos.getPosition());
}
}

@@ -560,7 +560,7 @@ public class ListItemLayoutManager extends BlockStackingLayoutManager
curBlockArea = null;
resetSpaces();
getPSLM().notifyEndOfLayout(((ListItem)getFObj()).getId());
getPSLM().notifyEndOfLayout(fobj.getId());
}

/**

+ 2
- 5
src/java/org/apache/fop/layoutmgr/table/TableAndCaptionLayoutManager.java View File

@@ -23,7 +23,6 @@ import org.apache.fop.fo.flow.table.TableAndCaption;
import org.apache.fop.layoutmgr.BlockStackingLayoutManager;
import org.apache.fop.layoutmgr.LayoutContext;
import org.apache.fop.layoutmgr.PositionIterator;
import org.apache.fop.layoutmgr.Position;
import org.apache.fop.area.Area;
import org.apache.fop.area.Block;

@@ -36,8 +35,7 @@ import org.apache.fop.area.Block;
* @todo Implement getNextKnuthElements()
*/
public class TableAndCaptionLayoutManager extends BlockStackingLayoutManager {
private TableAndCaption fobj;

private Block curBlockArea;

//private List childBreaks = new java.util.ArrayList();
@@ -48,7 +46,6 @@ public class TableAndCaptionLayoutManager extends BlockStackingLayoutManager {
*/
public TableAndCaptionLayoutManager(TableAndCaption node) {
super(node);
fobj = node;
}

/**
@@ -134,7 +131,7 @@ public class TableAndCaptionLayoutManager extends BlockStackingLayoutManager {
public void addAreas(PositionIterator parentIter,
LayoutContext layoutContext) {
getParentArea(null);
getPSLM().addIDToPage(fobj.getId());
addId();

/* TODO: Reimplement using Knuth approach
LayoutManager childLM;

+ 1
- 4
src/java/org/apache/fop/layoutmgr/table/TableCaptionLayoutManager.java View File

@@ -23,7 +23,6 @@ import org.apache.fop.fo.flow.table.TableCaption;
import org.apache.fop.layoutmgr.BlockStackingLayoutManager;
import org.apache.fop.layoutmgr.LayoutContext;
import org.apache.fop.layoutmgr.PositionIterator;
import org.apache.fop.layoutmgr.Position;
import org.apache.fop.area.Area;
import org.apache.fop.area.Block;

@@ -34,7 +33,6 @@ import org.apache.fop.area.Block;
* @todo Implement getNextKnuthElements()
*/
public class TableCaptionLayoutManager extends BlockStackingLayoutManager {
private TableCaption fobj;

private Block curBlockArea;

@@ -46,7 +44,6 @@ public class TableCaptionLayoutManager extends BlockStackingLayoutManager {
*/
public TableCaptionLayoutManager(TableCaption node) {
super(node);
fobj = node;
}

/**
@@ -133,7 +130,7 @@ public class TableCaptionLayoutManager extends BlockStackingLayoutManager {
public void addAreas(PositionIterator parentIter,
LayoutContext layoutContext) {
getParentArea(null);
getPSLM().addIDToPage(fobj.getId());
addId();

/* TODO: Reimplement using Knuth approach
LayoutManager childLM;

+ 5
- 5
src/java/org/apache/fop/layoutmgr/table/TableCellLayoutManager.java View File

@@ -136,7 +136,7 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager
cellIPD = referenceIPD;
cellIPD -= getIPIndents();

LinkedList returnedList = null;
LinkedList returnedList;
LinkedList contentList = new LinkedList();
LinkedList returnList = new LinkedList();

@@ -242,7 +242,7 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager
p.setP(0);
}

getPSLM().notifyEndOfLayout(((TableCell)getFObj()).getId());
getPSLM().notifyEndOfLayout(fobj.getId());

setFinished(true);
return returnList;
@@ -330,7 +330,7 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager
int firstRowHeight) {
getParentArea(null);

getPSLM().addIDToPage(getTableCell().getId());
addId();

int borderBeforeWidth = primaryGridUnit.getBeforeBorderWidth(startRow, borderBeforeWhich);
int borderAfterWidth = primaryGridUnit.getAfterBorderWidth(endRow, borderAfterWhich);
@@ -419,7 +419,7 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager
int dx = xoffset;
for (int x = 0; x < gridUnits.length; x++) {
int ipd = getTable().getColumn(primaryGridUnit.getColIndex() + x)
.getColumnWidth().getValue((PercentBaseContext) getParent());
.getColumnWidth().getValue(getParent());
if (blocks[y][x] != null) {
Block block = blocks[y][x];
adjustYOffset(block, dy);
@@ -491,7 +491,7 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager
Block rowBackgroundArea = getBackgroundArea(paddingRectBPD, borderBeforeWidth);
((TableLayoutManager) parentLM).addBackgroundArea(rowBackgroundArea);
TraitSetter.addBackground(rowBackgroundArea, row.getCommonBorderPaddingBackground(),
(TableLayoutManager) parentLM,
parentLM,
-xoffset - startIndent, -borderBeforeWidth,
parentLM.getContentAreaIPD(), firstRowHeight);
}

+ 3
- 3
src/java/org/apache/fop/layoutmgr/table/TableLayoutManager.java View File

@@ -235,7 +235,7 @@ public class TableLayoutManager extends BlockStackingLayoutManager


// Elements for the table-header/footer/body
LinkedList contentKnuthElements = null;
LinkedList contentKnuthElements;
contentLM = new TableContentLayoutManager(this);
LayoutContext childLC = new LayoutContext(0);
/*
@@ -321,7 +321,7 @@ public class TableLayoutManager extends BlockStackingLayoutManager
public void addAreas(PositionIterator parentIter,
LayoutContext layoutContext) {
getParentArea(null);
getPSLM().addIDToPage(getTable().getId());
addId();

// add space before, in order to implement display-align = "center" or "after"
if (layoutContext.getSpaceBefore() != 0) {
@@ -381,7 +381,7 @@ public class TableLayoutManager extends BlockStackingLayoutManager
resetSpaces();
curBlockArea = null;
getPSLM().notifyEndOfLayout(((Table)getFObj()).getId());
getPSLM().notifyEndOfLayout(fobj.getId());
}

/**

+ 1
- 4
src/java/org/apache/fop/pdf/PDFCMap.java View File

@@ -21,7 +21,6 @@ package org.apache.fop.pdf;

import java.io.IOException;
import java.io.OutputStream;
import java.io.StringWriter;
import java.io.Writer;

/**
@@ -425,10 +424,8 @@ public class PDFCMap extends PDFStream {
/** {@inheritDoc} */
protected int output(OutputStream stream) throws IOException {
StringWriter writer = new StringWriter();
CMapBuilder builder = createCMapBuilder(writer);
CMapBuilder builder = createCMapBuilder(getBufferWriter());
builder.writeCMap();
add(writer.getBuffer().toString()); //TODO Could be optimized by not buffering
return super.output(stream);
}
}

+ 8
- 0
src/java/org/apache/fop/pdf/PDFEncoding.java View File

@@ -131,6 +131,14 @@ public class PDFEncoding extends PDFDictionary {
return this;
}
/**
* Indicates whether any differences have been recorded.
* @return true if there are differences.
*/
public boolean hasDifferences() {
return (this.differences.length() > 0);
}
/**
* Creates and returns the PDFArray representing the Differences entry.
* @return the Differences entry

+ 92
- 41
src/java/org/apache/fop/pdf/PDFFactory.java View File

@@ -43,6 +43,7 @@ import org.apache.commons.logging.LogFactory;
import org.apache.xmlgraphics.xmp.Metadata;

import org.apache.fop.fonts.CIDFont;
import org.apache.fop.fonts.CIDSubset;
import org.apache.fop.fonts.CodePointMapping;
import org.apache.fop.fonts.CustomFont;
import org.apache.fop.fonts.FontDescriptor;
@@ -50,6 +51,8 @@ import org.apache.fop.fonts.FontMetrics;
import org.apache.fop.fonts.FontType;
import org.apache.fop.fonts.LazyFont;
import org.apache.fop.fonts.MultiByteFont;
import org.apache.fop.fonts.SimpleSingleByteEncoding;
import org.apache.fop.fonts.SingleByteEncoding;
import org.apache.fop.fonts.SingleByteFont;
import org.apache.fop.fonts.Typeface;
import org.apache.fop.fonts.truetype.FontFileReader;
@@ -1166,7 +1169,7 @@ public class PDFFactory {
}

/**
* make a Type1 /Font object
* Make a Type1 /Font object.
*
* @param fontname internal name to use for this font (eg "F1")
* @param basefont name of the base font (eg "Helvetica")
@@ -1217,10 +1220,12 @@ public class PDFFactory {
(PDFCIDFontDescriptor)pdfdesc);
getDocument().registerObject(cidFont);

PDFCMap cmap = new PDFToUnicodeCMap(cidMetrics.getCharsUsed(), "fop-ucs-H",
new PDFCIDSystemInfo("Adobe",
"Identity",
0));
PDFCMap cmap = new PDFToUnicodeCMap(
cidMetrics.getCIDSubset().getSubsetChars(),
"fop-ucs-H",
new PDFCIDSystemInfo("Adobe",
"Identity",
0));
getDocument().registerObject(cmap);
((PDFFontType0)font).setCMAP(cmap);
((PDFFontType0)font).setDescendantFonts(cidFont);
@@ -1238,35 +1243,20 @@ public class PDFFactory {
int lastChar = singleByteFont.getLastChar();
nonBase14.setWidthMetrics(firstChar,
lastChar,
makeArray(metrics.getWidths()));
new PDFArray(null, metrics.getWidths()));
//Handle encoding
CodePointMapping mapping = singleByteFont.getCodePointMapping();
SingleByteEncoding mapping = singleByteFont.getEncoding();
if (PDFEncoding.isPredefinedEncoding(mapping.getName())) {
font.setEncoding(mapping.getName());
} else {
CodePointMapping winansi = CodePointMapping.getMapping(
CodePointMapping.WIN_ANSI_ENCODING);
PDFEncoding pdfEncoding = new PDFEncoding(winansi.getName());
PDFEncoding.DifferencesBuilder builder
= pdfEncoding.createDifferencesBuilder();
int start = -1;
String[] winansiNames = winansi.getCharNameMap();
String[] charNameMap = mapping.getCharNameMap();
for (int i = 0; i < 256; i++) {
String wac = winansiNames[i];
String c = charNameMap[i];
if (!wac.equals(c)) {
if (start != i) {
builder.addDifference(i);
start = i;
}
builder.addName(c);
start++;
}
Object pdfEncoding = createPDFEncoding(mapping,
singleByteFont.getFontName());
if (pdfEncoding instanceof PDFEncoding) {
font.setEncoding((PDFEncoding)pdfEncoding);
} else {
font.setEncoding((String)pdfEncoding);
}
pdfEncoding.setDifferences(builder.toPDFArray());
font.setEncoding(pdfEncoding);
/* JM: What I thought would be a necessity with custom encodings turned out to
* be a bug in Adobe Acrobat 8. The following section just demonstrates how
@@ -1278,21 +1268,88 @@ public class PDFFactory {
nonBase14.setToUnicode(cmap);
*/
}
//Handle additional encodings (characters outside the primary encoding)
if (singleByteFont.hasAdditionalEncodings()) {
for (int i = 0, c = singleByteFont.getAdditionalEncodingCount(); i < c; i++) {
SimpleSingleByteEncoding addEncoding
= singleByteFont.getAdditionalEncoding(i);
String name = fontname + "_" + (i + 1);
Object pdfenc = createPDFEncoding(addEncoding,
singleByteFont.getFontName());
PDFFontNonBase14 addFont = (PDFFontNonBase14)PDFFont.createFont(
name, fonttype,
basefont, pdfenc);
addFont.setDescriptor(pdfdesc);
addFont.setWidthMetrics(
addEncoding.getFirstChar(),
addEncoding.getLastChar(),
new PDFArray(null, singleByteFont.getAdditionalWidths(i)));
getDocument().registerObject(addFont);
getDocument().getResources().addFont(addFont);
}
}
}

return font;
}
}

/**
* Creates a PDFEncoding instance from a CodePointMapping instance.
* @param encoding the code point mapping (encoding)
* @return the PDF Encoding dictionary (or a String with the predefined encoding)
*/
public Object createPDFEncoding(SingleByteEncoding encoding, String fontNameHint) {
SingleByteEncoding baseEncoding;
if (fontNameHint.indexOf("Symbol") >= 0) {
baseEncoding = CodePointMapping.getMapping(
CodePointMapping.SYMBOL_ENCODING);
} else {
baseEncoding = CodePointMapping.getMapping(
CodePointMapping.STANDARD_ENCODING);
}
PDFEncoding pdfEncoding = new PDFEncoding(baseEncoding.getName());
PDFEncoding.DifferencesBuilder builder
= pdfEncoding.createDifferencesBuilder();
int start = -1;
String[] baseNames = baseEncoding.getCharNameMap();
String[] charNameMap = encoding.getCharNameMap();
for (int i = 0, ci = charNameMap.length; i < ci; i++) {
String basec = baseNames[i];
String c = charNameMap[i];
if (!basec.equals(c)) {
if (start != i) {
builder.addDifference(i);
start = i;
}
builder.addName(c);
start++;
}
}
if (builder.hasDifferences()) {
pdfEncoding.setDifferences(builder.toPDFArray());
return pdfEncoding;
} else {
return baseEncoding.getName();
}
}

/**
* Creates and returns a width array with the widths of all the characters in the subset.
* @param cidFont the font
* @return the width array
*/
public PDFWArray getSubsetWidths(CIDFont cidFont) {
// Create widths for reencoded chars
PDFWArray warray = new PDFWArray();
int[] tmpWidth = new int[cidFont.usedGlyphsCount];
int[] widths = cidFont.getWidths();
CIDSubset subset = cidFont.getCIDSubset();
int[] tmpWidth = new int[subset.getSubsetSize()];

for (int i = 0; i < cidFont.usedGlyphsCount; i++) {
Integer nw = (Integer)cidFont.usedGlyphsIndex.get(new Integer(i));
int nwx = (nw == null) ? 0 : nw.intValue();
tmpWidth[i] = cidFont.width[nwx];
for (int i = 0, c = subset.getSubsetSize(); i < c; i++) {
int nwx = Math.max(0, subset.getGlyphIndexForSubsetIndex(i));
tmpWidth[i] = widths[nwx];
}
warray.addEntry(0, tmpWidth);
return warray;
@@ -1345,12 +1402,7 @@ public class PDFFactory {
}

private void buildCIDSet(PDFFontDescriptor descriptor, CIDFont cidFont) {
BitSet cidSubset = new BitSet();
Iterator iter = cidFont.usedGlyphs.keySet().iterator();
while (iter.hasNext()) {
Integer cid = (Integer)iter.next();
cidSubset.set(cid.intValue());
}
BitSet cidSubset = cidFont.getCIDSubset().getGlyphIndexBitSet();
PDFStream cidSet = makeStream(null, true);
ByteArrayOutputStream baout = new ByteArrayOutputStream(cidSubset.length() / 8 + 1);
int value = 0;
@@ -1548,14 +1600,13 @@ public class PDFFactory {
}

/**
* make an Array object (ex. Widths array for a font)
* Make an Array object (ex. Widths array for a font).
*
* @param values the int array values
* @return the PDF Array with the int values
*/
public PDFArray makeArray(int[] values) {
PDFArray array = new PDFArray(null, values);

getDocument().registerObject(array);
return array;
}

+ 1
- 1
src/java/org/apache/fop/pdf/PDFResources.java View File

@@ -110,7 +110,7 @@ public class PDFResources extends PDFObject {
desc = (FontDescriptor)font;
}
addFont(doc.getFactory().makeFont(
f, font.getEmbedFontName(), font.getEncoding(), font, desc));
f, font.getEmbedFontName(), font.getEncodingName(), font, desc));
}
}
}

+ 29
- 9
src/java/org/apache/fop/pdf/PDFStream.java View File

@@ -19,8 +19,9 @@
package org.apache.fop.pdf;

import java.io.OutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Writer;

/**
* Class representing a PDF stream.
@@ -37,6 +38,8 @@ public class PDFStream extends AbstractPDFStream {
*/
protected StreamCache data;

private transient Writer streamWriter;
/**
* Create an empty stream object
*/
@@ -44,6 +47,10 @@ public class PDFStream extends AbstractPDFStream {
super();
try {
data = StreamCacheFactory.getInstance().createStreamCache();
this.streamWriter = new java.io.OutputStreamWriter(
getBufferOutputStream(), PDFDocument.ENCODING);
//Buffer to minimize calls to the converter
this.streamWriter = new java.io.BufferedWriter(this.streamWriter);
} catch (IOException ex) {
//TODO throw the exception and catch it elsewhere
ex.printStackTrace();
@@ -57,14 +64,25 @@ public class PDFStream extends AbstractPDFStream {
*/
public void add(String s) {
try {
data.getOutputStream().write(PDFDocument.encode(s));
this.streamWriter.write(s);
} catch (IOException ex) {
//TODO throw the exception and catch it elsewhere
ex.printStackTrace();
}

}
private void flush() throws IOException {
this.streamWriter.flush();
}
/**
* Returns a Writer that writes to the OutputStream of the buffer.
* @return the Writer
*/
public Writer getBufferWriter() {
return this.streamWriter;
}

/**
* Returns an OutputStream that can be used to write to the buffer which is used
* to build up the PDF stream.
@@ -72,6 +90,9 @@ public class PDFStream extends AbstractPDFStream {
* @throws IOException In case of an I/O problem
*/
public OutputStream getBufferOutputStream() throws IOException {
if (this.streamWriter != null) {
flush(); //Just to be sure
}
return this.data.getOutputStream();
}
@@ -91,6 +112,7 @@ public class PDFStream extends AbstractPDFStream {
*/
public int getDataLength() {
try {
flush();
return data.getSize();
} catch (Exception e) {
//TODO throw the exception and catch it elsewhere
@@ -99,17 +121,15 @@ public class PDFStream extends AbstractPDFStream {
}
}

/**
* {@inheritDoc}
*/
/** {@inheritDoc} */
protected int getSizeHint() throws IOException {
flush();
return data.getSize();
}

/**
* {@inheritDoc}
*/
/** {@inheritDoc} */
protected void outputRawStreamData(OutputStream out) throws IOException {
flush();
data.outputContents(out);
}


+ 1
- 1
src/java/org/apache/fop/pdf/PDFTTFStream.java View File

@@ -67,7 +67,7 @@ public class PDFTTFStream extends PDFStream {
*/
public void setData(byte[] data, int size) throws IOException {
this.data.clear();
this.data.getOutputStream().write(data, 0, size);
getBufferOutputStream().write(data, 0, size);
}

}

+ 295
- 0
src/java/org/apache/fop/pdf/PDFTextUtil.java View File

@@ -0,0 +1,295 @@
/*
* 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.pdf;

import java.awt.geom.AffineTransform;

/**
* Utility class for generating PDF text objects. It needs to be subclassed to add writing
* functionality (see {@link #write(String)}).
*/
public abstract class PDFTextUtil {

/** The number of decimal places. */
private static final int DEC = 8;
/** PDF text rendering mode: Fill text */
public static final int TR_FILL = 0;
/** PDF text rendering mode: Stroke text */
public static final int TR_STROKE = 1;
/** PDF text rendering mode: Fill, then stroke text */
public static final int TR_FILL_STROKE = 2;
/** PDF text rendering mode: Neither fill nor stroke text (invisible) */
public static final int TR_INVISIBLE = 3;
/** PDF text rendering mode: Fill text and add to path for clipping */
public static final int TR_FILL_CLIP = 4;
/** PDF text rendering mode: Stroke text and add to path for clipping */
public static final int TR_STROKE_CLIP = 5;
/** PDF text rendering mode: Fill, then stroke text and add to path for clipping */
public static final int TR_FILL_STROKE_CLIP = 6;
/** PDF text rendering mode: Add text to path for clipping */
public static final int TR_CLIP = 7;
private boolean inTextObject = false;
private String startText;
private String endText;
private boolean useMultiByte;
private StringBuffer bufTJ;
private int textRenderingMode = TR_FILL;
private String currentFontName;
private double currentFontSize;
/**
* Main constructor.
*/
public PDFTextUtil() {
//nop
}
/**
* Writes PDF code.
* @param code the PDF code to write
*/
protected abstract void write(String code);
private void writeAffineTransform(AffineTransform at, StringBuffer sb) {
double[] lt = new double[6];
at.getMatrix(lt);
sb.append(PDFNumber.doubleOut(lt[0], DEC)).append(" ");
sb.append(PDFNumber.doubleOut(lt[1], DEC)).append(" ");
sb.append(PDFNumber.doubleOut(lt[2], DEC)).append(" ");
sb.append(PDFNumber.doubleOut(lt[3], DEC)).append(" ");
sb.append(PDFNumber.doubleOut(lt[4], DEC)).append(" ");
sb.append(PDFNumber.doubleOut(lt[5], DEC));
}

private void writeChar(char ch, StringBuffer sb) {
if (!useMultiByte) {
if (ch < 32 || ch > 127) {
sb.append("\\").append(Integer.toOctalString((int)ch));
} else {
switch (ch) {
case '(':
case ')':
case '\\':
sb.append("\\");
break;
default:
}
sb.append(ch);
}
} else {
sb.append(PDFText.toUnicodeHex(ch));
}
}
private void checkInTextObject() {
if (!inTextObject) {
throw new IllegalStateException("Not in text object");
}
}
/**
* Indicates whether we are in a text object or not.
* @return true if we are in a text object
*/
public boolean isInTextObject() {
return inTextObject;
}
/**
* Called when a new text object should be started. Be sure to call setFont() before
* issuing any text painting commands.
*/
public void beginTextObject() {
if (inTextObject) {
throw new IllegalStateException("Already in text object");
}
write("BT\n");
this.inTextObject = true;
}
/**
* Called when a text object should be ended.
*/
public void endTextObject() {
checkInTextObject();
write("ET\n");
this.inTextObject = false;
initValues();
}
/**
* Resets the state fields.
*/
protected void initValues() {
this.currentFontName = null;
this.currentFontSize = 0.0;
this.textRenderingMode = TR_FILL;
}
/**
* Creates a "q" command, pushing a copy of the entire graphics state onto the stack.
*/
public void saveGraphicsState() {
write("q\n");
}
/**
* Creates a "Q" command, restoring the entire graphics state to its former value by popping
* it from the stack.
*/
public void restoreGraphicsState() {
write("Q\n");
}
/**
* Creates a "cm" command.
* @param at the transformation matrix
*/
public void concatMatrix(AffineTransform at) {
if (!at.isIdentity()) {
writeTJ();
StringBuffer sb = new StringBuffer();
writeAffineTransform(at, sb);
sb.append(" cm\n");
write(sb.toString());
}
}
/**
* Writes a "Tf" command, setting a new current font.
* @param fontName the name of the font to select
* @param fontSize the font size (in points)
*/
public void writeTf(String fontName, double fontSize) {
checkInTextObject();
write("/" + fontName + " " + PDFNumber.doubleOut(fontSize) + " Tf\n");
this.startText = useMultiByte ? "<" : "(";
this.endText = useMultiByte ? ">" : ")";
}

/**
* Updates the current font. This method only writes a "Tf" if the current font changes.
* @param fontName the name of the font to select
* @param fontSize the font size (in points)
* @param multiByte true indicates the font is a multi-byte font, false means single-byte
*/
public void updateTf(String fontName, double fontSize, boolean multiByte) {
checkInTextObject();
if (!fontName.equals(this.currentFontName) || (fontSize != this.currentFontSize)) {
writeTJ();
this.currentFontName = fontName;
this.currentFontSize = fontSize;
this.useMultiByte = multiByte;
writeTf(fontName, fontSize);
}
}

/**
* Sets the text rendering mode.
* @param mode the rendering mode (value 0 to 7, see PDF Spec, constants: TR_*)
*/
public void setTextRenderingMode(int mode) {
if (mode < 0 || mode > 7) {
throw new IllegalArgumentException(
"Illegal value for text rendering mode. Expected: 0-7");
}
if (mode != this.textRenderingMode) {
writeTJ();
this.textRenderingMode = mode;
write(this.textRenderingMode + " Tr\n");
}
}
/**
* Sets the text rendering mode.
* @param fill true if the text should be filled
* @param stroke true if the text should be stroked
* @param addToClip true if the path should be added for clipping
*/
public void setTextRenderingMode(boolean fill, boolean stroke, boolean addToClip) {
int mode;
if (fill) {
mode = (stroke ? 2 : 0);
} else {
mode = (stroke ? 1 : 3);
}
if (addToClip) {
mode += 4;
}
setTextRenderingMode(mode);
}
/**
* Writes a "Tm" command, setting a new text transformation matrix.
* @param localTransform the new text transformation matrix
*/
public void writeTextMatrix(AffineTransform localTransform) {
StringBuffer sb = new StringBuffer();
writeAffineTransform(localTransform, sb);
sb.append(" Tm ");
write(sb.toString());
}

/**
* Writes a char to the "TJ-Buffer".
* @param codepoint the mapped character (code point/character code)
*/
public void writeTJMappedChar(char codepoint) {
if (bufTJ == null) {
bufTJ = new StringBuffer();
}
if (bufTJ.length() == 0) {
bufTJ.append("[").append(startText);
}
writeChar(codepoint, bufTJ);
}

/**
* Writes a glyph adjust value to the "TJ-Buffer".
* @param adjust the glyph adjust value in thousands of text unit space.
*/
public void adjustGlyphTJ(double adjust) {
bufTJ.append(endText).append(" ");
bufTJ.append(PDFNumber.doubleOut(adjust, DEC - 4));
bufTJ.append(" ");
bufTJ.append(startText);
}

/**
* Writes a "TJ" command, writing out the accumulated buffer with the characters and glyph
* positioning values. The buffer is reset afterwards.
*/
public void writeTJ() {
if (isInString()) {
bufTJ.append(endText).append("] TJ\n");
write(bufTJ.toString());
bufTJ.setLength(0);
}
}

private boolean isInString() {
return bufTJ != null && bufTJ.length() > 0;
}

}

+ 2
- 5
src/java/org/apache/fop/render/afp/fonts/OutlineFont.java View File

@@ -174,11 +174,8 @@ public class OutlineFont extends AFPFont {
return charSet.mapChar(c);
}

/**
* Get the encoding of the font.
* @return the encoding
*/
public String getEncoding() {
/** {@inheritDoc} */
public String getEncodingName() {
return charSet.getEncoding();
}
}

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

@@ -25,6 +25,7 @@ import java.util.Map;

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

import org.apache.fop.fo.properties.FixedLength;
import org.apache.fop.render.afp.exceptions.FontRuntimeException;

@@ -229,11 +230,8 @@ public class RasterFont extends AFPFont {
return charSet.mapChar(c);
}

/**
* Get the encoding of the font.
* @return the encoding
*/
public String getEncoding() {
/** {@inheritDoc} */
public String getEncodingName() {
return charSet.getEncoding();
}


+ 1
- 1
src/java/org/apache/fop/render/java2d/CustomFontMetricsMapper.java View File

@@ -112,7 +112,7 @@ public class CustomFontMetricsMapper extends Typeface implements FontMetricsMapp
}

/** {@inheritDoc} */
public final String getEncoding() {
public final String getEncodingName() {
return null; //Not applicable to Java2D rendering
}


+ 1
- 1
src/java/org/apache/fop/render/java2d/SystemFontMetricsMapper.java View File

@@ -172,7 +172,7 @@ public class SystemFontMetricsMapper extends Typeface implements FontMetricsMapp
}

/** {@inheritDoc} */
public String getEncoding() {
public String getEncodingName() {
return null; //Not applicable to Java2D rendering
}


+ 9
- 1
src/java/org/apache/fop/render/pdf/AbstractImageAdapter.java View File

@@ -81,7 +81,7 @@ public abstract class AbstractImageAdapter implements PDFImage {
/** {@inheritDoc} */
public void setup(PDFDocument doc) {

ICC_Profile prof = image.getICCProfile();
ICC_Profile prof = getEffectiveICCProfile();
PDFDeviceColorSpace pdfCS = toPDFColorSpace(getImageColorSpace());
if (prof != null) {
pdfICCStream = setupColorProfile(doc, prof, pdfCS);
@@ -100,6 +100,14 @@ public abstract class AbstractImageAdapter implements PDFImage {
}
}

/**
* Returns the effective ICC profile for the image.
* @return an ICC profile or null
*/
protected ICC_Profile getEffectiveICCProfile() {
return image.getICCProfile();
}
private static PDFICCStream setupColorProfile(PDFDocument doc,
ICC_Profile prof, PDFDeviceColorSpace pdfCS) {
boolean defaultsRGB = ColorProfileUtil.isDefaultsRGB(prof);

+ 100
- 1
src/java/org/apache/fop/render/pdf/ImageRawJPEGAdapter.java View File

@@ -18,19 +18,33 @@
/* $Id$ */

package org.apache.fop.render.pdf;
import java.awt.color.ICC_Profile;
import java.io.DataInput;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import org.apache.commons.io.IOUtils;

import org.apache.xmlgraphics.image.loader.impl.ImageRawJPEG;
import org.apache.xmlgraphics.image.loader.impl.JPEGConstants;
import org.apache.xmlgraphics.image.loader.impl.JPEGFile;
import org.apache.xmlgraphics.image.loader.util.ImageUtil;

import org.apache.fop.pdf.DCTFilter;
import org.apache.fop.pdf.PDFDeviceColorSpace;
import org.apache.fop.pdf.PDFDocument;
import org.apache.fop.pdf.PDFFilter;
import org.apache.fop.pdf.PDFFilterList;
import org.apache.fop.util.ColorProfileUtil;

/**
* PDFImage implementation for the PDF renderer which handles raw JPEG images.
* <p>
* The JPEG is copied to the XObject's stream as-is but some elements (marker segments) are
* filtered. For example, an embedded color profile is filtered since it is already added as
* a PDF object and associated with the XObject. This way, the PDF file size is kept as small
* as possible.
*/
public class ImageRawJPEGAdapter extends AbstractImageAdapter {

@@ -67,6 +81,21 @@ public class ImageRawJPEGAdapter extends AbstractImageAdapter {
return toPDFColorSpace(getImageColorSpace());
}

/** {@inheritDoc} */
protected ICC_Profile getEffectiveICCProfile() {
ICC_Profile profile = super.getEffectiveICCProfile();
if (profile != null
&& profile.getNumComponents() == 3
&& !ColorProfileUtil.isDefaultsRGB(profile)) {
//RGB profiles which are not sRGB don't seem to work.
//Without this override, the image drifts into yellow for an unknown reason.
//TODO Find out why this happens.
//Test using a JPEG images with, for example, "Adobe RGB 1998" color profile.
profile = null;
}
return profile;
}
/** {@inheritDoc} */
public int getBitsPerComponent() {
return 8;
@@ -84,7 +113,77 @@ public class ImageRawJPEGAdapter extends AbstractImageAdapter {
/** {@inheritDoc} */
public void outputContents(OutputStream out) throws IOException {
getImage().writeTo(out);
InputStream in = getImage().createInputStream();
in = ImageUtil.decorateMarkSupported(in);
try {
JPEGFile jpeg = new JPEGFile(in);
DataInput din = jpeg.getDataInput();
//Copy the whole JPEG file except:
// - the ICC profile
//TODO Thumbnails could safely be skipped, too.
//TODO Metadata (XMP, IPTC, EXIF) could safely be skipped, too.
while (true) {
int reclen;
int segID = jpeg.readMarkerSegment();
switch (segID) {
case JPEGConstants.SOI:
out.write(0xFF);
out.write(segID);
break;
case JPEGConstants.EOI:
case JPEGConstants.SOS:
out.write(0xFF);
out.write(segID);
IOUtils.copy(in, out); //Just copy the rest!
return;
/*
case JPEGConstants.APP1: //Metadata
case JPEGConstants.APPD:
jpeg.skipCurrentMarkerSegment();
break;*/
case JPEGConstants.APP2: //ICC (see ICC1V42.pdf)
boolean skipICCProfile = false;
in.mark(16);
try {
reclen = jpeg.readSegmentLength();
// Check for ICC profile
byte[] iccString = new byte[11];
din.readFully(iccString);
din.skipBytes(1); //string terminator (null byte)

if ("ICC_PROFILE".equals(new String(iccString, "US-ASCII"))) {
skipICCProfile = (this.image.getICCProfile() != null);
}
} finally {
in.reset();
}
if (skipICCProfile) {
//ICC profile is skipped as it is already embedded as a PDF object
jpeg.skipCurrentMarkerSegment();
break;
}
default:
out.write(0xFF);
out.write(segID);
reclen = jpeg.readSegmentLength();
//write short
out.write((reclen >>> 8) & 0xFF);
out.write((reclen >>> 0) & 0xFF);
int left = reclen - 2;
byte[] buf = new byte[2048];
while (left > 0) {
int part = Math.min(buf.length, left);
din.readFully(buf, 0, part);
out.write(buf, 0, part);
left -= part;
}
}
}
} finally {
IOUtils.closeQuietly(in);
}
}

/** {@inheritDoc} */

+ 2
- 2
src/java/org/apache/fop/render/pdf/PDFGraphics2DAdapter.java View File

@@ -89,8 +89,8 @@ public class PDFGraphics2DAdapter extends AbstractGraphics2DAdapter {
PDFGraphics2D graphics = new PDFGraphics2D(textAsShapes,
pdfInfo.fi, pdfInfo.pdfDoc,
pdfInfo.pdfContext, pdfInfo.pdfPage.referencePDF(),
renderer.currentFontName,
renderer.currentFontSize);
pdfInfo.currentFontName,
pdfInfo.currentFontSize);
graphics.setGraphicContext(new org.apache.xmlgraphics.java2d.GraphicContext());
AffineTransform transform = new AffineTransform();

+ 92
- 164
src/java/org/apache/fop/render/pdf/PDFRenderer.java View File

@@ -63,7 +63,6 @@ import org.apache.fop.area.OffDocumentExtensionAttachment;
import org.apache.fop.area.OffDocumentItem;
import org.apache.fop.area.PageSequence;
import org.apache.fop.area.PageViewport;
import org.apache.fop.area.RegionViewport;
import org.apache.fop.area.Trait;
import org.apache.fop.area.inline.AbstractTextArea;
import org.apache.fop.area.inline.Image;
@@ -79,6 +78,8 @@ import org.apache.fop.fo.Constants;
import org.apache.fop.fo.extensions.ExtensionAttachment;
import org.apache.fop.fo.extensions.xmp.XMPMetadata;
import org.apache.fop.fonts.Font;
import org.apache.fop.fonts.LazyFont;
import org.apache.fop.fonts.SingleByteFont;
import org.apache.fop.fonts.Typeface;
import org.apache.fop.pdf.PDFAMode;
import org.apache.fop.pdf.PDFAction;
@@ -107,7 +108,7 @@ import org.apache.fop.pdf.PDFResourceContext;
import org.apache.fop.pdf.PDFResources;
import org.apache.fop.pdf.PDFState;
import org.apache.fop.pdf.PDFStream;
import org.apache.fop.pdf.PDFText;
import org.apache.fop.pdf.PDFTextUtil;
import org.apache.fop.pdf.PDFXMode;
import org.apache.fop.pdf.PDFXObject;
import org.apache.fop.render.AbstractPathOrientedRenderer;
@@ -251,21 +252,14 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
/** drawing state */
protected PDFState currentState = null;

/** Name of currently selected font */
protected String currentFontName = "";
/** Size of currently selected font */
protected int currentFontSize = 0;
/** Text generation utility holding the current font status */
protected PDFTextUtil textutil;
/** page height */
protected int pageHeight;

/** Registry of PDF filters */
protected Map filterMap;

/**
* true if a BT command has been written.
*/
protected boolean inTextMode = false;

/** Image handler registry */
private PDFImageHandlerRegistry imageHandlerRegistry = new PDFImageHandlerRegistry();
@@ -525,7 +519,7 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
currentContext = null;
currentPage = null;
currentState = null;
currentFontName = "";
this.textutil = null;

idPositions.clear();
idGoTos.clear();
@@ -664,19 +658,15 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {

/** Indicates the beginning of a text object. */
protected void beginTextObject() {
if (!inTextMode) {
currentStream.add("BT\n");
currentFontName = "";
inTextMode = true;
if (!textutil.isInTextObject()) {
textutil.beginTextObject();
}
}

/** Indicates the end of a text object. */
protected void endTextObject() {
closeText();
if (inTextMode) {
currentStream.add("ET\n");
inTextMode = false;
if (textutil.isInTextObject()) {
textutil.endTextObject();
}
}

@@ -786,6 +776,11 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {

currentStream = this.pdfDoc.getFactory()
.makeStream(PDFFilterList.CONTENT_FILTER, false);
this.textutil = new PDFTextUtil() {
protected void write(String code) {
currentStream.add(code);
}
};

currentState = new PDFState();
// Transform the PDF's default coordinate system (0,0 at lower left) to the PDFRenderer's
@@ -794,9 +789,6 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
currentState.concatenate(basicPageTransform);
currentStream.add(CTMHelper.toPDFString(basicPageTransform, false) + " cm\n");
currentFontName = "";

super.renderPage(page);

this.pdfDoc.registerObject(currentStream);
@@ -807,6 +799,7 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
}
this.pdfDoc.addObject(currentPage);
this.pdfDoc.output(ostream);
this.textutil = null;
}

/** {@inheritDoc} */
@@ -839,17 +832,6 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
}
}
/**
* Handle the traits for a region
* This is used to draw the traits for the given page region.
* (See Sect. 6.4.1.2 of XSL-FO spec.)
* @param region the RegionViewport whose region is to be drawn
*/
protected void handleRegionTraits(RegionViewport region) {
currentFontName = "";
super.handleRegionTraits(region);
}

/**
* Formats a float value (normally coordinates) as Strings.
* @param value the value
@@ -865,7 +847,8 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
float w = x2 - x1;
float h = y2 - y1;
if ((w < 0) || (h < 0)) {
log.error("Negative extent received (w=" + w + ", h=" + h + "). Border won't be painted.");
log.error("Negative extent received (w=" + w + ", h=" + h
+ "). Border won't be painted.");
return;
}
switch (style) {
@@ -1328,12 +1311,9 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
super.renderBlock(block);
}

/**
* {@inheritDoc}
*/
/** {@inheritDoc} */
protected void renderLineArea(LineArea line) {
super.renderLineArea(line);
closeText();
}

/**
@@ -1415,11 +1395,20 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
}
}

/**
* {@inheritDoc}
*/
private Typeface getTypeface(String fontName) {
Typeface tf = (Typeface) fontInfo.getFonts().get(fontName);
if (tf instanceof LazyFont) {
tf = ((LazyFont)tf).getRealFont();
}
return tf;
}
/** {@inheritDoc} */
public void renderText(TextArea text) {
renderInlineAreaBackAndBorders(text);
Color ct = (Color) text.getTrait(Trait.COLOR);
updateColor(ct, true);
beginTextObject();
StringBuffer pdf = new StringBuffer();

@@ -1427,12 +1416,10 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
int size = ((Integer) text.getTrait(Trait.FONT_SIZE)).intValue();
// This assumes that *all* CIDFonts use a /ToUnicode mapping
Typeface tf = (Typeface) fontInfo.getFonts().get(fontName);
boolean useMultiByte = tf.isMultiByte();
Typeface tf = getTypeface(fontName);
textutil.updateTf(fontName, size / 1000f, tf.isMultiByte());
updateFont(fontName, size, pdf);
Color ct = (Color) text.getTrait(Trait.COLOR);
updateColor(ct, true, pdf);

// word.getOffset() = only height of text itself
// currentBlockIPPosition: 0 for beginning of line; nonzero
@@ -1440,66 +1427,46 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
int rx = currentIPPosition + text.getBorderAndPaddingWidthStart();
int bl = currentBPPosition + text.getOffset() + text.getBaselineOffset();

pdf.append("1 0 0 -1 " + format(rx / 1000f) + " " + format(bl / 1000f) + " Tm "
/*+ format(text.getTextLetterSpaceAdjust() / 1000f) + " Tc\n"*/
/*+ format(text.getTextWordSpaceAdjust() / 1000f) + " Tw ["*/);
textutil.writeTextMatrix(new AffineTransform(1, 0, 0, -1, rx / 1000f, bl / 1000f));

pdf.append("[");
currentStream.add(pdf.toString());

super.renderText(text);

currentStream.add("] TJ\n");
textutil.writeTJ();
renderTextDecoration(tf, size, text, bl, rx);
}
/**
* {@inheritDoc}
*/

/** {@inheritDoc} */
public void renderWord(WordArea word) {
Font font = getFontFromArea(word.getParentArea());
Typeface tf = (Typeface) fontInfo.getFonts().get(font.getFontName());
boolean useMultiByte = tf.isMultiByte();

StringBuffer pdf = new StringBuffer();
String s = word.getWord();
escapeText(s, word.getLetterAdjustArray(),
font, (AbstractTextArea)word.getParentArea(), useMultiByte, pdf);

currentStream.add(pdf.toString());
escapeText(s, word.getLetterAdjustArray(),
font, (AbstractTextArea)word.getParentArea());

super.renderWord(word);
}

/**
* {@inheritDoc}
*/
/** {@inheritDoc} */
public void renderSpace(SpaceArea space) {
Font font = getFontFromArea(space.getParentArea());
Typeface tf = (Typeface) fontInfo.getFonts().get(font.getFontName());
boolean useMultiByte = tf.isMultiByte();

String s = space.getSpace();
StringBuffer pdf = new StringBuffer();

AbstractTextArea textArea = (AbstractTextArea)space.getParentArea();
escapeText(s, null, font, textArea, useMultiByte, pdf);
escapeText(s, null, font, textArea);

if (space.isAdjustable()) {
int tws = -((TextArea) space.getParentArea()).getTextWordSpaceAdjust()
- 2 * textArea.getTextLetterSpaceAdjust();

if (tws != 0) {
pdf.append(format(tws / (font.getFontSize() / 1000f)));
pdf.append(" ");
float adjust = tws / (font.getFontSize() / 1000f);
textutil.adjustGlyphTJ(adjust);
}
}

currentStream.add(pdf.toString());

super.renderSpace(space);
}

@@ -1507,101 +1474,77 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
* Escapes text according to PDF rules.
* @param s Text to escape
* @param letterAdjust an array of widths for letter adjustment (may be null)
* @param fs Font state
* @param font to font in use
* @param parentArea the parent text area to retrieve certain traits from
* @param useMultiByte Indicates the use of multi byte convention
* @param pdf target buffer for the escaped text
*/
public void escapeText(String s, int[] letterAdjust,
Font fs, AbstractTextArea parentArea,
boolean useMultiByte, StringBuffer pdf) {
String startText = useMultiByte ? "<" : "(";
String endText = useMultiByte ? "> " : ") ";

/*
boolean kerningAvailable = false;
Map kerning = fs.getKerning();
if (kerning != null && !kerning.isEmpty()) {
//kerningAvailable = true;
//TODO Reenable me when the layout engine supports kerning, too
log.warn("Kerning support is disabled until it is supported by the layout engine!");
}
*/
protected void escapeText(String s,
int[] letterAdjust,
Font font, AbstractTextArea parentArea) {
escapeText(s, 0, s.length(), letterAdjust, font, parentArea);
}
/**
* Escapes text according to PDF rules.
* @param s Text to escape
* @param start the start position in the text
* @param end the end position in the text
* @param letterAdjust an array of widths for letter adjustment (may be null)
* @param font to font in use
* @param parentArea the parent text area to retrieve certain traits from
*/
protected void escapeText(String s, int start, int end,
int[] letterAdjust,
Font font, AbstractTextArea parentArea) {
String fontName = font.getFontName();
float fontSize = font.getFontSize() / 1000f;
Typeface tf = getTypeface(fontName);
SingleByteFont singleByteFont = null;
if (tf instanceof SingleByteFont) {
singleByteFont = (SingleByteFont)tf;
}

int l = s.length();

float fontSize = fs.getFontSize() / 1000f;
boolean startPending = true;
for (int i = 0; i < l; i++) {
for (int i = start; i < end; i++) {
char orgChar = s.charAt(i);
char ch;
float glyphAdjust = 0;
if (fs.hasChar(orgChar)) {
ch = fs.mapChar(orgChar);
if (font.hasChar(orgChar)) {
ch = font.mapChar(orgChar);
if (singleByteFont != null && singleByteFont.hasAdditionalEncodings()) {
int encoding = ch / 256;
if (encoding == 0) {
textutil.updateTf(fontName, fontSize, tf.isMultiByte());
} else {
textutil.updateTf(fontName + "_" + Integer.toString(encoding),
fontSize, tf.isMultiByte());
ch = (char)(ch % 256);
}
}
int tls = (i < l - 1 ? parentArea.getTextLetterSpaceAdjust() : 0);
glyphAdjust -= tls;
} else {
if (CharUtilities.isFixedWidthSpace(orgChar)) {
//Fixed width space are rendered as spaces so copy/paste works in a reader
ch = fs.mapChar(CharUtilities.SPACE);
glyphAdjust = fs.getCharWidth(ch) - fs.getCharWidth(orgChar);
ch = font.mapChar(CharUtilities.SPACE);
glyphAdjust = font.getCharWidth(ch) - font.getCharWidth(orgChar);
} else {
ch = fs.mapChar(orgChar);
ch = font.mapChar(orgChar);
}
}
if (letterAdjust != null && i < l - 1) {
glyphAdjust -= letterAdjust[i + 1];
}

if (startPending) {
pdf.append(startText);
startPending = false;
}
if (!useMultiByte) {
if (ch < 32 || ch > 127) {
pdf.append("\\");
pdf.append(Integer.toOctalString((int) ch));
} else {
switch (ch) {
case '(':
case ')':
case '\\':
pdf.append("\\");
break;
default:
}
pdf.append(ch);
}
} else {
pdf.append(PDFText.toUnicodeHex(ch));
}
textutil.writeTJMappedChar(ch);

float adjust = glyphAdjust / fontSize;

if (adjust != 0) {
pdf.append(endText).append(format(adjust)).append(' ');
startPending = true;
textutil.adjustGlyphTJ(adjust);
}

}
if (!startPending) {
pdf.append(endText);
}
}

/**
* Checks to see if we have some text rendering commands open
* still and writes out the TJ command to the stream if we do
*/
protected void closeText() {
/*
if (textOpen) {
currentStream.add("] TJ\n");
textOpen = false;
prevWordX = 0;
prevWordY = 0;
currentFontName = "";
}*/
}

/**
@@ -1615,8 +1558,6 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
protected void setColor(Color col, boolean fill, StringBuffer pdf) {
PDFColor color = new PDFColor(this.pdfDoc, col);

closeText();
if (pdf != null) {
pdf.append(color.getColorSpaceOut(fill));
} else {
@@ -1648,22 +1589,10 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
}

/** {@inheritDoc} */
protected void updateColor(Color col, boolean fill) {
protected void updateColor(Color col, boolean fill) {
updateColor(col, fill, null);
}
private void updateFont(String name, int size, StringBuffer pdf) {
if ((!name.equals(this.currentFontName))
|| (size != this.currentFontSize)) {
closeText();

this.currentFontName = name;
this.currentFontSize = size;
pdf = pdf.append("/" + name + " " + format((float) size / 1000f)
+ " Tf\n");
}
}

/** {@inheritDoc} */
public void renderImage(Image image, Rectangle2D pos) {
endTextObject();
@@ -1798,9 +1727,8 @@ public class PDFRenderer extends AbstractPathOrientedRenderer {
context.setProperty(PDFRendererContextConstants.PDF_CONTEXT, currentContext);
context.setProperty(PDFRendererContextConstants.PDF_STREAM, currentStream);
context.setProperty(PDFRendererContextConstants.PDF_FONT_INFO, fontInfo);
context.setProperty(PDFRendererContextConstants.PDF_FONT_NAME, currentFontName);
context.setProperty(PDFRendererContextConstants.PDF_FONT_SIZE,
new Integer(currentFontSize));
context.setProperty(PDFRendererContextConstants.PDF_FONT_NAME, "");
context.setProperty(PDFRendererContextConstants.PDF_FONT_SIZE, new Integer(0));
return context;
}


+ 114
- 14
src/java/org/apache/fop/render/ps/PSFontUtils.java View File

@@ -32,16 +32,20 @@ import javax.xml.transform.stream.StreamSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.apache.xmlgraphics.fonts.Glyphs;
import org.apache.xmlgraphics.ps.DSCConstants;
import org.apache.xmlgraphics.ps.PSGenerator;
import org.apache.xmlgraphics.ps.PSResource;
import org.apache.xmlgraphics.ps.dsc.ResourceTracker;

import org.apache.fop.fonts.Base14Font;
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.SingleByteEncoding;
import org.apache.fop.fonts.SingleByteFont;
import org.apache.fop.fonts.Typeface;

/**
@@ -81,9 +85,21 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils {
while (iter.hasNext()) {
String key = (String)iter.next();
Typeface tf = getTypeFace(fontInfo, fonts, key);
PSResource fontRes = new PSResource("font", tf.getFontName());
PSResource fontRes = new PSResource(PSResource.TYPE_FONT, tf.getFontName());
fontResources.put(key, fontRes);
embedFont(gen, tf, fontRes);
if (tf instanceof SingleByteFont) {
SingleByteFont sbf = (SingleByteFont)tf;
for (int i = 0, c = sbf.getAdditionalEncodingCount(); i < c; i++) {
SingleByteEncoding encoding = sbf.getAdditionalEncoding(i);
defineEncoding(gen, encoding);
String postFix = "_" + (i + 1);
PSResource derivedFontRes = defineDerivedFont(gen, tf.getFontName(),
tf.getFontName() + postFix, encoding.getName());
fontResources.put(key + postFix, derivedFontRes);
}
}
}
gen.commentln("%FOPEndFontDict");
reencodeFonts(gen, fonts);
@@ -91,29 +107,35 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils {
}

private static void reencodeFonts(PSGenerator gen, Map fonts) throws IOException {
ResourceTracker tracker = gen.getResourceTracker();
if (!tracker.isResourceSupplied(WINANSI_ENCODING_RESOURCE)) {
defineWinAnsiEncoding(gen);
}
gen.commentln("%FOPBeginFontReencode");
defineWinAnsiEncoding(gen);
//Rewrite font encodings
Iterator iter = fonts.keySet().iterator();
while (iter.hasNext()) {
String key = (String)iter.next();
Typeface fm = (Typeface)fonts.get(key);
if (fm instanceof LazyFont && ((LazyFont)fm).getRealFont() == null) {
continue;
} else if (null == fm.getEncoding()) {
Typeface tf = (Typeface)fonts.get(key);
if (tf instanceof LazyFont) {
tf = ((LazyFont)tf).getRealFont();
if (tf == null) {
continue;
}
}
if (null == tf.getEncodingName()) {
//ignore (ZapfDingbats and Symbol used to run through here, kept for safety reasons)
} else if ("SymbolEncoding".equals(fm.getEncoding())) {
} else if ("SymbolEncoding".equals(tf.getEncodingName())) {
//ignore (no encoding redefinition)
} else if ("ZapfDingbatsEncoding".equals(fm.getEncoding())) {
} else if ("ZapfDingbatsEncoding".equals(tf.getEncodingName())) {
//ignore (no encoding redefinition)
} else if ("WinAnsiEncoding".equals(fm.getEncoding())) {
redefineFontEncoding(gen, fm.getFontName(), fm.getEncoding());
} else {
/* Don't complain anymore, just use the font's default encoding.
gen.commentln("%WARNING: Only WinAnsiEncoding is supported. Font '"
+ fm.getFontName() + "' asks for: " + fm.getEncoding());
*/
if (tf instanceof Base14Font) {
//Our Base 14 fonts don't use the default encoding
redefineFontEncoding(gen, tf.getFontName(), tf.getEncodingName());
}
}
}
gen.commentln("%FOPEndFontReencode");
@@ -233,10 +255,88 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils {
if (isEmbeddable(cf)) {
resTracker.registerSuppliedResource(fontRes);
}
if (tf instanceof SingleByteFont) {
SingleByteFont sbf = (SingleByteFont)tf;
for (int i = 0, c = sbf.getAdditionalEncodingCount(); i < c; i++) {
SingleByteEncoding encoding = sbf.getAdditionalEncoding(i);
PSResource encodingRes = new PSResource(
PSResource.TYPE_ENCODING, encoding.getName());
resTracker.registerSuppliedResource(encodingRes);
PSResource derivedFontRes = new PSResource(
PSResource.TYPE_FONT, tf.getFontName() + "_" + (i + 1));
resTracker.registerSuppliedResource(derivedFontRes);
}
}
}
}
}
return fontResources;
}

/**
* Defines the single-byte encoding for use in PostScript files.
* @param gen the PostScript generator
* @param encoding the single-byte encoding
* @return the PSResource instance that represents the encoding
* @throws IOException In case of an I/O problem
*/
public static PSResource defineEncoding(PSGenerator gen, SingleByteEncoding encoding)
throws IOException {
PSResource res = new PSResource(PSResource.TYPE_ENCODING, encoding.getName());
gen.writeDSCComment(DSCConstants.BEGIN_RESOURCE, res);
gen.writeln("/" + encoding.getName() + " [");
String[] charNames = encoding.getCharNameMap();
for (int i = 0; i < 256; i++) {
if (i > 0) {
if ((i % 5) == 0) {
gen.newLine();
} else {
gen.write(" ");
}
}
String glyphname = null;
if (i < charNames.length) {
glyphname = charNames[i];
}
if (glyphname == null || "".equals(glyphname)) {
glyphname = Glyphs.NOTDEF;
}
gen.write("/");
gen.write(glyphname);
}
gen.newLine();
gen.writeln("] def");
gen.writeDSCComment(DSCConstants.END_RESOURCE);
gen.getResourceTracker().registerSuppliedResource(res);
return res;
}

/**
* Derives a new font based on an existing font with a given encoding. The encoding must
* have been registered before.
* @param gen the PostScript generator
* @param baseFontName the font name of the font to derive from
* @param fontName the font name of the new font to be define
* @param encoding the new encoding (must be predefined in the PS file)
* @return the PSResource representing the derived font
* @throws IOException In case of an I/O problem
*/
public static PSResource defineDerivedFont(PSGenerator gen, String baseFontName, String fontName,
String encoding) throws IOException {
PSResource res = new PSResource(PSResource.TYPE_FONT, fontName);
gen.writeDSCComment(DSCConstants.BEGIN_RESOURCE, res);
gen.commentln("%XGCDependencies: font " + baseFontName);
gen.commentln("%XGC+ encoding " + encoding);
gen.writeln("/" + baseFontName + " findfont");
gen.writeln("dup length dict begin");
gen.writeln(" {1 index /FID ne {def} {pop pop} ifelse} forall");
gen.writeln(" /Encoding " + encoding + " def");
gen.writeln(" currentdict");
gen.writeln("end");
gen.writeln("/" + fontName + " exch definefont pop");
gen.writeDSCComment(DSCConstants.END_RESOURCE);
gen.getResourceTracker().registerSuppliedResource(res);
return res;
}
}

+ 72
- 19
src/java/org/apache/fop/render/ps/PSRenderer.java View File

@@ -90,6 +90,7 @@ import org.apache.fop.fo.Constants;
import org.apache.fop.fo.extensions.ExtensionAttachment;
import org.apache.fop.fonts.Font;
import org.apache.fop.fonts.LazyFont;
import org.apache.fop.fonts.SingleByteFont;
import org.apache.fop.fonts.Typeface;
import org.apache.fop.render.AbstractPathOrientedRenderer;
import org.apache.fop.render.Graphics2DAdapter;
@@ -669,6 +670,12 @@ public class PSRenderer extends AbstractPathOrientedRenderer
}
private String getPostScriptNameForFontKey(String key) {
int pos = key.indexOf('_');
String postFix = null;
if (pos > 0) {
postFix = key.substring(pos);
key = key.substring(0, pos);
}
Map fonts = fontInfo.getFonts();
Typeface tf = (Typeface)fonts.get(key);
if (tf instanceof LazyFont) {
@@ -677,7 +684,11 @@ public class PSRenderer extends AbstractPathOrientedRenderer
if (tf == null) {
throw new IllegalStateException("Font not available: " + key);
}
return tf.getFontName();
if (postFix == null) {
return tf.getFontName();
} else {
return tf.getFontName() + postFix;
}
}
/**
@@ -707,7 +718,6 @@ public class PSRenderer extends AbstractPathOrientedRenderer
protected void useFont(String key, int size) {
try {
PSResource res = getPSResourceForFontKey(key);
//gen.useFont(key, size / 1000f);
gen.useFont("/" + res.getName(), size / 1000f);
gen.getResourceTracker().notifyResourceUsageOnPage(res);
} catch (IOException ioe) {
@@ -960,7 +970,7 @@ public class PSRenderer extends AbstractPathOrientedRenderer
if (!isOptimizeResources()) {
this.fontResources = PSFontUtils.writeFontDict(gen, fontInfo);
} else {
gen.commentln("%FOPFontSetup");
gen.commentln("%FOPFontSetup"); //Place-holder, will be replaced in the second pass
}
gen.writeDSCComment(DSCConstants.END_SETUP);
}
@@ -1303,17 +1313,16 @@ public class PSRenderer extends AbstractPathOrientedRenderer
*/
public void renderText(TextArea area) {
renderInlineAreaBackAndBorders(area);
String fontname = getInternalFontNameForArea(area);
String fontkey = getInternalFontNameForArea(area);
int fontsize = area.getTraitAsInteger(Trait.FONT_SIZE);

// This assumes that *all* CIDFonts use a /ToUnicode mapping
Typeface tf = (Typeface) fontInfo.getFonts().get(fontname);
Typeface tf = (Typeface) fontInfo.getFonts().get(fontkey);

//Determine position
int rx = currentIPPosition + area.getBorderAndPaddingWidthStart();
int bl = currentBPPosition + area.getOffset() + area.getBaselineOffset();

useFont(fontname, fontsize);
Color ct = (Color)area.getTrait(Trait.COLOR);
if (ct != null) {
try {
@@ -1358,30 +1367,75 @@ public class PSRenderer extends AbstractPathOrientedRenderer
super.renderSpace(space);
}

private Typeface getTypeface(String fontName) {
Typeface tf = (Typeface)fontInfo.getFonts().get(fontName);
if (tf instanceof LazyFont) {
tf = ((LazyFont)tf).getRealFont();
}
return tf;
}
private void renderText(AbstractTextArea area, String text, int[] letterAdjust) {
String fontkey = getInternalFontNameForArea(area);
int fontSize = area.getTraitAsInteger(Trait.FONT_SIZE);
Font font = getFontFromArea(area);
Typeface tf = (Typeface) fontInfo.getFonts().get(font.getFontName());
Typeface tf = getTypeface(font.getFontName());
SingleByteFont singleByteFont = null;
if (tf instanceof SingleByteFont) {
singleByteFont = (SingleByteFont)tf;
}

int textLen = text.length();
if (singleByteFont != null && singleByteFont.hasAdditionalEncodings()) {
int start = 0;
int currentEncoding = -1;
for (int i = 0; i < textLen; i++) {
char c = text.charAt(i);
char mapped = tf.mapChar(c);
int encoding = mapped / 256;
if (currentEncoding != encoding) {
if (i > 0) {
writeText(area, text, start, i - start, letterAdjust, fontSize, tf);
}
if (encoding == 0) {
useFont(fontkey, fontSize);
} else {
useFont(fontkey + "_" + Integer.toString(encoding), fontSize);
}
currentEncoding = encoding;
start = i;
}
}
writeText(area, text, start, textLen - start, letterAdjust, fontSize, tf);
} else {
useFont(fontkey, fontSize);
writeText(area, text, 0, textLen, letterAdjust, fontSize, tf);
}
}

private void writeText(AbstractTextArea area, String text, int start, int len,
int[] letterAdjust, int fontsize, Typeface tf) {
int end = start + len;
int initialSize = text.length();
initialSize += initialSize / 2;
StringBuffer sb = new StringBuffer(initialSize);
int textLen = text.length();
if (letterAdjust == null
&& area.getTextLetterSpaceAdjust() == 0
&& area.getTextWordSpaceAdjust() == 0) {
sb.append("(");
for (int i = 0; i < textLen; i++) {
for (int i = start; i < end; i++) {
final char c = text.charAt(i);
final char mapped = tf.mapChar(c);
final char mapped = (char)(tf.mapChar(c) % 256);
PSGenerator.escapeChar(mapped, sb);
}
sb.append(") t");
} else {
sb.append("(");
int[] offsets = new int[textLen];
for (int i = 0; i < textLen; i++) {
int[] offsets = new int[len];
for (int i = start; i < end; i++) {
final char c = text.charAt(i);
final char mapped = tf.mapChar(c);
char codepoint = (char)(mapped % 256);
int wordSpace;

if (CharUtilities.isAdjustableSpace(mapped)) {
@@ -1389,14 +1443,14 @@ public class PSRenderer extends AbstractPathOrientedRenderer
} else {
wordSpace = 0;
}
int cw = tf.getWidth(mapped, font.getFontSize()) / 1000;
int ladj = (letterAdjust != null && i < textLen - 1 ? letterAdjust[i + 1] : 0);
int tls = (i < textLen - 1 ? area.getTextLetterSpaceAdjust() : 0);
offsets[i] = cw + ladj + tls + wordSpace;
PSGenerator.escapeChar(mapped, sb);
int cw = tf.getWidth(mapped, fontsize) / 1000;
int ladj = (letterAdjust != null && i < end - 1 ? letterAdjust[i + 1] : 0);
int tls = (i < end - 1 ? area.getTextLetterSpaceAdjust() : 0);
offsets[i - start] = cw + ladj + tls + wordSpace;
PSGenerator.escapeChar(codepoint, sb);
}
sb.append(")" + PSGenerator.LF + "[");
for (int i = 0; i < textLen; i++) {
for (int i = 0; i < len; i++) {
if (i > 0) {
if (i % 8 == 0) {
sb.append(PSGenerator.LF);
@@ -1409,7 +1463,6 @@ public class PSRenderer extends AbstractPathOrientedRenderer
sb.append("]" + PSGenerator.LF + "xshow");
}
writeln(sb.toString());

}

/** {@inheritDoc} */

+ 1
- 10
src/java/org/apache/fop/svg/PDFGraphics2D.java View File

@@ -70,12 +70,10 @@ import org.apache.xmlgraphics.image.loader.impl.ImageRendered;
import org.apache.xmlgraphics.java2d.AbstractGraphics2D;
import org.apache.xmlgraphics.java2d.GraphicContext;

import org.apache.fop.fonts.CIDFont;
import org.apache.fop.fonts.Font;
import org.apache.fop.fonts.FontInfo;
import org.apache.fop.fonts.FontSetup;
import org.apache.fop.fonts.FontTriplet;
import org.apache.fop.fonts.LazyFont;
import org.apache.fop.pdf.BitmapImage;
import org.apache.fop.pdf.PDFAnnotList;
import org.apache.fop.pdf.PDFColor;
@@ -1473,14 +1471,7 @@ public class PDFGraphics2D extends AbstractGraphics2D {
// This assumes that *all* CIDFonts use a /ToUnicode mapping
org.apache.fop.fonts.Typeface f
= (org.apache.fop.fonts.Typeface)fontInfo.getFonts().get(name);
if (f instanceof LazyFont) {
if (((LazyFont) f).getRealFont() instanceof CIDFont) {
return true;
}
} else if (f instanceof CIDFont) {
return true;
}
return false;
return f.isMultiByte();
}

private void addKerning(StringWriter buf, Integer ch1, Integer ch2,

+ 8
- 3
src/java/org/apache/fop/svg/PDFTextPainter.java View File

@@ -43,6 +43,7 @@ import org.apache.batik.gvt.renderer.StrokingTextPainter;
import org.apache.batik.gvt.text.GVTAttributedCharacterIterator;
import org.apache.batik.gvt.text.TextPaintInfo;
import org.apache.batik.gvt.text.TextSpanLayout;

import org.apache.fop.fonts.Font;
import org.apache.fop.fonts.FontInfo;
import org.apache.fop.fonts.FontTriplet;
@@ -83,8 +84,12 @@ public class PDFTextPainter extends StrokingTextPainter {
super.paintTextRuns(textRuns, g2d);
return;
}
PDFGraphics2D pdf = (PDFGraphics2D)g2d;
PDFTextUtil textUtil = new PDFTextUtil(pdf);
final PDFGraphics2D pdf = (PDFGraphics2D)g2d;
PDFTextUtil textUtil = new PDFTextUtil(pdf.fontInfo) {
protected void write(String code) {
pdf.currentStream.write(code);
}
};
for (int i = 0; i < textRuns.size(); i++) {
TextRun textRun = (TextRun)textRuns.get(i);
AttributedCharacterIterator runaci = textRun.getACI();
@@ -134,7 +139,7 @@ public class PDFTextPainter extends StrokingTextPainter {
}
textUtil.saveGraphicsState();
textUtil.concatMatrixCurrentTransform();
textUtil.concatMatrix(g2d.getTransform());
Shape imclip = g2d.getClip();
pdf.writeClip(imclip);

+ 25
- 206
src/java/org/apache/fop/svg/PDFTextUtil.java View File

@@ -19,145 +19,33 @@

package org.apache.fop.svg;

import java.awt.geom.AffineTransform;

import org.apache.fop.fonts.Font;
import org.apache.fop.pdf.PDFNumber;
import org.apache.fop.pdf.PDFText;
import org.apache.fop.fonts.FontInfo;
import org.apache.fop.fonts.Typeface;

/**
* Utility class for generating PDF text objects.
* Utility class for generating PDF text objects. It needs to be subclassed to add writing
* functionality (see {@link #write(String)}).
*/
public class PDFTextUtil {
public abstract class PDFTextUtil extends org.apache.fop.pdf.PDFTextUtil {

/** The number of decimal places. */
private static final int DEC = 8;
/** PDF text rendering mode: Fill text */
public static final int TR_FILL = 0;
/** PDF text rendering mode: Stroke text */
public static final int TR_STROKE = 1;
/** PDF text rendering mode: Fill, then stroke text */
public static final int TR_FILL_STROKE = 2;
/** PDF text rendering mode: Neither fill nor stroke text (invisible) */
public static final int TR_INVISIBLE = 3;
/** PDF text rendering mode: Fill text and add to path for clipping */
public static final int TR_FILL_CLIP = 4;
/** PDF text rendering mode: Stroke text and add to path for clipping */
public static final int TR_STROKE_CLIP = 5;
/** PDF text rendering mode: Fill, then stroke text and add to path for clipping */
public static final int TR_FILL_STROKE_CLIP = 6;
/** PDF text rendering mode: Add text to path for clipping */
public static final int TR_CLIP = 7;
private PDFGraphics2D g2d;
private boolean inTextObject = false;
private FontInfo fontInfo;
private Font[] fonts;
private Font font;
private String startText;
private String endText;
private boolean useMultiByte;
private StringBuffer bufTJ;
private int textRenderingMode = 0;
/**
* Main constructor.
* @param g2d the PDFGraphics2D instance to work with
* @param fontInfo the font catalog
*/
public PDFTextUtil(PDFGraphics2D g2d) {
this.g2d = g2d;
public PDFTextUtil(FontInfo fontInfo) {
super();
this.fontInfo = fontInfo;
}
private void writeAffineTransform(AffineTransform at, StringBuffer sb) {
double[] lt = new double[6];
at.getMatrix(lt);
sb.append(PDFNumber.doubleOut(lt[0], DEC)).append(" ");
sb.append(PDFNumber.doubleOut(lt[1], DEC)).append(" ");
sb.append(PDFNumber.doubleOut(lt[2], DEC)).append(" ");
sb.append(PDFNumber.doubleOut(lt[3], DEC)).append(" ");
sb.append(PDFNumber.doubleOut(lt[4], DEC)).append(" ");
sb.append(PDFNumber.doubleOut(lt[5], DEC));
}

private void writeChar(char ch, StringBuffer sb) {
if (!useMultiByte) {
if (ch > 127) {
sb.append("\\").append(Integer.toOctalString((int)ch));
} else {
switch (ch) {
case '(':
case ')':
case '\\':
sb.append("\\");
break;
default:
}
sb.append(ch);
}
} else {
sb.append(PDFText.toUnicodeHex(ch));
}
}
private void checkInTextObject() {
if (!inTextObject) {
throw new IllegalStateException("Not in text object");
}
}
/**
* Called when a new text object should be started. Be sure to call setFont() before
* issuing any text painting commands.
*/
public void beginTextObject() {
if (inTextObject) {
throw new IllegalStateException("Already in text object");
}
g2d.currentStream.write("BT\n");
this.inTextObject = true;
}
/**
* Called when a text object should be ended.
*/
public void endTextObject() {
checkInTextObject();
g2d.currentStream.write("ET\n");
this.inTextObject = false;
initValues();
}
private void initValues() {
/** {@inheritDoc} */
protected void initValues() {
super.initValues();
this.font = null;
this.textRenderingMode = TR_FILL;
}
/**
* Creates a "q" command, pushing a copy of the entire graphics state onto the stack.
*/
public void saveGraphicsState() {
g2d.currentStream.write("q\n");
}
/**
* Creates a "Q" command, restoring the entire graphics state to its former value by popping
* it from the stack.
*/
public void restoreGraphicsState() {
g2d.currentStream.write("Q\n");
}
/**
* Creates a "cm" command using the current transformation as the matrix.
*/
public void concatMatrixCurrentTransform() {
StringBuffer sb = new StringBuffer();
if (!g2d.getTransform().isIdentity()) {
writeAffineTransform(g2d.getTransform(), sb);
sb.append(" cm\n");
}
g2d.currentStream.write(sb.toString());
}
/**
@@ -193,64 +81,24 @@ public class PDFTextUtil {
this.font = f;
}
/**
* Determines whether the font with the given name is a multi-byte font.
* @param name the name of the font
* @return true if it's a multi-byte font
*/
protected boolean isMultiByteFont(String name) {
Typeface f = (Typeface)fontInfo.getFonts().get(name);
return f.isMultiByte();
}

/**
* Writes a "Tf" command, setting a new current font.
* @param f the font to select
*/
public void writeTf(Font f) {
checkInTextObject();
String fontName = f.getFontName();
float fontSize = (float)f.getFontSize() / 1000f;
g2d.currentStream.write("/" + fontName + " " + PDFNumber.doubleOut(fontSize) + " Tf\n");
this.useMultiByte = g2d.isMultiByteFont(fontName);
this.startText = useMultiByte ? "<" : "(";
this.endText = useMultiByte ? ">" : ")";
}

/**
* Sets the text rendering mode.
* @param mode the rendering mode (value 0 to 7, see PDF Spec, constants: TR_*)
*/
public void setTextRenderingMode(int mode) {
if (mode < 0 || mode > 7) {
throw new IllegalArgumentException(
"Illegal value for text rendering mode. Expected: 0-7");
}
if (mode != this.textRenderingMode) {
this.textRenderingMode = mode;
g2d.currentStream.write(this.textRenderingMode + " Tr\n");
}
}
/**
* Sets the text rendering mode.
* @param fill true if the text should be filled
* @param stroke true if the text should be stroked
* @param addToClip true if the path should be added for clipping
*/
public void setTextRenderingMode(boolean fill, boolean stroke, boolean addToClip) {
int mode;
if (fill) {
mode = (stroke ? 2 : 0);
} else {
mode = (stroke ? 1 : 3);
}
if (addToClip) {
mode += 4;
}
setTextRenderingMode(mode);
}
/**
* Writes a "Tm" command, setting a new text transformation matrix.
* @param localTransform the new text transformation matrix
*/
public void writeTextMatrix(AffineTransform localTransform) {
StringBuffer sb = new StringBuffer();
writeAffineTransform(localTransform, sb);
sb.append(" Tm\n");
g2d.currentStream.write(sb.toString());
updateTf(fontName, fontSize, isMultiByteFont(fontName));
}

/**
@@ -272,37 +120,8 @@ public class PDFTextUtil {
* @param ch the unmapped character
*/
public void writeTJChar(char ch) {
if (bufTJ == null) {
bufTJ = new StringBuffer();
}
if (bufTJ.length() == 0) {
bufTJ.append("[").append(startText);
}
char mappedChar = font.mapChar(ch);
writeChar(mappedChar, bufTJ);
}

/**
* Writes a glyph adjust value to the "TJ-Buffer".
* @param adjust the glyph adjust value in thousands of text unit space.
*/
public void adjustGlyphTJ(double adjust) {
bufTJ.append(endText).append(" ");
bufTJ.append(PDFNumber.doubleOut(adjust, DEC - 4));
bufTJ.append(" ");
bufTJ.append(startText);
}

/**
* Writes a "TJ" command, writing out the accumulated buffer with the characters and glyph
* positioning values. The buffer is reset afterwards.
*/
public void writeTJ() {
if (bufTJ != null && bufTJ.length() > 0) {
bufTJ.append(endText).append("] TJ\n");
g2d.currentStream.write(bufTJ.toString());
bufTJ.setLength(0);
}
writeTJMappedChar(mappedChar);
}

}

+ 15
- 2
status.xml View File

@@ -52,13 +52,26 @@
</contexts>
<changes>
<!--release version="FOP Trunk" date="TBD"-->
<release version="FOP Trunk" date="TBD">
<!-- change reverted, to be added back later
<action context="Renderers" dev="AC" importance="high" type="add">
Added SVG support for AFP (GOCA).
</action>
-->
<!--/release-->
<action context="Renderers" dev="JM" type="update">
When a JPEG image is embedded, an optionally embedded color profile is filtered out
as it's already embedded separately in the PDF file.
</action>
<action context="Renderers" dev="JM" type="fix">
Worked around a problem (PDF renderer) with JPEG image containing RGB color profiles which
are not sRGB. The images drifted into yellow. The color profile is simply disabled in this
case. Please let us know if you know what the problem could be.
</action>
<action context="Fonts" dev="JM" type="add">
Added support for addressing all glyphs available in a Type 1 font, not just the ones
in the font's primary encoding.
</action>
</release>
<release version="0.95beta" date="22 March 2008">
<notes>
<section>

+ 4
- 2
test/java/org/apache/fop/render/pdf/PDFEncodingTestCase.java View File

@@ -65,7 +65,7 @@ public class PDFEncodingTestCase extends BasePDFTestCase {
* The following array is used to look for these patterns
*/
final String[] testPatterns = {
TEST_MARKER + "1", "(Standard)",
TEST_MARKER + "1", "Standard",
TEST_MARKER + "2", "XX_\\351_XX",
TEST_MARKER + "3", "XX_\\342\\352\\356\\364\\373_XX"
};
@@ -75,7 +75,9 @@ public class PDFEncodingTestCase extends BasePDFTestCase {

/**
* TODO test disabled for now, fails due (probably) do different PDF
* encoding when custom font is used
* encoding when custom font is used.
* TODO This should be tested using PDFBox. If PDFBox can extract the text correctly,
* everything is fine. The tests here are too unstable.
*
* @throws Exception
* checkstyle wants a comment here, even a silly one

Loading…
Cancel
Save