summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGlenn Adams <gadams@apache.org>2012-07-05 20:29:53 +0000
committerGlenn Adams <gadams@apache.org>2012-07-05 20:29:53 +0000
commite2ef36f857d24816dbc856792fdb811778bf8df6 (patch)
tree357409807d667bd7e36b662aef400d195aed0048
parentec0cb9611cdc2faf759010395f0a4dbe328c6994 (diff)
parent015538e0f11f031e3d7bd05db8c29e2a40365678 (diff)
downloadxmlgraphics-fop-e2ef36f857d24816dbc856792fdb811778bf8df6.tar.gz
xmlgraphics-fop-e2ef36f857d24816dbc856792fdb811778bf8df6.zip
Merge from trunk@1354651.
git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/fop-1_1@1357883 13f79535-47bb-0310-9956-ffa450edef68
-rw-r--r--build.xml16
-rw-r--r--examples/fo/advanced/cid-fonts.fo2
-rw-r--r--examples/fo/advanced/giro.fo14
-rw-r--r--examples/fo/basic/images.fo2
-rw-r--r--examples/fo/basic/tableunits.fo40
-rw-r--r--examples/fo/build.xml5
-rw-r--r--examples/fo/svg/embedding.fo4
-rw-r--r--examples/fo/tables/background.fo22
-rw-r--r--examples/fo/tables/borders.fo39
-rw-r--r--examples/fo/tables/break.fo10
-rw-r--r--examples/fo/tables/headfoot.fo14
-rw-r--r--examples/fo/tables/keep.fo12
-rw-r--r--examples/fo/tables/omit.fo10
-rw-r--r--examples/fo/tables/space.fo18
-rw-r--r--findbugs-exclude.xml51
-rw-r--r--lib/xmlgraphics-commons-1.5svn.jarbin585921 -> 600660 bytes
-rw-r--r--src/documentation/content/xdocs/examples.xml10
-rw-r--r--src/documentation/content/xdocs/fo/align.fo25
-rw-r--r--src/documentation/content/xdocs/fo/align.fo.pdfbin4689 -> 0 bytes
-rw-r--r--src/documentation/content/xdocs/fo/align.pdfbin0 -> 9284 bytes
-rw-r--r--src/documentation/content/xdocs/fo/align2.fo27
-rw-r--r--src/documentation/content/xdocs/fo/align2.fo.pdfbin4299 -> 0 bytes
-rw-r--r--src/documentation/content/xdocs/fo/align2.pdfbin0 -> 8797 bytes
-rw-r--r--src/documentation/content/xdocs/fo/build.xml70
-rw-r--r--src/documentation/content/xdocs/fo/embed.fo25
-rw-r--r--src/documentation/content/xdocs/fo/embed.fo.pdf87
-rw-r--r--src/documentation/content/xdocs/fo/embed.pdfbin0 -> 8249 bytes
-rw-r--r--src/documentation/content/xdocs/fo/fonts.fo48
-rw-r--r--src/documentation/content/xdocs/fo/fonts.fo.pdf160
-rw-r--r--src/documentation/content/xdocs/fo/fonts.pdfbin0 -> 20432 bytes
-rw-r--r--src/documentation/content/xdocs/fo/size.fo25
-rw-r--r--src/documentation/content/xdocs/fo/size.fo.pdfbin5516 -> 0 bytes
-rw-r--r--src/documentation/content/xdocs/fo/size.pdfbin0 -> 9508 bytes
-rw-r--r--src/documentation/content/xdocs/trunk/configuration.xml18
-rw-r--r--src/documentation/content/xdocs/trunk/fonts.xml10
-rw-r--r--src/documentation/content/xdocs/trunk/graphics.xml13
-rw-r--r--src/documentation/intermediate-format-ng/fop-intermediate-format-ng.xsd1
-rw-r--r--src/java/META-INF/services/org.apache.fop.render.ImageHandler2
-rw-r--r--src/java/org/apache/fop/accessibility/Accessibility.java6
-rw-r--r--src/java/org/apache/fop/accessibility/fo/FO2StructureTreeConverter.java25
-rw-r--r--src/java/org/apache/fop/afp/AFPGraphics2D.java4
-rw-r--r--src/java/org/apache/fop/afp/modca/ImageObject.java6
-rw-r--r--src/java/org/apache/fop/afp/modca/PresentationTextData.java2
-rw-r--r--src/java/org/apache/fop/afp/util/DefaultFOPResourceAccessor.java3
-rw-r--r--src/java/org/apache/fop/apps/FOUserAgent.java5
-rw-r--r--src/java/org/apache/fop/complexscripts/fonts/GlyphPositioningTable.java2
-rw-r--r--src/java/org/apache/fop/complexscripts/fonts/OTFAdvancedTypographicTableReader.java41
-rw-r--r--src/java/org/apache/fop/complexscripts/scripts/IndicScriptProcessor.java2
-rw-r--r--src/java/org/apache/fop/fo/FOEventHandler.java14
-rw-r--r--src/java/org/apache/fop/fo/flow/Wrapper.java19
-rw-r--r--src/java/org/apache/fop/fo/properties/CondLengthProperty.java5
-rw-r--r--src/java/org/apache/fop/fonts/CIDFontType.java2
-rw-r--r--src/java/org/apache/fop/fonts/CMapSegment.java (renamed from src/java/org/apache/fop/fonts/BFEntry.java)39
-rw-r--r--src/java/org/apache/fop/fonts/CustomFont.java40
-rw-r--r--src/java/org/apache/fop/fonts/CustomFontCollection.java2
-rw-r--r--src/java/org/apache/fop/fonts/EmbedFontInfo.java23
-rw-r--r--src/java/org/apache/fop/fonts/EmbeddingMode.java58
-rw-r--r--src/java/org/apache/fop/fonts/EncodingMode.java2
-rw-r--r--src/java/org/apache/fop/fonts/FontInfoConfigurator.java6
-rw-r--r--src/java/org/apache/fop/fonts/FontLoader.java22
-rw-r--r--src/java/org/apache/fop/fonts/FontReader.java13
-rw-r--r--src/java/org/apache/fop/fonts/FontType.java5
-rw-r--r--src/java/org/apache/fop/fonts/LazyFont.java10
-rw-r--r--src/java/org/apache/fop/fonts/MultiByteFont.java62
-rw-r--r--src/java/org/apache/fop/fonts/MutableFont.java6
-rw-r--r--src/java/org/apache/fop/fonts/SingleByteFont.java24
-rw-r--r--src/java/org/apache/fop/fonts/apps/TTFReader.java30
-rw-r--r--src/java/org/apache/fop/fonts/autodetect/FontInfoFinder.java6
-rw-r--r--src/java/org/apache/fop/fonts/truetype/FontFileReader.java29
-rw-r--r--src/java/org/apache/fop/fonts/truetype/TTFCmapEntry.java118
-rw-r--r--src/java/org/apache/fop/fonts/truetype/TTFDirTabEntry.java8
-rw-r--r--src/java/org/apache/fop/fonts/truetype/TTFFile.java709
-rw-r--r--src/java/org/apache/fop/fonts/truetype/TTFFontLoader.java65
-rw-r--r--src/java/org/apache/fop/fonts/truetype/TTFGlyphOutputStream.java48
-rw-r--r--src/java/org/apache/fop/fonts/truetype/TTFOutputStream.java49
-rw-r--r--src/java/org/apache/fop/fonts/truetype/TTFSubSetFile.java525
-rw-r--r--src/java/org/apache/fop/fonts/truetype/TTFTableName.java163
-rw-r--r--src/java/org/apache/fop/fonts/truetype/TTFTableOutputStream.java37
-rw-r--r--src/java/org/apache/fop/hyphenation/HyphenationTreeCache.java6
-rw-r--r--src/java/org/apache/fop/layoutmgr/BalancingColumnBreakingAlgorithm.java16
-rw-r--r--src/java/org/apache/fop/layoutmgr/BreakElement.java34
-rw-r--r--src/java/org/apache/fop/layoutmgr/KnuthPenalty.java7
-rw-r--r--src/java/org/apache/fop/pdf/AbstractPDFStream.java9
-rw-r--r--src/java/org/apache/fop/pdf/AlphaRasterImage.java4
-rw-r--r--src/java/org/apache/fop/pdf/BitmapImage.java20
-rw-r--r--src/java/org/apache/fop/pdf/PDFColor.java2
-rw-r--r--src/java/org/apache/fop/pdf/PDFEmbeddedFile.java5
-rw-r--r--src/java/org/apache/fop/pdf/PDFFactory.java5
-rw-r--r--src/java/org/apache/fop/pdf/PDFImage.java8
-rw-r--r--src/java/org/apache/fop/pdf/PDFImageXObject.java5
-rw-r--r--src/java/org/apache/fop/pdf/PDFObject.java6
-rw-r--r--src/java/org/apache/fop/pdf/PDFResources.java54
-rw-r--r--src/java/org/apache/fop/render/ImageHandler.java3
-rw-r--r--src/java/org/apache/fop/render/afp/AFPDocumentHandler.java3
-rw-r--r--src/java/org/apache/fop/render/afp/extensions/AFPIncludeFormMap.java2
-rw-r--r--src/java/org/apache/fop/render/afp/extensions/AFPIncludeFormMapElement.java4
-rw-r--r--src/java/org/apache/fop/render/afp/extensions/AFPPageOverlay.java2
-rw-r--r--src/java/org/apache/fop/render/bitmap/AbstractBitmapDocumentHandler.java3
-rw-r--r--src/java/org/apache/fop/render/bitmap/PNGDocumentHandler.java3
-rw-r--r--src/java/org/apache/fop/render/bitmap/TIFFDocumentHandler.java3
-rw-r--r--src/java/org/apache/fop/render/intermediate/IFContext.java4
-rw-r--r--src/java/org/apache/fop/render/intermediate/IFSerializer.java13
-rw-r--r--src/java/org/apache/fop/render/intermediate/IFUtil.java8
-rw-r--r--src/java/org/apache/fop/render/java2d/ConfiguredFontCollection.java3
-rw-r--r--src/java/org/apache/fop/render/java2d/CustomFontMetricsMapper.java2
-rw-r--r--src/java/org/apache/fop/render/java2d/Java2DPainter.java3
-rw-r--r--src/java/org/apache/fop/render/pcl/PCLDocumentHandler.java3
-rw-r--r--src/java/org/apache/fop/render/pcl/PCLPainter.java3
-rw-r--r--src/java/org/apache/fop/render/pdf/AbstractImageAdapter.java83
-rw-r--r--src/java/org/apache/fop/render/pdf/ImageRawPNGAdapter.java260
-rw-r--r--src/java/org/apache/fop/render/pdf/ImageRenderedAdapter.java73
-rw-r--r--src/java/org/apache/fop/render/pdf/PDFImageHandlerRawPNG.java65
-rw-r--r--src/java/org/apache/fop/render/ps/FontResourceCache.java17
-rw-r--r--src/java/org/apache/fop/render/ps/ImageEncoderPNG.java113
-rw-r--r--src/java/org/apache/fop/render/ps/NativeTextHandler.java6
-rw-r--r--src/java/org/apache/fop/render/ps/PSDocumentHandler.java35
-rw-r--r--src/java/org/apache/fop/render/ps/PSEventProducer.java7
-rw-r--r--src/java/org/apache/fop/render/ps/PSEventProducer.xml1
-rw-r--r--src/java/org/apache/fop/render/ps/PSFontResource.java77
-rw-r--r--src/java/org/apache/fop/render/ps/PSFontUtils.java440
-rw-r--r--src/java/org/apache/fop/render/ps/PSImageHandlerRawPNG.java111
-rw-r--r--src/java/org/apache/fop/render/ps/PSImageHandlerRenderedImage.java26
-rw-r--r--src/java/org/apache/fop/render/ps/PSPainter.java66
-rw-r--r--src/java/org/apache/fop/render/ps/PSTextPainter.java45
-rw-r--r--src/java/org/apache/fop/render/ps/ResourceHandler.java10
-rw-r--r--src/java/org/apache/fop/render/ps/extensions/PSExtensionElementMapping.java7
-rw-r--r--src/java/org/apache/fop/render/ps/extensions/PSExtensionHandler.java3
-rw-r--r--src/java/org/apache/fop/render/ps/extensions/PSPageTrailerCodeBefore.java50
-rw-r--r--src/java/org/apache/fop/render/ps/extensions/PSPageTrailerCodeBeforeElement.java55
-rw-r--r--src/java/org/apache/fop/render/ps/fonts/PSTTFGenerator.java101
-rw-r--r--src/java/org/apache/fop/render/ps/fonts/PSTTFGlyphOutputStream.java75
-rw-r--r--src/java/org/apache/fop/render/ps/fonts/PSTTFOutputStream.java62
-rw-r--r--src/java/org/apache/fop/render/ps/fonts/PSTTFTableOutputStream.java59
-rw-r--r--src/java/org/apache/fop/render/xml/XMLRenderer.java26
-rw-r--r--src/java/org/apache/fop/svg/PDFAElementBridge.java2
-rw-r--r--src/java/org/apache/fop/svg/PDFGraphics2D.java2
-rw-r--r--src/java/org/apache/fop/traits/MinOptMax.java5
-rw-r--r--src/java/org/apache/fop/traits/TraitEnum.java4
-rw-r--r--src/java/org/apache/fop/util/ColorExt.java2
-rw-r--r--src/java/org/apache/fop/util/HexEncoder.java57
-rw-r--r--src/sandbox/org/apache/fop/render/mif/MIFHandler.java60
-rw-r--r--src/sandbox/org/apache/fop/render/svg/SVGDocumentHandler.java3
-rw-r--r--src/sandbox/org/apache/fop/render/svg/SVGPrintDocumentHandler.java3
-rw-r--r--status.xml45
-rw-r--r--test/java/org/apache/fop/UtilityCodeTestSuite.java8
-rw-r--r--test/java/org/apache/fop/accessibility/fo/FO2StructureTreeConverterTestCase.java15
-rw-r--r--test/java/org/apache/fop/accessibility/fo/artifact.fo97
-rw-r--r--test/java/org/apache/fop/accessibility/fo/fo2StructureTree.xsl2
-rw-r--r--test/java/org/apache/fop/fonts/DejaVuLGCSerifTestCase.java3
-rw-r--r--test/java/org/apache/fop/fonts/EncodingModeTestCase.java18
-rw-r--r--test/java/org/apache/fop/fonts/FOPFontsTestSuite.java42
-rw-r--r--test/java/org/apache/fop/fonts/truetype/FontFileReaderTestCase.java304
-rw-r--r--test/java/org/apache/fop/fonts/truetype/GlyfTableTestCase.java7
-rw-r--r--test/java/org/apache/fop/fonts/truetype/TTFFileTestCase.java427
-rw-r--r--test/java/org/apache/fop/fonts/truetype/TTFFontLoaderTestCase.java13
-rw-r--r--test/java/org/apache/fop/fonts/truetype/TTFSubSetFileTestCase.java76
-rw-r--r--test/java/org/apache/fop/fonts/truetype/TTFTableNameTestCase.java153
-rw-r--r--test/java/org/apache/fop/render/RawPNGTestUtil.java92
-rw-r--r--test/java/org/apache/fop/render/pdf/ImageRawPNGAdapterTestCase.java142
-rw-r--r--test/java/org/apache/fop/render/ps/ImageEncoderPNGTestCase.java133
-rw-r--r--test/java/org/apache/fop/render/ps/PSRenderingUtilTestCase.java45
-rw-r--r--test/java/org/apache/fop/render/ps/RenderPSTestSuite.java43
-rw-r--r--test/java/org/apache/fop/render/ps/fonts/PSTTFGeneratorTestCase.java120
-rw-r--r--test/java/org/apache/fop/render/ps/fonts/PSTTFGlyphOutputStreamTestCase.java109
-rw-r--r--test/java/org/apache/fop/render/ps/fonts/PSTTFOutputStreamTestCase.java90
-rw-r--r--test/java/org/apache/fop/render/ps/fonts/PSTTFTableOutputStreamTestCase.java87
-rw-r--r--test/java/org/apache/fop/svg/PDFGraphics2DTestCase.java48
-rw-r--r--test/java/org/apache/fop/util/HexEncoderTestCase.java61
-rw-r--r--test/layoutengine/standard-testcases/bidi_propagation_1.xml4
-rw-r--r--test/layoutengine/standard-testcases/character_writing-mode_rl.xml10
-rw-r--r--test/pdf/accessibility/pdf/role.pdfbin19023 -> 18600 bytes
-rw-r--r--test/pdf/accessibility/role.fo4
-rw-r--r--test/resources/fonts/ttf/DroidSansMono.LICENSE18
-rw-r--r--test/resources/fonts/ttf/DroidSansMono.ttfbin0 -> 78296 bytes
174 files changed, 5889 insertions, 1596 deletions
diff --git a/build.xml b/build.xml
index 8d56b982f..e0c3c97bc 100644
--- a/build.xml
+++ b/build.xml
@@ -864,16 +864,28 @@ list of possible build targets.
<target name="junit-text-linebreak" depends="junit-compile" description="Runs FOP's JUnit unicode linebreak tests" if="junit.present">
<junit-run title="Unicode UAX#14 support" testsuite="org.apache.fop.text.linebreak.LineBreakStatusTestCase" outfile="TEST-linebreak"/>
</target>
+ <target name="junit-fonts" depends="junit-compile">
+ <echo message="Running tests for the fonts package"/>
+ <junit-run title="fonts" testsuite="org.apache.fop.fonts.FOPFontsTestSuite" outfile="TEST-fonts"/>
+ </target>
+ <target name="junit-render-ps" depends="junit-compile">
+ <echo message="Running tests for the render ps package"/>
+ <junit-run title="render-ps" testsuite="org.apache.fop.render.ps.RenderPSTestSuite" outfile="TEST-render-ps"/>
+ </target>
<target name="junit-render-pdf" depends="junit-compile">
<junit-run title="render-pdf" testsuite="org.apache.fop.render.pdf.RenderPDFTestSuite" outfile="TEST-render-pdf"/>
</target>
<target name="junit-complexscripts" depends="junit-compile">
<junit-run title="complexscripts" testsuite="org.apache.fop.complexscripts.ComplexScriptsTestSuite" outfile="TEST-complexscripts"/>
</target>
- <target name="junit-reduced" depends="junit-userconfig, junit-basic, junit-transcoder, junit-text-linebreak, junit-fotree, junit-render-pdf, junit-complexscripts"/>
+ <target name="junit-reduced" depends="junit-userconfig, junit-basic, junit-transcoder,
+ junit-text-linebreak, junit-fotree, junit-fonts, junit-render-pdf, junit-render-ps,
+ junit-complexscripts"/>
<target name="junit" depends="junit-all" description="Runs all of FOP's JUnit tests"
if="junit.present">
- <fail><condition><or><isset property="fop.junit.error"/><isset property="fop.junit.failure"/><not><isset property="hyphenation.present"/></not></or></condition>
+ <fail><condition><or><isset property="fop.junit.error"/><isset
+ property="fop.junit.failure"/><not><isset
+ property="hyphenation.present"/></not></or></condition>
NOTE:
**************************************************************************
* One or more of the Junit tests had Failures or Errors or were skipped! *
diff --git a/examples/fo/advanced/cid-fonts.fo b/examples/fo/advanced/cid-fonts.fo
index 526ebea06..cdcb4737c 100644
--- a/examples/fo/advanced/cid-fonts.fo
+++ b/examples/fo/advanced/cid-fonts.fo
@@ -369,7 +369,7 @@ This font contains no embedding license restrictions
</fo:block>
</fo:wrapper>
- <fo:table>
+ <fo:table table-layout="fixed" width="100%">
<fo:table-column column-width="50mm"/>
<fo:table-column column-width="50mm"/>
<fo:table-column column-width="50mm"/>
diff --git a/examples/fo/advanced/giro.fo b/examples/fo/advanced/giro.fo
index 5b5422ec9..9f1a6723a 100644
--- a/examples/fo/advanced/giro.fo
+++ b/examples/fo/advanced/giro.fo
@@ -32,7 +32,7 @@
<fo:block-container absolute-position="absolute" width="100%" height="100%" border-top-color="silver" border-top-style="dotted" border-top-width="0.13mm">
<fo:block/>
</fo:block-container>
- <fo:table space-before.optimum="1in div 12" margin-left="2in div 12" margin-top="1in div 10" font-family="sans-serif" font-size="7pt" color="green">
+ <fo:table space-before.optimum="1in div 12" margin-left="2in div 12" margin-top="1in div 10" font-family="sans-serif" font-size="7pt" color="green" table-layout="fixed" width="100%">
<fo:table-column column-width="8in div 10 - 2in div 12"/>
<fo:table-column border-right-style="solid" border-right-width="0.5mm" column-width="36in div 10"/>
<fo:table-column column-width="5in div 10"/>
@@ -107,7 +107,7 @@
<fo:block>Från konto nr</fo:block>
</fo:table-cell>
<fo:table-cell border-bottom-style="solid" border-bottom-width="0.13mm" padding-top="6mm" display-align="after">
- <fo:table height="3mm">
+ <fo:table height="3mm" table-layout="fixed" width="100%">
<fo:table-column border-right-style="solid" border-right-width="0.13mm" column-width="0.2in"/>
<fo:table-column border-right-style="solid" border-right-width="0.13mm" column-width="0.2in"/>
<fo:table-column border-right-style="solid" border-right-width="0.13mm" column-width="0.2in"/>
@@ -182,7 +182,7 @@
<fo:block color="black">16.6.2006</fo:block>
</fo:table-cell>
<fo:table-cell border-bottom-style="solid" border-bottom-width="0.5mm" padding-left="2mm" padding-top="4mm" font-size="10pt">
- <fo:table>
+ <fo:table table-layout="fixed" width="100%">
<fo:table-column column-width="0.5cm"/>
<fo:table-column column-width="2cm"/>
<fo:table-body>
@@ -201,7 +201,7 @@
<fo:table-row height="13in div 12 - 5mm">
<fo:table-cell number-columns-spanned="4" padding-left="40mm" display-align="after">
<fo:block>
- <fo:table height="5in div 12" margin-left="2.5mm" margin-right="2.5mm"><!--105-->
+ <fo:table height="5in div 12" margin-left="2.5mm" margin-right="2.5mm" table-layout="fixed" width="100%"><!--105-->
<fo:table-column column-width="0.5mm"/>
<fo:table-column column-width="0.25mm"/>
<fo:table-column column-width="0.25mm"/>
@@ -950,7 +950,7 @@
</fo:page-sequence>
<fo:page-sequence master-reference="A4">
<fo:static-content flow-name="xsl-region-after">
- <fo:table space-before.optimum="1in div 12" margin-left="2in div 12" margin-top="1in div 10" font-family="sans-serif" font-size="7pt" color="green">
+ <fo:table space-before.optimum="1in div 12" margin-left="2in div 12" margin-top="1in div 10" font-family="sans-serif" font-size="7pt" color="green" table-layout="fixed" width="100%">
<fo:table-column column-width="8in div 10 - 2in div 12"/>
<fo:table-column border-right-style="solid" border-right-width="0.5mm" column-width="36in div 10"/>
<fo:table-column column-width="5in div 10"/>
@@ -1025,7 +1025,7 @@
<fo:block>Från konto nr</fo:block>
</fo:table-cell>
<fo:table-cell border-bottom-style="solid" border-bottom-width="0.13mm" padding-top="6mm" display-align="after">
- <fo:table height="3mm">
+ <fo:table height="3mm" table-layout="fixed" width="100%">
<fo:table-column border-right-style="solid" border-right-width="0.13mm" column-width="0.2in"/>
<fo:table-column border-right-style="solid" border-right-width="0.13mm" column-width="0.2in"/>
<fo:table-column border-right-style="solid" border-right-width="0.13mm" column-width="0.2in"/>
@@ -1100,7 +1100,7 @@
<fo:block color="black">16.6.2006</fo:block>
</fo:table-cell>
<fo:table-cell border-bottom-style="solid" border-bottom-width="0.5mm" padding-left="2mm" padding-top="4mm" font-size="10pt">
- <fo:table>
+ <fo:table table-layout="fixed" width="100%">
<fo:table-column column-width="0.5cm"/>
<fo:table-column column-width="2cm"/>
<fo:table-body>
diff --git a/examples/fo/basic/images.fo b/examples/fo/basic/images.fo
index 3bb864fd8..daabcd31b 100644
--- a/examples/fo/basic/images.fo
+++ b/examples/fo/basic/images.fo
@@ -31,8 +31,6 @@
odd-or-even="even" />
<fo:conditional-page-master-reference master-reference="right"
odd-or-even="odd" />
- <!-- recommended fallback procedure -->
- <fo:conditional-page-master-reference master-reference="rest" />
</fo:repeatable-page-master-alternatives>
</fo:page-sequence-master>
</fo:layout-master-set>
diff --git a/examples/fo/basic/tableunits.fo b/examples/fo/basic/tableunits.fo
index aca927c4a..1a1c8c9e1 100644
--- a/examples/fo/basic/tableunits.fo
+++ b/examples/fo/basic/tableunits.fo
@@ -67,9 +67,9 @@
<fo:block space-before="12pt" space-after="6pt">The next table has width=100% on the table no column widths specified on the table-column element.</fo:block>
<!-- table start -->
<fo:table border-collapse="separate" table-layout="fixed" width="100%">
- <fo:table-column background-color="yellow"/>
- <fo:table-column background-color="blue"/>
- <fo:table-column background-color="green"/>
+ <fo:table-column column-width="proportional-column-width(1)" background-color="yellow"/>
+ <fo:table-column column-width="proportional-column-width(1)" background-color="blue"/>
+ <fo:table-column column-width="proportional-column-width(1)" background-color="green"/>
<fo:table-body>
<fo:table-row>
<fo:table-cell display-align="before"><fo:block>Some text to make this cell
@@ -83,7 +83,7 @@
<fo:block space-before="12pt" space-after="6pt">The next table has fixed column widths=13cm, ipd.optimum=12cm and ipd.max = 100%.</fo:block>
<!-- table start -->
<fo:table border-collapse="separate"
- table-layout="fixed"
+ table-layout="fixed" width="100%"
inline-progression-dimension="12cm"
inline-progression-dimension.maximum="100%"
>
@@ -122,8 +122,8 @@
<!-- table start -->
<fo:table border-collapse="separate" table-layout="fixed" width="70% + 1cm">
<fo:table-column column-width="from-parent('width') div 3" background-color="yellow"/>
- <fo:table-column background-color="blue"/>
- <fo:table-column background-color="green"/>
+ <fo:table-column column-width="proportional-column-width(1)" background-color="blue"/>
+ <fo:table-column column-width="proportional-column-width(1)" background-color="green"/>
<fo:table-body>
<fo:table-row>
<fo:table-cell display-align="before"><fo:block>Some text to make this cell
@@ -140,11 +140,11 @@
</fo:block>
<!-- table start -->
<fo:table border-collapse="separate"
- table-layout="fixed"
+ table-layout="fixed" width="100%"
inline-progression-dimension="15cm">
- <fo:table-column background-color="yellow"/>
+ <fo:table-column column-width="proportional-column-width(1)" background-color="yellow"/>
<fo:table-column column-width="3cm" background-color="blue"/>
- <fo:table-column background-color="green"/>
+ <fo:table-column column-width="proportional-column-width(1)" background-color="green"/>
<fo:table-body>
<fo:table-row>
<fo:table-cell display-align="before"><fo:block>Some text to make this cell
@@ -159,10 +159,10 @@
The next table specifies neither width nor inline-progression-dimension.
</fo:block>
<!-- table start -->
- <fo:table table-layout="fixed" border-collapse="separate">
- <fo:table-column background-color="yellow"/>
+ <fo:table table-layout="fixed" width="100%" border-collapse="separate">
+ <fo:table-column column-width="proportional-column-width(1)" background-color="yellow"/>
<fo:table-column column-width="3cm" background-color="blue"/>
- <fo:table-column background-color="green"/>
+ <fo:table-column column-width="proportional-column-width(1)" background-color="green"/>
<fo:table-body>
<fo:table-row>
<fo:table-cell display-align="before"><fo:block>Some text to make this cell
@@ -178,11 +178,11 @@
specifies a column width of 5cm on the middle column only.
</fo:block>
<!-- table start -->
- <fo:table table-layout="fixed" border-collapse="separate"
+ <fo:table table-layout="fixed" width="100%" border-collapse="separate"
inline-progression-dimension.minimum="10cm">
- <fo:table-column background-color="yellow"/>
+ <fo:table-column column-width="proportional-column-width(1)" background-color="yellow"/>
<fo:table-column column-width="5cm" background-color="blue"/>
- <fo:table-column background-color="green"/>
+ <fo:table-column column-width="proportional-column-width(1)" background-color="green"/>
<fo:table-body>
<fo:table-row>
<fo:table-cell display-align="before"><fo:block>Some text to make this cell
@@ -194,17 +194,17 @@
</fo:table>
<fo:block space-before="12pt" space-after="6pt">
- The next table specifies inline-progression-dimension.minimum="10cm",
- inline-progression-dimension.maximum="17cm and
+ The next table specifies inline-progression-dimension.minimum=&quot;10cm&quot;,
+ inline-progression-dimension.maximum=&quot;17cm and
specifies a column width of 5cm on the middle column only.
</fo:block>
<!-- table start -->
- <fo:table table-layout="fixed" border-collapse="separate"
+ <fo:table table-layout="fixed" width="100%" border-collapse="separate"
inline-progression-dimension.minimum="10cm"
inline-progression-dimension.maximum="17cm">
- <fo:table-column background-color="yellow"/>
+ <fo:table-column column-width="proportional-column-width(1)" background-color="yellow"/>
<fo:table-column column-width="5cm" background-color="blue"/>
- <fo:table-column background-color="green"/>
+ <fo:table-column column-width="proportional-column-width(1)" background-color="green"/>
<fo:table-body>
<fo:table-row>
<fo:table-cell display-align="before"><fo:block>Some text to make this cell
diff --git a/examples/fo/build.xml b/examples/fo/build.xml
index 2c7c18259..de83b945c 100644
--- a/examples/fo/build.xml
+++ b/examples/fo/build.xml
@@ -127,6 +127,7 @@
</target>
<!-- =================================================================== -->
<!-- Compares new test pdf files to reference pdf files -->
+ <!-- N.B. All comparisons fail due to differences in /CreationDate -->
<!-- =================================================================== -->
<target name="comparePDF" depends="newPDF">
<compare referenceDirectory="${referenceDir}" testDirectory="${testDir}" filenames="normal.pdf,table.pdf,list.pdf,link.pdf,border.pdf,images.pdf,extensive.pdf,readme.pdf,fonts.pdf,bordershorthand.pdf,character.pdf,corresprop.pdf,hyphen.pdf,inhprop.pdf,instream.pdf,leader.pdf,newlinktest.pdf,normalex.pdf,pdfoutline.pdf,simple.pdf,textdeko.pdf,tableunits.pdf"/>
@@ -144,6 +145,7 @@
<!-- =================================================================== -->
<!-- Starts the test -->
+ <!-- N.B. All tests fail due to differences in /CreationDate -->
<!-- =================================================================== -->
<target name="runtest" depends="comparePDF">
<echo message="Running Fop tests"/>
@@ -154,6 +156,9 @@
<target name="clean" depends="init">
<delete dir="${testDir}"/>
</target>
+ <target name="cleantest" depends="init">
+ <delete dir="${referenceDir}"/>
+ </target>
</project>
<!-- End of file -->
diff --git a/examples/fo/svg/embedding.fo b/examples/fo/svg/embedding.fo
index dbcac747b..a4af1675e 100644
--- a/examples/fo/svg/embedding.fo
+++ b/examples/fo/svg/embedding.fo
@@ -794,7 +794,7 @@ XML Syntax
<fo:block>
Here we have some examples of how the XML can be specified in the fo document.
<fo:block>
- <fo:table>
+ <fo:table table-layout="fixed" width="100%">
<fo:table-column column-width="350pt"/>
<fo:table-column column-width="150pt"/>
<fo:table-body>
@@ -932,7 +932,7 @@ Sizing
The size of the instream-foreign-object is obtained in a number of ways.
<fo:block>
- <fo:table xmlns:svg="http://www.w3.org/2000/svg">
+ <fo:table xmlns:svg="http://www.w3.org/2000/svg" table-layout="fixed" width="100%">
<fo:table-column column-width="350pt"/>
<fo:table-column column-width="150pt"/>
<fo:table-body>
diff --git a/examples/fo/tables/background.fo b/examples/fo/tables/background.fo
index fe2cf128d..e516348da 100644
--- a/examples/fo/tables/background.fo
+++ b/examples/fo/tables/background.fo
@@ -30,7 +30,7 @@
Table 1: cell background
</fo:block>
- <fo:table>
+ <fo:table table-layout="fixed" width="100%">
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
@@ -72,7 +72,7 @@
Table 1: row background
</fo:block>
- <fo:table>
+ <fo:table table-layout="fixed" width="100%">
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
@@ -152,7 +152,7 @@
Table 1: column background
</fo:block>
- <fo:table>
+ <fo:table table-layout="fixed" width="100%">
<fo:table-column column-width="3cm" background-color="green"/>
<fo:table-column column-width="3cm" background-color="red"/>
<fo:table-column column-width="3cm" background-color="blue"/>
@@ -219,7 +219,7 @@
Table 1: column backgrounds over page
</fo:block>
- <fo:table>
+ <fo:table table-layout="fixed" width="100%">
<fo:table-column column-width="3cm" background-color="green"/>
<fo:table-column column-width="3cm" background-color="red"/>
<fo:table-column column-width="3cm" background-color="blue"/>
@@ -286,7 +286,7 @@
Table 1: body background
</fo:block>
- <fo:table>
+ <fo:table table-layout="fixed" width="100%">
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
@@ -323,7 +323,7 @@
</fo:table-body>
</fo:table>
- <fo:table>
+ <fo:table table-layout="fixed" width="100%">
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
@@ -360,7 +360,7 @@
</fo:table-body>
</fo:table>
- <fo:table>
+ <fo:table table-layout="fixed" width="100%">
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
@@ -397,7 +397,7 @@
</fo:table-body>
</fo:table>
- <fo:table>
+ <fo:table table-layout="fixed" width="100%">
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
@@ -438,7 +438,7 @@
Table 1: table background
</fo:block>
- <fo:table background-color="green">
+ <fo:table background-color="green" table-layout="fixed" width="100%">
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
@@ -475,7 +475,7 @@
</fo:table-body>
</fo:table>
- <fo:table background-color="red">
+ <fo:table background-color="red" table-layout="fixed" width="100%">
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
@@ -516,7 +516,7 @@
Table 1: combinations
</fo:block>
- <fo:table background-color="green">
+ <fo:table background-color="green" table-layout="fixed" width="100%">
<fo:table-column column-width="2.5cm"/>
<fo:table-column column-width="2.5cm" background-color="green"/>
<fo:table-column column-width="2.5cm" background-color="red"/>
diff --git a/examples/fo/tables/borders.fo b/examples/fo/tables/borders.fo
index a38a6b148..f6704ef61 100644
--- a/examples/fo/tables/borders.fo
+++ b/examples/fo/tables/borders.fo
@@ -30,7 +30,7 @@
Table 1: cell borders
</fo:block>
- <fo:table border-collapse="separate">
+ <fo:table border-collapse="separate" table-layout="fixed" width="100%">
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
@@ -186,7 +186,7 @@
Table 2: row borders
</fo:block>
- <fo:table border-collapse="collapse">
+ <fo:table border-collapse="collapse" table-layout="fixed" width="100%">
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
@@ -310,7 +310,7 @@
Table 3: column borders
</fo:block>
- <fo:table border-collapse="collapse">
+ <fo:table border-collapse="collapse" table-layout="fixed" width="100%">
<fo:table-column column-width="3cm" border-left-color="green" border-left-width="0.5pt" border-left-style="solid"/>
<fo:table-column column-width="3cm" border-top-color="red" border-top-width="0.5pt" border-top-style="solid"/>
<fo:table-column column-width="3cm" border-right-color="blue" border-right-width="0.5pt" border-right-style="solid"/>
@@ -377,7 +377,7 @@
Table 4: column borders over page
</fo:block>
- <fo:table border-collapse="collapse">
+ <fo:table border-collapse="collapse" table-layout="fixed" width="100%">
<fo:table-column column-width="3cm" border-left-color="green" border-left-width="0.5pt" border-left-style="solid"/>
<fo:table-column column-width="3cm" border-top-color="red" border-top-width="0.5pt" border-top-style="solid"/>
<fo:table-column column-width="3cm" border-right-color="blue" border-right-width="0.5pt" border-right-style="solid"/>
@@ -444,7 +444,7 @@
Table 5: body borders
</fo:block>
- <fo:table border-collapse="separate">
+ <fo:table border-collapse="separate" table-layout="fixed" width="100%">
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
@@ -480,7 +480,7 @@
</fo:table-body>
</fo:table>
- <fo:table border-collapse="separate">
+ <fo:table border-collapse="separate" table-layout="fixed" width="100%">
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
@@ -516,7 +516,7 @@
</fo:table-body>
</fo:table>
- <fo:table border-collapse="separate">
+ <fo:table border-collapse="separate" table-layout="fixed" width="100%">
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
@@ -552,7 +552,7 @@
</fo:table-body>
</fo:table>
- <fo:table border-collapse="separate">
+ <fo:table border-collapse="separate" table-layout="fixed" width="100%">
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
@@ -588,7 +588,7 @@
</fo:table-body>
</fo:table>
- <fo:table border-collapse="separate">
+ <fo:table border-collapse="separate" table-layout="fixed" width="100%">
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
@@ -628,8 +628,7 @@
Table 6: table borders
</fo:block>
- <fo:table border-left-color="green" border-left-width="0.5pt" border-left-style="solid"
- border-collapse="separate">
+ <fo:table border-left-color="green" border-left-width="0.5pt" border-left-style="solid" border-collapse="separate" table-layout="fixed" width="100%">
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
@@ -665,7 +664,7 @@
</fo:table-body>
</fo:table>
- <fo:table border-top-color="red" border-top-width="0.5pt" border-top-style="solid" border-collapse="separate">
+ <fo:table border-top-color="red" border-top-width="0.5pt" border-top-style="solid" border-collapse="separate" table-layout="fixed" width="100%">
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
@@ -701,7 +700,7 @@
</fo:table-body>
</fo:table>
- <fo:table border-right-color="blue" border-right-width="0.5pt" border-right-style="solid" border-collapse="separate">
+ <fo:table border-right-color="blue" border-right-width="0.5pt" border-right-style="solid" border-collapse="separate" table-layout="fixed" width="100%">
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
@@ -737,7 +736,7 @@
</fo:table-body>
</fo:table>
- <fo:table border-bottom-color="yellow" border-bottom-width="0.5pt" border-bottom-style="solid" border-collapse="separate">
+ <fo:table border-bottom-color="yellow" border-bottom-width="0.5pt" border-bottom-style="solid" border-collapse="separate" table-layout="fixed" width="100%">
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
@@ -773,7 +772,7 @@
</fo:table-body>
</fo:table>
- <fo:table border-color="orange" border-width="0.5pt" border-style="solid" border-collapse="separate">
+ <fo:table border-color="orange" border-width="0.5pt" border-style="solid" border-collapse="separate" table-layout="fixed" width="100%">
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
@@ -813,7 +812,7 @@
Table 7: combinations
</fo:block>
- <fo:table border-left-color="green" border-left-width="0.5pt" border-left-style="solid" border-collapse="collapse">
+ <fo:table border-left-color="green" border-left-width="0.5pt" border-left-style="solid" border-collapse="collapse" table-layout="fixed" width="100%">
<fo:table-column column-width="2.5cm"/>
<fo:table-column column-width="2.5cm" border-left-color="green" border-left-width="0.5pt" border-left-style="solid"/>
<fo:table-column column-width="2.5cm" border-top-color="red" border-top-width="0.5pt" border-top-style="solid"/>
@@ -1025,7 +1024,7 @@
implementation status.
</fo:block>
- <fo:table border-collapse="collapse" table-layout="fixed">
+ <fo:table border-collapse="collapse" table-layout="fixed" width="100%">
<fo:table-column column-width="3cm" border-color="blue" border-width="0.5pt" border-style="solid"/>
<fo:table-column column-width="3cm" border-color="blue" border-width="0.5pt" border-style="solid"/>
<fo:table-column column-width="3cm" border-color="blue" border-width="0.5pt" border-style="solid"/>
@@ -1090,7 +1089,7 @@
fo:table-cell level.
</fo:block>
- <fo:table border-collapse="collapse" table-layout="fixed">
+ <fo:table border-collapse="collapse" table-layout="fixed" width="100%">
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
@@ -1159,7 +1158,7 @@
Check the FOP compliance page for current implementation status.
</fo:block>
- <fo:table border-collapse="collapse" table-layout="fixed"
+ <fo:table border-collapse="collapse" table-layout="fixed" width="100%"
border-style="solid" border-width="5pt" border-color="yellow">
<fo:table-column column-width="3cm"
border-style="solid" border-width="3pt" border-color="black"/>
@@ -1264,7 +1263,7 @@
the border properties differ only on color.
</fo:block>
- <fo:table border-collapse="collapse" table-layout="fixed">
+ <fo:table border-collapse="collapse" table-layout="fixed" width="100%">
<fo:table-column column-width="3cm"
border-style="solid" border-width="3pt" border-color="black"/>
diff --git a/examples/fo/tables/break.fo b/examples/fo/tables/break.fo
index 81325c4ed..91559699d 100644
--- a/examples/fo/tables/break.fo
+++ b/examples/fo/tables/break.fo
@@ -34,7 +34,7 @@
Table 1: basic break after with next
</fo:block>
- <fo:table border-width="0.5pt" border-color="red">
+ <fo:table border-width="0.5pt" border-color="red" table-layout="fixed" width="100%">
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
@@ -81,7 +81,7 @@
Table 2: basic break before with next
</fo:block>
- <fo:table border-width="0.5pt" border-color="red">
+ <fo:table border-width="0.5pt" border-color="red" table-layout="fixed" width="100%">
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
@@ -128,7 +128,7 @@
Table 3: basic break before a keep with next
</fo:block>
- <fo:table border-width="0.5pt" border-color="red">
+ <fo:table border-width="0.5pt" border-color="red" table-layout="fixed" width="100%">
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
@@ -175,7 +175,7 @@
Table 4: basic break after a keep with previous
</fo:block>
- <fo:table border-width="0.5pt" border-color="red">
+ <fo:table border-width="0.5pt" border-color="red" table-layout="fixed" width="100%">
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
@@ -222,7 +222,7 @@
Table 5: basic break after a keep with previous
</fo:block>
- <fo:table border-width="0.5pt" border-color="red">
+ <fo:table border-width="0.5pt" border-color="red" table-layout="fixed" width="100%">
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
diff --git a/examples/fo/tables/headfoot.fo b/examples/fo/tables/headfoot.fo
index fd0a14590..4401a2c77 100644
--- a/examples/fo/tables/headfoot.fo
+++ b/examples/fo/tables/headfoot.fo
@@ -30,7 +30,7 @@
Table 1: with header
</fo:block>
- <fo:table>
+ <fo:table table-layout="fixed" width="100%">
<fo:table-column column-width="2cm"/>
<fo:table-column column-width="2cm"/>
<fo:table-column column-width="2cm"/>
@@ -311,7 +311,7 @@
Table 2: with footer
</fo:block>
- <fo:table>
+ <fo:table table-layout="fixed" width="100%">
<fo:table-column column-width="2cm"/>
<fo:table-column column-width="2cm"/>
<fo:table-column column-width="2cm"/>
@@ -592,7 +592,7 @@
Table 3: with header and footer and keeps
</fo:block>
- <fo:table>
+ <fo:table table-layout="fixed" width="100%">
<fo:table-column column-width="2cm"/>
<fo:table-column column-width="2cm"/>
<fo:table-column column-width="2cm"/>
@@ -888,7 +888,7 @@
Table 4: cells spanning columns
</fo:block>
- <fo:table>
+ <fo:table table-layout="fixed" width="100%">
<fo:table-column column-width="2cm"/>
<fo:table-column column-width="2cm"/>
<fo:table-column column-width="2cm"/>
@@ -940,7 +940,7 @@
Table 5: cells spanning rows
</fo:block>
- <fo:table>
+ <fo:table table-layout="fixed" width="100%">
<fo:table-column column-width="48pt"/>
<fo:table-column column-width="48pt"/>
<fo:table-column column-width="48pt"/>
@@ -1054,7 +1054,7 @@
Table 6: table with header and multiple body's
</fo:block>
- <fo:table>
+ <fo:table table-layout="fixed" width="100%">
<fo:table-column column-width="2cm"/>
<fo:table-column column-width="2cm"/>
<fo:table-column column-width="2cm"/>
@@ -1242,7 +1242,7 @@
Table 6: table with footer and multiple body's
</fo:block>
- <fo:table>
+ <fo:table table-layout="fixed" width="100%">
<fo:table-column column-width="2cm"/>
<fo:table-column column-width="2cm"/>
<fo:table-column column-width="2cm"/>
diff --git a/examples/fo/tables/keep.fo b/examples/fo/tables/keep.fo
index b369d7162..929ee760d 100644
--- a/examples/fo/tables/keep.fo
+++ b/examples/fo/tables/keep.fo
@@ -34,7 +34,7 @@
Table 1: basic keep with next
</fo:block>
- <fo:table border-width="0.5pt" border-color="red">
+ <fo:table border-width="0.5pt" border-color="red" table-layout="fixed" width="100%">
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
@@ -81,7 +81,7 @@
Table 1: basic keep with previous
</fo:block>
- <fo:table border-width="0.5pt" border-color="red">
+ <fo:table border-width="0.5pt" border-color="red" table-layout="fixed" width="100%">
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
@@ -128,7 +128,7 @@
Table 1: basic keep with next and keep with previous
</fo:block>
- <fo:table border-width="0.5pt" border-color="red">
+ <fo:table border-width="0.5pt" border-color="red" table-layout="fixed" width="100%">
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
@@ -192,7 +192,7 @@
Table 1: basic multiple keep with next after normal row
</fo:block>
- <fo:table border-width="0.5pt" border-color="red">
+ <fo:table border-width="0.5pt" border-color="red" table-layout="fixed" width="100%">
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
@@ -324,7 +324,7 @@
Table 1: basic multiple keep (next and previous) after normal row
</fo:block>
- <fo:table border-width="0.5pt" border-color="red">
+ <fo:table border-width="0.5pt" border-color="red" table-layout="fixed" width="100%">
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="6cm"/>
@@ -456,7 +456,7 @@
Table 1: basic multiple keep with next after normal row with normal row in middle
</fo:block>
- <fo:table border-width="0.5pt" border-color="red">
+ <fo:table border-width="0.5pt" border-color="red" table-layout="fixed" width="100%">
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="6cm"/>
diff --git a/examples/fo/tables/omit.fo b/examples/fo/tables/omit.fo
index 042f557ed..875afe77e 100644
--- a/examples/fo/tables/omit.fo
+++ b/examples/fo/tables/omit.fo
@@ -30,7 +30,7 @@
Table 1: with header
</fo:block>
- <fo:table table-omit-header-at-break="true">
+ <fo:table table-omit-header-at-break="true" table-layout="fixed" width="100%">
<fo:table-column column-width="2cm"/>
<fo:table-column column-width="2cm"/>
<fo:table-column column-width="2cm"/>
@@ -311,7 +311,7 @@
Table 2: with footer
</fo:block>
- <fo:table table-omit-footer-at-break="true">
+ <fo:table table-omit-footer-at-break="true" table-layout="fixed" width="100%">
<fo:table-column column-width="2cm"/>
<fo:table-column column-width="2cm"/>
<fo:table-column column-width="2cm"/>
@@ -592,7 +592,7 @@
Table 3: with header and footer and keeps
</fo:block>
- <fo:table table-omit-header-at-break="true" table-omit-footer-at-break="true">
+ <fo:table table-omit-header-at-break="true" table-omit-footer-at-break="true" table-layout="fixed" width="100%">
<fo:table-column column-width="2cm"/>
<fo:table-column column-width="2cm"/>
<fo:table-column column-width="2cm"/>
@@ -888,7 +888,7 @@
Table 6: table with multiple body's
</fo:block>
- <fo:table table-omit-header-at-break="true">
+ <fo:table table-omit-header-at-break="true" table-layout="fixed" width="100%">
<fo:table-column column-width="2cm"/>
<fo:table-column column-width="2cm"/>
<fo:table-column column-width="2cm"/>
@@ -1076,7 +1076,7 @@
Table 6: table with multiple body's
</fo:block>
- <fo:table table-omit-footer-at-break="true">
+ <fo:table table-omit-footer-at-break="true" table-layout="fixed" width="100%">
<fo:table-column column-width="2cm"/>
<fo:table-column column-width="2cm"/>
<fo:table-column column-width="2cm"/>
diff --git a/examples/fo/tables/space.fo b/examples/fo/tables/space.fo
index c0562d8f9..e409720bb 100644
--- a/examples/fo/tables/space.fo
+++ b/examples/fo/tables/space.fo
@@ -30,7 +30,7 @@
Table 1: spaces around cells
</fo:block>
- <fo:table border-style="solid" border-width="0.5pt" border-color="red">
+ <fo:table border-style="solid" border-width="0.5pt" border-color="red" table-layout="fixed" width="100%">
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
@@ -109,7 +109,7 @@
Table 2: spaces around rows
</fo:block>
- <fo:table border-style="solid" border-width="0.5pt" border-color="red">
+ <fo:table border-style="solid" border-width="0.5pt" border-color="red" table-layout="fixed" width="100%">
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
@@ -190,7 +190,7 @@
Table 3: spaces around body
</fo:block>
- <fo:table border-style="solid" border-width="0.5pt" border-color="red">
+ <fo:table border-style="solid" border-width="0.5pt" border-color="red" table-layout="fixed" width="100%">
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
@@ -281,7 +281,7 @@
</fo:block>
<fo:block border-style="solid" border-width="0.5pt">
- <fo:table space-before.optimum="5pt" border-style="solid" border-width="0.5pt" border-color="red">
+ <fo:table space-before.optimum="5pt" border-style="solid" border-width="0.5pt" border-color="red" table-layout="fixed" width="100%">
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
@@ -309,7 +309,7 @@
</fo:block>
<fo:block border-style="solid" border-width="0.5pt">
- <fo:table padding-left="5pt" border-style="solid" border-width="0.5pt" border-color="red">
+ <fo:table padding-left="5pt" border-style="solid" border-width="0.5pt" border-color="red" table-layout="fixed" width="100%">
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
@@ -337,7 +337,7 @@
</fo:block>
<fo:block border-style="solid" border-width="0.5pt">
- <fo:table padding-right="5pt" border-style="solid" border-width="0.5pt" border-color="red">
+ <fo:table padding-right="5pt" border-style="solid" border-width="0.5pt" border-color="red" table-layout="fixed" width="100%">
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
@@ -365,7 +365,7 @@
</fo:block>
<fo:block border-style="solid" border-width="0.5pt">
- <fo:table space-after.optimum="5pt" border-style="solid" border-width="0.5pt" border-color="red">
+ <fo:table space-after.optimum="5pt" border-style="solid" border-width="0.5pt" border-color="red" table-layout="fixed" width="100%">
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
@@ -397,7 +397,7 @@
</fo:block>
<fo:block border-style="solid" border-width="0.5pt">
- <fo:table space-after.optimum="20pt" border-style="solid" border-width="0.5pt" border-color="red">
+ <fo:table space-after.optimum="20pt" border-style="solid" border-width="0.5pt" border-color="red" table-layout="fixed" width="100%">
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
@@ -462,7 +462,7 @@
</fo:block>
<fo:block border-style="solid" border-width="0.5pt">
- <fo:table space-after.optimum="5pt" border-style="solid" border-width="0.5pt" border-color="red">
+ <fo:table space-after.optimum="5pt" border-style="solid" border-width="0.5pt" border-color="red" table-layout="fixed" width="100%">
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
<fo:table-column column-width="3cm"/>
diff --git a/findbugs-exclude.xml b/findbugs-exclude.xml
index 46879d1f6..a764eac41 100644
--- a/findbugs-exclude.xml
+++ b/findbugs-exclude.xml
@@ -1,6 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<FindBugsFilter>
<Match>
+ <Class name="org.apache.fop.fonts.truetype.TTFFile$1"/>
+ <Bug pattern="SIC_INNER_SHOULD_BE_STATIC_ANON"/>
+ </Match>
+ <Match>
+ <Class name="org.apache.fop.fonts.truetype.FontFileReader"/>
+ <Method name="getAllBytes"/>
+ <Bug pattern="EI_EXPOSE_REP"/>
+ </Match>
+ <Match>
<Class name="org.apache.fop.fo.properties.FontFamilyProperty"/>
<Bug pattern="EQ_DOESNT_OVERRIDE_EQUALS"/>
</Match>
@@ -950,11 +959,6 @@
<Bug pattern="RV_EXCEPTION_NOT_THROWN"/>
</Match>
<Match>
- <Class name="org.apache.fop.hyphenation.HyphenationTreeCache"/>
- <Method name="constructUserKey"/>
- <Bug pattern="RV_RETURN_VALUE_IGNORED"/>
- </Match>
- <Match>
<Class name="org.apache.fop.afp.AFPResourceLevel"/>
<Method name="equals"/>
<Bug pattern="SA_FIELD_SELF_COMPARISON"/>
@@ -5162,4 +5166,41 @@
<Method name="getNonEmptyLevels"/>
<Bug pattern="PZLA_PREFER_ZERO_LENGTH_ARRAYS"/>
</Match>
+ <Match>
+ <Class name="org.apache.fop.render.pdf.AbstractImageAdapter"/>
+ <Method name="populateXObjectDictionaryForIndexColorModel"/>
+ <Bug pattern="OS_OPEN_STREAM"/>
+ </Match>
+ <Match>
+ <Class name="org.apache.fop.render.pdf.ImageRawPNGAdapter"/>
+ <Or>
+ <Method name="outputContents"/>
+ <Method name="setup"/>
+ </Or>
+ <Or>
+ <Bug pattern="OS_OPEN_STREAM"/>
+ <Bug pattern="OS_OPEN_STREAM_EXCEPTION_PATH"/>
+ </Or>
+ </Match>
+ <Match>
+ <Class name="org.apache.fop.render.ps.ImageEncoderPNG"/>
+ <Method name="writeTo"/>
+ <Bug pattern="OS_OPEN_STREAM"/>
+ </Match>
+ <Match>
+ <Or>
+ <Class name="org.apache.fop.render.pdf.PDFImageHandlerRawPNG"/>
+ <Class name="org.apache.fop.render.ps.PSImageHandlerRawPNG"/>
+ </Or>
+ <Method name="getSupportedImageFlavors"/>
+ <Bug pattern="EI_EXPOSE_REP"/>
+ </Match>
+ <Match>
+ <Class name="org.apache.fop.render.ps.PSImageHandlerRawPNG"/>
+ <Or>
+ <Method name="handleImage"/>
+ <Method name="generateForm"/>
+ </Or>
+ <Bug pattern="BC_UNCONFIRMED_CAST"/>
+ </Match>
</FindBugsFilter>
diff --git a/lib/xmlgraphics-commons-1.5svn.jar b/lib/xmlgraphics-commons-1.5svn.jar
index d8c5e0989..be61b0b6e 100644
--- a/lib/xmlgraphics-commons-1.5svn.jar
+++ b/lib/xmlgraphics-commons-1.5svn.jar
Binary files differ
diff --git a/src/documentation/content/xdocs/examples.xml b/src/documentation/content/xdocs/examples.xml
index f64a0d702..6565c1b4e 100644
--- a/src/documentation/content/xdocs/examples.xml
+++ b/src/documentation/content/xdocs/examples.xml
@@ -39,7 +39,7 @@
<tr>
<td>default font characters</td>
<td><link href="fo/fonts.fo">fonts.fo</link></td>
- <td><link href="fo/fonts.fo.pdf">fonts.fo.pdf</link></td>
+ <td><link href="fo/fonts.pdf">fonts.pdf</link></td>
</tr>
</table>
<p>Other basic examples on the use of XSL-FO can be found in the FOP distribution in
@@ -100,17 +100,17 @@ Embedding images in FO:
<tr>
<td>align in larger viewport</td>
<td><link href="fo/align.fo">align.fo</link></td>
- <td><link href="fo/align.fo.pdf">align.fo.pdf</link></td>
+ <td><link href="fo/align.pdf">align.pdf</link></td>
</tr>
<tr>
<td>align in smaller viewport</td>
<td><link href="fo/align2.fo">align2.fo</link></td>
- <td><link href="fo/align2.fo.pdf">align2.fo.pdf</link></td>
+ <td><link href="fo/align2.pdf">align2.pdf</link></td>
</tr>
<tr>
<td>scaling image</td>
<td><link href="fo/size.fo">size.fo</link></td>
- <td><link href="fo/size.fo.pdf">size.fo.pdf</link></td>
+ <td><link href="fo/size.pdf">size.pdf</link></td>
</tr>
</table>
<p>Look also into the directory examples/fo/svg. There you find some very extensive SVG examples.
@@ -132,7 +132,7 @@ Instream Foreign Object images in FO, there are more on the
<tr>
<td>embedding svg in viewport</td>
<td><link href="fo/embed.fo">embed.fo</link></td>
- <td><link href="fo/embed.fo.pdf">embed.fo.pdf</link></td>
+ <td><link href="fo/embed.pdf">embed.pdf</link></td>
</tr>
</table>
</section>
diff --git a/src/documentation/content/xdocs/fo/align.fo b/src/documentation/content/xdocs/fo/align.fo
index ee0cc8827..06e6a2929 100644
--- a/src/documentation/content/xdocs/fo/align.fo
+++ b/src/documentation/content/xdocs/fo/align.fo
@@ -19,19 +19,18 @@
<fo:root font-family="Times Roman" font-size="12pt" text-align="center" xmlns:fo="http://www.w3.org/1999/XSL/Format">
<fo:layout-master-set>
- <fo:simple-page-master
- margin-right="1.5cm"
- margin-left="1.5cm"
- margin-bottom="2cm"
- margin-top="1cm"
- page-width="21cm"
- page-height="29.7cm"
- master-name="left">
- <fo:region-before extent="1cm"/>
- <fo:region-body margin-top="1cm"/>
- <fo:region-after extent="1.5cm"/>
- </fo:simple-page-master>
-
+ <fo:simple-page-master
+ margin-right="1.5cm"
+ margin-left="1.5cm"
+ margin-bottom="2cm"
+ margin-top="1cm"
+ page-width="21cm"
+ page-height="29.7cm"
+ master-name="left">
+ <fo:region-body margin-top="1cm"/>
+ <fo:region-before extent="1cm"/>
+ <fo:region-after extent="1.5cm"/>
+ </fo:simple-page-master>
</fo:layout-master-set>
<fo:page-sequence id="N2528" master-reference="left">
diff --git a/src/documentation/content/xdocs/fo/align.fo.pdf b/src/documentation/content/xdocs/fo/align.fo.pdf
deleted file mode 100644
index 9e5393e1e..000000000
--- a/src/documentation/content/xdocs/fo/align.fo.pdf
+++ /dev/null
Binary files differ
diff --git a/src/documentation/content/xdocs/fo/align.pdf b/src/documentation/content/xdocs/fo/align.pdf
new file mode 100644
index 000000000..4b9413b1c
--- /dev/null
+++ b/src/documentation/content/xdocs/fo/align.pdf
Binary files differ
diff --git a/src/documentation/content/xdocs/fo/align2.fo b/src/documentation/content/xdocs/fo/align2.fo
index d16553139..64449dbb3 100644
--- a/src/documentation/content/xdocs/fo/align2.fo
+++ b/src/documentation/content/xdocs/fo/align2.fo
@@ -19,19 +19,18 @@
<fo:root font-family="Times Roman" font-size="12pt" text-align="center" xmlns:fo="http://www.w3.org/1999/XSL/Format">
<fo:layout-master-set>
- <fo:simple-page-master
- margin-right="1.5cm"
- margin-left="1.5cm"
- margin-bottom="2cm"
- margin-top="1cm"
- page-width="21cm"
- page-height="29.7cm"
- master-name="left">
- <fo:region-before extent="1cm"/>
- <fo:region-body margin-top="1cm"/>
- <fo:region-after extent="1.5cm"/>
- </fo:simple-page-master>
-
+ <fo:simple-page-master
+ margin-right="1.5cm"
+ margin-left="1.5cm"
+ margin-bottom="2cm"
+ margin-top="1cm"
+ page-width="21cm"
+ page-height="29.7cm"
+ master-name="left">
+ <fo:region-body margin-top="1cm"/>
+ <fo:region-before extent="1cm"/>
+ <fo:region-after extent="1.5cm"/>
+ </fo:simple-page-master>
</fo:layout-master-set>
<fo:page-sequence id="N2528" master-reference="left">
@@ -59,7 +58,7 @@ Default align:
<fo:block>
Default align:
(<fo:external-graphic width="50pt" height="50pt" overflow="hidden" src="images/fop.jpg"/>), start-before
-(<fo:external-graphic width="50pt" height="50pt" overflow="hidden" text-align="start" display-align="start" src="images/fop.jpg"/>), start-center
+(<fo:external-graphic width="50pt" height="50pt" overflow="hidden" text-align="start" display-align="before" src="images/fop.jpg"/>), start-center
(<fo:external-graphic width="50pt" height="50pt" overflow="hidden" text-align="start" display-align="center" src="images/fop.jpg"/>), start-after
(<fo:external-graphic width="50pt" height="50pt" overflow="hidden" text-align="start" display-align="after" src="images/fop.jpg"/>), center-before
(<fo:external-graphic width="50pt" height="50pt" overflow="hidden" text-align="center" display-align="before" src="images/fop.jpg"/>), center-after
diff --git a/src/documentation/content/xdocs/fo/align2.fo.pdf b/src/documentation/content/xdocs/fo/align2.fo.pdf
deleted file mode 100644
index f5a02666b..000000000
--- a/src/documentation/content/xdocs/fo/align2.fo.pdf
+++ /dev/null
Binary files differ
diff --git a/src/documentation/content/xdocs/fo/align2.pdf b/src/documentation/content/xdocs/fo/align2.pdf
new file mode 100644
index 000000000..01b8d1bfb
--- /dev/null
+++ b/src/documentation/content/xdocs/fo/align2.pdf
Binary files differ
diff --git a/src/documentation/content/xdocs/fo/build.xml b/src/documentation/content/xdocs/fo/build.xml
new file mode 100644
index 000000000..4b4a099b4
--- /dev/null
+++ b/src/documentation/content/xdocs/fo/build.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0"?>
+<!--
+ 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.
+-->
+<!-- =========================================================================== -->
+<project default="newPDF" basedir=".">
+ <!-- =================================================================== -->
+ <!-- Initialization target -->
+ <!-- =================================================================== -->
+ <target name="init">
+ <tstamp/>
+ <mkdir dir="tests"/>
+ <property name="testDir" value="tests"/>
+ <property name="foDir" value="."/>
+ <path id="run-classpath">
+ <fileset dir="../../../../../lib">
+ <include name="*.jar"/>
+ </fileset>
+ <pathelement location="../../../../../build/fop.jar"/>
+ </path>
+ <taskdef name="fop" classname="org.apache.fop.tools.anttasks.Fop" classpathref="run-classpath"/>
+ </target>
+ <!-- =================================================================== -->
+ <!-- Help on usage -->
+ <!-- =================================================================== -->
+ <target name="usage">
+ <echo message="Use '-projecthelp' instead"/>
+ </target>
+ <!-- =================================================================== -->
+ <!-- Produces new test files (function) -->
+ <!-- =================================================================== -->
+ <target name="newTestFiles">
+ <fop format="${mimetype}" outdir="${outDir}" messagelevel="${msglevel}" relativebase="true" throwexceptions="false">
+ <fileset dir=".">
+ <include name="**/*.fo"/>
+ </fileset>
+ </fop>
+ </target>
+ <!-- =================================================================== -->
+ <!-- Produces new test PDF files -->
+ <!-- =================================================================== -->
+ <target name="newPDF" depends="init" description="Creates a new set of PDF test files">
+ <antcall target="newTestFiles">
+ <param name="mimetype" value="application/pdf"/>
+ <param name="msglevel" value="warn"/>
+ <param name="outDir" value="${testDir}"/>
+ </antcall>
+ </target>
+ <!-- =================================================================== -->
+ <!-- Clean targets -->
+ <!-- =================================================================== -->
+ <target name="clean" depends="init">
+ <delete dir="${testDir}"/>
+ </target>
+
+</project>
+<!-- End of file -->
diff --git a/src/documentation/content/xdocs/fo/embed.fo b/src/documentation/content/xdocs/fo/embed.fo
index 558e9d397..6b052e0bf 100644
--- a/src/documentation/content/xdocs/fo/embed.fo
+++ b/src/documentation/content/xdocs/fo/embed.fo
@@ -23,19 +23,18 @@
<fo:root font-family="Times Roman" font-size="12pt" xmlns:fo="http://www.w3.org/1999/XSL/Format">
<fo:layout-master-set>
- <fo:simple-page-master
- margin-right="1.5cm"
- margin-left="1.5cm"
- margin-bottom="2cm"
- margin-top="1cm"
- page-width="21cm"
- page-height="29.7cm"
- master-name="left">
- <fo:region-before extent="1cm"/>
- <fo:region-body margin-top="1cm"/>
- <fo:region-after extent="1.5cm"/>
- </fo:simple-page-master>
-
+ <fo:simple-page-master
+ margin-right="1.5cm"
+ margin-left="1.5cm"
+ margin-bottom="2cm"
+ margin-top="1cm"
+ page-width="21cm"
+ page-height="29.7cm"
+ master-name="left">
+ <fo:region-body margin-top="1cm"/>
+ <fo:region-before extent="1cm"/>
+ <fo:region-after extent="1.5cm"/>
+ </fo:simple-page-master>
</fo:layout-master-set>
<fo:page-sequence id="N2528" master-reference="left">
diff --git a/src/documentation/content/xdocs/fo/embed.fo.pdf b/src/documentation/content/xdocs/fo/embed.fo.pdf
deleted file mode 100644
index 6da06b559..000000000
--- a/src/documentation/content/xdocs/fo/embed.fo.pdf
+++ /dev/null
@@ -1,87 +0,0 @@
-%PDF-1.3
-%ª«¬­
-4 0 obj
-<< /Type /Info
-/Producer (FOP 0.20.5) >>
-endobj
-5 0 obj
-<< /Length 1556 /Filter [ /ASCII85Decode /FlateDecode ]
- >>
-stream
-Gb"0W9p=9h(^KQB5D>011Kd^2(:&!s-8.0G?92NC6am<+$Zp"a8L^3ZrE?Z%8B:u_OFn)DOGr-/INIQ=q\oZh2p:3YpSJW^j5k;[E4:*BGe#CoakN"mYH!sFZ8>$).@Y@g/-!,+\,3geIX]sgn&ks8h-P:I5CJ>(?M<peVjI(+?b[S@pZ8%/ns@)HU2Lio<0CtCPX[Q)8_(<FftM:sit[&L*Ob:OKat[.EkI%ON)unFn92KJrG.J!cH<PB*A9Y6q24X,`S^&S[!UEIh+u"#V93'eZc#bITL93O^P^p,I3BHrmbEFVPF$aP[Jemg]0^JG[GBF4ZUN[?=KSW(*k#W!J+1'Bh;+g]["$p!rMJIh;uH?[:#+dG<U]5@$Jf'eIGN8iGJ0k>3srEp%+j`GFC2lVAW?-k]'A%*A+X0,Y&n>gXrgJRd8etuLIh.uop[SIXoW1(Me8(mZ+eI84[tkfD8Y2>AOP5_.60qf-b].hK50R#%EJ8&ck"-ed6),XDAZ4I/+.NABKpXVTecSBnJhE/-BIl,-QFP0P>7hC<B]QN&C.,Y8Gd*8%[k^9Hi4&$B)L$>kMsi)STHZs%$'J5nT--!YC%F5o`L74Wr:Be]r#sCZ+lgS/E:AD3,3jV\ej=Ch)Mm&T3L9Qof&akqNaE.ePBeD$+%h=PJ*s!3>n&C]igN#KOB]'j8H"aks*5=iHX3ZW>2HPS[b]*`)73_Q%NVce\!_DKr]/-Y/42K3d4X3_6^:.XnTm.M/Ws*C)U,oQ,$R7M5;IQWWpQNXEeNq$<R<Z?j3W^FmcIHJnJLtn"7q,lIV18P-__e<Fgr_6rPiH7SM(LJ5hU;;F)d'"fZPjPmA)PrUM!Qp%*M$jflJgibHB!cmc"N9.VAd;)(V]?<k_^!EQuC=90p["fud#Pn6%clf97o>-MaM&m21cZ'7XGeK>`6"O7>T<ko&E!@qO]XE0/^&3L?dC3.eQYbEX_<h1cDLEnJS=-O,Fo?4qC$r@,-5S^4!<M2Bs<$Qsaan'ESI[F:IQD.j]$*GaN/1&\A3Cq0H&Y'R;YM^/.JlhFK?K]UL7.;t8P=rd#Au7HmY>plj8g:-IOGkatB8+,OGQk;/'tf.M\ap^O]Zf]N/IY_K;_Cf\L-lRiL(u"ei;CH2-j*X=.qf*gTG=iZSQ6qr)cg<j)q4H%YZ.\Z,10D%M5;J,.L4)Jb&CAe^;<tH$=DaBI%@mQK]&)u;bP"C=,]);Y<#<---<LF5r/0+2(Jf:i@B;0YOE:@cJ;PgE8YMeBD+'tMJH7H>BtH@(6&KA0C:ed<NAL]hd<_.;BdP%K4MpV]JSDt]5SSS_HjZ5GBXA0<7Li@R\gm\NFJc%&ogoS@O[.ub!@ekBUfe=Y.td^!a":e>j%+t+a=UNaVEi.YkDdNJud0091bqd)C/fC``XJC$=&-4E!<D[FmcaPJcf8u<fEkZ?OjPcl:rlH2j2]h[<`2[_>YWO/1#n25qkWC<X.PM>_ic-+YL^(]=:7:C?abAqh?bqD4b-,1\n_j+*T5NVnRDAT6?<U@WcP17PIi#^4hB/rspg7gYM~>
-endstream
-endobj
-6 0 obj
-<< /Type /Page
-/Parent 1 0 R
-/MediaBox [ 0 0 595 842 ]
-/Resources 3 0 R
-/Contents 5 0 R
->>
-endobj
-7 0 obj
-<< /Length 1817 /Filter [ /ASCII85Decode /FlateDecode ]
- >>
-stream
-Gb"0W:N+uG(^BKA5)#"'Yd"$U8imem#OF-PR"6:/*k]utN6sEG3%4Xj7nFsp;3J?+4m;Cb/S%)3?CFKB?VSYnqb?ePK?>&Z1uIXURQk@7VtS,NabaJ,Y-fH"AbkJUs)n(bDSLTWQi$]3.r%#RgR?#YrF>m&j5#._.Gt&lrOg7dfGe=DCLO9.JlJ#\Nu:_+'9XObCm7%pIqEiC#]3r(6ZXV,rHP^:2:!="7[p5gQd%aYoh4"?CMW%pVqf;,\4sMKf<>Y[[oqT?]$9s%n,K_VArek#gUbF!FjDL<f/M_nI7j[N(Sm.]Wjj^^X.[)1g9opE<O[0BX3jCRC#>/(H\Zt^D8g"tMc@0c>I<YL)PBJq<Yb-Gl^BiRqaf,5=oU6pl&g!lMKY/W;-W=o:>%?@\t`PT_@NNSUM//U^7dnFB>]k,gsGT'8p>p-/_"t]4N`X)^SA/n>4O8[WK$DZi:"X[8@`0iGF;K/oBJDZ9tRQ^T20MuX/In(9BX/odV^W3rKV8qhu&C&.Ui@aH`nog^">K.8!\<CbO;jA8pD*MA_f>SZl<4uYB;@Nk0tuRbZQ^4Zl`RW%CQc6l9V>:g\X^LPnhTI(tXiC5P1&O>dih=]W7P0+tFD=.[%`'UfWeAOpP4=dVor(_H")Q=5iGu?005^2='PfY.j8J=(&-[];:<l9bJ,&#hDj."`%!AYQ@k@$<R<Z?sPjq69;_j@GTJ+k>`7n,`KdDas8\bkm\B&A4"hR1fT1/:_fus<N*KP<MaYXc)UX7DLX_9k$aN$,J&\><CpXd;FK$3aTS\ZBtt'X!@qO]XFPH\YU\Vu)St.eR@`EV"d+89V$UU:P#AMhgD3`r/kZfjOs20H@)CZo%(HJMi"k+">B%2tDAdYA`K1Sda):WQ;.YHi:?tGNSg5X.4ecbW9V!N#'S<B)'s>DR\-c<hmODkpY$"i\dtAg/Ar1e%#oi@`P/N$La-;XJaci0JKto3`"g?i!]RnbCo.$QBCOt*6.:L?"KM>*j=-Y$X,X&#FPY<*N9aZGQn-WL<.sM6$CZe\5cE7=W(ZpJ.6/"Y,KQRN)PJi,BKk(lkffCYd>!XZP"g+F4['#tNb0soB]CI].`JJ$#/^9_Q\FLn"+mCRiUL`Z"CJkGNJlhFK?GS.:>s#6[Hb1&@?()XK^4M2BZ*,oS9LsJ(`@./D2(]8Ei@B;0YOE:@Y2*0Q]NkoZ/kuj+,'5q+XHLMT,b>rMPKCr*@3P+I?GS.:"g?j,mCZ'Ym@*'X=+=PH)to%WGsV)l\>r8sj/sOC"]p_r\of,GA;.+H's^0W[EIir]<[S6Z`l&V$F><sVH>)o:D+>(*`=mgV@L0aKjZEIKrqQo[_gV'gWGqjlRK%F1W!Bq+pYQ6,V^J,-0:n4_Uc(NCk]!q't1t[fXc61"a>,!D']\<G9=2?le#JE2Yh5Y,q?c(\&BGM>B?a8_H-VhA(.Qi,aB[Mh<MIJrU<u4p8@NPg4a!]HUU%C"C_!=2A_5k-70un,]&]=]dZ\`!H,[^\,ePH#2F!L4!5Am3d=S.rtet1DeYBZ)9"mCOX)8!]qp+@9YE#Q$:.bW$=0>UFG6l5f<tDUW7J+S>o.&e<C*#%OfU<09Lr])D44.H_C$c8=Wo;+Krg@NZE@KaJDf0nF]6C[:nh/r=d&F?+,c5>K),#!I2T6r<l$7f"fl$RmAr(?41i![XCmel?e:;s#lD"fc\IkNRhp:0Qc#fjKD<PeD8,\(cFYa5L\!.0])1S''m`&lNLkFP?K+K&a,A18gZeADJ+r!>R/Z're+oUP&g(8R_^!@;&u7($?83Z%nS$eZO5XGIU386~>
-endstream
-endobj
-8 0 obj
-<< /Type /Page
-/Parent 1 0 R
-/MediaBox [ 0 0 595 842 ]
-/Resources 3 0 R
-/Contents 7 0 R
->>
-endobj
-9 0 obj
-<< /Type /Font
-/Subtype /Type1
-/Name /F5
-/BaseFont /Times-Roman
-/Encoding /WinAnsiEncoding >>
-endobj
-10 0 obj
-<< /Type /Font
-/Subtype /Type1
-/Name /F7
-/BaseFont /Times-Bold
-/Encoding /WinAnsiEncoding >>
-endobj
-1 0 obj
-<< /Type /Pages
-/Count 2
-/Kids [6 0 R 8 0 R ] >>
-endobj
-2 0 obj
-<< /Type /Catalog
-/Pages 1 0 R
- >>
-endobj
-3 0 obj
-<<
-/Font << /F5 9 0 R /F7 10 0 R >>
-/ProcSet [ /PDF /ImageC /Text ] >>
-endobj
-xref
-0 11
-0000000000 65535 f
-0000004058 00000 n
-0000004122 00000 n
-0000004172 00000 n
-0000000015 00000 n
-0000000071 00000 n
-0000001719 00000 n
-0000001825 00000 n
-0000003734 00000 n
-0000003840 00000 n
-0000003949 00000 n
-trailer
-<<
-/Size 11
-/Root 2 0 R
-/Info 4 0 R
->>
-startxref
-4261
-%%EOF
diff --git a/src/documentation/content/xdocs/fo/embed.pdf b/src/documentation/content/xdocs/fo/embed.pdf
new file mode 100644
index 000000000..59dec7763
--- /dev/null
+++ b/src/documentation/content/xdocs/fo/embed.pdf
Binary files differ
diff --git a/src/documentation/content/xdocs/fo/fonts.fo b/src/documentation/content/xdocs/fo/fonts.fo
index 12e176eba..0c2646d05 100644
--- a/src/documentation/content/xdocs/fo/fonts.fo
+++ b/src/documentation/content/xdocs/fo/fonts.fo
@@ -18,32 +18,32 @@
<!-- $Id$ -->
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
- <!-- defines the layout master -->
- <fo:layout-master-set>
- <fo:simple-page-master master-name="first"
- page-height="29.7cm"
- page-width="21cm"
- margin-top="1cm"
- margin-bottom="2cm"
- margin-left="2.5cm"
- margin-right="2.5cm">
- <fo:region-body margin-top="1cm"/>
- <fo:region-before extent="1cm"/>
- <fo:region-after extent="1.5cm"/>
- </fo:simple-page-master>
- </fo:layout-master-set>
-
- <!-- starts actual layout -->
- <fo:page-sequence master-reference="first">
+<!-- defines the layout master -->
+<fo:layout-master-set>
+ <fo:simple-page-master
+ master-name="first"
+ page-height="29.7cm"
+ page-width="21cm"
+ margin-top="1cm"
+ margin-bottom="2cm"
+ margin-left="2.5cm"
+ margin-right="2.5cm">
+ <fo:region-body margin-top="1cm"/>
+ <fo:region-before extent="1cm"/>
+ <fo:region-after extent="1.5cm"/>
+ </fo:simple-page-master>
+</fo:layout-master-set>
+<!-- starts actual layout -->
+<fo:page-sequence master-reference="first">
<fo:flow flow-name="xsl-region-body">
- <fo:block font-family="Helvetica" font-size="14pt">
+<fo:block font-family="Helvetica" font-size="14pt">
Helvetica
</fo:block>
- <fo:block space-after.optimum="10pt" font-family="Helvetica" font-size="10pt">
-<fo:table>
+<fo:block space-after.optimum="10pt" font-family="Helvetica" font-size="10pt">
+<fo:table table-layout="fixed" width="100%">
<fo:table-column column-width="65pt"/>
<fo:table-column column-width="30pt"/>
<fo:table-column column-width="65pt"/>
@@ -303,7 +303,7 @@ Helvetica
Times Roman
</fo:block>
<fo:block space-after.optimum="10pt" font-family="Times Roman" font-size="10pt">
-<fo:table>
+<fo:table table-layout="fixed" width="100%">
<fo:table-column column-width="65pt"/>
<fo:table-column column-width="30pt"/>
<fo:table-column column-width="65pt"/>
@@ -563,7 +563,7 @@ Times Roman
Courier
</fo:block>
<fo:block space-after.optimum="10pt" font-family="Courier" font-size="10pt">
-<fo:table>
+<fo:table table-layout="fixed" width="100%">
<fo:table-column column-width="65pt"/>
<fo:table-column column-width="30pt"/>
<fo:table-column column-width="65pt"/>
@@ -823,7 +823,7 @@ Courier
ZapfDingbats:
</fo:block>
<fo:block space-after.optimum="10pt" font-family="ZapfDingbats" font-size="10pt">
-<fo:table>
+<fo:table table-layout="fixed" width="100%">
<fo:table-column column-width="65pt"/>
<fo:table-column column-width="30pt"/>
<fo:table-column column-width="65pt"/>
@@ -1071,7 +1071,7 @@ Courier
Symbol:
</fo:block>
<fo:block space-after.optimum="10pt" font-family="Symbol" font-size="10pt">
-<fo:table>
+<fo:table table-layout="fixed" width="100%">
<fo:table-column column-width="65pt"/>
<fo:table-column column-width="30pt"/>
<fo:table-column column-width="65pt"/>
diff --git a/src/documentation/content/xdocs/fo/fonts.fo.pdf b/src/documentation/content/xdocs/fo/fonts.fo.pdf
deleted file mode 100644
index 85a37fd0e..000000000
--- a/src/documentation/content/xdocs/fo/fonts.fo.pdf
+++ /dev/null
@@ -1,160 +0,0 @@
-%PDF-1.4
-%ª«¬­
-4 0 obj
-<< /Type /Info
-/Producer (FOP 0.20.5) >>
-endobj
-5 0 obj
-<< /Length 2756 /Filter [ /ASCII85Decode /FlateDecode ]
- >>
-stream
-Gas2O=aO+:'LgpN.p>dQQ0),tXf?8Xl8sh^A"4[?L]L?S.Yp(`G)J$07;CF[q0Xk,m[_:;p.hg?\%DUPMj%;2G1eG?n*DpXrcNRk=kqghM^JC4(9q#F6<M1iQEg+@rM.>kF&rk?5<4+=n+NmTMjoY'X2c"#Z%_Ip=kXFJF_B#_[H@&U`<@2^l+]E+l1pZ!Cop\)PH?hg\+B/,61.dsGk/JYH2/>>gUQl'^O&$qIgP0_PeXi&QbNt"`g=GeR$XG9Q\Vt!J+koUIg+m`>Q4&kH259:cpnuO)YE2Y+(Xr3H2XcdDe8Lq]_jXK4.1I\07P#'<[L&Tb:eD$aQ1Beq%DbJ9R`Fs?_!J[FZFa!bf^reYKF[C8A"tK/EcFDnJ:3"S@aEl4-6r5g`7ZS"h?/HmND$@jL1SjK>\"VRr[p,&g'EraP7s/X*K!&QFTB@f-"GBg`c-Q5TXJ(OS?p@pG-s6q\pYrAaYZS2aQkBmZ@1P5]10$OS?4MDT]N!OS<rahNW)"+n(nG\QbDAD^"QiaC@7)(@p=74Hso*+n#NSI6%$;nihZ</a(tWhC"DsEpbO?rEYLhR60L.$HS4N*V01Na;^O/jSeAVgob2UOSAK8*bFc$pCBM1AF>QR%mfVpmZ@1(5i0Z=+n$M*gm)sTOS;O9DTfS;aSVkX2s&gXjSdfFgoS]0HcZ(s+CCCTpQ5,tON2D0*qK<@aA)011uohL?]\T5a@"t%,e`]/LWFs6H3#3#RK&`J6:;k]J"?4`6lo6U"8'`(*b'V<5R'PD*eJm*oKktfP5c!UKo.u\s'*nZ('VeYGGH$-$5!2@U_b]lOT+]?04Yb>rjPHPBG;Th5Cd(n-pReQP$[&p5QTX`^!KW,!k:mL_4pu6n0@1ONrg$24jj]\"Xnnn82#W1TE'Ef]c^4'!k;$P@AEo!56AMb;Ln[0+MCTLJG_VW]jO`kr;T*0iM6bTs-<3X8eLXo)sN'@,38b9klm=rjSpt!Kp<kCs*)m!/4IY['@86]7EGKNU_d9nOT25S(LDu\s$'(.^_@slJ!"&n.74%6-G:O#K`(j;H=<ZVq>ZfR!_j9tru^KZRPp8B"5E*hD.fR'["/K0$b`fV$_aI-UbGs8SLn%O6#@a#a[0o_fg7*%"'J;5H>gtB6;ebYA$XIN>IG*3g-?jGL#1XkKb2dh:@6Wubr&]2<GgsRDE[B@P19c2R4N#)Pccp+c=B4fTnKhI=6S6$?J$t3QI?$nQ7R-Vfg7Z6"+OpHKaotE#\nMOQhDO.a?jR@_ls@dC:4]3l7hQjLRKfJ\N1.Gd%TkY'Y?C7H_@&^G]aksTOH.^_Hn7;4j[L`IWZM$5^T1-em[2Um=dl/LLH7N(Wp/,L-<sk.#N-ap5"&=1tH*f,OL0d=U0G<P6L?-10KB\&SWm4b<#!J8:.%@el,qOQN1P>E7Rrp\;']Dq5gL.E"22g,$Icji>UDXV6lij_DSh>!bsmkKkk?TPgt*F$eNa3.'E6m(TsIF:h&ai0:\`L:h]0o0@Hn"Z4Lp>W@sd8]hih:W@sd8]hj%@W@sd8]hj7FW@sdX]b#qaW@sd8]hj[RW@sd8]hjk2YV.QpKu2VqI:6hCPii8;$VPi?82jp`=P=.Z\cM]FdU"?S%G2J_p-RGM^qR%_(P6i5p4]?V:qRaO0-'A5in]-5iFjkYmfr+d7sgqS2dfuPg'bm3!W0a]?7=Y>fGBSA6Les/^2UFqMK]0rKql=X]Gn+/>1sA']+Oq73^.'^_7L<VU,kIt5Q;eYLj`,@+0pUYh2$JV*?VS@i8'3;;&paaL#CdCI8M",ZA%5mLj`,@+0pUYh2$JV*?TG?MK^<=7<O"!(4p`Tp-j+Oa;0AO,PFQKI)m(TG9cfMFaV7GJp/I5Kr;U\]Gn+/Wr#5n&A/se0<4n<[Lg*@YV3$R7CEc?S$4QGqo-iInSZ4XW9tJT6L\m0^8UH9fg>1G_S2u:(YM.EDSj,dmMFPCn8?+WW9tJT6L\m0^8UH9fg>1G_S2u:(YM.EDSj,dmMFPCn8?+WW9tH`$cT^aqkCZ/A.-5c$2^I.?XsP\]*\A/\mJtU_7L$NU:O]B$c0F]qkCZ/A.-5c`mBo4#I^A5Ca+Cr9^pb<]k#;5Jp<9KV]=,\K%hH1QV?oF>.oUFR4.3oYOU=J]l;.A!d_rkDd4#rAeRJDA:dmcl;_LU&_LmC)Fn&gG-Zt_?=WM*\ZH,`RgAN:`o*FfYa%ocq2u'@V(:?D-3AV;"@l]oF>a`t9."o3XZ1W>U>VA3n>KP3n$7eC?S]I^<&gID"TFPjDJTL;fgP<_RGc(c=28Y#lCM+ZK'J/Y9q:VXI>B>?d%2Wg-54TuF^`?<7e_R"'9mqY1/STV;<<,"NE$Zd-Fg>23A6jih%P+u5?.`Q"PFI+s'tZ2ioFb/G)!V!4POK$Y.#KpgV7rSpuo9+[>.8]*b5XST6W59[<2(ShO[$rp'3YX&jGo!9J#:K?4NTQ1JK:ZDI*gFH!C/Dh9N([,B'Wk<;UquIH&M:l7fE=&%pRn*TtuLH+3^sZV8W]GGf(pF_i,55<YL:F5&mJ>NtD*>T)>jdG.>hO64UG[SQ$do'7Ebd;9uLXlME+0!j:rQWqS4kAp0"8VX^&cbDBrp\]/_g\:X//F^Y+,M^#r5@]95q9'I6W/ag5ns;XcclT*uj'U*;K*"(#%KCI=*L+Xhqu+(VAY::H&:ETgJWBA$n<HcS3^TI0EoiL9NmK8R*;O-<,P9,m2nj=*,;dSX-baVo,F$B.$fAJ/pHP_$<5J~>
-endstream
-endobj
-6 0 obj
-<< /Type /Page
-/Parent 1 0 R
-/MediaBox [ 0 0 595 842 ]
-/Resources 3 0 R
-/Contents 5 0 R
->>
-endobj
-7 0 obj
-<< /Length 2935 /Filter [ /ASCII85Decode /FlateDecode ]
- >>
-stream
-Gas2O=-jma'SPBB.u%SN_RLo"U`"2C^gpR<OtetcAE%Mt#"GslqY'^#I]5BohPsK0gZn=qd^@Wo9XjJ6B:iGoG+W(r].X%:]@cK]+m`ZsHPb]0m-EK^="`BQb8q_=\pMg#gP@?jrVt48.p$r`m*;b8p^g'q&b&j&msU$7gA(OFhY/=DCr>]H\8rsDC;V+n\@!W1DSmP!\1o:WBuBZFn%'.G[c,iln8L?&E7_MV*Pg^/KUXmT!'HLWF1)Oe!;E>VS86"__#%#PY&D4)J@e^%cF;aRi3lUU$Z1+g\1(G@@N-?hhNb*^i:oYYmZ440*#SY\n0k:=^HVkj+K>OY57DKaWWU"#!q9Mq#C_.:i(d5VIX_da<`B<QIiRCD<*^#`IfeP6WOa/K5.';IYuG!>INJT>-rnlOh^<r0;KY.tI0R27<S)l\(P)7Vl?_ss$?0(HXp;^+$#j+KXp;^+"`RD?Xp;^+%Id+l#CaGEL:a4Z%fH;7^^m/#nGBNlO.n])qf;c3U,qf@o)S5JFl<!1!:EJ0H1:]h!4Sfi4S\k$J=BGZ*rQ!m^q3Ke*rQ"0)fC_5l9+k?%,cn=FWUJ[Wk_?ubu3pp2t%;a9!no_]M!JueK0tH*oWqq#E[7.KU5j_5W^&!?@$_onU*lP.FnOLo+BrX=U1&7a+2cV*C)hJ#VY!p^k)"^iMBca3&*TC]X6u)8&f1pQ[0\1bkR^td]W`m)IF%^$?gquj%I6^_[^X%Hpr:ZfE(pM2;klP#OqaPKU7iB_#N:)?<VLTs*mO`WS%m]s#o>;B1MIk9u9QNNo9pZ#VSn6K)^<G]N"8(rr7iEd^:=ps(B,_foG"j?O/<%4+dKN&ROaG"TSKoGj3rpr;R_pV/Sros6kW-`Z;nqIh5p9G6IsG8U0LT&cDYFiMCW,i4j?fl[%\]TuD?Y$RQ3dd:<S5hZLAB;<XGq;b-p2^kdh6=#HC49)&Bm9GG(@aV7Xk#I%^4d$7>&O>9"M,85d6D)-eZnpr^,l#3;X;EiI5ep>i"'_;@,R4(TB>0[@lB;-MSBL*`UTSZ!X@7,D34eaQ\l69.t9cm4K6$8;=UoBERMkIZ&'N3LPe4!>BA.%Se,O-JQ/i&,[p(;EZ<>V&i>5O,eLoQA,+iSjt>t!+pG4uTS4&M(l[(_S4hhcr,TacI(#+$`/(L#fG-unFQ,@+uDTBI87*U,nkr']_(lUViGjsN2[>XuBP69ijXP2_HLUsi,((TnugBW[mT03h#Yct5L>?FZ#;U#g`t]PrUIU&BG7]PrUITbW'O]PrUITe1bg]PrUITgaI*]PrUITj</B]L[f2;+TN8-nh)6?"i8j-nh)6?"iJp0eY*B6@8`kH!s8\>5`*X$VS(Xp-3%n=OMm5!&Nbk-!f^C0OnEkdY3\^1ZjB[?-"9F@pW9cKh_\*k(P$ckThil?9$Ae:'*66Kd0me,VW_737<LU!]/tm,@0LY0OnEKdY1Et1ZjB9?0EOf@pV.EKhMOUk!`(Wcq.W]]VsuB9HfV8JpC5<WQqu^oa=Bm)JLs\9EGSoWu>\-KhMOU8uikE15/C'$VJUCZnd>/!XhWDX0aX99EGSobH&!*KhMOu8h6jI15/C'.nV=jZq_;r$*5PW5e><6@)\lhO=kVf=RddHU^N^r<]l8>;)_Fp,d?9*</nJW67.6uO.pC=bnMo>XEYFZUMcrj8RTN4W#Be7KhMOuk!_/mSg\[n=>;J/;)_Fp,d:`a<24?.O@`j!]Vn<U9HfV8MKr'Y\sYl3JJIdN)Kdii9ELcYf?NKe.e8uqAq<@%$d7gWcQk7TFGAYCVOn"A+nIrC_$+`,G"s;D&Tic9pkaYFXNgs*"P4gICm%A#.S(hp&?-"0-BYj31Iq?X=)3=[,*P<*Z,#%mb[qKU%;]ZR-WMhG,VgAc&mT9Tbrh32I#png='Q2>R7&BT7"X1.C?.F<k_tToE;s#,Gqt4M$&Je744WDX8RtRe,cK@1G0;-d.3oO*Cpbt>,VgmGs#<J6[I;3_1+aaaN.Pg)P=_i*etRAg@5O/b<NO/^GI7Ip,b6ENDB5W;+iW`rO@W3Qe`J6C*)s1b'/`2ZP!i0Ki<Pi"gM<kC-0+uG+oarT\p'C!_V"a8q5/([](u/]Ri-@mfD$)G:-[dJ5K3#^+a!Nmq!Xi!aT(6&:JkY`)n9b&mf[m`7IN-Rf7r/dnBUKNgq`eFr9cJu*;r'i%G\GOpAk,QIZ`AT(Y\B%!0;SYgci=%L>NC.1c:`hUH>jAo^.?(^#!Y!h%B/Gm=e,PlaICCB]J(+$^2&e^O:?:)\1b3R$:3Oqs^I@*59*?-L;8tLGu^$nEkqo_Z%&nG"Wbahd.;_hJAh>g.&uX(PfN0Rg8\+#1dJ_o&BAS@e8@pfUY4=LjQm^C_#6\05i.Q4?2I\_FrU-YfuEdQalSDR5YC)H/l^sDZX5Ia+J$],.o1]#Lg=[G_mCNXuS]o!XTkX^7i0<)EJpu6[)HdF?EZ\m\Xaa">CAB:"@l&?lutLWjreY5]eqbK."H[8U")ZGljM&F>['m-bt"!2cD#L!k9=*VO$0S_#/L32(s;&Hj7sS\ZN<(R6pIW!;RoJ>'t)gNM+(.>AY`DKr!'$,"r]XpspQ+6&1;K?AN81N?H2.5qbV_N^f+?U7`;H)Fu)Dnq)cZRRnaFnq)cZ)G;;Go7Dl[RS4sV3PL<EiA=H-j,5mc:EpiU(L4o+L`562]&4cmUsqr5']6fjTGldV]&4KeACZKE_7+C+\/I(PE&4JM[?/@4FU'[#87F/]0j!1^bS64B$X(\RG9A\q,h>9tF4D;P'87da65FXm*QUntM7&Ga>]g9,;b1@LA2.0AJ]ngk=:88j?PmNe/S$Sb@Ao$jON7-MTdJ>W-K>nsRTKs.N*dE:@P)+HIUT13[F12]0+9/3<]ZB=IA%q;8d@,d/sO:X0eE*:@@:4JAjnnDQa9>7"16kd=o~>
-endstream
-endobj
-8 0 obj
-<< /Type /Page
-/Parent 1 0 R
-/MediaBox [ 0 0 595 842 ]
-/Resources 3 0 R
-/Contents 7 0 R
->>
-endobj
-9 0 obj
-<< /Length 3803 /Filter [ /ASCII85Decode /FlateDecode ]
- >>
-stream
-Gat=/BpTgY&cAe=0fD3@gQ_!gWbr-%H[Bq?nSK$`Y[E-$#ltYUY`6udmM]f;-7cEtQ,Vbc]Vh).W&c.ZU3c[qlIOe/ZuagNh:9DIG5(3dgY).YgLY0?P]f'"1YKFQHEqn\p30j>.YR\CFtY4iXLN]Ir1DrnmbQ^L)koHS`^WiIf?5Be+%G9[;Eh1K&"l%F-?rF[f#n^T&#4u<Piq`1%*JN^&j<gG]mKtEDD[b&h6^KPJT<i@OWN>k?@Hh+4AD$Eo]RpYbO:)SEAEOqh6LAd$7O5a$2!HTlfG=%nhcb*O7%&%r.gqmR*7gVm]^$s)CUZ1alMZ[>qc3i-kB8P2^rt:jYJA>InG_RqXsCe;!tH*jQIf7qZ=p`..QB^,1-3.Pa=Uoo]Uh^0A"C&adRc%*ROZaP`OC+H?;F@YClE+^Zj,H^&Jq6-Yk&TF?o:Z!TeqiNZK4fq\u1j3ofJP!PSLIPQthF4Z]81T7M%ObWq4!kl'\;gc:S^d/gr\!gHo%7XB/An%W*\O-;@53P#7TrE#Y>oZ!#g)h?dp%Ec"9SfHPdo_oe+T"GBjkQ[^S2aR8tbs7;,!.)B]jjBPcNt1CP/QV;fYj+srOPGViT%T;12"qj;1OkR<kWV?+bb-5S\rT]OF!q@9mLrRG/4ouTH\1ON7nGsj:Jj82.1%Ek;c(eG8d<`@%+R/'P(E?+MX$U*aZt6GUO$NO1/QjEW1H#7%-7./Wb5?Y"9&0-eMFCl_8?.4XDc?U"rm:YW1LPb%.sQ7Wb6K$"9&65eMFt'Jc>ZQlFC?F5l^jRous[OT`>%bH=g;8d/SSm4nC3Bkl6.2Ssbl^kjR4qLEMt(R=PJKKLZTt]Z`Wp"bOeM$Gd.p%"q_*Ggri,bf%^jMZn^Qh$UuWbREQ7+F)s16#\V9"%<t2*d]C?1Ggm!jg?0]JmbHf#.c>sW%YZcR=R1&"R6A4DJ#Qe!eL3")k7cOZp=,pn7hh*AcY!]3`pj$'nY,/"j&;tI[h.)3Lp>q/6I@k'nVjE"j&;tI[h.)q4.]2/6I.ebjm;UB#/MU<=%`5(*p4o\]cN+1Hl20=`G5XJpX*naXfF$(Yo!@87o]3^>KK75r&fXo`[9?$^+Y3W%YXk>*-s[6[ceQciR6VJO"Y"h9Z53@=^[l_%"cn$upiKIDJ%n*mP-(qh!Lq4e*<0p'#M,HT3Z@l3hKip2F>_e/-m^mGY_IW%YYLg;A3p;.":/["n7iUV>YUBb0B\7t&45dN6\lJGOj:V*1=b!VHUT8p`NN!pg;,PkK'&"P=O7..-!+#cKDd;;/p5&PuhRUU5gJ,+u[.7VNCr76l:;NRB,C(D6<D2bKEU?3hKW+dc-<K_%O_pb8dV"s'@7'7V2@Jf1Be<,1DgUd?c1FFZtXr/"AYAs<d%WL5u5$Gh\L!@ftu-W2+T;*$t=B)"c];*$t=B!0Vj_2QeC.Q):DSfkO?3^BNRe/-mg*M?:dl3hKo%T',Bo`[9HetL:DCG1'1"^tH2JAAZFL0k*mB$%W;VM5o$YC\I?<'Xjh(X(_FdMaIuclGp`JHs@L;ZT0Iie;NU+NuYW&C`"4bju1YCm/uTI)I3TSNs])Z6&Em06VaI@FiP)#Q$pAQ3tLQ;ZVFc,Jpri&8%OA#^jS-O&cg(TF(t;;\4trJhRR45iAbrLqsgZRA^/gT8kV6'nk8=!WkGa=PF2"N)Th/*@p]&"^tH3r5IVC&97IJ_Tc$\JYPI5iX3Qh;uqR(&RN/&0LJi*i$Fi%+K-F,;AH1jnVB5lS#QMk4&uLE$GpUZ!J)7Agi<Q)hMh)7*Br+jr9k]^c:.?kG=DG3?eO[h*1l3(rSY#;qo;JSIIQ1<0CI4k]36/NTp`2MgLm):o6Bm,E^GQ`_A1J:JRNpn_)V^`L5+0$F&Is8.QC'C;U\N'6T6C3GY,_S#&T_GcIgB50tsLB/7(9a##4ehcWLt!0tsdJ%,&/;r&n!Qhm:-qE;IBZ[c,HdS.t"q$;MD]QlF>US!.;dhc>hB2F+U>\k+?jopln_qVla_UKuq5HFp,lpn!)D?oPM>\aIa[0YXaK9\G)B&hSFT@%:^D5>Z<b/We^Y[.!.Q(b[C!A(_jh,jITuMI@3hA]rKB>W<6WSCTHS]*cgr4N:tG3SQ'M#/_oMm::T#M-TcZD(sb'3SfqEcqGY8]g3SGYM^B*1Gsg+F`5V2,:GNm1r2&>h&E`;L^MF:2B*I)h9aD<0a7eo8Q3'c6`tcg2Af$%)5\`!(XDFXg0eFpVp?\KF3&KBcj!uZE4XQS2&4F)"km?X$;MPbd*A>qGMY4p$7A(6k-h]pG->]hEW=":@nH.V&F*Sn']PAo1T&9iFp)rk:($OV#34qa#;RD*p"8l4D+$X8;aW#I;Gb]Q8kLh@1o)<>"ngfSU_Mi@%B7E>5s>#n.E2(aL6RIh*7UaeP#@7rBh1Zb$L6;G8L_[J25DEC"nhAcU_Mi")H)]]JpR9=Q_.neMXe-0<90jJSq!#i@_AHY'fd]Joa(2-I.$)F`q?`oPp`CJe75_g;N,fWU`q8lFbD`FDhISOL/_][T["*=dUsGL@f0(j.`,(_LPV.+)N=DJU`qBZFc*,n']Xb!<5bW3NY.P@@XOgl'mV6O0@U3d:C9u;Qt#eVD(tsj+Vfh_Ft_>Lj"D<K&Xd,&6>\CtC.Ld*MVSZJ_0Db!'qm("0@U3d6Og"c9sK&=2AhK4U`q_9mY0A&I&%o#\LD\Te-=rC%4Mf__pU:/8!>pP-ONb6S7;4RV%?E:PWUu9F2s/`I^TsMWIe#YVqA[T.E&,'Pc#THam>8A@j/@IQV6C@nsd9S5,G&]0pS)7OIt*Bo'uo`g9I-6@Z]L-Dt<H1Q'-I@<*kGJI`0"C.pb+[FW4CgMEu^e^WFZo<;[ZI'SCccYfY);<(q%j<d?,`iSD'cXLpYl`ME3AQ+)&/jG0iV_TL%X<W!arC>M,L6).d!X("u=[1dHtWsZ.eQNsSDYG2;sn7qSGZ;p7`)\#9qe`^)QX4TY/)iVY7dB?)C0r^^JjeWfJ=/0+7Z=6_$\&q>u$Elq612#_Jk=[N:Y=?8^C?>HX9/-dW7_^]qjmM<d?sNT1dB<s`\bl1\(kl%d?^\I1g7JUoF59VH$Elq613R6:7Ba?<U[W$j/!F%8?[Egfd*XR,(kl%d?Q$S`g8S.[MG\UUqN^k,X7*df3E(mRX3n,0?s2mMO1\!/hI%%(Q%$^<X0LDd2&PCYat6p3`5Z\A73Tb8QWpQN2:gDsXsaGK@WRH-`3HT%>#>RrOlmYKl)0:lO\P*I)=>T$/FmN'-EhZf25qs6Zo>Jc9d2ZKca:eEhJ#^L$!DPC7!AP8C6M`3esb)%o([:Oj.pE'g%igj9j[@%CJmsLb1e2IoB0/D]]/&(Un3\@S=;oir^L[e=1mFZe._DoH^p17,!1;,[E0q*ETo,qlJK'BAEU/42L38GY$!n3.rUWmcC^28n]DYRZ[,06GhYW0rpFTd=1mgeeRPoU2MitQNIL!DY$%;\.rUWm6Ib4#^@7TA$+07]M(D&SIcWYp=e/A=Dj/7/eEBuJp6sM<V=HO$/8X/S<aLp0Ap$G>]M0RXRl?"c=!Y)^A,kET2Miu$h@Q9'AKmU*p?uZmk(tX*h@N^oOiZ[FrHlH:,dOY"6WeV&,+C[fD`M1N'3Wu#+fr=#Oj+pDpP-.KA7>(aYk!Z>2L"NFRr`H[=kLA1XLoa>boQ\*>Y/I7o7jL?MF;X8]a*:[(,g*r.^)BR(YsGB9k7](VQTKI7!?tk#?`8*]W"!_MF;X8]a!4Z[WtYm@MOadU3d3TM/;dLmeg?H94Sjp/$M`YooAJ2lIPt9%SlFX~>
-endstream
-endobj
-10 0 obj
-<< /Type /Page
-/Parent 1 0 R
-/MediaBox [ 0 0 595 842 ]
-/Resources 3 0 R
-/Contents 9 0 R
->>
-endobj
-11 0 obj
-<< /Length 3625 /Filter [ /ASCII85Decode /FlateDecode ]
- >>
-stream
-Gat=pl'!AO&]MNR?dW8Ah#PVEWCj1r\[H]o!-@0DJ8h!q!>kkWF(9:im::-ZV))$pbtDN\D8a(oQWhOZ:F5](rlJr"nIVTP*U\FKA3.Bt,T\</(CtWqCFP$BQ`t?"E<!c5mAHS'jeGf8m2O@]YGSnZh`Q>cg#8L>rL7GbR:UPuQr7%f``GA"MrkOnAsl"=ML"An:6X*#._o:eW*(n)KJR,*(SgLXJ2tX]-cM$^VUTi#0[gg"WgLoQPT!&B)/E>e`QW4se9D!JV\2m,bh:7*)E3^5b;P,e<QrG*<#W4GF%p(I'XP<;;WA6peeiF*bj&e4NNWH.AICrXTecGa<bd!a@*YRMl;63i9W@PVJ=0&tFWGerXg^p_9n8YoYj+8kF>Kc5[fC$a+mM,\!b]s=7\fKjJ2tTQkW3hGAkJ'O4O.3_/Sr!.\bi;Qpb'?-mA0'0]72;imfCX16;_kR]DJMSpccJuQ8#:!Phl::C&@s=c8fFsLU+OA.ZGM[R'A1_'=P]/Rprob1TBn7"PUAi"Wgs(8rZ%Yd)LelnF/24CA\s#=s(0m*RK-&\5M??#h0ctjTl[<'Xs0edJ,3n[8(O`!P=%"f9[4KC&AEk2HFX65YGn3rXM140=KZO*bBh!V=>'!g6f-VoffA'p]uF/DJXl0$:CZ0`sC2KZ+@_/AR@2bj.?UanRDsn;]$uAre3TV6aeu*l#e1ANIN2"`fVjW;dlWSp^[FC/D1)/'cp\\nQuFQGd?>s1.pSkjp5p1?WPsA7VNDm+l(),'fZRW=)&:*,M^;2'fZ^[=)&:j7_"FJ.r]B9Y1+T^JoD[$<npD>Bb7`^\<KKF<c*Q23QKcU[8WYdj36;uXf_(P1KS<&G;RUhD%,/+O#(jR#'$>VDYD@V\A'h$C(Riugh-S&Kj-Z\V8at)$/mdhBIhm"R4C.N40"LgD9U`A2u4MtqnH$]\>)*qF5+C8(%1`V=(r4i<%"*g=L])`pGlRS#c%ZEOnIJ?\/"H,b`s*b7FlBmjAreTo/KAPiZd]tol;Rq7rd[_pF)71EKTqKg:7ZQdec(')Q0*ep"MO=%JDg+Po@S0eo,?h2V@1H=)&9_>/-'#gL-Yk>ik9l6Ip)@lS`tee\3X`AGnF#0)EUkqmr;Zp"T8QpjYE0noZr9laQD,EHD!dmH^]k+l()hD=7D(I9k-nop_<)(>KMq)tR%`pRimS=pVV-F[u4PRB(aAKq>dDF[t)&0N\._3iZ9*D1(.Op)EV1L/Jst=%Dq@\W*dS_L@-Jg=OkP]jbm>lgSc,H+IkqC0t%4onp;>Y<0UMK=T^fePX$6\ZN%s_L>u:BB/=CW*V41pq"*1(@M'4DX6W:cp'><pq"*1`pp]GW7CaU1s@Dm285*B([f'9]ZSj&ToWcfnTFK:pq3@$T^H<;>oMikjr.Ss>Xs4>H"AjhMpt>alY)es?cV3.HsIZQ,N]nO8'gM"=4__oHsIZY1GIGU3oW3sd<1uPIbf[Y,O=ZO$g351odVlsFi3"Z>iY.+lf^j*EAm]:'@I\uY9U3Fs'E^um+4tQ]X#83\9L6LY$\MB2r$o4Y46YANaNs@Y9UA\2pB%\bqW0tYARe+c<R>7>kS'@qJZ)ZaCLFaFh.Fl>9A@amYC8G55;M:^RDVGAed'Bri!B`(-V]<@MD!0&8NT8/;TiD1*B2C<rc^':FLia-WUY3;W4q^ENV?#F>bfX7&9kpF4-r";aL^WWGm.j+NYj?l/t.YA:TmOVe6!MU<b6iI&B7nKHpY<'u'/s0s<G[C#V4NlX=7^7cB7*Z)3VYIIEQ8r\H$X!"bKsNI.Q455[j8aXFe+&Eab%W&hVE4oM@T'LlAP-`.fVW&hV%4ThEX)#9)\)!M]kWma&Z1E];D;5_)?doO@3W_R_=<i%M@'K_H:MmVWWdpNDKWA&@N40d-['LaC]CUm=[p%+`aS@=4Vd9Xq\_C0]r6jaBmW&_C%3sJ*P0ZnQ#0u^ARRZlR`6Rf):Ob1;(hq?gJPf!,R0lE-OOW\g@GJC:1"i+?]>/Db)Nk8LEKf9!@Thd(PNk8LEKeEJF\V)GJ:+)J@Lf33T!7BHbm-]p,!Ot6YL0SS#Ap8`($Gb2S89)(qRaX?_1jDKhnFkn\Q#`ED3PDabrNB'*]^n+d`_](>=kl@o"b&YnmADmZd$aG/Q^m[3%9eWm@s%:5TdK(:'WsL[o1s'X)P^g3$oN$[T^_S^W72ZTK4h,S49U@RcPa<+UG4e8cVP:$#R\m^L.[HQ8U,uYfL&KF!qGbPNLq=i'7:&mYF+qJXiY78cJS(eRsh13*;8XoP:DR>!ITWjpd7m1W\[Uh+K+18![9&ZSJ`n7hEmk^";)?VcO?'WpB]:0+G.H?R\<X>7gRU4+bIQXR\<VhFJEZ-g,)2ofLPC,V(oP*aA<P+#RXdML.[Gt,#FJ[VL3J=)5Zjr"DVBX6/d@;:oWt;!]A)UT^Hs&1u:Tm-6*WI]hXj4Y7RYS7g*t<;l0#"!,<_k6/Cj+Bcoq8D+Z"NH^uXGh]bl1PR-P\Vb.'%M.Eq*5oga5$C)OE6_GbY&_(5T=#+;Uj7`i/,US+(/Edfu2;Uir-8Z=ai<E@eT17iO!7^'V@QM`kj[#^GE]_<0-UKkqi5P=\M%9j4rJ4GEQ=u`G&pQSaeV'hjNqtQ`^+5qbl/kTt;]`XkB!-Pl'I-j(Qn(%MM]=TjhAaE>Vl6pfC/PacOe\2dgS9S>Z/Xr3)F$]apAIk,2ffk:hB:q=q$Q9<QUnB*??a2M)Q0qbPIYt('RT"cqcos`>i\"[o0TF'Nc`(%)T5(iF#L4e@dgttJ1EL5Mk&t;\Z1..Fhj7\`'kHFNE.LfQL2>0RqQr=D]7T'FHtjF@m47WL=/'CN6GoRY__V4cPNb4+>,FAD8md&@m481;M2L#I'tbf\;pB4j[p!A,2]Ru>/YR-k:9VK&/Ub\2Jf3H\a-'>pbW%0@>gfH@\knVWBagna]l]+(Fu?P)$"FC(.$as:hbG=h/)-rY.NL?NR9Bs1"8TDK#0O/Ti)!*Y$t<GESXqrBn4ftD"&W$SG&>NNe(WjO_j_8/tBWK2P`/nO$HG5+t+F'c4>9YT2llg>nn$fB]ZiXQ'=8iiD&6+DGI277T=SkOqOr.OSb`g-@o#!6^dQ<_!A$k/eKkj-EskoFcj3qJO,(HRfo%'Q_"FeS\TuH2484n=R'0U*cQ6'K+<l!#qYa%*tPmH"Q/uP/9>Y69>*u""0Mr=d.>s&7S_=T<?Z1+WI<qf+Y;U@5L,UWRWhq"6_H>gV&(cJ98BY_f_B%tF);!%]jIH!&$er<\KHg/8RNE#<m4g6^iD^f<Kk3RIO:>j.Y6^?07P\--Eu;&KtY<udKuutga7S(0-&cjNi,HV1NJ,A8RSC)kESeMPYVjn9jjgl9.'hE8mo$#&?\`8V>#lm"0P7(Bn=!*S%qYb(In[7WBjeQq_]P`Y4QUh4?TgQIH1"A9l&m[&4TpFI,;`Q)3%.(QLA(J_h\\)k+-:.f^OVSF%m.V>Ar>G6#$5uJXRQJ:rfBC6<+Lgco"Oe_hdXBpfFW@@0sNBBlb,2iV^`$S\p1HbJ[YL_hdV\6s$P@+>0sUC_S+s].'*q2LJT4F%m>nXCD]FYU55Nhl_fF7"o!8lml.0&9Vms\+`\t#E)-F\G~>
-endstream
-endobj
-12 0 obj
-<< /Type /Page
-/Parent 1 0 R
-/MediaBox [ 0 0 595 842 ]
-/Resources 3 0 R
-/Contents 11 0 R
->>
-endobj
-13 0 obj
-<< /Length 1495 /Filter [ /ASCII85Decode /FlateDecode ]
- >>
-stream
-Gat%e6#YO:&4Z-f'`nFNG*'!T7j=r);VSgQ,.A\Q*a85ECc>(H)@>c+9oufu4l[f^3$X(O#[b5&oKA>r%%k$GqigJlY&i1HDZA_7G/'ZZcLR%^U,=BQ0#lc#0m<H[m[)C@*kVlMn'AR`I8#%-l/T,<f5CW\/q\2c*8m>CdA_d(b8%&cB&YK_)\unqBH]F-n,X[*I"tZ40:U!a-N"5_4l;qbM!_NP-N")[*LSW7`:J^<HBZ!6R#(+\Tj^W,lkel>D1.Wm@IaGM-GE8W5[?1Q9OOe$THT_f$L#hF/JG8fi]nh"$ZQhaQ]Obr@J0\cpa5q@h).I"$!&?G4sCNePDM^9Z>T:gf(85tPqi(u!*G^a/GbI)&RS*c[`g`23cc/62G#TlLFP&3!%)>K/""i1/2NjtbWmLC_8H^nAsO<=JCu:`HGL$7bVJ3kJ4Y-L:,)3qR@.X+Nr[WT1F0e1c-.s'=bZ+WE3CTj\qGFDGL7[6)6XXN1'NEL'q1h-`XMuLZG9;H6o23op^)L$?XP9eUD[6Ig`fRk$k2NmTk24_!32<8Tl=Y3Td&MK!`dCY:q/AnBUSAlJLY5=.,5as[*1KP:h:(EKptGaQ5T-'W3QV^Nm5p+iu\E(XarCTbs>l&7A)/DfJGMlOhn%qZd5&Op4c:_eVK/K&a2h,2U,5H1B'b9arJ(e%5T!Tk.osq&[&DEju*W+XR1Sp[jKmuWsljLC*.A^_(3YQ>\+MkD:ijFg*1d5CKu=r]2p7?Y8!3om^'T_N/%[<M$-2lEMtf[WJl[]m0t:]PtNeI4p\[YY$?Q+GsJl3-^_M_*cII_M[JGnU[[-XQC&bs4GN@5(W`#FV1Q8&Mb9R/&CoKTjsmQB0"fqNXuM@_6Ae=7"`rW32fm0$M/9:cB!/EIju1hR4I%#o4^TKPb_6Q:1KPPi&Cdeg@sCeR+m4+d-PI.[<Kb0tpb6XS)C"3k!3-LGF2bTu<Fl#X5a>b3[(1WR#&h)+E4'6G5FT,\1*Y."<g)DDm&ZVU,Qh2i/'7:SGo^\BJEC!-#&dkn>U:7F/e.QI<p\6MhKO<.RXS3R'+M?5Q+>$nQj`Ap]tN0M<h;%qelCi^cX?1DKhG!a"q@t_Q+>$nG%h35;/`6";0rKVd.:8/_-FL1108eA:m:R?X$)*GYcQ/UjV^G1X"mb%#'43UcWo]P6d8so=*Y!kiP(GbdPHWHB4S19%-GET,BK\o<58CQ>lF=cg;,FnRB&FPRV1_"b_?!QF+n7'oqaTgQ"_oghL@=_*+r+@$_NifAV7Ot^Wp)H_-Ed3C>WDOm&tK&PeKH6rASe_D_N"$f?=uD5ul7Q>icpKKh_-eIRMmKW-YD)\]QjugT^^,i<nJ8M"F:+)pE0QIm;2an-t8LI_EDVb;V3TfULC&c^-bDFa[MV\$(jJ5Hauhrc!+-F`V\MF1f&Smm`'YB4qe0^:eg">W\K/^]/I>/0EO>qa\iMQcF<n#B.pAVmFE4MRRYdDYGM#!>>4h*r~>
-endstream
-endobj
-14 0 obj
-<< /Type /Page
-/Parent 1 0 R
-/MediaBox [ 0 0 595 842 ]
-/Resources 3 0 R
-/Contents 13 0 R
->>
-endobj
-15 0 obj
-<< /Type /Font
-/Subtype /Type1
-/Name /F14
-/BaseFont /ZapfDingbats >>
-endobj
-16 0 obj
-<< /Type /Font
-/Subtype /Type1
-/Name /F5
-/BaseFont /Times-Roman
-/Encoding /WinAnsiEncoding >>
-endobj
-17 0 obj
-<< /Type /Font
-/Subtype /Type1
-/Name /F13
-/BaseFont /Symbol >>
-endobj
-18 0 obj
-<< /Type /Font
-/Subtype /Type1
-/Name /F1
-/BaseFont /Helvetica
-/Encoding /WinAnsiEncoding >>
-endobj
-19 0 obj
-<< /Type /Font
-/Subtype /Type1
-/Name /F9
-/BaseFont /Courier
-/Encoding /WinAnsiEncoding >>
-endobj
-1 0 obj
-<< /Type /Pages
-/Count 5
-/Kids [6 0 R 8 0 R 10 0 R 12 0 R 14 0 R ] >>
-endobj
-2 0 obj
-<< /Type /Catalog
-/Pages 1 0 R
- >>
-endobj
-3 0 obj
-<<
-/Font << /F5 16 0 R /F14 15 0 R /F13 17 0 R /F1 18 0 R /F9 19 0 R >>
-/ProcSet [ /PDF /ImageB /ImageC /Text ] >>
-endobj
-xref
-0 20
-0000000000 65535 f
-0000016170 00000 n
-0000016255 00000 n
-0000016305 00000 n
-0000000015 00000 n
-0000000071 00000 n
-0000002919 00000 n
-0000003025 00000 n
-0000006052 00000 n
-0000006158 00000 n
-0000010053 00000 n
-0000010160 00000 n
-0000013878 00000 n
-0000013986 00000 n
-0000015574 00000 n
-0000015682 00000 n
-0000015767 00000 n
-0000015877 00000 n
-0000015956 00000 n
-0000016064 00000 n
-trailer
-<<
-/Size 20
-/Root 2 0 R
-/Info 4 0 R
->>
-startxref
-16438
-%%EOF
diff --git a/src/documentation/content/xdocs/fo/fonts.pdf b/src/documentation/content/xdocs/fo/fonts.pdf
new file mode 100644
index 000000000..5c84a41b2
--- /dev/null
+++ b/src/documentation/content/xdocs/fo/fonts.pdf
Binary files differ
diff --git a/src/documentation/content/xdocs/fo/size.fo b/src/documentation/content/xdocs/fo/size.fo
index 225adb491..1de6b0245 100644
--- a/src/documentation/content/xdocs/fo/size.fo
+++ b/src/documentation/content/xdocs/fo/size.fo
@@ -19,19 +19,18 @@
<fo:root font-family="Times Roman" font-size="12pt" xmlns:fo="http://www.w3.org/1999/XSL/Format">
<fo:layout-master-set>
- <fo:simple-page-master
- margin-right="1.5cm"
- margin-left="1.5cm"
- margin-bottom="2cm"
- margin-top="1cm"
- page-width="21cm"
- page-height="29.7cm"
- master-name="left">
- <fo:region-before extent="0.5cm"/>
- <fo:region-body margin-top="0.5cm" margin-bottom="1.7cm"/>
- <fo:region-after extent="1.5cm"/>
- </fo:simple-page-master>
-
+ <fo:simple-page-master
+ margin-right="1.5cm"
+ margin-left="1.5cm"
+ margin-bottom="2cm"
+ margin-top="1cm"
+ page-width="21cm"
+ page-height="29.7cm"
+ master-name="left">
+ <fo:region-body margin-top="0.5cm" margin-bottom="1.7cm"/>
+ <fo:region-before extent="0.5cm"/>
+ <fo:region-after extent="1.5cm"/>
+ </fo:simple-page-master>
</fo:layout-master-set>
<fo:page-sequence id="N2528" master-reference="left">
diff --git a/src/documentation/content/xdocs/fo/size.fo.pdf b/src/documentation/content/xdocs/fo/size.fo.pdf
deleted file mode 100644
index 177160580..000000000
--- a/src/documentation/content/xdocs/fo/size.fo.pdf
+++ /dev/null
Binary files differ
diff --git a/src/documentation/content/xdocs/fo/size.pdf b/src/documentation/content/xdocs/fo/size.pdf
new file mode 100644
index 000000000..add8fe871
--- /dev/null
+++ b/src/documentation/content/xdocs/fo/size.pdf
Binary files differ
diff --git a/src/documentation/content/xdocs/trunk/configuration.xml b/src/documentation/content/xdocs/trunk/configuration.xml
index b30a091a5..563ae9647 100644
--- a/src/documentation/content/xdocs/trunk/configuration.xml
+++ b/src/documentation/content/xdocs/trunk/configuration.xml
@@ -281,6 +281,24 @@
treated as zero penalty in most cases. For more details on the image loading framework,
please consult the documentation there.
</p>
+ <p>
+ The ImageLoaderPNG and ImageLoaderRawPNG have a hard-coded penalty of 1000 and as such the
+ ImageLoaderImageIO image loader will be selected by default when loading PNGs unless
+ the latter is disabled by awarding a INFINITE penalty to it, or one of the former two is
+ promoted by awarding a strong negative penalty (say, -10000) to it.
+ </p>
+ <source><![CDATA[<fop version="1.0">
+ [..]
+ <image-loading>
+ <penalty value="-10000"
+ class="org.apache.xmlgraphics.image.loader.impl.ImageLoaderRawPNG"/>
+ <penalty value="INFINITE"
+ class="org.apache.xmlgraphics.image.loader.impl.ImageLoaderPNG"/>
+ <penalty value="INFINITE"
+ class="org.apache.xmlgraphics.image.loader.impl.imageio.ImageLoaderImageIO"/>
+ </image-loading>
+ <renderers....
+</fop>]]></source>
</section>
<section id="renderers">
<title>Renderer configuration</title>
diff --git a/src/documentation/content/xdocs/trunk/fonts.xml b/src/documentation/content/xdocs/trunk/fonts.xml
index f3481607d..ba607e40b 100644
--- a/src/documentation/content/xdocs/trunk/fonts.xml
+++ b/src/documentation/content/xdocs/trunk/fonts.xml
@@ -493,10 +493,10 @@
Various notes related to embedded fonts:
</p>
<ul>
- <li>The PostScript renderer does not yet support TrueType fonts, but can embed Type 1 fonts.</li>
- <li>The font is simply embedded into the PDF file, it is not converted.</li>
- <li>When FOP embeds a font, it adds a prefix to the fontname to ensure that the name will not match the fontname of an installed font.
- This is helpful with older versions of Acrobat Reader that preferred installed fonts over embedded fonts.</li>
+ <li>The font is simply embedded into the output file, it is not converted.</li>
+ <li>When FOP embeds a font in a PDF file, it adds a prefix to the fontname to ensure that
+ the name will not match the fontname of an installed font. This is helpful with older
+ versions of Acrobat Reader that preferred installed fonts over embedded fonts.</li>
<li>When embedding PostScript fonts, the entire font is always embedded.</li>
<li>When embedding TrueType fonts (ttf) or TrueType Collections (ttc), a subset of the
original font, containing only the glyphs used, is embedded in the output document.
@@ -576,4 +576,4 @@
</p>
</section>
</body>
-</document> \ No newline at end of file
+</document>
diff --git a/src/documentation/content/xdocs/trunk/graphics.xml b/src/documentation/content/xdocs/trunk/graphics.xml
index b1653443a..eb05013b7 100644
--- a/src/documentation/content/xdocs/trunk/graphics.xml
+++ b/src/documentation/content/xdocs/trunk/graphics.xml
@@ -108,7 +108,7 @@
<tr>
<td><a href="#png">PNG</a> (Portable Network Graphic)</td>
<td>bitmap</td>
- <td/>
+ <td>(X)</td>
<td/>
<td>X</td>
</tr>
@@ -217,8 +217,8 @@
</tr>
<tr>
<td><a href="#png">PNG</a> (Portable Network Graphic)</td>
- <td>X</td>
- <td>X</td>
+ <td>X [2]</td>
+ <td>X [2]</td>
<td>X</td>
<td>X</td>
<td>X</td>
@@ -383,8 +383,11 @@
<section id="png">
<title>PNG</title>
<p>
- PNG images are supported through an Image&amp;nbsp;I/O codec. Transparency is supported but
- not guaranteed to work with every output format.
+ FOP native support of PNG only includes the variants with 8 bits per channel and without
+ interlacing. Native support requires using the ImageLoaderRawPNG image loader.
+ Support through a Image I/O codec can use either the internal XGC PNG codec or the JRE PNG
+ codec. The associated image loaders are, respectively, ImageLoaderPNG and ImageLoaderImageIO.
+ Transparency is supported but not guaranteed to work with every output format.
</p>
</section>
<section id="svg">
diff --git a/src/documentation/intermediate-format-ng/fop-intermediate-format-ng.xsd b/src/documentation/intermediate-format-ng/fop-intermediate-format-ng.xsd
index bea5275a5..6e17c793b 100644
--- a/src/documentation/intermediate-format-ng/fop-intermediate-format-ng.xsd
+++ b/src/documentation/intermediate-format-ng/fop-intermediate-format-ng.xsd
@@ -34,6 +34,7 @@
<xs:element ref="mf:page-sequence" minOccurs="1" maxOccurs="unbounded"/>
<xs:element ref="mf:trailer"/>
</xs:sequence>
+ <xs:attribute name="version" type="xs:string"/>
</xs:complexType>
</xs:element>
<xs:element name="header">
diff --git a/src/java/META-INF/services/org.apache.fop.render.ImageHandler b/src/java/META-INF/services/org.apache.fop.render.ImageHandler
index f9b890c8e..9066913ad 100644
--- a/src/java/META-INF/services/org.apache.fop.render.ImageHandler
+++ b/src/java/META-INF/services/org.apache.fop.render.ImageHandler
@@ -1,6 +1,7 @@
org.apache.fop.render.pdf.PDFImageHandlerGraphics2D
org.apache.fop.render.pdf.PDFImageHandlerRenderedImage
org.apache.fop.render.pdf.PDFImageHandlerRawJPEG
+org.apache.fop.render.pdf.PDFImageHandlerRawPNG
org.apache.fop.render.pdf.PDFImageHandlerRawCCITTFax
org.apache.fop.render.pdf.PDFImageHandlerSVG
org.apache.fop.render.java2d.Java2DImageHandlerRenderedImage
@@ -11,6 +12,7 @@ org.apache.fop.render.ps.PSImageHandlerRenderedImage
org.apache.fop.render.ps.PSImageHandlerEPS
org.apache.fop.render.ps.PSImageHandlerRawCCITTFax
org.apache.fop.render.ps.PSImageHandlerRawJPEG
+org.apache.fop.render.ps.PSImageHandlerRawPNG
org.apache.fop.render.ps.PSImageHandlerGraphics2D
org.apache.fop.render.ps.PSImageHandlerSVG
org.apache.fop.render.afp.AFPImageHandlerRenderedImage
diff --git a/src/java/org/apache/fop/accessibility/Accessibility.java b/src/java/org/apache/fop/accessibility/Accessibility.java
index c842cf43f..88ec6dbdd 100644
--- a/src/java/org/apache/fop/accessibility/Accessibility.java
+++ b/src/java/org/apache/fop/accessibility/Accessibility.java
@@ -28,6 +28,12 @@ public final class Accessibility {
/** Constant string for the rendering options key to enable accessibility features. */
public static final String ACCESSIBILITY = "accessibility";
+ /**
+ * The value to be set on the 'role' property for the element and its descendants to
+ * be considered as artifacts.
+ */
+ public static final String ROLE_ARTIFACT = "artifact";
+
private Accessibility() { }
}
diff --git a/src/java/org/apache/fop/accessibility/fo/FO2StructureTreeConverter.java b/src/java/org/apache/fop/accessibility/fo/FO2StructureTreeConverter.java
index 47c227e9a..27469d6e0 100644
--- a/src/java/org/apache/fop/accessibility/fo/FO2StructureTreeConverter.java
+++ b/src/java/org/apache/fop/accessibility/fo/FO2StructureTreeConverter.java
@@ -23,6 +23,7 @@ import java.util.Stack;
import org.xml.sax.SAXException;
+import org.apache.fop.accessibility.Accessibility;
import org.apache.fop.accessibility.StructureTreeEventHandler;
import org.apache.fop.fo.DelegatingFOEventHandler;
import org.apache.fop.fo.FOEventHandler;
@@ -57,6 +58,8 @@ import org.apache.fop.fo.pagination.Flow;
import org.apache.fop.fo.pagination.PageSequence;
import org.apache.fop.fo.pagination.Root;
import org.apache.fop.fo.pagination.StaticContent;
+import org.apache.fop.fo.properties.CommonAccessibility;
+import org.apache.fop.fo.properties.CommonAccessibilityHolder;
/**
* Allows to create the structure tree of an FO document, by converting FO
@@ -355,6 +358,7 @@ public class FO2StructureTreeConverter extends DelegatingFOEventHandler {
@Override
public void startStatic(StaticContent staticContent) {
+ handleStartArtifact(staticContent);
converter.startStatic(staticContent);
super.startStatic(staticContent);
}
@@ -362,6 +366,7 @@ public class FO2StructureTreeConverter extends DelegatingFOEventHandler {
@Override
public void endStatic(StaticContent statisContent) {
converter.endStatic(statisContent);
+ handleEndArtifact(statisContent);
super.endStatic(statisContent);
}
@@ -454,6 +459,7 @@ public class FO2StructureTreeConverter extends DelegatingFOEventHandler {
@Override
public void startWrapper(Wrapper wrapper) {
+ handleStartArtifact(wrapper);
converter.startWrapper(wrapper);
super.startWrapper(wrapper);
}
@@ -461,6 +467,7 @@ public class FO2StructureTreeConverter extends DelegatingFOEventHandler {
@Override
public void endWrapper(Wrapper wrapper) {
converter.endWrapper(wrapper);
+ handleEndArtifact(wrapper);
super.endWrapper(wrapper);
}
@@ -488,4 +495,22 @@ public class FO2StructureTreeConverter extends DelegatingFOEventHandler {
super.endExternalDocument(document);
}
+ private void handleStartArtifact(CommonAccessibilityHolder fobj) {
+ if (isArtifact(fobj)) {
+ converters.push(converter);
+ converter = eventSwallower;
+ }
+ }
+
+ private void handleEndArtifact(CommonAccessibilityHolder fobj) {
+ if (isArtifact(fobj)) {
+ converter = converters.pop();
+ }
+ }
+
+ private boolean isArtifact(CommonAccessibilityHolder fobj) {
+ CommonAccessibility accessibility = fobj.getCommonAccessibility();
+ return Accessibility.ROLE_ARTIFACT.equalsIgnoreCase(accessibility.getRole());
+ }
+
}
diff --git a/src/java/org/apache/fop/afp/AFPGraphics2D.java b/src/java/org/apache/fop/afp/AFPGraphics2D.java
index e9a269ac4..7172b0ee3 100644
--- a/src/java/org/apache/fop/afp/AFPGraphics2D.java
+++ b/src/java/org/apache/fop/afp/AFPGraphics2D.java
@@ -67,7 +67,7 @@ import org.apache.fop.svg.NativeImageHandler;
/**
* This is a concrete implementation of {@link AbstractGraphics2D} (and
- * therefore of {@link Graphics2D}) which is able to generate GOCA byte
+ * therefore of {@link java.awt.Graphics2D}) which is able to generate GOCA byte
* codes.
*
* @see org.apache.xmlgraphics.java2d.AbstractGraphics2D
@@ -165,7 +165,7 @@ public class AFPGraphics2D extends AbstractGraphics2D implements NativeImageHand
}
/**
- * Returns the AFP resource manager associated with this {@link Graphics2D} instance.
+ * Returns the AFP resource manager associated with this {@link java.awt.Graphics2D} instance.
* @return the resource manager
*/
public AFPResourceManager getResourceManager() {
diff --git a/src/java/org/apache/fop/afp/modca/ImageObject.java b/src/java/org/apache/fop/afp/modca/ImageObject.java
index adb56e626..e42639248 100644
--- a/src/java/org/apache/fop/afp/modca/ImageObject.java
+++ b/src/java/org/apache/fop/afp/modca/ImageObject.java
@@ -119,7 +119,8 @@ public class ImageObject extends AbstractDataObject {
* Sets the image IDE color model.
*
* @param colorModel the IDE color model.
- * @deprecated Use {@link IDEStructureParameter#setColorModel(byte)} instead.
+ * @deprecated Use {@link org.apache.fop.afp.ioca.IDEStructureParameter#setColorModel(byte)}
+ * instead.
*/
public void setIDEColorModel(byte colorModel) {
getImageSegment().setIDEColorModel(colorModel);
@@ -128,7 +129,8 @@ public class ImageObject extends AbstractDataObject {
/**
* Set either additive or subtractive mode (used for ASFLAG).
* @param subtractive true for subtractive mode, false for additive mode
- * @deprecated Use {@link IDEStructureParameter#setSubtractive(boolean)} instead.
+ * @deprecated Use {@link org.apache.fop.afp.ioca.IDEStructureParameter#setSubtractive(boolean)}
+ * instead.
*/
public void setSubtractive(boolean subtractive) {
getImageSegment().setSubtractive(subtractive);
diff --git a/src/java/org/apache/fop/afp/modca/PresentationTextData.java b/src/java/org/apache/fop/afp/modca/PresentationTextData.java
index b68131413..c0934b1e5 100644
--- a/src/java/org/apache/fop/afp/modca/PresentationTextData.java
+++ b/src/java/org/apache/fop/afp/modca/PresentationTextData.java
@@ -46,7 +46,7 @@ import org.apache.fop.afp.util.BinaryUtils;
* which signal an alternate mode of processing for the content of the current
* Presentation Text data.
* <p>
- * The content for this object can be created using {@link PtocaBuilder}.
+ * The content for this object can be created using {@link org.apache.fop.afp.ptoca.PtocaBuilder}.
*/
public class PresentationTextData extends AbstractAFPObject implements PtocaConstants {
diff --git a/src/java/org/apache/fop/afp/util/DefaultFOPResourceAccessor.java b/src/java/org/apache/fop/afp/util/DefaultFOPResourceAccessor.java
index f379546e2..2872976b6 100644
--- a/src/java/org/apache/fop/afp/util/DefaultFOPResourceAccessor.java
+++ b/src/java/org/apache/fop/afp/util/DefaultFOPResourceAccessor.java
@@ -44,7 +44,8 @@ public class DefaultFOPResourceAccessor extends SimpleResourceAccessor {
* Constructor for resource to be accessed via the {@link FOUserAgent}. This contructor
* can take two base URIs: the category base URI is the one to use when differentiating between
* normal resources (ex. images) and font resources. So, if fonts need to be accessed, you can
- * set the {@link FontManager}'s base URI instead of the one on the {@link FopFactory}.
+ * set the {@link org.apache.fop.fonts.FontManager}'s base URI instead of the one on the
+ * {@link org.apache.fop.apps.FopFactory}.
* @param userAgent the FO user agent
* @param categoryBaseURI the category base URI (may be null)
* @param baseURI the custom base URI to resolve relative URIs against (may be null)
diff --git a/src/java/org/apache/fop/apps/FOUserAgent.java b/src/java/org/apache/fop/apps/FOUserAgent.java
index 665c1abf8..035b6f1a1 100644
--- a/src/java/org/apache/fop/apps/FOUserAgent.java
+++ b/src/java/org/apache/fop/apps/FOUserAgent.java
@@ -355,7 +355,7 @@ public class FOUserAgent {
/**
* Sets font base URL.
* @param fontBaseUrl font base URL
- * @deprecated Use {@link FontManager#setFontBaseURL(String)} instead.
+ * @deprecated Use {@link org.apache.fop.fonts.FontManager#setFontBaseURL(String)} instead.
*/
public void setFontBaseURL(String fontBaseUrl) {
try {
@@ -500,7 +500,8 @@ public class FOUserAgent {
/**
* Returns the font base URL.
* @return the font base URL
- * @deprecated Use {@link FontManager#getFontBaseURL()} instead. This method is not used by FOP.
+ * @deprecated Use {@link org.apache.fop.fonts.FontManager#getFontBaseURL()} instead.
+ * This method is not used by FOP.
*/
public String getFontBaseURL() {
String fontBase = getFactory().getFontManager().getFontBaseURL();
diff --git a/src/java/org/apache/fop/complexscripts/fonts/GlyphPositioningTable.java b/src/java/org/apache/fop/complexscripts/fonts/GlyphPositioningTable.java
index 3b7c14b0e..6db6be726 100644
--- a/src/java/org/apache/fop/complexscripts/fonts/GlyphPositioningTable.java
+++ b/src/java/org/apache/fop/complexscripts/fonts/GlyphPositioningTable.java
@@ -1783,9 +1783,9 @@ public class GlyphPositioningTable extends GlyphTable {
* Find device adjustment.
* @param fontSize the font size to search for
* @return an adjustment if font size matches an entry
- * @asf.todo at present, assumes that 1 device unit equals one point
*/
public int findAdjustment ( int fontSize ) {
+ // [TODO] at present, assumes that 1 device unit equals one point
int fs = fontSize / 1000;
if ( fs < startSize ) {
return 0;
diff --git a/src/java/org/apache/fop/complexscripts/fonts/OTFAdvancedTypographicTableReader.java b/src/java/org/apache/fop/complexscripts/fonts/OTFAdvancedTypographicTableReader.java
index a9110d378..9afb893b5 100644
--- a/src/java/org/apache/fop/complexscripts/fonts/OTFAdvancedTypographicTableReader.java
+++ b/src/java/org/apache/fop/complexscripts/fonts/OTFAdvancedTypographicTableReader.java
@@ -31,6 +31,7 @@ import org.apache.commons.logging.LogFactory;
import org.apache.fop.fonts.truetype.FontFileReader;
import org.apache.fop.fonts.truetype.TTFDirTabEntry;
import org.apache.fop.fonts.truetype.TTFFile;
+import org.apache.fop.fonts.truetype.TTFTableName;
// CSOFF: AvoidNestedBlocksCheck
// CSOFF: NoWhitespaceAfterCheck
@@ -126,7 +127,7 @@ public final class OTFAdvancedTypographicTableReader {
return gpos;
}
- private void readLangSysTable(String tableTag, long langSysTable, String langSysTag) throws IOException {
+ private void readLangSysTable(TTFTableName tableTag, long langSysTable, String langSysTag) throws IOException {
in.seekSet(langSysTable);
if (log.isDebugEnabled()) {
log.debug(tableTag + " lang sys table: " + langSysTag );
@@ -168,7 +169,7 @@ public final class OTFAdvancedTypographicTableReader {
private static String defaultTag = "dflt";
- private void readScriptTable(String tableTag, long scriptTable, String scriptTag) throws IOException {
+ private void readScriptTable(TTFTableName tableTag, long scriptTable, String scriptTag) throws IOException {
in.seekSet(scriptTable);
if (log.isDebugEnabled()) {
log.debug(tableTag + " script table: " + scriptTag );
@@ -221,7 +222,7 @@ public final class OTFAdvancedTypographicTableReader {
seLanguages = null;
}
- private void readScriptList(String tableTag, long scriptList) throws IOException {
+ private void readScriptList(TTFTableName tableTag, long scriptList) throws IOException {
in.seekSet(scriptList);
// read script record count
int ns = in.readTTFUShort();
@@ -250,7 +251,7 @@ public final class OTFAdvancedTypographicTableReader {
}
}
- private void readFeatureTable(String tableTag, long featureTable, String featureTag, int featureIndex) throws IOException {
+ private void readFeatureTable(TTFTableName tableTag, long featureTable, String featureTag, int featureIndex) throws IOException {
in.seekSet(featureTable);
if (log.isDebugEnabled()) {
log.debug(tableTag + " feature table: " + featureTag );
@@ -278,7 +279,7 @@ public final class OTFAdvancedTypographicTableReader {
seFeatures.put ( "f" + featureIndex, new Object[] { featureTag, lul } );
}
- private void readFeatureList(String tableTag, long featureList) throws IOException {
+ private void readFeatureList(TTFTableName tableTag, long featureList) throws IOException {
in.seekSet(featureList);
// read feature record count
int nf = in.readTTFUShort();
@@ -3144,9 +3145,9 @@ public final class OTFAdvancedTypographicTableReader {
resetATSubState();
}
- private void readLookupTable(String tableTag, int lookupSequence, long lookupTable) throws IOException {
- boolean isGSUB = tableTag.equals ( "GSUB" );
- boolean isGPOS = tableTag.equals ( "GPOS" );
+ private void readLookupTable(TTFTableName tableTag, int lookupSequence, long lookupTable) throws IOException {
+ boolean isGSUB = tableTag.equals ( TTFTableName.GSUB );
+ boolean isGPOS = tableTag.equals ( TTFTableName.GPOS );
in.seekSet(lookupTable);
// read lookup type
int lt = in.readTTFUShort();
@@ -3197,7 +3198,7 @@ public final class OTFAdvancedTypographicTableReader {
}
}
- private void readLookupList(String tableTag, long lookupList) throws IOException {
+ private void readLookupList(TTFTableName tableTag, long lookupList) throws IOException {
in.seekSet(lookupList);
// read lookup record count
int nl = in.readTTFUShort();
@@ -3232,7 +3233,7 @@ public final class OTFAdvancedTypographicTableReader {
* @param lookupList offset to lookup list from beginning of font file
* @throws IOException In case of a I/O problem
*/
- private void readCommonLayoutTables(String tableTag, long scriptList, long featureList, long lookupList) throws IOException {
+ private void readCommonLayoutTables(TTFTableName tableTag, long scriptList, long featureList, long lookupList) throws IOException {
if ( scriptList > 0 ) {
readScriptList ( tableTag, scriptList );
}
@@ -3244,7 +3245,7 @@ public final class OTFAdvancedTypographicTableReader {
}
}
- private void readGDEFClassDefTable(String tableTag, int lookupSequence, long subtableOffset) throws IOException {
+ private void readGDEFClassDefTable(TTFTableName tableTag, int lookupSequence, long subtableOffset) throws IOException {
initATSubState();
in.seekSet(subtableOffset);
// subtable is a bare class definition table
@@ -3256,7 +3257,7 @@ public final class OTFAdvancedTypographicTableReader {
resetATSubState();
}
- private void readGDEFAttachmentTable(String tableTag, int lookupSequence, long subtableOffset) throws IOException {
+ private void readGDEFAttachmentTable(TTFTableName tableTag, int lookupSequence, long subtableOffset) throws IOException {
initATSubState();
in.seekSet(subtableOffset);
// read coverage offset
@@ -3274,7 +3275,7 @@ public final class OTFAdvancedTypographicTableReader {
resetATSubState();
}
- private void readGDEFLigatureCaretTable(String tableTag, int lookupSequence, long subtableOffset) throws IOException {
+ private void readGDEFLigatureCaretTable(TTFTableName tableTag, int lookupSequence, long subtableOffset) throws IOException {
initATSubState();
in.seekSet(subtableOffset);
// read coverage offset
@@ -3304,7 +3305,7 @@ public final class OTFAdvancedTypographicTableReader {
resetATSubState();
}
- private void readGDEFMarkAttachmentTable(String tableTag, int lookupSequence, long subtableOffset) throws IOException {
+ private void readGDEFMarkAttachmentTable(TTFTableName tableTag, int lookupSequence, long subtableOffset) throws IOException {
initATSubState();
in.seekSet(subtableOffset);
// subtable is a bare class definition table
@@ -3316,7 +3317,7 @@ public final class OTFAdvancedTypographicTableReader {
resetATSubState();
}
- private void readGDEFMarkGlyphsTableFormat1(String tableTag, int lookupSequence, long subtableOffset, int subtableFormat) throws IOException {
+ private void readGDEFMarkGlyphsTableFormat1(TTFTableName tableTag, int lookupSequence, long subtableOffset, int subtableFormat) throws IOException {
initATSubState();
in.seekSet(subtableOffset);
// skip over format (already known)
@@ -3350,7 +3351,7 @@ public final class OTFAdvancedTypographicTableReader {
resetATSubState();
}
- private void readGDEFMarkGlyphsTable(String tableTag, int lookupSequence, long subtableOffset) throws IOException {
+ private void readGDEFMarkGlyphsTable(TTFTableName tableTag, int lookupSequence, long subtableOffset) throws IOException {
in.seekSet(subtableOffset);
// read mark set subtable format
int sf = in.readTTFUShort();
@@ -3366,11 +3367,11 @@ public final class OTFAdvancedTypographicTableReader {
* @throws IOException In case of a I/O problem
*/
private void readGDEF() throws IOException {
- String tableTag = "GDEF";
+ TTFTableName tableTag = TTFTableName.GDEF;
// Initialize temporary state
initATState();
// Read glyph definition (GDEF) table
- TTFDirTabEntry dirTab = ttf.getDirectoryEntry ( tableTag );
+ TTFDirTabEntry dirTab = ttf.getDirectoryEntry( tableTag );
if ( gdef != null ) {
if (log.isDebugEnabled()) {
log.debug(tableTag + ": ignoring duplicate table");
@@ -3439,7 +3440,7 @@ public final class OTFAdvancedTypographicTableReader {
* @throws IOException In case of a I/O problem
*/
private void readGSUB() throws IOException {
- String tableTag = "GSUB";
+ TTFTableName tableTag = TTFTableName.GSUB;
// Initialize temporary state
initATState();
// Read glyph substitution (GSUB) table
@@ -3476,7 +3477,7 @@ public final class OTFAdvancedTypographicTableReader {
* @throws IOException In case of a I/O problem
*/
private void readGPOS() throws IOException {
- String tableTag = "GPOS";
+ TTFTableName tableTag = TTFTableName.GPOS;
// Initialize temporary state
initATState();
// Read glyph positioning (GPOS) table
diff --git a/src/java/org/apache/fop/complexscripts/scripts/IndicScriptProcessor.java b/src/java/org/apache/fop/complexscripts/scripts/IndicScriptProcessor.java
index ce4585828..26249bc22 100644
--- a/src/java/org/apache/fop/complexscripts/scripts/IndicScriptProcessor.java
+++ b/src/java/org/apache/fop/complexscripts/scripts/IndicScriptProcessor.java
@@ -360,7 +360,7 @@ public class IndicScriptProcessor extends DefaultScriptProcessor {
}
/** Abstract syllabizer. */
- protected abstract static class Syllabizer {
+ protected abstract static class Syllabizer implements Comparable {
private String script;
private String language;
Syllabizer ( String script, String language ) {
diff --git a/src/java/org/apache/fop/fo/FOEventHandler.java b/src/java/org/apache/fop/fo/FOEventHandler.java
index 743708b72..11b6d4a2f 100644
--- a/src/java/org/apache/fop/fo/FOEventHandler.java
+++ b/src/java/org/apache/fop/fo/FOEventHandler.java
@@ -123,11 +123,17 @@ public abstract class FOEventHandler {
public void endDocument() throws SAXException {
}
- /** {@inheritDoc} */
+ /**
+ * Called upon start of root element.
+ * @param root element
+ */
public void startRoot(Root root) {
}
- /** {@inheritDoc} */
+ /**
+ * Called upon end of root element.
+ * @param root element
+ */
public void endRoot(Root root) {
}
@@ -413,9 +419,9 @@ public abstract class FOEventHandler {
/**
* Process end of a Static.
- * @param statisContent StaticContent that is ending
+ * @param staticContent StaticContent that is ending
*/
- public void endStatic(StaticContent statisContent) {
+ public void endStatic(StaticContent staticContent) {
}
diff --git a/src/java/org/apache/fop/fo/flow/Wrapper.java b/src/java/org/apache/fop/fo/flow/Wrapper.java
index 1302e3134..0aec7ce16 100644
--- a/src/java/org/apache/fop/fo/flow/Wrapper.java
+++ b/src/java/org/apache/fop/fo/flow/Wrapper.java
@@ -26,7 +26,10 @@ import org.apache.fop.fo.Constants;
import org.apache.fop.fo.FONode;
import org.apache.fop.fo.FOText;
import org.apache.fop.fo.FObjMixed;
+import org.apache.fop.fo.PropertyList;
import org.apache.fop.fo.ValidationException;
+import org.apache.fop.fo.properties.CommonAccessibility;
+import org.apache.fop.fo.properties.CommonAccessibilityHolder;
/**
* Class modelling the <a href=http://www.w3.org/TR/xsl/#fo_wrapper">
@@ -34,13 +37,13 @@ import org.apache.fop.fo.ValidationException;
* The <code>fo:wrapper</code> object serves as a property holder for
* its child node objects.
*/
-public class Wrapper extends FObjMixed {
- // The value of properties relevant for fo:wrapper.
- // End of property values
+public class Wrapper extends FObjMixed implements CommonAccessibilityHolder {
// used for FO validation
private boolean blockOrInlineItemFound = false;
+ private CommonAccessibility commonAccessibility;
+
/**
* Create a Wrapper instance that is a child of the
* given {@link FONode}
@@ -52,6 +55,12 @@ public class Wrapper extends FObjMixed {
}
@Override
+ public void bind(PropertyList pList) throws FOPException {
+ super.bind(pList);
+ commonAccessibility = CommonAccessibility.getInstance(pList);
+ }
+
+ @Override
protected void startOfNode() throws FOPException {
super.startOfNode();
getFOEventHandler().startWrapper(this);
@@ -136,6 +145,10 @@ public class Wrapper extends FObjMixed {
return FO_WRAPPER;
}
+ public CommonAccessibility getCommonAccessibility() {
+ return commonAccessibility;
+ }
+
@Override
public boolean isDelimitedTextRangeBoundary ( int boundary ) {
return false;
diff --git a/src/java/org/apache/fop/fo/properties/CondLengthProperty.java b/src/java/org/apache/fop/fo/properties/CondLengthProperty.java
index 07eec7361..19c8af1bb 100644
--- a/src/java/org/apache/fop/fo/properties/CondLengthProperty.java
+++ b/src/java/org/apache/fop/fo/properties/CondLengthProperty.java
@@ -26,6 +26,7 @@ import org.apache.fop.fo.Constants;
import org.apache.fop.fo.FObj;
import org.apache.fop.fo.PropertyList;
import org.apache.fop.fo.expr.PropertyException;
+import org.apache.fop.util.CompareUtil;
/**
* Superclass for properties that have conditional lengths
@@ -192,8 +193,8 @@ public class CondLengthProperty extends Property implements CompoundDatatype {
if (obj instanceof CondLengthProperty) {
CondLengthProperty clp = (CondLengthProperty)obj;
- return (this.length == clp.length
- && this.conditionality == clp.conditionality);
+ return (CompareUtil.equal(this.length, clp.length)
+ && CompareUtil.equal(this.conditionality, clp.conditionality));
}
return false;
}
diff --git a/src/java/org/apache/fop/fonts/CIDFontType.java b/src/java/org/apache/fop/fonts/CIDFontType.java
index ce01fa629..20a94b9dd 100644
--- a/src/java/org/apache/fop/fonts/CIDFontType.java
+++ b/src/java/org/apache/fop/fonts/CIDFontType.java
@@ -34,7 +34,7 @@ public class CIDFontType extends ValuedEnum {
/**
* CID Font Type 2 (based on TrueType format)
*/
- public static final CIDFontType CIDTYPE2 = new CIDFontType("CIDFontType2", 1);
+ public static final CIDFontType CIDTYPE2 = new CIDFontType("CIDFontType2", 2);
/**
diff --git a/src/java/org/apache/fop/fonts/BFEntry.java b/src/java/org/apache/fop/fonts/CMapSegment.java
index d3c7956ba..816df2ca0 100644
--- a/src/java/org/apache/fop/fonts/BFEntry.java
+++ b/src/java/org/apache/fop/fonts/CMapSegment.java
@@ -20,26 +20,49 @@
package org.apache.fop.fonts;
/**
- * This is just a holder class for bfentries, groups of characters of a base font (bf).
+ * A segment in a cmap table of format 4. Unicode code points between
+ * {@link #getUnicodeStart()} and {@link #getUnicodeEnd()} map to contiguous glyph indices
+ * starting from {@link #getGlyphStartIndex()}.
*/
-public class BFEntry {
+public final class CMapSegment {
- private int unicodeStart;
- private int unicodeEnd;
- private int glyphStartIndex;
+ private final int unicodeStart;
+ private final int unicodeEnd;
+ private final int glyphStartIndex;
/**
- * Main constructor.
+ * Creates a new segment.
+ *
* @param unicodeStart Unicode start index
* @param unicodeEnd Unicode end index
* @param glyphStartIndex glyph start index
*/
- public BFEntry(int unicodeStart, int unicodeEnd, int glyphStartIndex) {
+ public CMapSegment(int unicodeStart, int unicodeEnd, int glyphStartIndex) {
this.unicodeStart = unicodeStart;
this.unicodeEnd = unicodeEnd;
this.glyphStartIndex = glyphStartIndex;
}
+ @Override
+ public int hashCode() {
+ int hc = 17;
+ hc = 31 * hc + unicodeStart;
+ hc = 31 * hc + unicodeEnd;
+ hc = 31 * hc + glyphStartIndex;
+ return hc;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof CMapSegment) {
+ CMapSegment ce = (CMapSegment) o;
+ return ce.unicodeStart == this.unicodeStart
+ && ce.unicodeEnd == this.unicodeEnd
+ && ce.glyphStartIndex == this.glyphStartIndex;
+ }
+ return false;
+ }
+
/**
* Returns the unicodeStart.
* @return the Unicode start index
@@ -67,7 +90,7 @@ public class BFEntry {
/** {@inheritDoc} */
@Override
public String toString() {
- StringBuilder sb = new StringBuilder("BFEntry: ");
+ StringBuilder sb = new StringBuilder("CMapSegment: ");
sb.append ( "{ UC[" );
sb.append ( unicodeStart );
sb.append ( ',' );
diff --git a/src/java/org/apache/fop/fonts/CustomFont.java b/src/java/org/apache/fop/fonts/CustomFont.java
index c6b43fe98..89f515205 100644
--- a/src/java/org/apache/fop/fonts/CustomFont.java
+++ b/src/java/org/apache/fop/fonts/CustomFont.java
@@ -42,6 +42,7 @@ public abstract class CustomFont extends Typeface
private String embedFileName = null;
private String embedResourceName = null;
private FontResolver resolver = null;
+ private EmbeddingMode embeddingMode = EmbeddingMode.AUTO;
private int capHeight = 0;
private int xHeight = 0;
@@ -62,6 +63,9 @@ public abstract class CustomFont extends Typeface
private boolean useKerning = true;
private boolean useAdvanced = true;
+ /** the character map, mapping Unicode ranges to glyph indices. */
+ protected CMapSegment[] cmap;
+
/** {@inheritDoc} */
public String getFontName() {
return fontName;
@@ -112,6 +116,14 @@ public abstract class CustomFont extends Typeface
}
/**
+ * Returns the embedding mode for this font.
+ * @return embedding mode
+ */
+ public EmbeddingMode getEmbeddingMode() {
+ return embeddingMode;
+ }
+
+ /**
* Returns a Source representing an embeddable font file.
* @return Source for an embeddable font file
* @throws IOException if embedFileName is not null but Source is not found
@@ -337,6 +349,13 @@ public abstract class CustomFont extends Typeface
/**
* {@inheritDoc}
*/
+ public void setEmbeddingMode(EmbeddingMode embeddingMode) {
+ this.embeddingMode = embeddingMode;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
public void setCapHeight(int capHeight) {
this.capHeight = capHeight;
}
@@ -473,4 +492,25 @@ public abstract class CustomFont extends Typeface
}
}
+ /**
+ * Sets the character map for this font. It maps all available Unicode characters
+ * to their glyph indices inside the font.
+ * @param cmap the character map
+ */
+ public void setCMap(CMapSegment[] cmap) {
+ this.cmap = new CMapSegment[cmap.length];
+ System.arraycopy(cmap, 0, this.cmap, 0, cmap.length);
+ }
+
+ /**
+ * Returns the character map for this font. It maps all available Unicode characters
+ * to their glyph indices inside the font.
+ * @return the character map
+ */
+ public CMapSegment[] getCMap() {
+ CMapSegment[] copy = new CMapSegment[cmap.length];
+ System.arraycopy(this.cmap, 0, copy, 0, this.cmap.length);
+ return copy;
+ }
+
}
diff --git a/src/java/org/apache/fop/fonts/CustomFontCollection.java b/src/java/org/apache/fop/fonts/CustomFontCollection.java
index 6e798a8f7..fc8c947e4 100644
--- a/src/java/org/apache/fop/fonts/CustomFontCollection.java
+++ b/src/java/org/apache/fop/fonts/CustomFontCollection.java
@@ -72,7 +72,7 @@ public class CustomFontCollection implements FontCollection {
List<FontTriplet> triplets = embedFontInfo.getFontTriplets();
for (int tripletIndex = 0; tripletIndex < triplets.size(); tripletIndex++) {
- FontTriplet triplet = (FontTriplet) triplets.get(tripletIndex);
+ FontTriplet triplet = triplets.get(tripletIndex);
fontInfo.addFontProperties(internalName, triplet);
}
}
diff --git a/src/java/org/apache/fop/fonts/EmbedFontInfo.java b/src/java/org/apache/fop/fonts/EmbedFontInfo.java
index 8848c0a87..64bd200be 100644
--- a/src/java/org/apache/fop/fonts/EmbedFontInfo.java
+++ b/src/java/org/apache/fop/fonts/EmbedFontInfo.java
@@ -25,6 +25,8 @@ import java.util.List;
/**
* FontInfo contains meta information on fonts (where is the metrics file etc.)
+ * TODO: We need to remove this class and think about more intelligent design patterns
+ * (Data classes => Procedural code)
*/
public class EmbedFontInfo implements Serializable {
@@ -41,6 +43,8 @@ public class EmbedFontInfo implements Serializable {
protected boolean advanced;
/** the requested encoding mode for the font */
protected EncodingMode encodingMode = EncodingMode.AUTO;
+ /** the requested embedding mode for this font */
+ protected EmbeddingMode embeddingMode = EmbeddingMode.AUTO;
/** the PostScript name of the font */
protected String postScriptName = null;
@@ -149,6 +153,14 @@ public class EmbedFontInfo implements Serializable {
}
/**
+ * Returns the embedding mode for this font.
+ * @return the embedding mode.
+ */
+ public EmbeddingMode getEmbeddingMode() {
+ return embeddingMode;
+ }
+
+ /**
* Defines whether the font is embedded or not.
* @param value true to embed the font, false to reference it
*/
@@ -175,6 +187,17 @@ public class EmbedFontInfo implements Serializable {
this.encodingMode = mode;
}
+ /**
+ * Sets the embedding mode for this font, currently not supported for Type 1 fonts.
+ * @param embeddingMode the new embedding mode.
+ */
+ public void setEmbeddingMode(EmbeddingMode embeddingMode) {
+ if (embeddingMode == null) {
+ throw new NullPointerException("embeddingMode must not be null");
+ }
+ this.embeddingMode = embeddingMode;
+ }
+
private void readObject(java.io.ObjectInputStream in)
throws IOException, ClassNotFoundException {
in.defaultReadObject();
diff --git a/src/java/org/apache/fop/fonts/EmbeddingMode.java b/src/java/org/apache/fop/fonts/EmbeddingMode.java
new file mode 100644
index 000000000..5a3e905c9
--- /dev/null
+++ b/src/java/org/apache/fop/fonts/EmbeddingMode.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.fonts;
+
+import java.util.Locale;
+
+/**
+ * This enumerates the embedding mode of fonts; full; subset; auto (auto defaults to full for
+ * Type 1 fonts and subset for TrueType fonts.
+ */
+public enum EmbeddingMode {
+ /** Default option: assumes FULL for Type 1 fonts and SUBSET for TrueType fonts. */
+ AUTO,
+ /** Full font embedding: This means the whole of the font is written to file. */
+ FULL,
+ /** Subset font embedding: Only the mandatory tables and a subset of glyphs are written
+ * to file.*/
+ SUBSET;
+
+ /**
+ * Returns the name of this embedding mode.
+ * @return the name of this embedding mode in lower case.
+ */
+ public String getName() {
+ return this.toString().toLowerCase(Locale.ENGLISH);
+ }
+
+ /**
+ * Returns the embedding mode corresponding to the given name.
+ * @param value the name of an embedding mode (not case sensitive)
+ * @return the corresponding embedding mode
+ */
+ public static EmbeddingMode getValue(String value) {
+ for (EmbeddingMode mode : EmbeddingMode.values()) {
+ if (mode.toString().equalsIgnoreCase(value)) {
+ return mode;
+ }
+ }
+ throw new IllegalArgumentException("Invalid embedding-mode: " + value);
+ }
+}
diff --git a/src/java/org/apache/fop/fonts/EncodingMode.java b/src/java/org/apache/fop/fonts/EncodingMode.java
index 8a40d6593..78ffb7ac6 100644
--- a/src/java/org/apache/fop/fonts/EncodingMode.java
+++ b/src/java/org/apache/fop/fonts/EncodingMode.java
@@ -52,7 +52,7 @@ public enum EncodingMode {
* @param name the name of the encoding mode to look up
* @return the encoding mode constant
*/
- public static EncodingMode getEncodingMode(String name) {
+ public static EncodingMode getValue(String name) {
for (EncodingMode em : EncodingMode.values()) {
if (name.equalsIgnoreCase(em.getName())) {
return em;
diff --git a/src/java/org/apache/fop/fonts/FontInfoConfigurator.java b/src/java/org/apache/fop/fonts/FontInfoConfigurator.java
index 34b6ed1d0..042709884 100644
--- a/src/java/org/apache/fop/fonts/FontInfoConfigurator.java
+++ b/src/java/org/apache/fop/fonts/FontInfoConfigurator.java
@@ -254,12 +254,16 @@ public class FontInfoConfigurator {
boolean useKerning = fontCfg.getAttributeAsBoolean("kerning", true);
boolean useAdvanced = fontCfg.getAttributeAsBoolean("advanced", true);
- EncodingMode encodingMode = EncodingMode.getEncodingMode(
+ EncodingMode encodingMode = EncodingMode.getValue(
fontCfg.getAttribute("encoding-mode", EncodingMode.AUTO.getName()));
+ EmbeddingMode embeddingMode = EmbeddingMode.getValue(
+ fontCfg.getAttribute("embedding-mode", EmbeddingMode.AUTO.toString()));
EmbedFontInfo embedFontInfo
= new EmbedFontInfo(metricsUrl, useKerning, useAdvanced, tripletList, embedUrl,
subFont);
embedFontInfo.setEncodingMode(encodingMode);
+ embedFontInfo.setEmbeddingMode(embeddingMode);
+
boolean skipCachedFont = false;
if (fontCache != null) {
if (!fontCache.containsFont(embedFontInfo)) {
diff --git a/src/java/org/apache/fop/fonts/FontLoader.java b/src/java/org/apache/fop/fonts/FontLoader.java
index 91b763939..9bdbcd350 100644
--- a/src/java/org/apache/fop/fonts/FontLoader.java
+++ b/src/java/org/apache/fop/fonts/FontLoader.java
@@ -85,15 +85,17 @@ public abstract class FontLoader {
* @param fontFile the File representation of the font
* @param subFontName the sub-fontname of a font (for TrueType Collections, null otherwise)
* @param embedded indicates whether the font is embedded or referenced
+ * @param embeddingMode the embedding mode
* @param encodingMode the requested encoding mode
* @param resolver the font resolver to use when resolving URIs
* @return the newly loaded font
* @throws IOException In case of an I/O error
*/
public static CustomFont loadFont(File fontFile, String subFontName,
- boolean embedded, EncodingMode encodingMode, FontResolver resolver) throws IOException {
+ boolean embedded, EmbeddingMode embeddingMode, EncodingMode encodingMode,
+ FontResolver resolver) throws IOException {
return loadFont(fontFile.toURI().toURL(), subFontName,
- embedded, encodingMode, resolver);
+ embedded, embeddingMode, encodingMode, resolver);
}
/**
@@ -101,16 +103,17 @@ public abstract class FontLoader {
* @param fontUrl the URL representation of the font
* @param subFontName the sub-fontname of a font (for TrueType Collections, null otherwise)
* @param embedded indicates whether the font is embedded or referenced
+ * @param embeddingMode the embedding mode of the font
* @param encodingMode the requested encoding mode
* @param resolver the font resolver to use when resolving URIs
* @return the newly loaded font
* @throws IOException In case of an I/O error
*/
public static CustomFont loadFont(URL fontUrl, String subFontName,
- boolean embedded, EncodingMode encodingMode,
+ boolean embedded, EmbeddingMode embeddingMode, EncodingMode encodingMode,
FontResolver resolver) throws IOException {
return loadFont(fontUrl.toExternalForm(), subFontName,
- embedded, encodingMode, true, true,
+ embedded, embeddingMode, encodingMode, true, true,
resolver);
}
@@ -119,6 +122,7 @@ public abstract class FontLoader {
* @param fontFileURI the URI to the font
* @param subFontName the sub-fontname of a font (for TrueType Collections, null otherwise)
* @param embedded indicates whether the font is embedded or referenced
+ * @param embeddingMode the embedding mode of the font
* @param encodingMode the requested encoding mode
* @param useKerning indicates whether kerning information should be loaded if available
* @param useAdvanced indicates whether advanced typographic information shall be loaded if
@@ -128,8 +132,8 @@ public abstract class FontLoader {
* @throws IOException In case of an I/O error
*/
public static CustomFont loadFont(String fontFileURI, String subFontName,
- boolean embedded, EncodingMode encodingMode, boolean useKerning,
- boolean useAdvanced, FontResolver resolver) throws IOException {
+ boolean embedded, EmbeddingMode embeddingMode, EncodingMode encodingMode,
+ boolean useKerning, boolean useAdvanced, FontResolver resolver) throws IOException {
fontFileURI = fontFileURI.trim();
boolean type1 = isType1(fontFileURI);
FontLoader loader;
@@ -138,10 +142,14 @@ public abstract class FontLoader {
throw new IllegalArgumentException(
"CID encoding mode not supported for Type 1 fonts");
}
+ if (embeddingMode == EmbeddingMode.SUBSET) {
+ throw new IllegalArgumentException(
+ "Subset embedding for Type 1 fonts is not supported");
+ }
loader = new Type1FontLoader(fontFileURI, embedded, useKerning, resolver);
} else {
loader = new TTFFontLoader(fontFileURI, subFontName,
- embedded, encodingMode, useKerning, useAdvanced, resolver);
+ embedded, embeddingMode, encodingMode, useKerning, useAdvanced, resolver);
}
return loader.getFont();
}
diff --git a/src/java/org/apache/fop/fonts/FontReader.java b/src/java/org/apache/fop/fonts/FontReader.java
index 9d75ad74f..46ea9123d 100644
--- a/src/java/org/apache/fop/fonts/FontReader.java
+++ b/src/java/org/apache/fop/fonts/FontReader.java
@@ -64,7 +64,7 @@ public class FontReader extends DefaultHandler {
private Map<Integer, Integer> currentKerning = null;
- private List<BFEntry> bfranges = null;
+ private List<CMapSegment> bfranges = null;
private void createFont(InputSource source) throws FOPException {
XMLReader parser = null;
@@ -154,12 +154,14 @@ public class FontReader extends DefaultHandler {
/**
* {@inheritDoc}
*/
+ @Override
public void startDocument() {
}
/**
* {@inheritDoc}
*/
+ @Override
public void setDocumentLocator(Locator locator) {
// this.locator = locator; // not used at present
}
@@ -167,6 +169,7 @@ public class FontReader extends DefaultHandler {
/**
* {@inheritDoc}
*/
+ @Override
public void startElement(String uri, String localName, String qName,
Attributes attributes) throws SAXException {
if (localName.equals("font-metrics")) {
@@ -198,9 +201,9 @@ public class FontReader extends DefaultHandler {
returnFont.putKerningEntry(new Integer(attributes.getValue("kpx1")),
currentKerning);
} else if ("bfranges".equals(localName)) {
- bfranges = new ArrayList<BFEntry>();
+ bfranges = new ArrayList<CMapSegment>();
} else if ("bf".equals(localName)) {
- BFEntry entry = new BFEntry(getInt(attributes.getValue("us")),
+ CMapSegment entry = new CMapSegment(getInt(attributes.getValue("us")),
getInt(attributes.getValue("ue")),
getInt(attributes.getValue("gi")));
bfranges.add(entry);
@@ -236,6 +239,7 @@ public class FontReader extends DefaultHandler {
/**
* {@inheritDoc}
*/
+ @Override
public void endElement(String uri, String localName, String qName) throws SAXException {
String content = text.toString().trim();
if ("font-name".equals(localName)) {
@@ -303,7 +307,7 @@ public class FontReader extends DefaultHandler {
multiFont.setWidthArray(wds);
} else if ("bfranges".equals(localName)) {
- multiFont.setBFEntries(bfranges.toArray(new BFEntry[0]));
+ multiFont.setCMap(bfranges.toArray(new CMapSegment[0]));
}
text.setLength(0); //Reset text buffer (see characters())
}
@@ -311,6 +315,7 @@ public class FontReader extends DefaultHandler {
/**
* {@inheritDoc}
*/
+ @Override
public void characters(char[] ch, int start, int length) {
text.append(ch, start, length);
}
diff --git a/src/java/org/apache/fop/fonts/FontType.java b/src/java/org/apache/fop/fonts/FontType.java
index 56039b519..edd8d0c37 100644
--- a/src/java/org/apache/fop/fonts/FontType.java
+++ b/src/java/org/apache/fop/fonts/FontType.java
@@ -130,4 +130,9 @@ public class FontType {
return value;
}
+ @Override
+ public String toString() {
+ return name;
+ }
+
}
diff --git a/src/java/org/apache/fop/fonts/LazyFont.java b/src/java/org/apache/fop/fonts/LazyFont.java
index 7077c53b9..dfad4ffce 100644
--- a/src/java/org/apache/fop/fonts/LazyFont.java
+++ b/src/java/org/apache/fop/fonts/LazyFont.java
@@ -36,7 +36,6 @@ import org.apache.fop.apps.FOPException;
import org.apache.fop.complexscripts.fonts.Positionable;
import org.apache.fop.complexscripts.fonts.Substitutable;
-
/**
* This class is used to defer the loading of a font until it is really used.
*/
@@ -49,7 +48,8 @@ public class LazyFont extends Typeface implements FontDescriptor, Substitutable,
private boolean useKerning;
private boolean useAdvanced;
private EncodingMode encodingMode = EncodingMode.AUTO;
- private boolean embedded;
+ private EmbeddingMode embeddingMode = EmbeddingMode.AUTO;
+ private boolean embedded = true;
private String subFontName;
private boolean isMetricsLoaded;
@@ -74,6 +74,7 @@ public class LazyFont extends Typeface implements FontDescriptor, Substitutable,
this.useAdvanced = fontInfo.getAdvanced();
}
this.encodingMode = fontInfo.getEncodingMode();
+ this.embeddingMode = fontInfo.getEmbeddingMode();
this.subFontName = fontInfo.getSubFontName();
this.embedded = fontInfo.isEmbedded();
this.resolver = resolver;
@@ -147,8 +148,9 @@ public class LazyFont extends Typeface implements FontDescriptor, Substitutable,
if (fontEmbedPath == null) {
throw new RuntimeException("Cannot load font. No font URIs available.");
}
- realFont = FontLoader.loadFont(fontEmbedPath, this.subFontName,
- this.embedded, this.encodingMode, useKerning, useAdvanced, resolver);
+ realFont = FontLoader.loadFont(fontEmbedPath, subFontName,
+ embedded, embeddingMode, encodingMode,
+ useKerning, useAdvanced, resolver);
}
if (realFont instanceof FontDescriptor) {
realFontDescriptor = (FontDescriptor) realFont;
diff --git a/src/java/org/apache/fop/fonts/MultiByteFont.java b/src/java/org/apache/fop/fonts/MultiByteFont.java
index 73ef7c228..54b772b2e 100644
--- a/src/java/org/apache/fop/fonts/MultiByteFont.java
+++ b/src/java/org/apache/fop/fonts/MultiByteFont.java
@@ -51,14 +51,6 @@ public class MultiByteFont extends CIDFont implements Substitutable, Positionabl
private CIDSubset subset = new CIDSubset();
- /**
- * A map from Unicode indices to glyph indices. No assumption
- * about ordering is made below. If lookup is changed to a binary
- * search (from the current linear search), then addPrivateUseMapping()
- * needs to be changed to perform ordered inserts.
- */
- private BFEntry[] bfentries = null;
-
/* advanced typographic support */
private GlyphDefinitionTable gdef;
private GlyphSubstitutionTable gsub;
@@ -82,26 +74,31 @@ public class MultiByteFont extends CIDFont implements Substitutable, Positionabl
}
/** {@inheritDoc} */
+ @Override
public int getDefaultWidth() {
return defaultWidth;
}
/** {@inheritDoc} */
+ @Override
public String getRegistry() {
return "Adobe";
}
/** {@inheritDoc} */
+ @Override
public String getOrdering() {
return "UCS";
}
/** {@inheritDoc} */
+ @Override
public int getSupplement() {
return 0;
}
/** {@inheritDoc} */
+ @Override
public CIDFontType getCIDType() {
return cidType;
}
@@ -115,6 +112,7 @@ public class MultiByteFont extends CIDFont implements Substitutable, Positionabl
}
/** {@inheritDoc} */
+ @Override
public String getEmbedFontName() {
if (isEmbeddable()) {
return FontUtil.stripWhiteSpace(super.getFontName());
@@ -128,17 +126,18 @@ public class MultiByteFont extends CIDFont implements Substitutable, Positionabl
return !(getEmbedFileName() == null && getEmbedResourceName() == null);
}
- /** {@inheritDoc} */
public boolean isSubsetEmbedded() {
return true;
}
/** {@inheritDoc} */
+ @Override
public CIDSubset getCIDSubset() {
return this.subset;
}
/** {@inheritDoc} */
+ @Override
public String getEncodingName() {
return encoding;
}
@@ -171,30 +170,30 @@ public class MultiByteFont extends CIDFont implements Substitutable, Positionabl
int idx = c;
int retIdx = SingleByteEncoding.NOT_FOUND_CODE_POINT;
- for (int i = 0; (i < bfentries.length) && retIdx == 0; i++) {
- if (bfentries[i].getUnicodeStart() <= idx
- && bfentries[i].getUnicodeEnd() >= idx) {
+ for (int i = 0; (i < cmap.length) && retIdx == 0; i++) {
+ if (cmap[i].getUnicodeStart() <= idx
+ && cmap[i].getUnicodeEnd() >= idx) {
- retIdx = bfentries[i].getGlyphStartIndex()
+ retIdx = cmap[i].getGlyphStartIndex()
+ idx
- - bfentries[i].getUnicodeStart();
+ - cmap[i].getUnicodeStart();
}
}
return retIdx;
}
/**
- * Add a private use mapping {PU,GI} to the existing BFENTRIES map.
+ * Add a private use mapping {PU,GI} to the existing character map.
* N.B. Does not insert in order, merely appends to end of existing map.
*/
private synchronized void addPrivateUseMapping ( int pu, int gi ) {
assert findGlyphIndex ( pu ) == SingleByteEncoding.NOT_FOUND_CODE_POINT;
- BFEntry[] bfeOld = bfentries;
- int bfeCnt = bfeOld.length;
- BFEntry[] bfeNew = new BFEntry [ bfeCnt + 1 ];
- System.arraycopy ( bfeOld, 0, bfeNew, 0, bfeCnt );
- bfeNew [ bfeCnt ] = new BFEntry ( pu, pu, gi );
- bfentries = bfeNew;
+ CMapSegment[] oldCmap = cmap;
+ int cmapLength = oldCmap.length;
+ CMapSegment[] newCmap = new CMapSegment [ cmapLength + 1 ];
+ System.arraycopy ( oldCmap, 0, newCmap, 0, cmapLength );
+ newCmap [ cmapLength ] = new CMapSegment ( pu, pu, gi );
+ cmap = newCmap;
}
/**
@@ -252,12 +251,12 @@ public class MultiByteFont extends CIDFont implements Substitutable, Positionabl
// [TBD] - needs optimization, i.e., change from linear search to binary search
private int findCharacterFromGlyphIndex ( int gi, boolean augment ) {
int cc = 0;
- for ( int i = 0, n = bfentries.length; i < n; i++ ) {
- BFEntry be = bfentries [ i ];
- int s = be.getGlyphStartIndex();
- int e = s + ( be.getUnicodeEnd() - be.getUnicodeStart() );
+ for ( int i = 0, n = cmap.length; i < n; i++ ) {
+ CMapSegment segment = cmap [ i ];
+ int s = segment.getGlyphStartIndex();
+ int e = s + ( segment.getUnicodeEnd() - segment.getUnicodeStart() );
if ( ( gi >= s ) && ( gi <= e ) ) {
- cc = be.getUnicodeStart() + ( gi - s );
+ cc = segment.getUnicodeStart() + ( gi - s );
break;
}
}
@@ -273,6 +272,7 @@ public class MultiByteFont extends CIDFont implements Substitutable, Positionabl
/** {@inheritDoc} */
+ @Override
public char mapChar(char c) {
notifyMapOperation();
int glyphIndex = findGlyphIndex(c);
@@ -287,20 +287,12 @@ public class MultiByteFont extends CIDFont implements Substitutable, Positionabl
}
/** {@inheritDoc} */
+ @Override
public boolean hasChar(char c) {
return (findGlyphIndex(c) != SingleByteEncoding.NOT_FOUND_CODE_POINT);
}
/**
- * 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[] entries) {
- this.bfentries = entries;
- }
-
- /**
* Sets the defaultWidth.
* @param defaultWidth The defaultWidth to set
*/
diff --git a/src/java/org/apache/fop/fonts/MutableFont.java b/src/java/org/apache/fop/fonts/MutableFont.java
index 41c552a0b..3ebc3c465 100644
--- a/src/java/org/apache/fop/fonts/MutableFont.java
+++ b/src/java/org/apache/fop/fonts/MutableFont.java
@@ -61,6 +61,12 @@ public interface MutableFont {
void setEmbedResourceName(String name);
/**
+ * Sets the embedding mode.
+ * @param embeddingMode the embedding mode
+ */
+ void setEmbeddingMode(EmbeddingMode embeddingMode);
+
+ /**
* Sets the capital height value.
* @param capHeight capital height
*/
diff --git a/src/java/org/apache/fop/fonts/SingleByteFont.java b/src/java/org/apache/fop/fonts/SingleByteFont.java
index d5901297f..1aed72d69 100644
--- a/src/java/org/apache/fop/fonts/SingleByteFont.java
+++ b/src/java/org/apache/fop/fonts/SingleByteFont.java
@@ -31,6 +31,8 @@ import org.apache.commons.logging.LogFactory;
import org.apache.xmlgraphics.fonts.Glyphs;
+import org.apache.fop.fonts.truetype.TTFFile.PostScriptVersion;
+
/**
* Generic SingleByte font
*/
@@ -48,6 +50,7 @@ public class SingleByteFont extends CustomFont {
private List<SimpleSingleByteEncoding> additionalEncodings;
private Map<Character, Character> alternativeCodes;
+ private PostScriptVersion ttPostScriptVersion;
/**
* Main constructor.
@@ -397,5 +400,26 @@ public class SingleByteFont extends CustomFont {
}
}
+ /**
+ * Sets the version of the PostScript table stored in the TrueType font represented by
+ * this instance.
+ *
+ * @param version version of the <q>post</q> table
+ */
+ public void setTrueTypePostScriptVersion(PostScriptVersion version) {
+ ttPostScriptVersion = version;
+ }
+
+ /**
+ * Returns the version of the PostScript table stored in the TrueType font represented by
+ * this instance.
+ *
+ * @return the version of the <q>post</q> table
+ */
+ public PostScriptVersion getTrueTypePostScriptVersion() {
+ assert getFontType() == FontType.TRUETYPE;
+ return ttPostScriptVersion;
+ }
+
}
diff --git a/src/java/org/apache/fop/fonts/apps/TTFReader.java b/src/java/org/apache/fop/fonts/apps/TTFReader.java
index 6acb490c2..224c8de2f 100644
--- a/src/java/org/apache/fop/fonts/apps/TTFReader.java
+++ b/src/java/org/apache/fop/fonts/apps/TTFReader.java
@@ -20,7 +20,6 @@
package org.apache.fop.fonts.apps;
import java.io.IOException;
-import java.util.Iterator;
import java.util.Map;
import java.util.Set;
@@ -34,9 +33,9 @@ import org.xml.sax.SAXException;
import org.apache.commons.logging.LogFactory;
import org.apache.fop.Version;
+import org.apache.fop.fonts.CMapSegment;
import org.apache.fop.fonts.FontUtil;
import org.apache.fop.fonts.truetype.FontFileReader;
-import org.apache.fop.fonts.truetype.TTFCmapEntry;
import org.apache.fop.fonts.truetype.TTFFile;
import org.apache.fop.util.CommandLineLogger;
@@ -288,9 +287,9 @@ public class TTFReader extends AbstractFontReader {
root.appendChild(el);
el.appendChild(doc.createTextNode(ttf.getFullName()));
}
- Set familyNames = ttf.getFamilyNames();
+ Set<String> familyNames = ttf.getFamilyNames();
if (familyNames.size() > 0) {
- String familyName = (String)familyNames.iterator().next();
+ String familyName = familyNames.iterator().next();
el = doc.createElement("family-name");
root.appendChild(el);
el.appendChild(doc.createTextNode(familyName));
@@ -386,9 +385,7 @@ public class TTFReader extends AbstractFontReader {
el = doc.createElement("bfranges");
mel.appendChild(el);
- Iterator iter = ttf.getCMaps().listIterator();
- while (iter.hasNext()) {
- TTFCmapEntry ce = (TTFCmapEntry)iter.next();
+ for (CMapSegment ce : ttf.getCMaps()) {
Element el2 = doc.createElement("bf");
el.appendChild(el2);
el2.setAttribute("us", String.valueOf(ce.getUnicodeStart()));
@@ -443,31 +440,28 @@ public class TTFReader extends AbstractFontReader {
Document doc = parent.getOwnerDocument();
// Get kerning
- Iterator iter;
+ Set<Integer> kerningKeys;
if (isCid) {
- iter = ttf.getKerning().keySet().iterator();
+ kerningKeys = ttf.getKerning().keySet();
} else {
- iter = ttf.getAnsiKerning().keySet().iterator();
+ kerningKeys = ttf.getAnsiKerning().keySet();
}
- while (iter.hasNext()) {
- Integer kpx1 = (Integer)iter.next();
+ for (Integer kpx1 : kerningKeys) {
el = doc.createElement("kerning");
el.setAttribute("kpx1", kpx1.toString());
parent.appendChild(el);
Element el2 = null;
- Map h2;
+ Map<Integer, Integer> h2;
if (isCid) {
- h2 = (Map)ttf.getKerning().get(kpx1);
+ h2 = ttf.getKerning().get(kpx1);
} else {
- h2 = (Map)ttf.getAnsiKerning().get(kpx1);
+ h2 = ttf.getAnsiKerning().get(kpx1);
}
- Iterator iter2 = h2.keySet().iterator();
- while (iter2.hasNext()) {
- Integer kpx2 = (Integer)iter2.next();
+ for (Integer kpx2 : h2.keySet()) {
if (isCid || kpx2.intValue() < 256) {
el2 = doc.createElement("pair");
el2.setAttribute("kpx2", kpx2.toString());
diff --git a/src/java/org/apache/fop/fonts/autodetect/FontInfoFinder.java b/src/java/org/apache/fop/fonts/autodetect/FontInfoFinder.java
index bd0a33cb1..deee4b018 100644
--- a/src/java/org/apache/fop/fonts/autodetect/FontInfoFinder.java
+++ b/src/java/org/apache/fop/fonts/autodetect/FontInfoFinder.java
@@ -32,6 +32,7 @@ import org.apache.commons.logging.LogFactory;
import org.apache.fop.fonts.CustomFont;
import org.apache.fop.fonts.EmbedFontInfo;
+import org.apache.fop.fonts.EmbeddingMode;
import org.apache.fop.fonts.EncodingMode;
import org.apache.fop.fonts.Font;
import org.apache.fop.fonts.FontCache;
@@ -222,7 +223,7 @@ public class FontInfoFinder {
}
try {
TTFFontLoader ttfLoader = new TTFFontLoader(
- fontFileURL, fontName, true, EncodingMode.AUTO,
+ fontFileURL, fontName, true, EmbeddingMode.AUTO, EncodingMode.AUTO,
useKerning, useAdvanced, resolver);
customFont = ttfLoader.getFont();
if (this.eventListener != null) {
@@ -247,7 +248,8 @@ public class FontInfoFinder {
} else {
// The normal case
try {
- customFont = FontLoader.loadFont(fontURL, null, true, EncodingMode.AUTO, resolver);
+ customFont = FontLoader.loadFont(fontURL, null, true, EmbeddingMode.AUTO,
+ EncodingMode.AUTO, resolver);
if (this.eventListener != null) {
customFont.setEventListener(this.eventListener);
}
diff --git a/src/java/org/apache/fop/fonts/truetype/FontFileReader.java b/src/java/org/apache/fop/fonts/truetype/FontFileReader.java
index b97120990..5da25e4b4 100644
--- a/src/java/org/apache/fop/fonts/truetype/FontFileReader.java
+++ b/src/java/org/apache/fop/fonts/truetype/FontFileReader.java
@@ -90,23 +90,13 @@ public class FontFileReader {
}
/**
- * Set current file position to offset
- *
- * @param add The number of bytes to advance
- * @throws IOException In case of an I/O problem
- */
- public void seekAdd(long add) throws IOException {
- seekSet(current + add);
- }
-
- /**
* Skip a given number of bytes.
*
* @param add The number of bytes to advance
* @throws IOException In case of an I/O problem
*/
public void skip(long add) throws IOException {
- seekAdd(add);
+ seekSet(current + add);
}
/**
@@ -133,7 +123,7 @@ public class FontFileReader {
* @return One byte
* @throws IOException If EOF is reached
*/
- public byte read() throws IOException {
+ private byte read() throws IOException {
if (current >= fsize) {
throw new java.io.EOFException("Reached EOF, file size=" + fsize);
}
@@ -278,14 +268,14 @@ public class FontFileReader {
public final String readTTFString() throws IOException {
int i = current;
while (file[i++] != 0) {
- if (i > fsize) {
+ if (i >= fsize) {
throw new java.io.EOFException("Reached EOF, file size="
+ fsize);
}
}
- byte[] tmp = new byte[i - current];
- System.arraycopy(file, current, tmp, 0, i - current);
+ byte[] tmp = new byte[i - current - 1];
+ System.arraycopy(file, current, tmp, 0, i - current - 1);
return new String(tmp, "ISO-8859-1");
}
@@ -353,6 +343,11 @@ public class FontFileReader {
System.arraycopy(file, offset, ret, 0, length);
return ret;
}
-
-
+ /**
+ * Returns the full byte array representation of the file.
+ * @return byte array.
+ */
+ public byte[] getAllBytes() {
+ return file;
+ }
}
diff --git a/src/java/org/apache/fop/fonts/truetype/TTFCmapEntry.java b/src/java/org/apache/fop/fonts/truetype/TTFCmapEntry.java
deleted file mode 100644
index 897d5e2de..000000000
--- a/src/java/org/apache/fop/fonts/truetype/TTFCmapEntry.java
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * 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.truetype;
-
-/**
- * The CMap entry contains information of a Unicode range and the
- * the glyph indexes related to the range
- */
-public class TTFCmapEntry {
-
- private int unicodeStart;
- private int unicodeEnd;
- private int glyphStartIndex;
-
- TTFCmapEntry() {
- unicodeStart = 0;
- unicodeEnd = 0;
- glyphStartIndex = 0;
- }
-
- TTFCmapEntry(int unicodeStart, int unicodeEnd, int glyphStartIndex) {
- this.unicodeStart = unicodeStart;
- this.unicodeEnd = unicodeEnd;
- this.glyphStartIndex = glyphStartIndex;
- }
-
- /**
- * {@inheritDoc}
- */
- public int hashCode() {
- int hc = super.hashCode();
- hc ^= ( hc * 11 ) + unicodeStart;
- hc ^= ( hc * 19 ) + unicodeEnd;
- hc ^= ( hc * 23 ) + glyphStartIndex;
- return hc;
- }
-
- /**
- * {@inheritDoc}
- */
- public boolean equals(Object o) {
- if (o instanceof TTFCmapEntry) {
- TTFCmapEntry ce = (TTFCmapEntry)o;
- if (ce.unicodeStart == this.unicodeStart
- && ce.unicodeEnd == this.unicodeEnd
- && ce.glyphStartIndex == this.glyphStartIndex) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Returns the glyphStartIndex.
- * @return int
- */
- public int getGlyphStartIndex() {
- return glyphStartIndex;
- }
-
- /**
- * Returns the unicodeEnd.
- * @return int
- */
- public int getUnicodeEnd() {
- return unicodeEnd;
- }
-
- /**
- * Returns the unicodeStart.
- * @return int
- */
- public int getUnicodeStart() {
- return unicodeStart;
- }
-
- /**
- * Sets the glyphStartIndex.
- * @param glyphStartIndex The glyphStartIndex to set
- */
- public void setGlyphStartIndex(int glyphStartIndex) {
- this.glyphStartIndex = glyphStartIndex;
- }
-
- /**
- * Sets the unicodeEnd.
- * @param unicodeEnd The unicodeEnd to set
- */
- public void setUnicodeEnd(int unicodeEnd) {
- this.unicodeEnd = unicodeEnd;
- }
-
- /**
- * Sets the unicodeStart.
- * @param unicodeStart The unicodeStart to set
- */
- public void setUnicodeStart(int unicodeStart) {
- this.unicodeStart = unicodeStart;
- }
-
-}
diff --git a/src/java/org/apache/fop/fonts/truetype/TTFDirTabEntry.java b/src/java/org/apache/fop/fonts/truetype/TTFDirTabEntry.java
index 1f05ebfa1..c273d4471 100644
--- a/src/java/org/apache/fop/fonts/truetype/TTFDirTabEntry.java
+++ b/src/java/org/apache/fop/fonts/truetype/TTFDirTabEntry.java
@@ -33,6 +33,14 @@ public class TTFDirTabEntry {
private long offset;
private long length;
+ public TTFDirTabEntry() {
+ }
+
+ public TTFDirTabEntry(long offset, long length) {
+ this.offset = offset;
+ this.length = length;
+ }
+
/**
* Read Dir Tab.
* @param in font file reader
diff --git a/src/java/org/apache/fop/fonts/truetype/TTFFile.java b/src/java/org/apache/fop/fonts/truetype/TTFFile.java
index 65ab560cf..bc979f277 100644
--- a/src/java/org/apache/fop/fonts/truetype/TTFFile.java
+++ b/src/java/org/apache/fop/fonts/truetype/TTFFile.java
@@ -20,11 +20,18 @@
package org.apache.fop.fonts.truetype;
import java.io.IOException;
+import java.util.ArrayList;
import java.util.BitSet;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -36,6 +43,7 @@ import org.apache.fop.complexscripts.fonts.GlyphDefinitionTable;
import org.apache.fop.complexscripts.fonts.GlyphPositioningTable;
import org.apache.fop.complexscripts.fonts.GlyphSubstitutionTable;
import org.apache.fop.complexscripts.fonts.OTFAdvancedTypographicTableReader;
+import org.apache.fop.fonts.CMapSegment;
import org.apache.fop.fonts.FontUtil;
/**
@@ -46,10 +54,100 @@ import org.apache.fop.fonts.FontUtil;
public class TTFFile {
static final byte NTABS = 24;
- static final int NMACGLYPHS = 258;
static final int MAX_CHAR_CODE = 255;
static final int ENC_BUF_SIZE = 1024;
+ private static final String[] MAC_GLYPH_ORDERING = {
+ /* 0x000 */
+ ".notdef", ".null", "nonmarkingreturn", "space",
+ "exclam", "quotedbl", "numbersign", "dollar",
+ "percent", "ampersand", "quotesingle", "parenleft",
+ "parenright", "asterisk", "plus", "comma",
+ /* 0x010 */
+ "hyphen", "period", "slash", "zero",
+ "one", "two", "three", "four",
+ "five", "six", "seven", "eight",
+ "nine", "colon", "semicolon", "less",
+ /* 0x020 */
+ "equal", "greater", "question", "at",
+ "A", "B", "C", "D",
+ "E", "F", "G", "H",
+ "I", "J", "K", "L",
+ /* 0x030 */
+ "M", "N", "O", "P",
+ "Q", "R", "S", "T",
+ "U", "V", "W", "X",
+ "Y", "Z", "bracketleft", "backslash",
+ /* 0x040 */
+ "bracketright", "asciicircum", "underscore", "grave",
+ "a", "b", "c", "d",
+ "e", "f", "g", "h",
+ "i", "j", "k", "l",
+ /* 0x050 */
+ "m", "n", "o", "p",
+ "q", "r", "s", "t",
+ "u", "v", "w", "x",
+ "y", "z", "braceleft", "bar",
+ /* 0x060 */
+ "braceright", "asciitilde", "Adieresis", "Aring",
+ "Ccedilla", "Eacute", "Ntilde", "Odieresis",
+ "Udieresis", "aacute", "agrave", "acircumflex",
+ "adieresis", "atilde", "aring", "ccedilla",
+ /* 0x070 */
+ "eacute", "egrave", "ecircumflex", "edieresis",
+ "iacute", "igrave", "icircumflex", "idieresis",
+ "ntilde", "oacute", "ograve", "ocircumflex",
+ "odieresis", "otilde", "uacute", "ugrave",
+ /* 0x080 */
+ "ucircumflex", "udieresis", "dagger", "degree",
+ "cent", "sterling", "section", "bullet",
+ "paragraph", "germandbls", "registered", "copyright",
+ "trademark", "acute", "dieresis", "notequal",
+ /* 0x090 */
+ "AE", "Oslash", "infinity", "plusminus",
+ "lessequal", "greaterequal", "yen", "mu",
+ "partialdiff", "summation", "product", "pi",
+ "integral", "ordfeminine", "ordmasculine", "Omega",
+ /* 0x0A0 */
+ "ae", "oslash", "questiondown", "exclamdown",
+ "logicalnot", "radical", "florin", "approxequal",
+ "Delta", "guillemotleft", "guillemotright", "ellipsis",
+ "nonbreakingspace", "Agrave", "Atilde", "Otilde",
+ /* 0x0B0 */
+ "OE", "oe", "endash", "emdash",
+ "quotedblleft", "quotedblright", "quoteleft", "quoteright",
+ "divide", "lozenge", "ydieresis", "Ydieresis",
+ "fraction", "currency", "guilsinglleft", "guilsinglright",
+ /* 0x0C0 */
+ "fi", "fl", "daggerdbl", "periodcentered",
+ "quotesinglbase", "quotedblbase", "perthousand", "Acircumflex",
+ "Ecircumflex", "Aacute", "Edieresis", "Egrave",
+ "Iacute", "Icircumflex", "Idieresis", "Igrave",
+ /* 0x0D0 */
+ "Oacute", "Ocircumflex", "apple", "Ograve",
+ "Uacute", "Ucircumflex", "Ugrave", "dotlessi",
+ "circumflex", "tilde", "macron", "breve",
+ "dotaccent", "ring", "cedilla", "hungarumlaut",
+ /* 0x0E0 */
+ "ogonek", "caron", "Lslash", "lslash",
+ "Scaron", "scaron", "Zcaron", "zcaron",
+ "brokenbar", "Eth", "eth", "Yacute",
+ "yacute", "Thorn", "thorn", "minus",
+ /* 0x0F0 */
+ "multiply", "onesuperior", "twosuperior", "threesuperior",
+ "onehalf", "onequarter", "threequarters", "franc",
+ "Gbreve", "gbreve", "Idotaccent", "Scedilla",
+ "scedilla", "Cacute", "cacute", "Ccaron",
+ /* 0x100 */
+ "ccaron", "dcroat"
+ };
+
+ /** The FontFileReader used to read this TrueType font. */
+ protected FontFileReader fontFile;
+
+ /** Set to true to get even more debug output than with level DEBUG */
+ public static final boolean TRACE_ENABLED = false;
+
private final String encoding = "WinAnsiEncoding"; // Default encoding
private final short firstChar = 0;
@@ -61,33 +159,31 @@ public class TTFFile {
/**
* Table directory
*/
- protected Map dirTabs;
- private Map<Integer, Map<Integer, Integer>> kerningTab; // for CIDs
+ protected Map<TTFTableName, TTFDirTabEntry> dirTabs;
+ private Map<Integer, Map<Integer, Integer>> kerningTab; // for CIDs
private Map<Integer, Map<Integer, Integer>> ansiKerningTab; // For winAnsiEncoding
- private List cmaps;
- private Set unicodeMappings;
+ private List<CMapSegment> cmaps;
+ private Set<UnicodeMapping> unicodeMappings;
private int upem; // unitsPerEm from "head" table
private int nhmtx; // Number of horizontal metrics
- private int postFormat;
+ private PostScriptVersion postScriptVersion;
private int locaFormat;
/**
* Offset to last loca
*/
protected long lastLoca = 0;
private int numberOfGlyphs; // Number of glyphs in font (read from "maxp" table)
- private int nmGlyphs; // Used in fixWidths - remove?
/**
* Contains glyph data
*/
protected TTFMtxEntry[] mtxTab; // Contains glyph data
- private final int[] mtxEncoded = null;
private String postScriptName = "";
private String fullName = "";
private String notice = "";
- private final Set familyNames = new java.util.HashSet(); //Set<String>
+ private Set<String> familyNames = new HashSet<String>();
private String subFamilyName = "";
private long italicAngle = 0;
@@ -116,12 +212,12 @@ public class TTFFile {
private short lastChar = 0;
private int[] ansiWidth;
- private Map ansiIndex;
+ private Map<Integer, List<Integer>> ansiIndex;
// internal mapping of glyph indexes to unicode indexes
// used for quick mappings in this class
- private final Map glyphToUnicodeMap = new java.util.HashMap();
- private final Map unicodeToGlyphMap = new java.util.HashMap();
+ private final Map<Integer, Integer> glyphToUnicodeMap = new HashMap<Integer, Integer> ();
+ private final Map<Integer, Integer> unicodeToGlyphMap = new HashMap<Integer, Integer> ();
private TTFDirTabEntry currentDirTab;
@@ -136,6 +232,10 @@ public class TTFFile {
*/
protected Log log = LogFactory.getLog(TTFFile.class);
+ public TTFFile() {
+ this(true, false);
+ }
+
/**
* Constructor
* @param useKerning true if kerning data should be loaded
@@ -147,9 +247,9 @@ public class TTFFile {
}
/**
- * Key-value helper class
+ * Key-value helper class.
*/
- class UnicodeMapping implements Comparable {
+ final class UnicodeMapping implements Comparable {
private final int unicodeIndex;
private final int glyphIndex;
@@ -217,28 +317,42 @@ public class TTFFile {
}
/**
+ * Version of the PostScript table (<q>post</q>) contained in this font.
+ */
+ public static enum PostScriptVersion {
+ /** PostScript table version 1.0. */
+ V1,
+ /** PostScript table version 2.0. */
+ V2,
+ /** PostScript table version 3.0. */
+ V3,
+ /** Unknown version of the PostScript table. */
+ UNKNOWN;
+ }
+
+ /**
* Obtain directory table entry.
* @param name (tag) of entry
* @return a directory table entry or null if none found
*/
- public TTFDirTabEntry getDirectoryEntry ( String name ) {
- return (TTFDirTabEntry) dirTabs.get ( name );
+ public TTFDirTabEntry getDirectoryEntry(TTFTableName name) {
+ return dirTabs.get(name);
}
/**
* Position inputstream to position indicated
* in the dirtab offset + offset
* @param in font file reader
- * @param name (tag) of table
+ * @param tableName (tag) of table
* @param offset from start of table
* @return true if seek succeeded
* @throws IOException if I/O exception occurs during seek
*/
- public boolean seekTab(FontFileReader in, String name,
+ public boolean seekTab(FontFileReader in, TTFTableName tableName,
long offset) throws IOException {
- TTFDirTabEntry dt = getDirectoryEntry ( name );
+ TTFDirTabEntry dt = dirTabs.get(tableName);
if (dt == null) {
- log.error("Dirtab " + name + " not found.");
+ log.error("Dirtab " + tableName.getName() + " not found.");
return false;
} else {
in.seekSet(dt.getOffset() + offset);
@@ -274,12 +388,12 @@ public class TTFFile {
* Set the unicodeIndex in the TTFMtxEntries and fills in the
* cmaps vector.
*/
- private boolean readCMAP(FontFileReader in) throws IOException {
+ private boolean readCMAP() throws IOException {
unicodeMappings = new java.util.TreeSet();
- seekTab(in, "cmap", 2);
- int numCMap = in.readTTFUShort(); // Number of cmap subtables
+ seekTab(fontFile, TTFTableName.CMAP, 2);
+ int numCMap = fontFile.readTTFUShort(); // Number of cmap subtables
long cmapUniOffset = 0;
long symbolMapOffset = 0;
@@ -289,9 +403,9 @@ public class TTFFile {
//Read offset for all tables. We are only interested in the unicode table
for (int i = 0; i < numCMap; i++) {
- int cmapPID = in.readTTFUShort();
- int cmapEID = in.readTTFUShort();
- long cmapOffset = in.readTTFULong();
+ int cmapPID = fontFile.readTTFUShort();
+ int cmapEID = fontFile.readTTFUShort();
+ long cmapOffset = fontFile.readTTFLong();
if (log.isDebugEnabled()) {
log.debug("Platform ID: " + cmapPID + " Encoding: " + cmapEID);
@@ -306,9 +420,9 @@ public class TTFFile {
}
if (cmapUniOffset > 0) {
- return readUnicodeCmap(in, cmapUniOffset, 1);
+ return readUnicodeCmap(cmapUniOffset, 1);
} else if (symbolMapOffset > 0) {
- return readUnicodeCmap(in, symbolMapOffset, 0);
+ return readUnicodeCmap(symbolMapOffset, 0);
} else {
log.fatal("Unsupported TrueType font: No Unicode or Symbol cmap table"
+ " not present. Aborting");
@@ -317,26 +431,26 @@ public class TTFFile {
}
private boolean readUnicodeCmap // CSOK: MethodLength
- (FontFileReader in, long cmapUniOffset, int encodingID)
+ (long cmapUniOffset, int encodingID)
throws IOException {
//Read CMAP table and correct mtxTab.index
int mtxPtr = 0;
// Read unicode cmap
- seekTab(in, "cmap", cmapUniOffset);
- int cmapFormat = in.readTTFUShort();
- /*int cmap_length =*/ in.readTTFUShort(); //skip cmap length
+ seekTab(fontFile, TTFTableName.CMAP, cmapUniOffset);
+ int cmapFormat = fontFile.readTTFUShort();
+ /*int cmap_length =*/ fontFile.readTTFUShort(); //skip cmap length
if (log.isDebugEnabled()) {
log.debug("CMAP format: " + cmapFormat);
}
if (cmapFormat == 4) {
- in.skip(2); // Skip version number
- int cmapSegCountX2 = in.readTTFUShort();
- int cmapSearchRange = in.readTTFUShort();
- int cmapEntrySelector = in.readTTFUShort();
- int cmapRangeShift = in.readTTFUShort();
+ fontFile.skip(2); // Skip version number
+ int cmapSegCountX2 = fontFile.readTTFUShort();
+ int cmapSearchRange = fontFile.readTTFUShort();
+ int cmapEntrySelector = fontFile.readTTFUShort();
+ int cmapRangeShift = fontFile.readTTFUShort();
if (log.isDebugEnabled()) {
log.debug("segCountX2 : " + cmapSegCountX2);
@@ -352,26 +466,26 @@ public class TTFFile {
int[] cmapRangeOffsets = new int[cmapSegCountX2 / 2];
for (int i = 0; i < (cmapSegCountX2 / 2); i++) {
- cmapEndCounts[i] = in.readTTFUShort();
+ cmapEndCounts[i] = fontFile.readTTFUShort();
}
- in.skip(2); // Skip reservedPad
+ fontFile.skip(2); // Skip reservedPad
for (int i = 0; i < (cmapSegCountX2 / 2); i++) {
- cmapStartCounts[i] = in.readTTFUShort();
+ cmapStartCounts[i] = fontFile.readTTFUShort();
}
for (int i = 0; i < (cmapSegCountX2 / 2); i++) {
- cmapDeltas[i] = in.readTTFShort();
+ cmapDeltas[i] = fontFile.readTTFShort();
}
//int startRangeOffset = in.getCurrentPos();
for (int i = 0; i < (cmapSegCountX2 / 2); i++) {
- cmapRangeOffsets[i] = in.readTTFUShort();
+ cmapRangeOffsets[i] = fontFile.readTTFUShort();
}
- int glyphIdArrayOffset = in.getCurrentPos();
+ int glyphIdArrayOffset = fontFile.getCurrentPos();
BitSet eightBitGlyphs = new BitSet(256);
@@ -413,19 +527,17 @@ public class TTFFile {
+ (j - cmapStartCounts[i])
+ (i)
- cmapSegCountX2 / 2) * 2;
- in.seekSet(glyphOffset);
- glyphIdx = (in.readTTFUShort() + cmapDeltas[i])
+ fontFile.seekSet(glyphOffset);
+ glyphIdx = (fontFile.readTTFUShort() + cmapDeltas[i])
& 0xffff;
unicodeMappings.add(new UnicodeMapping(glyphIdx, j));
mtxTab[glyphIdx].getUnicodeIndex().add(new Integer(j));
// Also add winAnsiWidth
- List v = (List)ansiIndex.get(new Integer(j));
+ List<Integer> v = ansiIndex.get(new Integer(j));
if (v != null) {
- Iterator e = v.listIterator();
- while (e.hasNext()) {
- Integer aIdx = (Integer)e.next();
+ for (Integer aIdx : v) {
ansiWidth[aIdx.intValue()]
= mtxTab[glyphIdx].getWx();
@@ -466,11 +578,9 @@ public class TTFFile {
}
// Also add winAnsiWidth
- List v = (List)ansiIndex.get(new Integer(j));
+ List<Integer> v = ansiIndex.get(new Integer(j));
if (v != null) {
- Iterator e = v.listIterator();
- while (e.hasNext()) {
- Integer aIdx = (Integer)e.next();
+ for (Integer aIdx : v) {
ansiWidth[aIdx.intValue()] = mtxTab[glyphIdx].getWx();
}
}
@@ -548,14 +658,14 @@ public class TTFFile {
// Create an index hash to the ansiWidth
// Can't just index the winAnsiEncoding when inserting widths
// same char (eg bullet) is repeated more than one place
- ansiIndex = new java.util.HashMap();
+ ansiIndex = new HashMap<Integer, List<Integer>>();
for (int i = 32; i < Glyphs.WINANSI_ENCODING.length; i++) {
Integer ansi = new Integer(i);
Integer uni = new Integer(Glyphs.WINANSI_ENCODING[i]);
- List v = (List)ansiIndex.get(uni);
+ List<Integer> v = ansiIndex.get(uni);
if (v == null) {
- v = new java.util.ArrayList();
+ v = new ArrayList<Integer>();
ansiIndex.put(uni, v);
}
v.add(ansi);
@@ -574,12 +684,12 @@ public class TTFFile {
* @throws IOException In case of an I/O problem
*/
public boolean readFont(FontFileReader in, String name) throws IOException {
-
+ fontFile = in;
/*
* Check if TrueType collection, and that the name
* exists in the collection
*/
- if (!checkTTC(in, name)) {
+ if (!checkTTC(name)) {
if (name == null) {
throw new IllegalArgumentException(
"For TrueType collection you must specify which font "
@@ -590,26 +700,26 @@ public class TTFFile {
}
}
- readDirTabs(in);
- readFontHeader(in);
- getNumGlyphs(in);
+ readDirTabs();
+ readFontHeader();
+ getNumGlyphs();
if (log.isDebugEnabled()) {
log.debug("Number of glyphs in font: " + numberOfGlyphs);
}
- readHorizontalHeader(in);
- readHorizontalMetrics(in);
+ readHorizontalHeader();
+ readHorizontalMetrics();
initAnsiWidths();
- readPostScript(in);
- readOS2(in);
+ readPostScript();
+ readOS2();
determineAscDesc();
if (!isCFF) {
- readIndexToLocation(in);
- readGlyf(in);
+ readIndexToLocation();
+ readGlyf();
}
- readName(in);
- boolean pcltFound = readPCLT(in);
+ readName();
+ boolean pcltFound = readPCLT();
// Read cmap table and fill in ansiwidths
- boolean valid = readCMAP(in);
+ boolean valid = readCMAP();
if (!valid) {
return false;
}
@@ -617,7 +727,7 @@ public class TTFFile {
createCMaps();
if ( useKerning ) {
- readKerning(in);
+ readKerning();
}
// Read advanced typographic tables.
@@ -640,33 +750,47 @@ public class TTFFile {
return true;
}
+ /**
+ * Reads a font.
+ *
+ * @param in FontFileReader to read from
+ * @param name Name to be checked for in the font file
+ * @param glyphs Map of glyphs (glyphs has old index as (Integer) key and
+ * new index as (Integer) value)
+ * @throws IOException in case of an I/O problem
+ */
+ public void readFont(FontFileReader in, String name,
+ Map<Integer, Integer> glyphs) throws IOException {
+ readFont(in, name);
+ }
+
private void createCMaps() {
- cmaps = new java.util.ArrayList();
- TTFCmapEntry tce = new TTFCmapEntry();
+ cmaps = new ArrayList<CMapSegment>();
+ int unicodeStart;
+ int glyphStart;
+ int unicodeEnd;
- Iterator e = unicodeMappings.iterator();
- UnicodeMapping um = (UnicodeMapping)e.next();
+ Iterator<UnicodeMapping> e = unicodeMappings.iterator();
+ UnicodeMapping um = e.next();
UnicodeMapping lastMapping = um;
- tce.setUnicodeStart(um.getUnicodeIndex());
- tce.setGlyphStartIndex(um.getGlyphIndex());
+ unicodeStart = um.getUnicodeIndex();
+ glyphStart = um.getGlyphIndex();
while (e.hasNext()) {
- um = (UnicodeMapping)e.next();
+ um = e.next();
if (((lastMapping.getUnicodeIndex() + 1) != um.getUnicodeIndex())
|| ((lastMapping.getGlyphIndex() + 1) != um.getGlyphIndex())) {
- tce.setUnicodeEnd(lastMapping.getUnicodeIndex());
- cmaps.add(tce);
-
- tce = new TTFCmapEntry();
- tce.setUnicodeStart(um.getUnicodeIndex());
- tce.setGlyphStartIndex(um.getGlyphIndex());
+ unicodeEnd = lastMapping.getUnicodeIndex();
+ cmaps.add(new CMapSegment(unicodeStart, unicodeEnd, glyphStart));
+ unicodeStart = um.getUnicodeIndex();
+ glyphStart = um.getGlyphIndex();
}
lastMapping = um;
}
- tce.setUnicodeEnd(um.getUnicodeIndex());
- cmaps.add(tce);
+ unicodeEnd = lastMapping.getUnicodeIndex();
+ cmaps.add(new CMapSegment(unicodeStart, unicodeEnd, glyphStart));
}
/**
@@ -681,11 +805,15 @@ public class TTFFile {
}
}
+ PostScriptVersion getPostScriptVersion() {
+ return postScriptVersion;
+ }
+
/**
* Returns the font family names of the font.
* @return Set The family names (a Set of Strings)
*/
- public Set getFamilyNames() {
+ public Set<String> getFamilyNames() {
return familyNames;
}
@@ -730,19 +858,30 @@ public class TTFFile {
}
/**
+ * Returns the number of bytes necessary to pad the currentPosition so that a table begins
+ * on a 4-byte boundary.
+ * @param currentPosition the position to pad.
+ * @return int the number of bytes to pad.
+ */
+ protected int getPadSize(int currentPosition) {
+ int padSize = 4 - (currentPosition % 4);
+ return padSize < 4 ? padSize : 0;
+ }
+
+ /**
* Returns the Flags attribute of the font.
* @return int The Flags
*/
public int getFlags() {
int flags = 32; // Use Adobe Standard charset
if (italicAngle != 0) {
- flags = flags | 64;
+ flags |= 64;
}
if (isFixedPitch != 0) {
- flags = flags | 2;
+ flags |= 2;
}
if (hasSerifs) {
- flags = flags | 1;
+ flags |= 1;
}
return flags;
}
@@ -780,7 +919,6 @@ public class TTFFile {
}
/**
- * Returns the font bounding box.
* @return int[] The font bbox
*/
public int[] getFontBBox() {
@@ -886,11 +1024,10 @@ public class TTFFile {
* FontFileReader and fill the global HashMap dirTabs
* with the table name (String) as key and a TTFDirTabEntry
* as value.
- * @param in FontFileReader to read the table directory from
* @throws IOException in case of an I/O problem
*/
- protected void readDirTabs(FontFileReader in) throws IOException {
- int sfntVersion = in.readTTFLong(); // TTF_FIXED_SIZE (4 bytes)
+ protected void readDirTabs() throws IOException {
+ int sfntVersion = fontFile.readTTFLong(); // TTF_FIXED_SIZE (4 bytes)
switch (sfntVersion) {
case 0x10000:
log.debug("sfnt version: OpenType 1.0");
@@ -909,42 +1046,45 @@ public class TTFFile {
log.debug("Unknown sfnt version: " + Integer.toHexString(sfntVersion));
break;
}
- int ntabs = in.readTTFUShort();
- in.skip(6); // 3xTTF_USHORT_SIZE
+ int ntabs = fontFile.readTTFUShort();
+ fontFile.skip(6); // 3xTTF_USHORT_SIZE
- dirTabs = new java.util.HashMap();
+ dirTabs = new HashMap<TTFTableName, TTFDirTabEntry>();
TTFDirTabEntry[] pd = new TTFDirTabEntry[ntabs];
log.debug("Reading " + ntabs + " dir tables");
+
for (int i = 0; i < ntabs; i++) {
pd[i] = new TTFDirTabEntry();
- dirTabs.put(pd[i].read(in), pd[i]);
+ String tableName = pd[i].read(fontFile);
+ dirTabs.put(TTFTableName.getValue(tableName), pd[i]);
}
+ dirTabs.put(TTFTableName.TABLE_DIRECTORY,
+ new TTFDirTabEntry(0L, fontFile.getCurrentPos()));
log.debug("dir tables: " + dirTabs.keySet());
}
/**
* Read the "head" table, this reads the bounding box and
* sets the upem (unitsPerEM) variable
- * @param in FontFileReader to read the header from
* @throws IOException in case of an I/O problem
*/
- protected void readFontHeader(FontFileReader in) throws IOException {
- seekTab(in, "head", 2 * 4 + 2 * 4);
- int flags = in.readTTFUShort();
+ protected void readFontHeader() throws IOException {
+ seekTab(fontFile, TTFTableName.HEAD, 2 * 4 + 2 * 4);
+ int flags = fontFile.readTTFUShort();
if (log.isDebugEnabled()) {
log.debug("flags: " + flags + " - " + Integer.toString(flags, 2));
}
- upem = in.readTTFUShort();
+ upem = fontFile.readTTFUShort();
if (log.isDebugEnabled()) {
log.debug("unit per em: " + upem);
}
- in.skip(16);
+ fontFile.skip(16);
- fontBBox1 = in.readTTFShort();
- fontBBox2 = in.readTTFShort();
- fontBBox3 = in.readTTFShort();
- fontBBox4 = in.readTTFShort();
+ fontBBox1 = fontFile.readTTFShort();
+ fontBBox2 = fontFile.readTTFShort();
+ fontBBox3 = fontFile.readTTFShort();
+ fontBBox4 = fontFile.readTTFShort();
if (log.isDebugEnabled()) {
log.debug("font bbox: xMin=" + fontBBox1
+ " yMin=" + fontBBox2
@@ -952,19 +1092,18 @@ public class TTFFile {
+ " yMax=" + fontBBox4);
}
- in.skip(2 + 2 + 2);
+ fontFile.skip(2 + 2 + 2);
- locaFormat = in.readTTFShort();
+ locaFormat = fontFile.readTTFShort();
}
/**
* Read the number of glyphs from the "maxp" table
- * @param in FontFileReader to read the number of glyphs from
* @throws IOException in case of an I/O problem
*/
- protected void getNumGlyphs(FontFileReader in) throws IOException {
- seekTab(in, "maxp", 4);
- numberOfGlyphs = in.readTTFUShort();
+ protected void getNumGlyphs() throws IOException {
+ seekTab(fontFile, TTFTableName.MAXP, 4);
+ numberOfGlyphs = fontFile.readTTFUShort();
}
@@ -972,17 +1111,16 @@ public class TTFFile {
* Read the "hhea" table to find the ascender and descender and
* size of "hmtx" table, as a fixed size font might have only
* one width.
- * @param in FontFileReader to read the hhea table from
* @throws IOException in case of an I/O problem
*/
- protected void readHorizontalHeader(FontFileReader in)
+ protected void readHorizontalHeader()
throws IOException {
- seekTab(in, "hhea", 4);
- hheaAscender = in.readTTFShort();
- hheaDescender = in.readTTFShort();
+ seekTab(fontFile, TTFTableName.HHEA, 4);
+ hheaAscender = fontFile.readTTFShort();
+ hheaDescender = fontFile.readTTFShort();
- in.skip(2 + 2 + 3 * 2 + 8 * 2);
- nhmtx = in.readTTFUShort();
+ fontFile.skip(2 + 2 + 3 * 2 + 8 * 2);
+ nhmtx = fontFile.readTTFUShort();
if (log.isDebugEnabled()) {
log.debug("hhea.Ascender: " + formatUnitsForDebug(hheaAscender));
@@ -996,12 +1134,11 @@ public class TTFFile {
* in the mtxTab array. If the number of metrics is less
* than the number of glyphs (eg fixed size fonts), extend
* the mtxTab array and fill in the missing widths
- * @param in FontFileReader to read the hmtx table from
* @throws IOException in case of an I/O problem
*/
- protected void readHorizontalMetrics(FontFileReader in)
+ protected void readHorizontalMetrics()
throws IOException {
- seekTab(in, "hmtx", 0);
+ seekTab(fontFile, TTFTableName.HMTX, 0);
int mtxSize = Math.max(numberOfGlyphs, nhmtx);
mtxTab = new TTFMtxEntry[mtxSize];
@@ -1013,8 +1150,8 @@ public class TTFFile {
mtxTab[i] = new TTFMtxEntry();
}
for (int i = 0; i < nhmtx; i++) {
- mtxTab[i].setWx(in.readTTFUShort());
- mtxTab[i].setLsb(in.readTTFUShort());
+ mtxTab[i].setWx(fontFile.readTTFUShort());
+ mtxTab[i].setLsb(fontFile.readTTFUShort());
if (log.isTraceEnabled()) {
log.trace(" width[" + i + "] = "
@@ -1027,7 +1164,7 @@ public class TTFFile {
int lastWidth = mtxTab[nhmtx - 1].getWx();
for (int i = nhmtx; i < mtxSize; i++) {
mtxTab[i].setWx(lastWidth);
- mtxTab[i].setLsb(in.readTTFUShort());
+ mtxTab[i].setLsb(fontFile.readTTFUShort());
}
}
}
@@ -1037,35 +1174,37 @@ public class TTFFile {
* Read the "post" table
* containing the PostScript names of the glyphs.
*/
- private void readPostScript(FontFileReader in) throws IOException {
- seekTab(in, "post", 0);
- postFormat = in.readTTFLong();
- italicAngle = in.readTTFULong();
- underlinePosition = in.readTTFShort();
- underlineThickness = in.readTTFShort();
- isFixedPitch = in.readTTFULong();
+ private void readPostScript() throws IOException {
+ seekTab(fontFile, TTFTableName.POST, 0);
+ int postFormat = fontFile.readTTFLong();
+ italicAngle = fontFile.readTTFULong();
+ underlinePosition = fontFile.readTTFShort();
+ underlineThickness = fontFile.readTTFShort();
+ isFixedPitch = fontFile.readTTFULong();
//Skip memory usage values
- in.skip(4 * 4);
+ fontFile.skip(4 * 4);
log.debug("PostScript format: 0x" + Integer.toHexString(postFormat));
switch (postFormat) {
case 0x00010000:
log.debug("PostScript format 1");
- for (int i = 0; i < Glyphs.MAC_GLYPH_NAMES.length; i++) {
- mtxTab[i].setName(Glyphs.MAC_GLYPH_NAMES[i]);
+ postScriptVersion = PostScriptVersion.V1;
+ for (int i = 0; i < MAC_GLYPH_ORDERING.length; i++) {
+ mtxTab[i].setName(MAC_GLYPH_ORDERING[i]);
}
break;
case 0x00020000:
log.debug("PostScript format 2");
+ postScriptVersion = PostScriptVersion.V2;
int numGlyphStrings = 0;
// Read Number of Glyphs
- int l = in.readTTFUShort();
+ int l = fontFile.readTTFUShort();
// Read indexes
for (int i = 0; i < l; i++) {
- mtxTab[i].setIndex(in.readTTFUShort());
+ mtxTab[i].setIndex(fontFile.readTTFUShort());
if (mtxTab[i].getIndex() > 257) {
//Index is not in the Macintosh standard set
@@ -1085,16 +1224,16 @@ public class TTFFile {
+ " set. Total number of glyphs=" + l);
}
for (int i = 0; i < psGlyphsBuffer.length; i++) {
- psGlyphsBuffer[i] = in.readTTFString(in.readTTFUByte());
+ psGlyphsBuffer[i] = fontFile.readTTFString(fontFile.readTTFUByte());
}
//Set glyph names
for (int i = 0; i < l; i++) {
- if (mtxTab[i].getIndex() < NMACGLYPHS) {
- mtxTab[i].setName(Glyphs.MAC_GLYPH_NAMES[mtxTab[i].getIndex()]);
+ if (mtxTab[i].getIndex() < MAC_GLYPH_ORDERING.length) {
+ mtxTab[i].setName(MAC_GLYPH_ORDERING[mtxTab[i].getIndex()]);
} else {
if (!mtxTab[i].isIndexReserved()) {
- int k = mtxTab[i].getIndex() - NMACGLYPHS;
+ int k = mtxTab[i].getIndex() - MAC_GLYPH_ORDERING.length;
if (log.isTraceEnabled()) {
log.trace(k + " i=" + i + " mtx=" + mtxTab.length
@@ -1110,9 +1249,11 @@ public class TTFFile {
case 0x00030000:
// PostScript format 3 contains no glyph names
log.debug("PostScript format 3");
+ postScriptVersion = PostScriptVersion.V3;
break;
default:
log.error("Unknown PostScript format: " + postFormat);
+ postScriptVersion = PostScriptVersion.UNKNOWN;
}
}
@@ -1120,60 +1261,60 @@ public class TTFFile {
/**
* Read the "OS/2" table
*/
- private void readOS2(FontFileReader in) throws IOException {
+ private void readOS2() throws IOException {
// Check if font is embeddable
- TTFDirTabEntry os2Entry = getDirectoryEntry ( "OS/2" );
+ TTFDirTabEntry os2Entry = dirTabs.get(TTFTableName.OS2);
if (os2Entry != null) {
- seekTab(in, "OS/2", 0);
- int version = in.readTTFUShort();
+ seekTab(fontFile, TTFTableName.OS2, 0);
+ int version = fontFile.readTTFUShort();
if (log.isDebugEnabled()) {
log.debug("OS/2 table: version=" + version
+ ", offset=" + os2Entry.getOffset() + ", len=" + os2Entry.getLength());
}
- in.skip(2); //xAvgCharWidth
- this.usWeightClass = in.readTTFUShort();
+ fontFile.skip(2); //xAvgCharWidth
+ this.usWeightClass = fontFile.readTTFUShort();
// usWidthClass
- in.skip(2);
+ fontFile.skip(2);
- int fsType = in.readTTFUShort();
+ int fsType = fontFile.readTTFUShort();
if (fsType == 2) {
isEmbeddable = false;
} else {
isEmbeddable = true;
}
- in.skip(11 * 2);
- in.skip(10); //panose array
- in.skip(4 * 4); //unicode ranges
- in.skip(4);
- in.skip(3 * 2);
+ fontFile.skip(11 * 2);
+ fontFile.skip(10); //panose array
+ fontFile.skip(4 * 4); //unicode ranges
+ fontFile.skip(4);
+ fontFile.skip(3 * 2);
int v;
- os2Ascender = in.readTTFShort(); //sTypoAscender
- os2Descender = in.readTTFShort(); //sTypoDescender
+ os2Ascender = fontFile.readTTFShort(); //sTypoAscender
+ os2Descender = fontFile.readTTFShort(); //sTypoDescender
if (log.isDebugEnabled()) {
log.debug("sTypoAscender: " + os2Ascender
+ " -> internal " + convertTTFUnit2PDFUnit(os2Ascender));
log.debug("sTypoDescender: " + os2Descender
+ " -> internal " + convertTTFUnit2PDFUnit(os2Descender));
}
- v = in.readTTFShort(); //sTypoLineGap
+ v = fontFile.readTTFShort(); //sTypoLineGap
if (log.isDebugEnabled()) {
log.debug("sTypoLineGap: " + v);
}
- v = in.readTTFUShort(); //usWinAscent
+ v = fontFile.readTTFUShort(); //usWinAscent
if (log.isDebugEnabled()) {
log.debug("usWinAscent: " + formatUnitsForDebug(v));
}
- v = in.readTTFUShort(); //usWinDescent
+ v = fontFile.readTTFUShort(); //usWinDescent
if (log.isDebugEnabled()) {
log.debug("usWinDescent: " + formatUnitsForDebug(v));
}
//version 1 OS/2 table might end here
if (os2Entry.getLength() >= 78 + (2 * 4) + (2 * 2)) {
- in.skip(2 * 4);
- this.os2xHeight = in.readTTFShort(); //sxHeight
- this.os2CapHeight = in.readTTFShort(); //sCapHeight
+ fontFile.skip(2 * 4);
+ this.os2xHeight = fontFile.readTTFShort(); //sxHeight
+ this.os2CapHeight = fontFile.readTTFShort(); //sCapHeight
if (log.isDebugEnabled()) {
log.debug("sxHeight: " + this.os2xHeight);
log.debug("sCapHeight: " + this.os2CapHeight);
@@ -1187,42 +1328,40 @@ public class TTFFile {
/**
* Read the "loca" table.
- * @param in FontFileReader to read from
* @throws IOException In case of a I/O problem
*/
- protected final void readIndexToLocation(FontFileReader in)
+ protected final void readIndexToLocation()
throws IOException {
- if (!seekTab(in, "loca", 0)) {
+ if (!seekTab(fontFile, TTFTableName.LOCA, 0)) {
throw new IOException("'loca' table not found, happens when the font file doesn't"
+ " contain TrueType outlines (trying to read an OpenType CFF font maybe?)");
}
for (int i = 0; i < numberOfGlyphs; i++) {
- mtxTab[i].setOffset(locaFormat == 1 ? in.readTTFULong()
- : (in.readTTFUShort() << 1));
+ mtxTab[i].setOffset(locaFormat == 1 ? fontFile.readTTFULong()
+ : (fontFile.readTTFUShort() << 1));
}
- lastLoca = (locaFormat == 1 ? in.readTTFULong()
- : (in.readTTFUShort() << 1));
+ lastLoca = (locaFormat == 1 ? fontFile.readTTFULong()
+ : (fontFile.readTTFUShort() << 1));
}
/**
* Read the "glyf" table to find the bounding boxes.
- * @param in FontFileReader to read from
* @throws IOException In case of a I/O problem
*/
- private void readGlyf(FontFileReader in) throws IOException {
- TTFDirTabEntry dirTab = getDirectoryEntry ( "glyf" );
+ private void readGlyf() throws IOException {
+ TTFDirTabEntry dirTab = dirTabs.get(TTFTableName.GLYF);
if (dirTab == null) {
throw new IOException("glyf table not found, cannot continue");
}
for (int i = 0; i < (numberOfGlyphs - 1); i++) {
if (mtxTab[i].getOffset() != mtxTab[i + 1].getOffset()) {
- in.seekSet(dirTab.getOffset() + mtxTab[i].getOffset());
- in.skip(2);
+ fontFile.seekSet(dirTab.getOffset() + mtxTab[i].getOffset());
+ fontFile.skip(2);
final int[] bbox = {
- in.readTTFShort(),
- in.readTTFShort(),
- in.readTTFShort(),
- in.readTTFShort()};
+ fontFile.readTTFShort(),
+ fontFile.readTTFShort(),
+ fontFile.readTTFShort(),
+ fontFile.readTTFShort()};
mtxTab[i].setBoundingBox(bbox);
} else {
mtxTab[i].setBoundingBox(mtxTab[0].getBoundingBox());
@@ -1230,17 +1369,17 @@ public class TTFFile {
}
- long n = dirTab.getOffset();
+ long n = (dirTabs.get(TTFTableName.GLYF)).getOffset();
for (int i = 0; i < numberOfGlyphs; i++) {
if ((i + 1) >= mtxTab.length
|| mtxTab[i].getOffset() != mtxTab[i + 1].getOffset()) {
- in.seekSet(n + mtxTab[i].getOffset());
- in.skip(2);
+ fontFile.seekSet(n + mtxTab[i].getOffset());
+ fontFile.skip(2);
final int[] bbox = {
- in.readTTFShort(),
- in.readTTFShort(),
- in.readTTFShort(),
- in.readTTFShort()};
+ fontFile.readTTFShort(),
+ fontFile.readTTFShort(),
+ fontFile.readTTFShort(),
+ fontFile.readTTFShort()};
mtxTab[i].setBoundingBox(bbox);
} else {
/**@todo Verify that this is correct, looks like a copy/paste bug (jm)*/
@@ -1261,34 +1400,33 @@ public class TTFFile {
/**
* Read the "name" table.
- * @param in FontFileReader to read from
* @throws IOException In case of a I/O problem
*/
- private void readName(FontFileReader in) throws IOException {
- seekTab(in, "name", 2);
- int i = in.getCurrentPos();
- int n = in.readTTFUShort();
- int j = in.readTTFUShort() + i - 2;
+ private void readName() throws IOException {
+ seekTab(fontFile, TTFTableName.NAME, 2);
+ int i = fontFile.getCurrentPos();
+ int n = fontFile.readTTFUShort();
+ int j = fontFile.readTTFUShort() + i - 2;
i += 2 * 2;
while (n-- > 0) {
// getLogger().debug("Iteration: " + n);
- in.seekSet(i);
- final int platformID = in.readTTFUShort();
- final int encodingID = in.readTTFUShort();
- final int languageID = in.readTTFUShort();
+ fontFile.seekSet(i);
+ final int platformID = fontFile.readTTFUShort();
+ final int encodingID = fontFile.readTTFUShort();
+ final int languageID = fontFile.readTTFUShort();
- int k = in.readTTFUShort();
- int l = in.readTTFUShort();
+ int k = fontFile.readTTFUShort();
+ int l = fontFile.readTTFUShort();
if (((platformID == 1 || platformID == 3)
&& (encodingID == 0 || encodingID == 1))) {
- in.seekSet(j + in.readTTFUShort());
+ fontFile.seekSet(j + fontFile.readTTFUShort());
String txt;
if (platformID == 3) {
- txt = in.readTTFString(l, encodingID);
+ txt = fontFile.readTTFString(l, encodingID);
} else {
- txt = in.readTTFString(l);
+ txt = fontFile.readTTFString(l);
}
if (log.isDebugEnabled()) {
@@ -1332,21 +1470,20 @@ public class TTFFile {
/**
* Read the "PCLT" table to find xHeight and capHeight.
- * @param in FontFileReader to read from
* @throws IOException In case of a I/O problem
*/
- private boolean readPCLT(FontFileReader in) throws IOException {
- TTFDirTabEntry dirTab = getDirectoryEntry ( "PCLT" );
+ private boolean readPCLT() throws IOException {
+ TTFDirTabEntry dirTab = dirTabs.get(TTFTableName.PCLT);
if (dirTab != null) {
- in.seekSet(dirTab.getOffset() + 4 + 4 + 2);
- xHeight = in.readTTFUShort();
+ fontFile.seekSet(dirTab.getOffset() + 4 + 4 + 2);
+ xHeight = fontFile.readTTFUShort();
log.debug("xHeight from PCLT: " + formatUnitsForDebug(xHeight));
- in.skip(2 * 2);
- capHeight = in.readTTFUShort();
+ fontFile.skip(2 * 2);
+ capHeight = fontFile.readTTFUShort();
log.debug("capHeight from PCLT: " + formatUnitsForDebug(capHeight));
- in.skip(2 + 16 + 8 + 6 + 1 + 1);
+ fontFile.skip(2 + 16 + 8 + 6 + 1 + 1);
- int serifStyle = in.readTTFUByte();
+ int serifStyle = fontFile.readTTFUByte();
serifStyle = serifStyle >> 6;
serifStyle = serifStyle & 3;
if (serifStyle == 1) {
@@ -1476,19 +1613,18 @@ public class TTFFile {
/**
* Read the kerning table, create a table for both CIDs and
* winAnsiEncoding.
- * @param in FontFileReader to read from
* @throws IOException In case of a I/O problem
*/
- private void readKerning(FontFileReader in) throws IOException {
+ private void readKerning() throws IOException {
// Read kerning
- kerningTab = new java.util.HashMap();
- ansiKerningTab = new java.util.HashMap();
- TTFDirTabEntry dirTab = getDirectoryEntry ( "kern" );
+ kerningTab = new HashMap<Integer, Map<Integer, Integer>>();
+ ansiKerningTab = new HashMap<Integer, Map<Integer, Integer>>();
+ TTFDirTabEntry dirTab = dirTabs.get(TTFTableName.KERN);
if (dirTab != null) {
- seekTab(in, "kern", 2);
- for (int n = in.readTTFUShort(); n > 0; n--) {
- in.skip(2 * 2);
- int k = in.readTTFUShort();
+ seekTab(fontFile, TTFTableName.KERN, 2);
+ for (int n = fontFile.readTTFUShort(); n > 0; n--) {
+ fontFile.skip(2 * 2);
+ int k = fontFile.readTTFUShort();
if (!((k & 1) != 0) || (k & 2) != 0 || (k & 4) != 0) {
return;
}
@@ -1496,12 +1632,12 @@ public class TTFFile {
continue;
}
- k = in.readTTFUShort();
- in.skip(3 * 2);
+ k = fontFile.readTTFUShort();
+ fontFile.skip(3 * 2);
while (k-- > 0) {
- int i = in.readTTFUShort();
- int j = in.readTTFUShort();
- int kpx = in.readTTFShort();
+ int i = fontFile.readTTFUShort();
+ int j = fontFile.readTTFUShort();
+ int kpx = fontFile.readTTFShort();
if (kpx != 0) {
// CID kerning table entry, using unicode indexes
final Integer iObj = glyphToUnicode(i);
@@ -1515,9 +1651,9 @@ public class TTFFile {
log.debug("Ignoring kerning pair because Unicode index was"
+ " found for the second glyph " + i);
} else {
- Map adjTab = kerningTab.get(iObj);
+ Map<Integer, Integer> adjTab = kerningTab.get(iObj);
if (adjTab == null) {
- adjTab = new java.util.HashMap();
+ adjTab = new HashMap<Integer, Integer>();
}
adjTab.put(u2, new Integer(convertTTFUnit2PDFUnit(kpx)));
kerningTab.put(iObj, adjTab);
@@ -1529,16 +1665,12 @@ public class TTFFile {
// Create winAnsiEncoded kerning table from kerningTab
// (could probably be simplified, for now we remap back to CID indexes and
// then to winAnsi)
- Iterator ae = kerningTab.keySet().iterator();
- while (ae.hasNext()) {
- Integer unicodeKey1 = (Integer)ae.next();
+ for (Integer unicodeKey1 : kerningTab.keySet()) {
Integer cidKey1 = unicodeToGlyph(unicodeKey1.intValue());
- Map<Integer, Integer> akpx = new java.util.HashMap();
- Map ckpx = kerningTab.get(unicodeKey1);
+ Map<Integer, Integer> akpx = new HashMap<Integer, Integer>();
+ Map<Integer, Integer> ckpx = kerningTab.get(unicodeKey1);
- Iterator aee = ckpx.keySet().iterator();
- while (aee.hasNext()) {
- Integer unicodeKey2 = (Integer)aee.next();
+ for (Integer unicodeKey2 : ckpx.keySet()) {
Integer cidKey2 = unicodeToGlyph(unicodeKey2.intValue());
Integer kern = (Integer)ckpx.get(unicodeKey2);
@@ -1567,10 +1699,71 @@ public class TTFFile {
}
/**
- * Return a List with TTFCmapEntry.
- * @return A list of TTFCmapEntry objects
+ * Streams a font.
+ * @param ttfOut The interface for streaming TrueType tables.
+ * @exception IOException file write error
+ */
+ public void stream(TTFOutputStream ttfOut) throws IOException {
+ SortedSet<Map.Entry<TTFTableName, TTFDirTabEntry>> sortedDirTabs = sortDirTabMap(dirTabs);
+ byte[] file = fontFile.getAllBytes();
+ TTFTableOutputStream tableOut = ttfOut.getTableOutputStream();
+ TTFGlyphOutputStream glyphOut = ttfOut.getGlyphOutputStream();
+ ttfOut.startFontStream();
+ for (Map.Entry<TTFTableName, TTFDirTabEntry> entry : sortedDirTabs) {
+ int offset = (int) entry.getValue().getOffset();
+ int paddedLength = (int) entry.getValue().getLength();
+ paddedLength += getPadSize(offset + paddedLength);
+ if (entry.getKey().equals(TTFTableName.GLYF)) {
+ streamGlyf(glyphOut, file, offset, paddedLength);
+ } else {
+ tableOut.streamTable(file, offset, paddedLength);
+ }
+ }
+ ttfOut.endFontStream();
+ }
+
+ private void streamGlyf(TTFGlyphOutputStream glyphOut, byte[] fontFile, int tableOffset,
+ int tableLength) throws IOException {
+ //Stream all but the last glyph
+ int glyphStart = 0;
+ int glyphEnd = 0;
+ glyphOut.startGlyphStream();
+ for (int i = 0; i < mtxTab.length - 1; i++) {
+ glyphStart = (int) mtxTab[i].getOffset() + tableOffset;
+ glyphEnd = (int) mtxTab[i + 1].getOffset() + tableOffset;
+ glyphOut.streamGlyph(fontFile, glyphStart, glyphEnd - glyphStart);
+ }
+ glyphOut.streamGlyph(fontFile, glyphEnd, (tableOffset + tableLength) - glyphEnd);
+ glyphOut.endGlyphStream();
+ }
+
+ /**
+ * Returns the order in which the tables in a TrueType font should be written to file.
+ * @param directoryTabs the map that is to be sorted.
+ * @return TTFTablesNames[] an array of table names sorted in the order they should appear in
+ * the TTF file.
*/
- public List getCMaps() {
+ SortedSet<Map.Entry<TTFTableName, TTFDirTabEntry>>
+ sortDirTabMap(Map<TTFTableName, TTFDirTabEntry> directoryTabs) {
+ SortedSet<Map.Entry<TTFTableName, TTFDirTabEntry>> sortedSet
+ = new TreeSet<Map.Entry<TTFTableName, TTFDirTabEntry>>(
+ new Comparator<Map.Entry<TTFTableName, TTFDirTabEntry>>() {
+
+ public int compare(Entry<TTFTableName, TTFDirTabEntry> o1,
+ Entry<TTFTableName, TTFDirTabEntry> o2) {
+ return (int) (o1.getValue().getOffset() - o2.getValue().getOffset());
+ }
+ });
+ sortedSet.addAll(directoryTabs.entrySet());
+ return sortedSet;
+ }
+
+ /**
+ * Returns this font's character to glyph mapping.
+ *
+ * @return the font's cmap
+ */
+ public List<CMapSegment> getCMaps() {
return cmaps;
}
@@ -1579,24 +1772,23 @@ public class TTFFile {
* name exists in the collection.
* If it does, set offset in fontfile to the beginning of
* the Table Directory for that font.
- * @param in FontFileReader to read from
* @param name The name to check
* @return True if not collection or font name present, false otherwise
* @throws IOException In case of an I/O problem
*/
- protected final boolean checkTTC(FontFileReader in, String name) throws IOException {
- String tag = in.readTTFString(4);
+ protected final boolean checkTTC(String name) throws IOException {
+ String tag = fontFile.readTTFString(4);
if ("ttcf".equals(tag)) {
// This is a TrueType Collection
- in.skip(4);
+ fontFile.skip(4);
// Read directory offsets
- int numDirectories = (int)in.readTTFULong();
+ int numDirectories = (int)fontFile.readTTFULong();
// int numDirectories=in.readTTFUShort();
long[] dirOffsets = new long[numDirectories];
for (int i = 0; i < numDirectories; i++) {
- dirOffsets[i] = in.readTTFULong();
+ dirOffsets[i] = fontFile.readTTFULong();
}
log.info("This is a TrueType collection file with "
@@ -1610,10 +1802,10 @@ public class TTFFile {
// Is found, just to show all the names
long dirTabOffset = 0;
for (int i = 0; (i < numDirectories); i++) {
- in.seekSet(dirOffsets[i]);
- readDirTabs(in);
+ fontFile.seekSet(dirOffsets[i]);
+ readDirTabs();
- readName(in);
+ readName();
if (fullName.equals(name)) {
found = true;
@@ -1631,10 +1823,10 @@ public class TTFFile {
subFamilyName = "";
}
- in.seekSet(dirTabOffset);
+ fontFile.seekSet(dirTabOffset);
return found;
} else {
- in.seekSet(0);
+ fontFile.seekSet(0);
return true;
}
}
@@ -1646,8 +1838,7 @@ public class TTFFile {
* @throws IOException In case of an I/O problem
*/
public final List<String> getTTCnames(FontFileReader in) throws IOException {
- List<String> fontNames = new java.util.ArrayList<String>();
-
+ List<String> fontNames = new ArrayList<String>();
String tag = in.readTTFString(4);
if ("ttcf".equals(tag)) {
@@ -1667,9 +1858,9 @@ public class TTFFile {
for (int i = 0; (i < numDirectories); i++) {
in.seekSet(dirOffsets[i]);
- readDirTabs(in);
+ readDirTabs();
- readName(in);
+ readName();
log.info(fullName);
fontNames.add(fullName);
@@ -1695,13 +1886,13 @@ public class TTFFile {
* doesn't matter...
*/
private Integer[] unicodeToWinAnsi(int unicode) {
- List ret = new java.util.ArrayList();
+ List<Integer> ret = new ArrayList<Integer>();
for (int i = 32; i < Glyphs.WINANSI_ENCODING.length; i++) {
if (unicode == Glyphs.WINANSI_ENCODING[i]) {
ret.add(new Integer(i));
}
}
- return (Integer[])ret.toArray(new Integer[0]);
+ return ret.toArray(new Integer[0]);
}
/**
@@ -1744,7 +1935,7 @@ public class TTFFile {
* @return unicode code point
*/
private Integer glyphToUnicode(int glyphIndex) {
- return (Integer) glyphToUnicodeMap.get(new Integer(glyphIndex));
+ return glyphToUnicodeMap.get(new Integer(glyphIndex));
}
/**
@@ -1755,7 +1946,7 @@ public class TTFFile {
*/
private Integer unicodeToGlyph(int unicodeIndex) throws IOException {
final Integer result
- = (Integer) unicodeToGlyphMap.get(new Integer(unicodeIndex));
+ = unicodeToGlyphMap.get(new Integer(unicodeIndex));
if (result == null) {
throw new IOException(
"Glyph index not found for unicode value " + unicodeIndex);
@@ -1763,6 +1954,10 @@ public class TTFFile {
return result;
}
+ String getGlyphName(int glyphIndex) {
+ return mtxTab[glyphIndex].getName();
+ }
+
/**
* Determine if advanced (typographic) table is present.
* @return true if advanced (typographic) table is present
diff --git a/src/java/org/apache/fop/fonts/truetype/TTFFontLoader.java b/src/java/org/apache/fop/fonts/truetype/TTFFontLoader.java
index c03f0fb6a..1410239ee 100644
--- a/src/java/org/apache/fop/fonts/truetype/TTFFontLoader.java
+++ b/src/java/org/apache/fop/fonts/truetype/TTFFontLoader.java
@@ -21,17 +21,14 @@ package org.apache.fop.fonts.truetype;
import java.io.IOException;
import java.io.InputStream;
-import java.util.Iterator;
-import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.io.IOUtils;
-import org.apache.xmlgraphics.fonts.Glyphs;
-
-import org.apache.fop.fonts.BFEntry;
import org.apache.fop.fonts.CIDFontType;
+import org.apache.fop.fonts.CMapSegment;
+import org.apache.fop.fonts.EmbeddingMode;
import org.apache.fop.fonts.EncodingMode;
import org.apache.fop.fonts.FontLoader;
import org.apache.fop.fonts.FontResolver;
@@ -39,6 +36,8 @@ import org.apache.fop.fonts.FontType;
import org.apache.fop.fonts.MultiByteFont;
import org.apache.fop.fonts.NamedCharacter;
import org.apache.fop.fonts.SingleByteFont;
+import org.apache.fop.fonts.truetype.TTFFile.PostScriptVersion;
+import org.apache.fop.util.HexEncoder;
/**
* Loads a TrueType font into memory directly from the original font file.
@@ -49,6 +48,7 @@ public class TTFFontLoader extends FontLoader {
private SingleByteFont singleFont;
private final String subFontName;
private EncodingMode encodingMode;
+ private EmbeddingMode embeddingMode;
/**
* Default constructor
@@ -56,7 +56,7 @@ public class TTFFontLoader extends FontLoader {
* @param resolver the FontResolver for font URI resolution
*/
public TTFFontLoader(String fontFileURI, FontResolver resolver) {
- this(fontFileURI, null, true, EncodingMode.AUTO, true, true, resolver);
+ this(fontFileURI, null, true, EmbeddingMode.AUTO, EncodingMode.AUTO, true, true, resolver);
}
/**
@@ -65,24 +65,28 @@ public class TTFFontLoader extends FontLoader {
* @param subFontName the sub-fontname of a font in a TrueType Collection (or null for normal
* TrueType fonts)
* @param embedded indicates whether the font is embedded or referenced
+ * @param embeddingMode the embedding mode of the font
* @param encodingMode the requested encoding mode
* @param useKerning true to enable loading kerning info if available, false to disable
* @param useAdvanced true to enable loading advanced info if available, false to disable
* @param resolver the FontResolver for font URI resolution
*/
public TTFFontLoader(String fontFileURI, String subFontName,
- boolean embedded, EncodingMode encodingMode, boolean useKerning,
- boolean useAdvanced, FontResolver resolver) {
+ boolean embedded, EmbeddingMode embeddingMode, EncodingMode encodingMode,
+ boolean useKerning, boolean useAdvanced, FontResolver resolver) {
super(fontFileURI, embedded, useKerning, useAdvanced, resolver);
this.subFontName = subFontName;
this.encodingMode = encodingMode;
+ this.embeddingMode = embeddingMode;
if (this.encodingMode == EncodingMode.AUTO) {
this.encodingMode = EncodingMode.CID; //Default to CID mode for TrueType
}
+ if (this.embeddingMode == EmbeddingMode.AUTO) {
+ this.embeddingMode = EmbeddingMode.SUBSET;
+ }
}
/** {@inheritDoc} */
- @Override
protected void read() throws IOException {
read(this.subFontName);
}
@@ -145,29 +149,20 @@ public class TTFFontLoader extends FontLoader {
returnFont.setItalicAngle(Integer.parseInt(ttf.getItalicAngle()));
returnFont.setMissingWidth(0);
returnFont.setWeight(ttf.getWeightClass());
-
+ returnFont.setEmbeddingMode(this.embeddingMode);
if (isCid) {
multiFont.setCIDType(CIDFontType.CIDTYPE2);
int[] wx = ttf.getWidths();
multiFont.setWidthArray(wx);
- List entries = ttf.getCMaps();
- BFEntry[] bfentries = new BFEntry[entries.size()];
- int pos = 0;
- Iterator iter = ttf.getCMaps().listIterator();
- while (iter.hasNext()) {
- TTFCmapEntry ce = (TTFCmapEntry)iter.next();
- bfentries[pos] = new BFEntry(ce.getUnicodeStart(), ce.getUnicodeEnd(),
- ce.getGlyphStartIndex());
- pos++;
- }
- multiFont.setBFEntries(bfentries);
} else {
singleFont.setFontType(FontType.TRUETYPE);
singleFont.setEncoding(ttf.getCharSetName());
returnFont.setFirstChar(ttf.getFirstChar());
returnFont.setLastChar(ttf.getLastChar());
+ singleFont.setTrueTypePostScriptVersion(ttf.getPostScriptVersion());
copyWidthsSingleByte(ttf);
}
+ returnFont.setCMap(getCMap(ttf));
if (useKerning) {
copyKerning(ttf, isCid);
@@ -186,23 +181,30 @@ public class TTFFontLoader extends FontLoader {
}
}
+ private CMapSegment[] getCMap(TTFFile ttf) {
+ CMapSegment[] array = new CMapSegment[ttf.getCMaps().size()];
+ return ttf.getCMaps().toArray(array);
+ }
+
private void copyWidthsSingleByte(TTFFile ttf) {
int[] wx = ttf.getWidths();
for (int i = singleFont.getFirstChar(); i <= singleFont.getLastChar(); i++) {
singleFont.setWidth(i, ttf.getCharWidth(i));
}
- Iterator iter = ttf.getCMaps().listIterator();
- while (iter.hasNext()) {
- TTFCmapEntry ce = (TTFCmapEntry)iter.next();
- if (ce.getUnicodeStart() < 0xFFFE) {
- for (char u = (char)ce.getUnicodeStart(); u <= ce.getUnicodeEnd(); u++) {
+
+ for (CMapSegment segment : ttf.getCMaps()) {
+ if (segment.getUnicodeStart() < 0xFFFE) {
+ for (char u = (char)segment.getUnicodeStart(); u <= segment.getUnicodeEnd(); u++) {
int codePoint = singleFont.getEncoding().mapChar(u);
if (codePoint <= 0) {
- String unicode = Character.toString(u);
- String charName = Glyphs.stringToGlyph(unicode);
- if (charName.length() > 0) {
- NamedCharacter nc = new NamedCharacter(charName, unicode);
- int glyphIndex = ce.getGlyphStartIndex() + u - ce.getUnicodeStart();
+ int glyphIndex = segment.getGlyphStartIndex() + u - segment.getUnicodeStart();
+ String glyphName = ttf.getGlyphName(glyphIndex);
+ if (glyphName.length() == 0 && ttf.getPostScriptVersion() != PostScriptVersion.V2) {
+ glyphName = "u" + HexEncoder.encode(u);
+ }
+ if (glyphName.length() > 0) {
+ String unicode = Character.toString(u);
+ NamedCharacter nc = new NamedCharacter(glyphName, unicode);
singleFont.addUnencodedCharacter(nc, wx[glyphIndex]);
}
}
@@ -225,7 +227,6 @@ public class TTFFontLoader extends FontLoader {
}
for (Integer kpx1 : kerningSet) {
-
Map<Integer, Integer> h2;
if (isCid) {
h2 = ttf.getKerning().get(kpx1);
diff --git a/src/java/org/apache/fop/fonts/truetype/TTFGlyphOutputStream.java b/src/java/org/apache/fop/fonts/truetype/TTFGlyphOutputStream.java
new file mode 100644
index 000000000..313d5836d
--- /dev/null
+++ b/src/java/org/apache/fop/fonts/truetype/TTFGlyphOutputStream.java
@@ -0,0 +1,48 @@
+/*
+ * 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.truetype;
+
+import java.io.IOException;
+
+/**
+ * An interface for writing individual glyphs from the glyf table of a TrueType font to an output stream.
+ */
+public interface TTFGlyphOutputStream {
+
+ /**
+ * Begins the streaming of glyphs.
+ */
+ void startGlyphStream() throws IOException;
+
+ /**
+ * Streams an individual glyph from the given byte array.
+ *
+ * @param glyphData the source of the glyph data to stream from
+ * @param offset the position in the glyph data where the glyph starts
+ * @param size the size of the glyph data in bytes
+ */
+ void streamGlyph(byte[] glyphData, int offset, int size) throws IOException;
+
+ /**
+ * Ends the streaming of glyphs.
+ */
+ void endGlyphStream() throws IOException;
+
+}
diff --git a/src/java/org/apache/fop/fonts/truetype/TTFOutputStream.java b/src/java/org/apache/fop/fonts/truetype/TTFOutputStream.java
new file mode 100644
index 000000000..09b5b6f50
--- /dev/null
+++ b/src/java/org/apache/fop/fonts/truetype/TTFOutputStream.java
@@ -0,0 +1,49 @@
+/*
+ * 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.truetype;
+
+import java.io.IOException;
+
+/**
+ * An interface for writing a TrueType font to an output stream.
+ */
+public interface TTFOutputStream {
+
+ /**
+ * Starts writing the font.
+ */
+ void startFontStream() throws IOException;
+
+ /**
+ * Returns an object for streaming TrueType tables.
+ */
+ TTFTableOutputStream getTableOutputStream();
+
+ /**
+ * Returns an object for streaming TrueType glyphs in the glyf table.
+ */
+ TTFGlyphOutputStream getGlyphOutputStream();
+
+ /**
+ * Ends writing the font.
+ */
+ void endFontStream() throws IOException;
+
+}
diff --git a/src/java/org/apache/fop/fonts/truetype/TTFSubSetFile.java b/src/java/org/apache/fop/fonts/truetype/TTFSubSetFile.java
index d400c0bfd..292ae191c 100644
--- a/src/java/org/apache/fop/fonts/truetype/TTFSubSetFile.java
+++ b/src/java/org/apache/fop/fonts/truetype/TTFSubSetFile.java
@@ -20,8 +20,9 @@
package org.apache.fop.fonts.truetype;
import java.io.IOException;
-import java.util.Iterator;
+import java.util.HashMap;
import java.util.Map;
+import java.util.SortedSet;
/**
@@ -42,24 +43,18 @@ public class TTFSubSetFile extends TTFFile {
* Offsets in name table to be filled out by table.
* The offsets are to the checkSum field
*/
- private int cvtDirOffset = 0;
- private int fpgmDirOffset = 0;
- private int glyfDirOffset = 0;
- private int headDirOffset = 0;
- private int hheaDirOffset = 0;
- private int hmtxDirOffset = 0;
- private int locaDirOffset = 0;
- private int maxpDirOffset = 0;
- private int prepDirOffset = 0;
+ private Map<TTFTableName, Integer> offsets = new HashMap<TTFTableName, Integer>();
private int checkSumAdjustmentOffset = 0;
private int locaOffset = 0;
+ /** Stores the glyph offsets so that we can end strings at glyph boundaries */
+ private int[] glyphOffsets;
+
/**
* Default Constructor
*/
public TTFSubSetFile() {
- this(false, false);
}
/**
@@ -71,16 +66,9 @@ public class TTFSubSetFile extends TTFFile {
super(useKerning, useAdvanced);
}
- /**
- * Initalize the output array
- */
- private void init(int size) {
- output = new byte[size];
- realSize = 0;
- currentPos = 0;
-
- // createDirectory()
- }
+ /** The dir tab entries in the new subset font. */
+ private Map<TTFTableName, TTFDirTabEntry> newDirTabs
+ = new HashMap<TTFTableName, TTFDirTabEntry>();
private int determineTableCount() {
int numTables = 4; //4 req'd tables: head,hhea,hmtx,maxp
@@ -88,7 +76,7 @@ public class TTFSubSetFile extends TTFFile {
throw new UnsupportedOperationException(
"OpenType fonts with CFF glyphs are not supported");
} else {
- numTables += 2; //1 req'd table: glyf,loca
+ numTables += 5; //5 req'd tables: glyf,loca,post,name,OS/2
if (hasCvt()) {
numTables++;
}
@@ -119,7 +107,7 @@ public class TTFSubSetFile extends TTFFile {
// Create searchRange, entrySelector and rangeShift
int maxPow = maxPow2(numTables);
- int searchRange = maxPow * 16;
+ int searchRange = (int) Math.pow(2, maxPow) * 16;
writeUShort(searchRange);
realSize += 2;
@@ -128,151 +116,122 @@ public class TTFSubSetFile extends TTFFile {
writeUShort((numTables * 16) - searchRange);
realSize += 2;
+ // Create space for the table entries (these must be in ASCII alphabetical order[A-Z] then[a-z])
+ writeTableName(TTFTableName.OS2);
- // Create space for the table entries
if (hasCvt()) {
- writeString("cvt ");
- cvtDirOffset = currentPos;
- currentPos += 12;
- realSize += 16;
+ writeTableName(TTFTableName.CVT);
}
-
if (hasFpgm()) {
- writeString("fpgm");
- fpgmDirOffset = currentPos;
- currentPos += 12;
- realSize += 16;
+ writeTableName(TTFTableName.FPGM);
}
+ writeTableName(TTFTableName.GLYF);
+ writeTableName(TTFTableName.HEAD);
+ writeTableName(TTFTableName.HHEA);
+ writeTableName(TTFTableName.HMTX);
+ writeTableName(TTFTableName.LOCA);
+ writeTableName(TTFTableName.MAXP);
+ writeTableName(TTFTableName.NAME);
+ writeTableName(TTFTableName.POST);
+ if (hasPrep()) {
+ writeTableName(TTFTableName.PREP);
+ }
+ newDirTabs.put(TTFTableName.TABLE_DIRECTORY, new TTFDirTabEntry(0, currentPos));
+ }
- writeString("glyf");
- glyfDirOffset = currentPos;
- currentPos += 12;
- realSize += 16;
-
- writeString("head");
- headDirOffset = currentPos;
- currentPos += 12;
- realSize += 16;
-
- writeString("hhea");
- hheaDirOffset = currentPos;
+ private void writeTableName(TTFTableName tableName) {
+ writeString(tableName.getName());
+ offsets.put(tableName, currentPos);
currentPos += 12;
realSize += 16;
+ }
- writeString("hmtx");
- hmtxDirOffset = currentPos;
- currentPos += 12;
- realSize += 16;
- writeString("loca");
- locaDirOffset = currentPos;
- currentPos += 12;
- realSize += 16;
-
- writeString("maxp");
- maxpDirOffset = currentPos;
- currentPos += 12;
- realSize += 16;
+ private boolean hasCvt() {
+ return dirTabs.containsKey(TTFTableName.CVT);
+ }
- if (hasPrep()) {
- writeString("prep");
- prepDirOffset = currentPos;
- currentPos += 12;
- realSize += 16;
- }
+ private boolean hasFpgm() {
+ return dirTabs.containsKey(TTFTableName.FPGM);
}
+ private boolean hasPrep() {
+ return dirTabs.containsKey(TTFTableName.PREP);
+ }
/**
- * Copy the cvt table as is from original font to subset font
+ * Create an empty loca table without updating checksum
*/
- private boolean createCvt(FontFileReader in) throws IOException {
- TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("cvt ");
+ private void createLoca(int size) throws IOException {
+ pad4();
+ locaOffset = currentPos;
+ int dirTableOffset = offsets.get(TTFTableName.LOCA);
+ writeULong(dirTableOffset + 4, currentPos);
+ writeULong(dirTableOffset + 8, size * 4 + 4);
+ currentPos += size * 4 + 4;
+ realSize += size * 4 + 4;
+ }
+
+ private boolean copyTable(FontFileReader in, TTFTableName tableName) throws IOException {
+ TTFDirTabEntry entry = dirTabs.get(tableName);
if (entry != null) {
pad4();
- seekTab(in, "cvt ", 0);
+ seekTab(in, tableName, 0);
System.arraycopy(in.getBytes((int)entry.getOffset(), (int)entry.getLength()),
0, output, currentPos, (int)entry.getLength());
- int checksum = getCheckSum(currentPos, (int)entry.getLength());
- writeULong(cvtDirOffset, checksum);
- writeULong(cvtDirOffset + 4, currentPos);
- writeULong(cvtDirOffset + 8, (int)entry.getLength());
- currentPos += (int)entry.getLength();
- realSize += (int)entry.getLength();
+ updateCheckSum(currentPos, (int) entry.getLength(), tableName);
+ currentPos += (int) entry.getLength();
+ realSize += (int) entry.getLength();
return true;
} else {
return false;
- //throw new IOException("Can't find cvt table");
}
}
- private boolean hasCvt() {
- return dirTabs.containsKey("cvt ");
- }
-
- private boolean hasFpgm() {
- return dirTabs.containsKey("fpgm");
- }
-
- private boolean hasPrep() {
- return dirTabs.containsKey("prep");
+ /**
+ * Copy the cvt table as is from original font to subset font
+ */
+ private boolean createCvt(FontFileReader in) throws IOException {
+ return copyTable(in, TTFTableName.CVT);
}
/**
* Copy the fpgm table as is from original font to subset font
*/
private boolean createFpgm(FontFileReader in) throws IOException {
- TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("fpgm");
- if (entry != null) {
- pad4();
- seekTab(in, "fpgm", 0);
- System.arraycopy(in.getBytes((int)entry.getOffset(), (int)entry.getLength()),
- 0, output, currentPos, (int)entry.getLength());
- int checksum = getCheckSum(currentPos, (int)entry.getLength());
- writeULong(fpgmDirOffset, checksum);
- writeULong(fpgmDirOffset + 4, currentPos);
- writeULong(fpgmDirOffset + 8, (int)entry.getLength());
- currentPos += (int)entry.getLength();
- realSize += (int)entry.getLength();
- return true;
- } else {
- return false;
- }
+ return copyTable(in, TTFTableName.FPGM);
}
-
-
/**
- * Create an empty loca table without updating checksum
+ * Copy the name table as is from the original.
*/
- private void createLoca(int size) throws IOException {
- pad4();
- locaOffset = currentPos;
- writeULong(locaDirOffset + 4, currentPos);
- writeULong(locaDirOffset + 8, size * 4 + 4);
- currentPos += size * 4 + 4;
- realSize += size * 4 + 4;
+ private boolean createName(FontFileReader in) throws IOException {
+ return copyTable(in, TTFTableName.NAME);
}
+ /**
+ * Copy the OS/2 table as is from the original.
+ */
+ private boolean createOS2(FontFileReader in) throws IOException {
+ return copyTable(in, TTFTableName.OS2);
+ }
/**
* Copy the maxp table as is from original font to subset font
* and set num glyphs to size
*/
private void createMaxp(FontFileReader in, int size) throws IOException {
- TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("maxp");
+ TTFTableName maxp = TTFTableName.MAXP;
+ TTFDirTabEntry entry = dirTabs.get(maxp);
if (entry != null) {
pad4();
- seekTab(in, "maxp", 0);
+ seekTab(in, maxp, 0);
System.arraycopy(in.getBytes((int)entry.getOffset(), (int)entry.getLength()),
0, output, currentPos, (int)entry.getLength());
writeUShort(currentPos + 4, size);
- int checksum = getCheckSum(currentPos, (int)entry.getLength());
- writeULong(maxpDirOffset, checksum);
- writeULong(maxpDirOffset + 4, currentPos);
- writeULong(maxpDirOffset + 8, (int)entry.getLength());
+ updateCheckSum(currentPos, (int)entry.getLength(), maxp);
currentPos += (int)entry.getLength();
realSize += (int)entry.getLength();
} else {
@@ -280,28 +239,34 @@ public class TTFSubSetFile extends TTFFile {
}
}
+ private void createPost(FontFileReader in) throws IOException {
+ TTFTableName post = TTFTableName.POST;
+ TTFDirTabEntry entry = dirTabs.get(post);
+ if (entry != null) {
+ pad4();
+ seekTab(in, post, 0);
+ int newTableSize = 32; // This is the post table size with glyphs truncated
+ byte[] newPostTable = new byte[newTableSize];
+ // We only want the first 28 bytes (truncate the glyph names);
+ System.arraycopy(in.getBytes((int) entry.getOffset(), newTableSize),
+ 0, newPostTable, 0, newTableSize);
+ // set the post table to Format 3.0
+ newPostTable[1] = 0x03;
+ System.arraycopy(newPostTable, 0, output, currentPos, newTableSize);
+ updateCheckSum(currentPos, newTableSize, post);
+ currentPos += newTableSize;
+ realSize += newTableSize;
+ } else {
+ throw new IOException("Can't find post table");
+ }
+ }
+
/**
* Copy the prep table as is from original font to subset font
*/
private boolean createPrep(FontFileReader in) throws IOException {
- TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("prep");
- if (entry != null) {
- pad4();
- seekTab(in, "prep", 0);
- System.arraycopy(in.getBytes((int)entry.getOffset(), (int)entry.getLength()),
- 0, output, currentPos, (int)entry.getLength());
-
- int checksum = getCheckSum(currentPos, (int)entry.getLength());
- writeULong(prepDirOffset, checksum);
- writeULong(prepDirOffset + 4, currentPos);
- writeULong(prepDirOffset + 8, (int)entry.getLength());
- currentPos += (int)entry.getLength();
- realSize += (int)entry.getLength();
- return true;
- } else {
- return false;
- }
+ return copyTable(in, TTFTableName.PREP);
}
@@ -310,20 +275,17 @@ public class TTFSubSetFile extends TTFFile {
* and fill in size of hmtx table
*/
private void createHhea(FontFileReader in, int size) throws IOException {
- TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("hhea");
+ TTFDirTabEntry entry = dirTabs.get(TTFTableName.HHEA);
if (entry != null) {
pad4();
- seekTab(in, "hhea", 0);
- System.arraycopy(in.getBytes((int)entry.getOffset(), (int)entry.getLength()),
- 0, output, currentPos, (int)entry.getLength());
- writeUShort((int)entry.getLength() + currentPos - 2, size);
-
- int checksum = getCheckSum(currentPos, (int)entry.getLength());
- writeULong(hheaDirOffset, checksum);
- writeULong(hheaDirOffset + 4, currentPos);
- writeULong(hheaDirOffset + 8, (int)entry.getLength());
- currentPos += (int)entry.getLength();
- realSize += (int)entry.getLength();
+ seekTab(in, TTFTableName.HHEA, 0);
+ System.arraycopy(in.getBytes((int) entry.getOffset(), (int) entry.getLength()), 0,
+ output, currentPos, (int) entry.getLength());
+ writeUShort((int) entry.getLength() + currentPos - 2, size);
+
+ updateCheckSum(currentPos, (int) entry.getLength(), TTFTableName.HHEA);
+ currentPos += (int) entry.getLength();
+ realSize += (int) entry.getLength();
} else {
throw new IOException("Can't find hhea table");
}
@@ -337,10 +299,11 @@ public class TTFSubSetFile extends TTFFile {
* in checkSumAdjustmentOffset
*/
private void createHead(FontFileReader in) throws IOException {
- TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("head");
+ TTFTableName head = TTFTableName.HEAD;
+ TTFDirTabEntry entry = dirTabs.get(head);
if (entry != null) {
pad4();
- seekTab(in, "head", 0);
+ seekTab(in, head, 0);
System.arraycopy(in.getBytes((int)entry.getOffset(), (int)entry.getLength()),
0, output, currentPos, (int)entry.getLength());
@@ -352,11 +315,7 @@ public class TTFSubSetFile extends TTFFile {
output[currentPos + 50] = 0; // long locaformat
output[currentPos + 51] = 1; // long locaformat
- int checksum = getCheckSum(currentPos, (int)entry.getLength());
- writeULong(headDirOffset, checksum);
- writeULong(headDirOffset + 4, currentPos);
- writeULong(headDirOffset + 8, (int)entry.getLength());
-
+ updateCheckSum(currentPos, (int)entry.getLength(), head);
currentPos += (int)entry.getLength();
realSize += (int)entry.getLength();
} else {
@@ -369,30 +328,24 @@ public class TTFSubSetFile extends TTFFile {
* Create the glyf table and fill in loca table
*/
private void createGlyf(FontFileReader in,
- Map glyphs) throws IOException {
- TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("glyf");
+ Map<Integer, Integer> glyphs) throws IOException {
+ TTFTableName glyf = TTFTableName.GLYF;
+ TTFDirTabEntry entry = dirTabs.get(glyf);
int size = 0;
- int start = 0;
+ int startPos = 0;
int endOffset = 0; // Store this as the last loca
if (entry != null) {
pad4();
- start = currentPos;
+ startPos = currentPos;
/* Loca table must be in order by glyph index, so build
* an array first and then write the glyph info and
* location offset.
*/
- int[] origIndexes = new int[glyphs.size()];
-
- Iterator e = glyphs.keySet().iterator();
- while (e.hasNext()) {
- Integer origIndex = (Integer)e.next();
- Integer subsetIndex = (Integer)glyphs.get(origIndex);
- origIndexes[subsetIndex.intValue()] = origIndex.intValue();
- }
+ int[] origIndexes = buildSubsetIndexToOrigIndexMap(glyphs);
+ glyphOffsets = new int[origIndexes.length];
for (int i = 0; i < origIndexes.length; i++) {
- int glyphLength = 0;
int nextOffset = 0;
int origGlyphIndex = origIndexes[i];
if (origGlyphIndex >= (mtxTab.length - 1)) {
@@ -400,46 +353,64 @@ public class TTFSubSetFile extends TTFFile {
} else {
nextOffset = (int)mtxTab[origGlyphIndex + 1].getOffset();
}
- glyphLength = nextOffset - (int)mtxTab[origGlyphIndex].getOffset();
+ int glyphOffset = (int)mtxTab[origGlyphIndex].getOffset();
+ int glyphLength = nextOffset - glyphOffset;
+ byte[] glyphData = in.getBytes(
+ (int)entry.getOffset() + glyphOffset,
+ glyphLength);
+ int endOffset1 = endOffset;
// Copy glyph
System.arraycopy(
- in.getBytes((int)entry.getOffset() + (int)mtxTab[origGlyphIndex].getOffset(),
- glyphLength), 0,
+ glyphData, 0,
output, currentPos,
glyphLength);
// Update loca table
- writeULong(locaOffset + i * 4, currentPos - start);
- if ((currentPos - start + glyphLength) > endOffset) {
- endOffset = (currentPos - start + glyphLength);
+ writeULong(locaOffset + i * 4, currentPos - startPos);
+ if ((currentPos - startPos + glyphLength) > endOffset1) {
+ endOffset1 = (currentPos - startPos + glyphLength);
}
+ // Store the glyph boundary positions relative to the start of the font
+ glyphOffsets[i] = currentPos;
currentPos += glyphLength;
realSize += glyphLength;
+
+ endOffset = endOffset1;
}
- size = currentPos - start;
- int checksum = getCheckSum(start, size);
- writeULong(glyfDirOffset, checksum);
- writeULong(glyfDirOffset + 4, start);
- writeULong(glyfDirOffset + 8, size);
+ size = currentPos - startPos;
+
currentPos += 12;
realSize += 12;
+ updateCheckSum(startPos, size + 12, glyf);
// Update loca checksum and last loca index
writeULong(locaOffset + glyphs.size() * 4, endOffset);
-
- checksum = getCheckSum(locaOffset, glyphs.size() * 4 + 4);
- writeULong(locaDirOffset, checksum);
+ int locaSize = glyphs.size() * 4 + 4;
+ int checksum = getCheckSum(output, locaOffset, locaSize);
+ writeULong(offsets.get(TTFTableName.LOCA), checksum);
+ int padSize = (locaOffset + locaSize) % 4;
+ newDirTabs.put(TTFTableName.LOCA,
+ new TTFDirTabEntry(locaOffset, locaSize + padSize));
} else {
throw new IOException("Can't find glyf table");
}
}
+ private int[] buildSubsetIndexToOrigIndexMap(Map<Integer, Integer> glyphs) {
+ int[] origIndexes = new int[glyphs.size()];
+ for (Map.Entry<Integer, Integer> glyph : glyphs.entrySet()) {
+ int origIndex = glyph.getKey();
+ int subsetIndex = glyph.getValue();
+ origIndexes[subsetIndex] = origIndex;
+ }
+ return origIndexes;
+ }
/**
* Create the hmtx table by copying metrics from original
@@ -448,8 +419,9 @@ public class TTFSubSetFile extends TTFFile {
* metric (key) to the subset metric (value)
*/
private void createHmtx(FontFileReader in,
- Map glyphs) throws IOException {
- TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("hmtx");
+ Map<Integer, Integer> glyphs) throws IOException {
+ TTFTableName hmtx = TTFTableName.HMTX;
+ TTFDirTabEntry entry = dirTabs.get(hmtx);
int longHorMetricSize = glyphs.size() * 2;
int leftSideBearingSize = glyphs.size() * 2;
@@ -458,10 +430,9 @@ public class TTFSubSetFile extends TTFFile {
if (entry != null) {
pad4();
//int offset = (int)entry.offset;
- Iterator e = glyphs.keySet().iterator();
- while (e.hasNext()) {
- Integer origIndex = (Integer)e.next();
- Integer subsetIndex = (Integer)glyphs.get(origIndex);
+ for (Map.Entry<Integer, Integer> glyph : glyphs.entrySet()) {
+ Integer origIndex = glyph.getKey();
+ Integer subsetIndex = glyph.getValue();
writeUShort(currentPos + subsetIndex.intValue() * 4,
mtxTab[origIndex.intValue()].getWx());
@@ -469,10 +440,7 @@ public class TTFSubSetFile extends TTFFile {
mtxTab[origIndex.intValue()].getLsb());
}
- int checksum = getCheckSum(currentPos, hmtxSize);
- writeULong(hmtxDirOffset, checksum);
- writeULong(hmtxDirOffset + 4, currentPos);
- writeULong(hmtxDirOffset + 8, hmtxSize);
+ updateCheckSum(currentPos, hmtxSize, hmtx);
currentPos += hmtxSize;
realSize += hmtxSize;
} else {
@@ -481,43 +449,37 @@ public class TTFSubSetFile extends TTFFile {
}
/**
- * Returns a subset of the original font.
+ * Reads a font and creates a subset of the font.
*
* @param in FontFileReader to read from
* @param name Name to be checked for in the font file
* @param glyphs Map of glyphs (glyphs has old index as (Integer) key and
* new index as (Integer) value)
- * @return A subset of the original font
* @throws IOException in case of an I/O problem
*/
- public byte[] readFont(FontFileReader in, String name,
+ public void readFont(FontFileReader in, String name,
Map<Integer, Integer> glyphs) throws IOException {
-
+ fontFile = in;
//Check if TrueType collection, and that the name exists in the collection
- if (!checkTTC(in, name)) {
+ if (!checkTTC(name)) {
throw new IOException("Failed to read font");
}
//Copy the Map as we're going to modify it
- Map<Integer, Integer> subsetGlyphs = new java.util.HashMap<Integer, Integer>(glyphs);
+ Map<Integer, Integer> subsetGlyphs = new HashMap<Integer, Integer>(glyphs);
output = new byte[in.getFileSize()];
- readDirTabs(in);
- readFontHeader(in);
- getNumGlyphs(in);
- readHorizontalHeader(in);
- readHorizontalMetrics(in);
- readIndexToLocation(in);
+ readDirTabs();
+ readFontHeader();
+ getNumGlyphs();
+ readHorizontalHeader();
+ readHorizontalMetrics();
+ readIndexToLocation();
scanGlyphs(in, subsetGlyphs);
- createDirectory(); // Create the TrueType header and directory
-
- createHead(in);
- createHhea(in, subsetGlyphs.size()); // Create the hhea table
- createHmtx(in, subsetGlyphs); // Create hmtx table
- createMaxp(in, subsetGlyphs.size()); // copy the maxp table
+ createDirectory(); // Create the TrueType header and directory
boolean optionalTableFound;
optionalTableFound = createCvt(in); // copy the cvt table
@@ -531,6 +493,16 @@ public class TTFSubSetFile extends TTFFile {
// fpgm is optional (used in TrueType fonts only)
log.debug("TrueType: fpgm table not present. Skipped.");
}
+ createLoca(subsetGlyphs.size()); // create empty loca table
+ createGlyf(in, subsetGlyphs); //create glyf table and update loca table
+
+ createOS2(in); // copy the OS/2 table
+ createHead(in);
+ createHhea(in, subsetGlyphs.size()); // Create the hhea table
+ createHmtx(in, subsetGlyphs); // Create hmtx table
+ createMaxp(in, subsetGlyphs.size()); // copy the maxp table
+ createName(in); // copy the name table
+ createPost(in); // copy the post table
optionalTableFound = createPrep(in); // copy prep table
if (!optionalTableFound) {
@@ -538,21 +510,59 @@ public class TTFSubSetFile extends TTFFile {
log.debug("TrueType: prep table not present. Skipped.");
}
- createLoca(subsetGlyphs.size()); // create empty loca table
- createGlyf(in, subsetGlyphs); //create glyf table and update loca table
-
pad4();
createCheckSumAdjustment();
+ }
+ /**
+ * Returns a subset of the fonts (readFont() MUST be called first in order to create the
+ * subset).
+ * @return byte array
+ */
+ public byte[] getFontSubset() {
byte[] ret = new byte[realSize];
System.arraycopy(output, 0, ret, 0, realSize);
-
return ret;
}
+ private void handleGlyphSubset(TTFGlyphOutputStream glyphOut) throws IOException {
+ glyphOut.startGlyphStream();
+ // Stream all but the last glyph
+ for (int i = 0; i < glyphOffsets.length - 1; i++) {
+ glyphOut.streamGlyph(output, glyphOffsets[i],
+ glyphOffsets[i + 1] - glyphOffsets[i]);
+ }
+ // Stream the last glyph
+ TTFDirTabEntry glyf = newDirTabs.get(TTFTableName.GLYF);
+ long lastGlyphLength = glyf.getLength()
+ - (glyphOffsets[glyphOffsets.length - 1] - glyf.getOffset());
+ glyphOut.streamGlyph(output, glyphOffsets[glyphOffsets.length - 1],
+ (int) lastGlyphLength);
+ glyphOut.endGlyphStream();
+ }
+
+ @Override
+ public void stream(TTFOutputStream ttfOut) throws IOException {
+ SortedSet<Map.Entry<TTFTableName, TTFDirTabEntry>> sortedDirTabs
+ = sortDirTabMap(newDirTabs);
+ TTFTableOutputStream tableOut = ttfOut.getTableOutputStream();
+ TTFGlyphOutputStream glyphOut = ttfOut.getGlyphOutputStream();
+
+ ttfOut.startFontStream();
+ for (Map.Entry<TTFTableName, TTFDirTabEntry> entry : sortedDirTabs) {
+ if (entry.getKey().equals(TTFTableName.GLYF)) {
+ handleGlyphSubset(glyphOut);
+ } else {
+ tableOut.streamTable(output, (int) entry.getValue().getOffset(),
+ (int) entry.getValue().getLength());
+ }
+ }
+ ttfOut.endFontStream();
+ }
+
private void scanGlyphs(FontFileReader in, Map<Integer, Integer> subsetGlyphs)
throws IOException {
- TTFDirTabEntry glyfTableInfo = (TTFDirTabEntry) dirTabs.get("glyf");
+ TTFDirTabEntry glyfTableInfo = dirTabs.get(TTFTableName.GLYF);
if (glyfTableInfo == null) {
throw new IOException("Glyf table could not be found");
}
@@ -610,20 +620,6 @@ public class TTFSubSetFile extends TTFFile {
output[pos + 1] = b2;
}
- /**
- * Appends a ULONG to the output array,
- * updates currentPos but not realSize
- */
- private void writeULong(int s) {
- byte b1 = (byte)((s >> 24) & 0xff);
- byte b2 = (byte)((s >> 16) & 0xff);
- byte b3 = (byte)((s >> 8) & 0xff);
- byte b4 = (byte)(s & 0xff);
- writeByte(b1);
- writeByte(b2);
- writeByte(b3);
- writeByte(b4);
- }
/**
* Appends a ULONG to the output array,
@@ -641,40 +637,16 @@ public class TTFSubSetFile extends TTFFile {
}
/**
- * Read a signed short value at given position
- */
- private short readShort(int pos) {
- int ret = readUShort(pos);
- return (short)ret;
- }
-
- /**
- * Read a unsigned short value at given position
- */
- private int readUShort(int pos) {
- int ret = output[pos];
- if (ret < 0) {
- ret += 256;
- }
- ret = ret << 8;
- if (output[pos + 1] < 0) {
- ret |= output[pos + 1] + 256;
- } else {
- ret |= output[pos + 1];
- }
-
- return ret;
- }
-
- /**
* Create a padding in the fontfile to align
* on a 4-byte boundary
*/
private void pad4() {
- int padSize = currentPos % 4;
- for (int i = 0; i < padSize; i++) {
- output[currentPos++] = 0;
- realSize++;
+ int padSize = getPadSize(currentPos);
+ if (padSize < 4) {
+ for (int i = 0; i < padSize; i++) {
+ output[currentPos++] = 0;
+ realSize++;
+ }
}
}
@@ -683,23 +655,25 @@ public class TTFSubSetFile extends TTFFile {
*/
private int maxPow2(int max) {
int i = 0;
- while (Math.pow(2, i) < max) {
+ while (Math.pow(2, i) <= max) {
i++;
}
return (i - 1);
}
- private int log2(int num) {
- return (int)(Math.log(num) / Math.log(2));
- }
-
- private int getCheckSum(int start, int size) {
- return (int)getLongCheckSum(start, size);
+ private void updateCheckSum(int tableStart, int tableSize, TTFTableName tableName) {
+ int checksum = getCheckSum(output, tableStart, tableSize);
+ int offset = offsets.get(tableName);
+ int padSize = getPadSize(tableStart + tableSize);
+ newDirTabs.put(tableName, new TTFDirTabEntry(tableStart, tableSize + padSize));
+ writeULong(offset, checksum);
+ writeULong(offset + 4, tableStart);
+ writeULong(offset + 8, tableSize);
}
- private long getLongCheckSum(int start, int size) {
+ private static int getCheckSum(byte[] data, int start, int size) {
// All the tables here are aligned on four byte boundaries
// Add remainder to size if it's not a multiple of 4
int remainder = size % 4;
@@ -710,26 +684,19 @@ public class TTFSubSetFile extends TTFFile {
long sum = 0;
for (int i = 0; i < size; i += 4) {
- int l = (output[start + i] << 24);
- l += (output[start + i + 1] << 16);
- l += (output[start + i + 2] << 16);
- l += (output[start + i + 3] << 16);
- sum += l;
- if (sum > 0xffffffff) {
- sum = sum - 0xffffffff;
+ long l = 0;
+ for (int j = 0; j < 4; j++) {
+ l <<= 8;
+ l |= data[start + i + j] & 0xff;
}
+ sum += l;
}
-
- return sum;
+ return (int) sum;
}
private void createCheckSumAdjustment() {
- long sum = getLongCheckSum(0, realSize);
+ long sum = getCheckSum(output, 0, realSize);
int checksum = (int)(0xb1b0afba - sum);
writeULong(checkSumAdjustmentOffset, checksum);
}
-
}
-
-
-
diff --git a/src/java/org/apache/fop/fonts/truetype/TTFTableName.java b/src/java/org/apache/fop/fonts/truetype/TTFTableName.java
new file mode 100644
index 000000000..e5ad63128
--- /dev/null
+++ b/src/java/org/apache/fop/fonts/truetype/TTFTableName.java
@@ -0,0 +1,163 @@
+/*
+ * 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.truetype;
+
+
+/**
+ * Represents table names as found in a TrueType font's Table Directory.
+ * TrueType fonts may have custom tables so we cannot use an enum.
+ */
+public final class TTFTableName {
+
+ /** The first table in a TrueType font file containing metadata about other tables. */
+ public static final TTFTableName TABLE_DIRECTORY = new TTFTableName("tableDirectory");
+
+ /** Embedded bitmap data. */
+ public static final TTFTableName EBDT = new TTFTableName("EBDT");
+
+ /** Embedded bitmap location data. */
+ public static final TTFTableName EBLC = new TTFTableName("EBLC");
+
+ /** Embedded bitmap scaling data. */
+ public static final TTFTableName EBSC = new TTFTableName("EBSC");
+
+ /** A FontForge specific table. */
+ public static final TTFTableName FFTM = new TTFTableName("FFTM");
+
+ /** Divides glyphs into various classes that make using the GPOS/GSUB tables easier. */
+ public static final TTFTableName GDEF = new TTFTableName("GDEF");
+
+ /** Provides kerning information, mark-to-base, etc. for opentype fonts. */
+ public static final TTFTableName GPOS = new TTFTableName("GPOS");
+
+ /** Provides ligature information, swash, etc. for opentype fonts. */
+ public static final TTFTableName GSUB = new TTFTableName("GSUB");
+
+ /** Linear threshold table. */
+ public static final TTFTableName LTSH = new TTFTableName("LTSH");
+
+ /** OS/2 and Windows specific metrics. */
+ public static final TTFTableName OS2 = new TTFTableName("OS/2");
+
+ /** PCL 5 data. */
+ public static final TTFTableName PCLT = new TTFTableName("PCLT");
+
+ /** Vertical Device Metrics table. */
+ public static final TTFTableName VDMX = new TTFTableName("VDMX");
+
+ /** Character to glyph mapping. */
+ public static final TTFTableName CMAP = new TTFTableName("cmap");
+
+ /** Control Value Table. */
+ public static final TTFTableName CVT = new TTFTableName("cvt ");
+
+ /** Font program. */
+ public static final TTFTableName FPGM = new TTFTableName("fpgm");
+
+ /** Grid-fitting and scan conversion procedure (grayscale). */
+ public static final TTFTableName GASP = new TTFTableName("gasp");
+
+ /** Glyph data. */
+ public static final TTFTableName GLYF = new TTFTableName("glyf");
+
+ /** Horizontal device metrics. */
+ public static final TTFTableName HDMX = new TTFTableName("hdmx");
+
+ /** Font header. */
+ public static final TTFTableName HEAD = new TTFTableName("head");
+
+ /** Horizontal header. */
+ public static final TTFTableName HHEA = new TTFTableName("hhea");
+
+ /** Horizontal metrics. */
+ public static final TTFTableName HMTX = new TTFTableName("hmtx");
+
+ /** Kerning. */
+ public static final TTFTableName KERN = new TTFTableName("kern");
+
+ /** Index to location. */
+ public static final TTFTableName LOCA = new TTFTableName("loca");
+
+ /** Maximum profile. */
+ public static final TTFTableName MAXP = new TTFTableName("maxp");
+
+ /** Naming table. */
+ public static final TTFTableName NAME = new TTFTableName("name");
+
+ /** PostScript information. */
+ public static final TTFTableName POST = new TTFTableName("post");
+
+ /** CVT Program. */
+ public static final TTFTableName PREP = new TTFTableName("prep");
+
+ /** Vertical Metrics header. */
+ public static final TTFTableName VHEA = new TTFTableName("vhea");
+
+ /** Vertical Metrics. */
+ public static final TTFTableName VMTX = new TTFTableName("vmtx");
+
+ private final String name;
+
+ private TTFTableName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Returns the name of the table as it should be in the Directory Table.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Returns an instance of this class corresponding to the given string representation.
+ * @param tableName table name as in the Table Directory
+ * @return TTFTableName
+ */
+ public static TTFTableName getValue(String tableName) {
+ if (tableName != null) {
+ return new TTFTableName(tableName);
+ }
+ throw new IllegalArgumentException("A TrueType font table name must not be null");
+ }
+
+ @Override
+ public int hashCode() {
+ return name.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (!(o instanceof TTFTableName)) {
+ return false;
+ }
+ TTFTableName to = (TTFTableName) o;
+ return this.name.equals(to.getName());
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
+
+}
diff --git a/src/java/org/apache/fop/fonts/truetype/TTFTableOutputStream.java b/src/java/org/apache/fop/fonts/truetype/TTFTableOutputStream.java
new file mode 100644
index 000000000..d0d2007f5
--- /dev/null
+++ b/src/java/org/apache/fop/fonts/truetype/TTFTableOutputStream.java
@@ -0,0 +1,37 @@
+/*
+ * 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.truetype;
+
+import java.io.IOException;
+
+/**
+ * An interface for writing a TrueType table to an output stream.
+ */
+public interface TTFTableOutputStream {
+
+ /**
+ * Streams a table from the given byte array.
+ *
+ * @param ttfData the source of the table to stream from
+ * @param offset the position in the byte array where the table starts
+ * @param size the size of the table in bytes
+ */
+ void streamTable(byte[] ttfData, int offset, int size) throws IOException;
+}
diff --git a/src/java/org/apache/fop/hyphenation/HyphenationTreeCache.java b/src/java/org/apache/fop/hyphenation/HyphenationTreeCache.java
index c5845977f..ec16810b3 100644
--- a/src/java/org/apache/fop/hyphenation/HyphenationTreeCache.java
+++ b/src/java/org/apache/fop/hyphenation/HyphenationTreeCache.java
@@ -79,7 +79,7 @@ public class HyphenationTreeCache {
String userKey = null;
if (hyphPatNames != null) {
String key = constructLlccKey(lang, country);
- key.replace('_', '-');
+ key = key.replace('_', '-');
userKey = (String) hyphPatNames.get(key);
}
return userKey;
@@ -96,7 +96,7 @@ public class HyphenationTreeCache {
/**
* Notes a key to a hyphenation tree as missing.
- * This is to avoid searching a second time for a hyphneation pattern file which is not
+ * This is to avoid searching a second time for a hyphenation pattern file which is not
* available.
* @param key the key (ex. "de_CH" or "en")
*/
@@ -109,7 +109,7 @@ public class HyphenationTreeCache {
/**
* Indicates whether a hyphenation file has been requested before but it wasn't available.
- * This is to avoid searching a second time for a hyphneation pattern file which is not
+ * This is to avoid searching a second time for a hyphenation pattern file which is not
* available.
* @param key the key (ex. "de_CH" or "en")
* @return true if the hyphenation tree is unavailable
diff --git a/src/java/org/apache/fop/layoutmgr/BalancingColumnBreakingAlgorithm.java b/src/java/org/apache/fop/layoutmgr/BalancingColumnBreakingAlgorithm.java
index 2bcdb6127..03c159843 100644
--- a/src/java/org/apache/fop/layoutmgr/BalancingColumnBreakingAlgorithm.java
+++ b/src/java/org/apache/fop/layoutmgr/BalancingColumnBreakingAlgorithm.java
@@ -41,12 +41,16 @@ public class BalancingColumnBreakingAlgorithm extends PageBreakingAlgorithm {
* @param topLevelLM the top level layout manager
* @param pageProvider the page provider
* @param layoutListener the layout listener
- * @param alignment alignment of the paragraph/page. One of {@link Constants#EN_START},
- * {@link Constants#EN_JUSTIFY}, {@link Constants#EN_CENTER},
- * {@link Constants#EN_END}.
- * For pages, {@link Constants#EN_BEFORE} and {@link Constants#EN_AFTER}
- * are mapped to the corresponding inline properties,
- * {@link Constants#EN_START} and {@link Constants#EN_END}.
+ * @param alignment alignment of the paragraph/page. One of
+ * {@link org.apache.fop.fo.Constants#EN_START},
+ * {@link org.apache.fop.fo.Constants#EN_JUSTIFY},
+ * {@link org.apache.fop.fo.Constants#EN_CENTER},
+ * {@link org.apache.fop.fo.Constants#EN_END}.
+ * For pages, {@link org.apache.fop.fo.Constants#EN_BEFORE} and
+ * {@link org.apache.fop.fo.Constants#EN_AFTER}
+ * are mapped to the corresponding inline properties,
+ * {@link org.apache.fop.fo.Constants#EN_START} and
+ * {@link org.apache.fop.fo.Constants#EN_END}.
* @param alignmentLast alignment of the paragraph's last line
* @param footnoteSeparatorLength length of footnote separator
* @param partOverflowRecovery {@code true} if too long elements should be moved to
diff --git a/src/java/org/apache/fop/layoutmgr/BreakElement.java b/src/java/org/apache/fop/layoutmgr/BreakElement.java
index 1c89c2009..77e7f140b 100644
--- a/src/java/org/apache/fop/layoutmgr/BreakElement.java
+++ b/src/java/org/apache/fop/layoutmgr/BreakElement.java
@@ -46,8 +46,11 @@ public class BreakElement extends UnresolvedListElement {
* Create a new BreakElement for the given {@code position}, {@code penaltyValue}
* and {@code breakClass}. (Used principally to generate break-possibilities in
* ranges of content that must be kept together within the context corresponding
- * to the {@code breakClass}; expected to be one of {@link Constants#EN_AUTO},
- * {@link Constants#EN_LINE}, {@link Constants#EN_COLUMN} or {@link Constants#EN_PAGE})
+ * to the {@code breakClass}; expected to be one of
+ * {@link org.apache.fop.fo.Constants#EN_AUTO},
+ * {@link org.apache.fop.fo.Constants#EN_LINE},
+ * {@link org.apache.fop.fo.Constants#EN_COLUMN} or
+ * {@link org.apache.fop.fo.Constants#EN_PAGE})
* @param position the corresponding {@link Position}
* @param penaltyValue the penalty value
* @param breakClass the break class
@@ -64,9 +67,12 @@ public class BreakElement extends UnresolvedListElement {
* @param position the Position instance needed by the addAreas stage of the LMs.
* @param penaltyWidth the penalty width
* @param penaltyValue the penalty value for the penalty element to be constructed
- * @param breakClass the break class of this penalty (one of {@link Constants#EN_AUTO},
- * {@link Constants#EN_COLUMN}, {@link Constants#EN_PAGE},
- * {@link Constants#EN_EVEN_PAGE}, {@link Constants#EN_ODD_PAGE})
+ * @param breakClass the break class of this penalty (one of
+ * {@link org.apache.fop.fo.Constants#EN_AUTO},
+ * {@link org.apache.fop.fo.Constants#EN_COLUMN},
+ * {@link org.apache.fop.fo.Constants#EN_PAGE},
+ * {@link org.apache.fop.fo.Constants#EN_EVEN_PAGE},
+ * {@link org.apache.fop.fo.Constants#EN_ODD_PAGE})
* @param context the layout context which contains the pending conditional elements
*/
public BreakElement(Position position, int penaltyWidth, int penaltyValue,
@@ -120,9 +126,12 @@ public class BreakElement extends UnresolvedListElement {
/**
* Returns the break class of this penalty.
*
- * @return one of {@link Constants#EN_AUTO}, {@link Constants#EN_COLUMN},
- * {@link Constants#EN_PAGE}, {@link Constants#EN_EVEN_PAGE},
- * {@link Constants#EN_ODD_PAGE}
+ * @return one of
+ * {@link org.apache.fop.fo.Constants#EN_AUTO},
+ * {@link org.apache.fop.fo.Constants#EN_COLUMN},
+ * {@link org.apache.fop.fo.Constants#EN_PAGE},
+ * {@link org.apache.fop.fo.Constants#EN_EVEN_PAGE},
+ * {@link org.apache.fop.fo.Constants#EN_ODD_PAGE}.
*/
public int getBreakClass() {
return breakClass;
@@ -131,9 +140,12 @@ public class BreakElement extends UnresolvedListElement {
/**
* Sets the break class.
*
- * @param breakClass one of {@link Constants#EN_AUTO}, {@link Constants#EN_COLUMN},
- * {@link Constants#EN_PAGE}, {@link Constants#EN_EVEN_PAGE},
- * {@link Constants#EN_ODD_PAGE}
+ * @param breakClass one of
+ * {@link org.apache.fop.fo.Constants#EN_AUTO},
+ * {@link org.apache.fop.fo.Constants#EN_COLUMN},
+ * {@link org.apache.fop.fo.Constants#EN_PAGE},
+ * {@link org.apache.fop.fo.Constants#EN_EVEN_PAGE},
+ * {@link org.apache.fop.fo.Constants#EN_ODD_PAGE}.
*/
public void setBreakClass(int breakClass) {
this.breakClass = breakClass;
diff --git a/src/java/org/apache/fop/layoutmgr/KnuthPenalty.java b/src/java/org/apache/fop/layoutmgr/KnuthPenalty.java
index 2d4e1f2be..220f9c35d 100644
--- a/src/java/org/apache/fop/layoutmgr/KnuthPenalty.java
+++ b/src/java/org/apache/fop/layoutmgr/KnuthPenalty.java
@@ -72,8 +72,11 @@ public class KnuthPenalty extends KnuthElement {
* @param penalty the penalty value of this penalty
* @param penaltyFlagged is this penalty flagged?
* @param breakClass the break class of this penalty (one of
- * {@link Constants#EN_AUTO}, {@link Constants#EN_COLUMN}, {@link Constants#EN_PAGE},
- * {@link Constants#EN_EVEN_PAGE}, {@link Constants#EN_ODD_PAGE})
+ * {@link org.apache.fop.fo.Constants#EN_AUTO},
+ * {@link org.apache.fop.fo.Constants#EN_COLUMN},
+ * {@link org.apache.fop.fo.Constants#EN_PAGE},
+ * {@link org.apache.fop.fo.Constants#EN_EVEN_PAGE},
+ * {@link org.apache.fop.fo.Constants#EN_ODD_PAGE}).
* @param pos the Position stored in this penalty
* @param isAuxiliary is this penalty auxiliary?
*/
diff --git a/src/java/org/apache/fop/pdf/AbstractPDFStream.java b/src/java/org/apache/fop/pdf/AbstractPDFStream.java
index 0181728b8..0948a7d2b 100644
--- a/src/java/org/apache/fop/pdf/AbstractPDFStream.java
+++ b/src/java/org/apache/fop/pdf/AbstractPDFStream.java
@@ -78,7 +78,7 @@ public abstract class AbstractPDFStream extends PDFObject {
* from outside.
*/
protected void setupFilterList() {
- if (!getFilterList().isInitialized()) {
+ if (multipleFiltersAllowed() && !getFilterList().isInitialized()) {
getFilterList().addDefaultFilters(
getDocumentSafely().getFilterMap(),
getDefaultFilterName());
@@ -273,4 +273,11 @@ public abstract class AbstractPDFStream extends PDFObject {
//nop: No default implicit filters
}
+ /**
+ * Whether multiple filters can be applied.
+ * @return true if multiple filters allowed
+ */
+ protected boolean multipleFiltersAllowed() {
+ return true;
+ }
}
diff --git a/src/java/org/apache/fop/pdf/AlphaRasterImage.java b/src/java/org/apache/fop/pdf/AlphaRasterImage.java
index 7272c5edd..250b5daa2 100644
--- a/src/java/org/apache/fop/pdf/AlphaRasterImage.java
+++ b/src/java/org/apache/fop/pdf/AlphaRasterImage.java
@@ -212,6 +212,10 @@ public class AlphaRasterImage implements PDFImage {
return null;
}
+ /** {@inheritDoc} */
+ public boolean multipleFiltersAllowed() {
+ return true;
+ }
}
diff --git a/src/java/org/apache/fop/pdf/BitmapImage.java b/src/java/org/apache/fop/pdf/BitmapImage.java
index 186c00986..ac7af1dbd 100644
--- a/src/java/org/apache/fop/pdf/BitmapImage.java
+++ b/src/java/org/apache/fop/pdf/BitmapImage.java
@@ -37,6 +37,8 @@ public class BitmapImage implements PDFImage {
private PDFColor transparent = null;
private String key;
private PDFDocument pdfDoc;
+ private PDFFilter pdfFilter;
+ private boolean multipleFiltersAllowed = true;
/**
* Create a bitmap image.
@@ -208,9 +210,23 @@ public class BitmapImage implements PDFImage {
* {@inheritDoc}
*/
public PDFFilter getPDFFilter() {
- return null;
+ return pdfFilter;
}
-}
+ public void setPDFFilter(PDFFilter pdfFilter) {
+ this.pdfFilter = pdfFilter;
+ }
+
+ /** {@inheritDoc} */
+ public boolean multipleFiltersAllowed() {
+ return multipleFiltersAllowed;
+ }
+ /**
+ * Disallows multiple filters.
+ */
+ public void disallowMultipleFilters() {
+ multipleFiltersAllowed = false;
+ }
+}
diff --git a/src/java/org/apache/fop/pdf/PDFColor.java b/src/java/org/apache/fop/pdf/PDFColor.java
index 8eea7e390..1fb544368 100644
--- a/src/java/org/apache/fop/pdf/PDFColor.java
+++ b/src/java/org/apache/fop/pdf/PDFColor.java
@@ -30,7 +30,7 @@ import org.apache.xmlgraphics.java2d.color.DeviceCMYKColorSpace;
* image. And in this context, only RGB and Gray values are used.
* <p>
* Use of this class is discouraged. {@link PDFColorHandler} is now used for in-content color
- * selection. For masked bitmaps, it may be wiser to switch to {@link Color} in the long run.
+ * selection. For masked bitmaps, it may be wiser to switch to {@link java.awt.Color} in the long run.
*/
public class PDFColor extends PDFPathPaint {
// could be 3.0 as well.
diff --git a/src/java/org/apache/fop/pdf/PDFEmbeddedFile.java b/src/java/org/apache/fop/pdf/PDFEmbeddedFile.java
index dc0681405..ae6893944 100644
--- a/src/java/org/apache/fop/pdf/PDFEmbeddedFile.java
+++ b/src/java/org/apache/fop/pdf/PDFEmbeddedFile.java
@@ -38,7 +38,10 @@ public class PDFEmbeddedFile extends PDFStream {
put("Params", params);
}
- /** {@inheritDoc} */
+ /**
+ * Determine if should encode on the fly.
+ * @return true if should encode on the fly
+ */
protected boolean isEncodingOnTheFly() {
//Acrobat doesn't like an indirect /Length object in this case,
//but only when the embedded file is a PDF file.
diff --git a/src/java/org/apache/fop/pdf/PDFFactory.java b/src/java/org/apache/fop/pdf/PDFFactory.java
index cc1d93de0..beb384dcf 100644
--- a/src/java/org/apache/fop/pdf/PDFFactory.java
+++ b/src/java/org/apache/fop/pdf/PDFFactory.java
@@ -45,6 +45,7 @@ import org.apache.commons.logging.LogFactory;
import org.apache.xmlgraphics.java2d.color.ColorUtil;
import org.apache.xmlgraphics.java2d.color.NamedColorSpace;
+
import org.apache.xmlgraphics.xmp.Metadata;
import org.apache.fop.fonts.CIDFont;
@@ -1674,8 +1675,8 @@ public class PDFFactory {
FontFileReader reader = new FontFileReader(in);
TTFSubSetFile subset = new TTFSubSetFile();
- byte[] subsetFont = subset.readFont(reader,
- mbfont.getTTCName(), mbfont.getUsedGlyphs());
+ subset.readFont(reader, mbfont.getTTCName(), mbfont.getUsedGlyphs());
+ byte[] subsetFont = subset.getFontSubset();
// Only TrueType CID fonts are supported now
embeddedFont = new PDFTTFStream(subsetFont.length);
diff --git a/src/java/org/apache/fop/pdf/PDFImage.java b/src/java/org/apache/fop/pdf/PDFImage.java
index e2b9e521c..38da60e7b 100644
--- a/src/java/org/apache/fop/pdf/PDFImage.java
+++ b/src/java/org/apache/fop/pdf/PDFImage.java
@@ -151,5 +151,13 @@ public interface PDFImage {
*/
String getFilterHint();
+ /**
+ * Indicates whether multiple image filters are allowed; this is implemented because Adobe
+ * Reader does not like multiple FlateDecode filters applied to an image even though that
+ * allowed by the PDF spec; this is probable due to security concerns since many PDF malware
+ * exploits, like zip bombs, make use of a chain of FlateDecode filters.
+ */
+ boolean multipleFiltersAllowed();
+
}
diff --git a/src/java/org/apache/fop/pdf/PDFImageXObject.java b/src/java/org/apache/fop/pdf/PDFImageXObject.java
index acab4c19c..1c28cb2a7 100644
--- a/src/java/org/apache/fop/pdf/PDFImageXObject.java
+++ b/src/java/org/apache/fop/pdf/PDFImageXObject.java
@@ -166,4 +166,9 @@ public class PDFImageXObject extends PDFXObject {
return pdfimage.getFilterHint();
}
+ /** {@inheritDoc} */
+ protected boolean multipleFiltersAllowed() {
+ return pdfimage.multipleFiltersAllowed();
+ }
+
}
diff --git a/src/java/org/apache/fop/pdf/PDFObject.java b/src/java/org/apache/fop/pdf/PDFObject.java
index 1b9c4eea7..e0f950154 100644
--- a/src/java/org/apache/fop/pdf/PDFObject.java
+++ b/src/java/org/apache/fop/pdf/PDFObject.java
@@ -22,7 +22,6 @@ package org.apache.fop.pdf;
// Java
import java.io.IOException;
import java.io.OutputStream;
-import java.io.Writer;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -211,11 +210,6 @@ public abstract class PDFObject implements PDFWritable {
}
/** {@inheritDoc} */
- public void outputInline(OutputStream out, Writer writer) throws IOException {
- throw new UnsupportedOperationException("Don't use anymore: " + getClass().getName());
- }
-
- /** {@inheritDoc} */
public void outputInline(OutputStream out, StringBuilder textBuffer) throws IOException {
if (hasObjectNumber()) {
textBuffer.append(referencePDF());
diff --git a/src/java/org/apache/fop/pdf/PDFResources.java b/src/java/org/apache/fop/pdf/PDFResources.java
index df8647bf4..cded7c00a 100644
--- a/src/java/org/apache/fop/pdf/PDFResources.java
+++ b/src/java/org/apache/fop/pdf/PDFResources.java
@@ -21,9 +21,8 @@ package org.apache.fop.pdf;
import java.io.IOException;
import java.io.OutputStream;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
@@ -46,33 +45,33 @@ public class PDFResources extends PDFDictionary {
/**
* /Font objects keyed by their internal name
*/
- protected Map fonts = new HashMap();
+ protected Map<String, PDFFont> fonts = new LinkedHashMap<String, PDFFont>();
/**
* Set of XObjects
*/
- protected Set xObjects = new HashSet();
+ protected Set<PDFXObject> xObjects = new LinkedHashSet<PDFXObject>();
/**
* Set of patterns
*/
- protected Set patterns = new HashSet();
+ protected Set<PDFPattern> patterns = new LinkedHashSet<PDFPattern>();
/**
* Set of shadings
*/
- protected Set shadings = new HashSet();
+ protected Set<PDFShading> shadings = new LinkedHashSet<PDFShading>();
/**
* Set of ExtGStates
*/
- protected Set gstates = new HashSet();
+ protected Set<PDFGState> gstates = new LinkedHashSet<PDFGState>();
/** Map of color spaces (key: color space name) */
- protected Map colorSpaces = new HashMap();
+ protected Map<PDFName, PDFColorSpace> colorSpaces = new LinkedHashMap<PDFName, PDFColorSpace>();
/** Map of ICC color spaces (key: ICC profile description) */
- protected Map iccColorSpaces = new HashMap();
+ protected Map<String, PDFICCBasedColorSpace> iccColorSpaces = new LinkedHashMap<String, PDFICCBasedColorSpace>();
/**
* create a /Resources object.
@@ -168,7 +167,7 @@ public class PDFResources extends PDFDictionary {
PDFICCBasedColorSpace icc = (PDFICCBasedColorSpace)colorSpace;
String desc = ColorProfileUtil.getICCProfileDescription(
icc.getICCStream().getICCProfile());
- this.iccColorSpaces.put(desc, colorSpace);
+ this.iccColorSpaces.put(desc, icc);
}
}
@@ -178,7 +177,7 @@ public class PDFResources extends PDFDictionary {
* @return the requested color space or null if it wasn't found
*/
public PDFICCBasedColorSpace getICCColorSpaceByProfileName(String desc) {
- PDFICCBasedColorSpace cs = (PDFICCBasedColorSpace)this.iccColorSpaces.get(desc);
+ PDFICCBasedColorSpace cs = this.iccColorSpaces.get(desc);
return cs;
}
@@ -188,7 +187,7 @@ public class PDFResources extends PDFDictionary {
* @return the requested color space or null if it wasn't found
*/
public PDFColorSpace getColorSpace(PDFName name) {
- PDFColorSpace cs = (PDFColorSpace)this.colorSpaces.get(name);
+ PDFColorSpace cs = this.colorSpaces.get(name);
return cs;
}
@@ -202,28 +201,24 @@ public class PDFResources extends PDFDictionary {
if (!this.fonts.isEmpty()) {
PDFDictionary dict = new PDFDictionary(this);
/* construct PDF dictionary of font object references */
- Iterator fontIterator = this.fonts.keySet().iterator();
- while (fontIterator.hasNext()) {
- String fontName = (String)fontIterator.next();
- dict.put(fontName, (PDFFont)this.fonts.get(fontName));
+ for (Map.Entry<String, PDFFont> entry : fonts.entrySet()) {
+ dict.put(entry.getKey(), entry.getValue());
}
put("Font", dict);
}
if (!this.shadings.isEmpty()) {
PDFDictionary dict = new PDFDictionary(this);
- for (Iterator iter = shadings.iterator(); iter.hasNext();) {
- PDFShading currentShading = (PDFShading)iter.next();
- dict.put(currentShading.getName(), currentShading);
+ for (PDFShading shading : shadings) {
+ dict.put(shading.getName(), shading);
}
put("Shading", dict);
}
if (!this.patterns.isEmpty()) {
PDFDictionary dict = new PDFDictionary(this);
- for (Iterator iter = patterns.iterator(); iter.hasNext();) {
- PDFPattern currentPattern = (PDFPattern)iter.next();
- dict.put(currentPattern.getName(), currentPattern);
+ for (PDFPattern pattern : patterns) {
+ dict.put(pattern.getName(), pattern);
}
put("Pattern", dict);
}
@@ -237,26 +232,23 @@ public class PDFResources extends PDFDictionary {
if (this.xObjects != null && !this.xObjects.isEmpty()) {
PDFDictionary dict = new PDFDictionary(this);
- for (Iterator iter = xObjects.iterator(); iter.hasNext();) {
- PDFXObject xobj = (PDFXObject)iter.next();
- dict.put(xobj.getName().toString(), xobj);
+ for (PDFXObject xObject : xObjects) {
+ dict.put(xObject.getName().toString(), xObject);
}
put("XObject", dict);
}
if (!this.gstates.isEmpty()) {
PDFDictionary dict = new PDFDictionary(this);
- for (Iterator iter = gstates.iterator(); iter.hasNext();) {
- PDFGState gs = (PDFGState)iter.next();
- dict.put(gs.getName(), gs);
+ for (PDFGState gstate : gstates) {
+ dict.put(gstate.getName(), gstate);
}
put("ExtGState", dict);
}
if (!this.colorSpaces.isEmpty()) {
PDFDictionary dict = new PDFDictionary(this);
- for (Iterator iter = colorSpaces.values().iterator(); iter.hasNext();) {
- PDFColorSpace colorSpace = (PDFColorSpace)iter.next();
+ for (PDFColorSpace colorSpace : colorSpaces.values()) {
dict.put(colorSpace.getName(), colorSpace);
}
put("ColorSpace", dict);
diff --git a/src/java/org/apache/fop/render/ImageHandler.java b/src/java/org/apache/fop/render/ImageHandler.java
index fe6952de1..190d7c5d6 100644
--- a/src/java/org/apache/fop/render/ImageHandler.java
+++ b/src/java/org/apache/fop/render/ImageHandler.java
@@ -33,7 +33,8 @@ public interface ImageHandler extends ImageHandlerBase {
* Indicates whether the image handler is compatible with the indicated target represented
* by the rendering context object and with the image to be processed. The image is also
* passed as a parameter because a handler might not support every subtype of image that is
- * presented. For example: in the case of {@link ImageXMLDOM}, the image might carry an SVG
+ * presented. For example: in the case of
+ * {@link org.apache.xmlgraphics.image.loader.impl.ImageXMLDOM}, the image might carry an SVG
* or some other XML format. One handler might only handle SVG but no other XML format.
* @param targetContext the target rendering context
* @param image the image to be processed (or null if only to check based on the rendering
diff --git a/src/java/org/apache/fop/render/afp/AFPDocumentHandler.java b/src/java/org/apache/fop/render/afp/AFPDocumentHandler.java
index 7823a2ce6..2ae95dd4c 100644
--- a/src/java/org/apache/fop/render/afp/AFPDocumentHandler.java
+++ b/src/java/org/apache/fop/render/afp/AFPDocumentHandler.java
@@ -58,7 +58,8 @@ import org.apache.fop.render.intermediate.IFException;
import org.apache.fop.render.intermediate.IFPainter;
/**
- * {@link IFDocumentHandler} implementation that produces AFP (MO:DCA).
+ * {@link org.apache.fop.render.intermediate.IFDocumentHandler} implementation that
+ * produces AFP (MO:DCA).
*/
public class AFPDocumentHandler extends AbstractBinaryWritingIFDocumentHandler
implements AFPCustomizable {
diff --git a/src/java/org/apache/fop/render/afp/extensions/AFPIncludeFormMap.java b/src/java/org/apache/fop/render/afp/extensions/AFPIncludeFormMap.java
index 842456f69..824ecbabb 100644
--- a/src/java/org/apache/fop/render/afp/extensions/AFPIncludeFormMap.java
+++ b/src/java/org/apache/fop/render/afp/extensions/AFPIncludeFormMap.java
@@ -27,7 +27,7 @@ import org.xml.sax.helpers.AttributesImpl;
/**
* This extension allows to include an AFP form map resource. It is implemented as an extension
- * attachment ({@link ExtensionAttachment}).
+ * attachment ({@link org.apache.fop.fo.extensions.ExtensionAttachment}).
*/
public class AFPIncludeFormMap extends AFPExtensionAttachment {
diff --git a/src/java/org/apache/fop/render/afp/extensions/AFPIncludeFormMapElement.java b/src/java/org/apache/fop/render/afp/extensions/AFPIncludeFormMapElement.java
index 37f818f64..c5079b2bd 100644
--- a/src/java/org/apache/fop/render/afp/extensions/AFPIncludeFormMapElement.java
+++ b/src/java/org/apache/fop/render/afp/extensions/AFPIncludeFormMapElement.java
@@ -32,8 +32,8 @@ import org.apache.fop.fo.PropertyList;
import org.apache.fop.fo.extensions.ExtensionAttachment;
/**
- * This class extends the {@link ExtensionObj} class. It represents the "include-form-map"
- * extension in the FO tree.
+ * This class extends the {@link org.apache.fop.fo.extensions.ExtensionObj} class.
+ * It represents the "include-form-map" extension in the FO tree.
*/
public class AFPIncludeFormMapElement extends AbstractAFPExtensionObject {
diff --git a/src/java/org/apache/fop/render/afp/extensions/AFPPageOverlay.java b/src/java/org/apache/fop/render/afp/extensions/AFPPageOverlay.java
index 0900d40b4..1e6aee7d5 100644
--- a/src/java/org/apache/fop/render/afp/extensions/AFPPageOverlay.java
+++ b/src/java/org/apache/fop/render/afp/extensions/AFPPageOverlay.java
@@ -25,7 +25,7 @@ import org.xml.sax.helpers.AttributesImpl;
/**
* This extension allows to include an AFP Page Overlay resource. It is implemented as an extension
- * attachment ({@link ExtensionAttachment}).
+ * attachment ({@link org.apache.fop.fo.extensions.ExtensionAttachment}).
*/
public class AFPPageOverlay extends AFPExtensionAttachment {
diff --git a/src/java/org/apache/fop/render/bitmap/AbstractBitmapDocumentHandler.java b/src/java/org/apache/fop/render/bitmap/AbstractBitmapDocumentHandler.java
index c98c05a8e..0c6ebde56 100644
--- a/src/java/org/apache/fop/render/bitmap/AbstractBitmapDocumentHandler.java
+++ b/src/java/org/apache/fop/render/bitmap/AbstractBitmapDocumentHandler.java
@@ -47,7 +47,8 @@ import org.apache.fop.render.java2d.Java2DPainter;
import org.apache.fop.render.java2d.Java2DUtil;
/**
- * Abstract {@link IFDocumentHandler} implementation for producing bitmap images.
+ * Abstract {@link org.apache.fop.render.intermediate.IFDocumentHandler} implementation
+ * for producing bitmap images.
*/
public abstract class AbstractBitmapDocumentHandler extends AbstractBinaryWritingIFDocumentHandler {
diff --git a/src/java/org/apache/fop/render/bitmap/PNGDocumentHandler.java b/src/java/org/apache/fop/render/bitmap/PNGDocumentHandler.java
index 05c44a1a6..ed92b852f 100644
--- a/src/java/org/apache/fop/render/bitmap/PNGDocumentHandler.java
+++ b/src/java/org/apache/fop/render/bitmap/PNGDocumentHandler.java
@@ -23,7 +23,8 @@ import org.apache.fop.apps.MimeConstants;
import org.apache.fop.render.intermediate.IFDocumentHandlerConfigurator;
/**
- * {@link IFDocumentHandler} implementation that produces PNG files.
+ * {@link org.apache.fop.render.intermediate.IFDocumentHandler} implementation
+ * that produces PNG files.
*/
public class PNGDocumentHandler extends AbstractBitmapDocumentHandler {
diff --git a/src/java/org/apache/fop/render/bitmap/TIFFDocumentHandler.java b/src/java/org/apache/fop/render/bitmap/TIFFDocumentHandler.java
index 4b7fa3c2d..1c9a39bbe 100644
--- a/src/java/org/apache/fop/render/bitmap/TIFFDocumentHandler.java
+++ b/src/java/org/apache/fop/render/bitmap/TIFFDocumentHandler.java
@@ -23,7 +23,8 @@ import org.apache.fop.apps.MimeConstants;
import org.apache.fop.render.intermediate.IFDocumentHandlerConfigurator;
/**
- * {@link IFDocumentHandler} implementation that produces TIFF files.
+ * {@link org.apache.fop.render.intermediate.IFDocumentHandler} implementation
+ * that produces TIFF files.
*/
public class TIFFDocumentHandler extends AbstractBitmapDocumentHandler {
diff --git a/src/java/org/apache/fop/render/intermediate/IFContext.java b/src/java/org/apache/fop/render/intermediate/IFContext.java
index c59a02ba8..681b996e4 100644
--- a/src/java/org/apache/fop/render/intermediate/IFContext.java
+++ b/src/java/org/apache/fop/render/intermediate/IFContext.java
@@ -145,7 +145,7 @@ public class IFContext {
/**
* Resets the current structure tree element.
- * @see #setStructureTreeElement(String)
+ * @see #setStructureTreeElement(StructureTreeElement)
*/
public void resetStructureTreeElement() {
setStructureTreeElement(null);
@@ -154,7 +154,7 @@ public class IFContext {
/**
* Returns the current structure tree element.
* @return the structure tree element (or null if no element is active)
- * @see #setStructureTreeElement(String)
+ * @see #setStructureTreeElement(StructureTreeElement)
*/
public StructureTreeElement getStructureTreeElement() {
return this.structureTreeElement;
diff --git a/src/java/org/apache/fop/render/intermediate/IFSerializer.java b/src/java/org/apache/fop/render/intermediate/IFSerializer.java
index f8f286cb3..7114f51e3 100644
--- a/src/java/org/apache/fop/render/intermediate/IFSerializer.java
+++ b/src/java/org/apache/fop/render/intermediate/IFSerializer.java
@@ -64,6 +64,15 @@ import org.apache.fop.util.XMLUtil;
public class IFSerializer extends AbstractXMLWritingIFDocumentHandler
implements IFConstants, IFPainter, IFDocumentNavigationHandler {
+ /**
+ * Intermediate Format (IF) version, used to express an @version attribute
+ * in the root element of the IF document, the initial value of which
+ * is set to '2.0' to signify that something preceded it (but didn't
+ * happen to be marked as such), and that this version is not necessarily
+ * backwards compatible with the unmarked (<2.0) version.
+ */
+ public static final String VERSION = "2.0";
+
private IFDocumentHandler mimicHandler;
private int pageSequenceIndex; // used for accessibility
@@ -167,7 +176,9 @@ public class IFSerializer extends AbstractXMLWritingIFDocumentHandler
DocumentNavigationExtensionConstants.NAMESPACE);
handler.startPrefixMapping(InternalElementMapping.STANDARD_PREFIX,
InternalElementMapping.URI);
- handler.startElement(EL_DOCUMENT);
+ AttributesImpl atts = new AttributesImpl();
+ addAttribute(atts, "version", VERSION);
+ handler.startElement(EL_DOCUMENT, atts);
} catch (SAXException e) {
throw new IFException("SAX error in startDocument()", e);
}
diff --git a/src/java/org/apache/fop/render/intermediate/IFUtil.java b/src/java/org/apache/fop/render/intermediate/IFUtil.java
index e8651a3df..c4f681936 100644
--- a/src/java/org/apache/fop/render/intermediate/IFUtil.java
+++ b/src/java/org/apache/fop/render/intermediate/IFUtil.java
@@ -147,8 +147,8 @@ public final class IFUtil {
/**
* Sets up the fonts on a document handler. If the document handler provides a configurator
- * object the configuration from the {@link FopFactory} will be used. Otherwise,
- * a default font configuration will be set up.
+ * object the configuration from the {@link org.apache.fop.apps.FopFactory} will be used.
+ * Otherwise, a default font configuration will be set up.
* @param documentHandler the document handler
* @param fontInfo the font info object (may be null)
* @throws FOPException if an error occurs while setting up the fonts
@@ -175,8 +175,8 @@ public final class IFUtil {
/**
* Sets up the fonts on a document handler. If the document handler provides a configurator
- * object the configuration from the {@link FopFactory} will be used. Otherwise,
- * a default font configuration will be set up.
+ * object the configuration from the {@link org.apache.fop.apps.FopFactory} will be used.
+ * Otherwise, a default font configuration will be set up.
* @param documentHandler the document handler
* @throws FOPException if an error occurs while setting up the fonts
*/
diff --git a/src/java/org/apache/fop/render/java2d/ConfiguredFontCollection.java b/src/java/org/apache/fop/render/java2d/ConfiguredFontCollection.java
index 7ac350d5d..9c404be3e 100644
--- a/src/java/org/apache/fop/render/java2d/ConfiguredFontCollection.java
+++ b/src/java/org/apache/fop/render/java2d/ConfiguredFontCollection.java
@@ -89,7 +89,8 @@ public class ConfiguredFontCollection implements FontCollection {
font = new CustomFontMetricsMapper(fontMetrics, fontSource);
} else {
CustomFont fontMetrics = FontLoader.loadFont(
- fontFile, null, true, EncodingMode.AUTO,
+ fontFile, null, true, configFontInfo.getEmbeddingMode(),
+ EncodingMode.AUTO,
configFontInfo.getKerning(),
configFontInfo.getAdvanced(), fontResolver);
font = new CustomFontMetricsMapper(fontMetrics);
diff --git a/src/java/org/apache/fop/render/java2d/CustomFontMetricsMapper.java b/src/java/org/apache/fop/render/java2d/CustomFontMetricsMapper.java
index 5d964cbbf..36df00f20 100644
--- a/src/java/org/apache/fop/render/java2d/CustomFontMetricsMapper.java
+++ b/src/java/org/apache/fop/render/java2d/CustomFontMetricsMapper.java
@@ -36,7 +36,7 @@ import org.apache.fop.fonts.Typeface;
/**
* FontMetricsMapper that delegates most methods to an underlying
- * {@link FontMetrics} instance. This class was designed to allow
+ * {@link org.apache.fop.fonts.FontMetrics} instance. This class was designed to allow
* the underlying {@link Font} to be loaded from a
* user-configured file not registered in the current graphics environment.
*/
diff --git a/src/java/org/apache/fop/render/java2d/Java2DPainter.java b/src/java/org/apache/fop/render/java2d/Java2DPainter.java
index dd830a15b..575242d38 100644
--- a/src/java/org/apache/fop/render/java2d/Java2DPainter.java
+++ b/src/java/org/apache/fop/render/java2d/Java2DPainter.java
@@ -47,7 +47,8 @@ import org.apache.fop.traits.RuleStyle;
import org.apache.fop.util.CharUtilities;
/**
- * {@link IFPainter} implementation that paints on a Graphics2D instance.
+ * {@link org.apache.fop.render.intermediate.IFPainter} implementation
+ * that paints on a Graphics2D instance.
*/
public class Java2DPainter extends AbstractIFPainter {
diff --git a/src/java/org/apache/fop/render/pcl/PCLDocumentHandler.java b/src/java/org/apache/fop/render/pcl/PCLDocumentHandler.java
index 52e522c7a..332b4a77d 100644
--- a/src/java/org/apache/fop/render/pcl/PCLDocumentHandler.java
+++ b/src/java/org/apache/fop/render/pcl/PCLDocumentHandler.java
@@ -45,7 +45,8 @@ import org.apache.fop.render.java2d.Java2DUtil;
import org.apache.fop.render.pcl.extensions.PCLElementMapping;
/**
- * {@link IFDocumentHandler} implementation that produces PCL 5.
+ * {@link org.apache.fop.render.intermediate.IFDocumentHandler} implementation
+ * that produces PCL 5.
*/
public class PCLDocumentHandler extends AbstractBinaryWritingIFDocumentHandler
implements PCLConstants {
diff --git a/src/java/org/apache/fop/render/pcl/PCLPainter.java b/src/java/org/apache/fop/render/pcl/PCLPainter.java
index 9fc575456..0d630826c 100644
--- a/src/java/org/apache/fop/render/pcl/PCLPainter.java
+++ b/src/java/org/apache/fop/render/pcl/PCLPainter.java
@@ -58,7 +58,8 @@ import org.apache.fop.traits.RuleStyle;
import org.apache.fop.util.CharUtilities;
/**
- * {@link IFPainter} implementation that produces PCL 5.
+ * {@link org.apache.fop.render.intermediate.IFPainter} implementation
+ * that produces PCL 5.
*/
public class PCLPainter extends AbstractIFPainter implements PCLConstants {
diff --git a/src/java/org/apache/fop/render/pdf/AbstractImageAdapter.java b/src/java/org/apache/fop/render/pdf/AbstractImageAdapter.java
index 59d3930f7..46e8ebe95 100644
--- a/src/java/org/apache/fop/render/pdf/AbstractImageAdapter.java
+++ b/src/java/org/apache/fop/render/pdf/AbstractImageAdapter.java
@@ -20,13 +20,16 @@
package org.apache.fop.render.pdf;
import java.awt.color.ColorSpace;
import java.awt.color.ICC_Profile;
+import java.awt.image.IndexColorModel;
+import org.apache.commons.io.output.ByteArrayOutputStream;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.xmlgraphics.image.loader.Image;
import org.apache.xmlgraphics.java2d.color.profile.ColorProfileUtil;
+import org.apache.fop.pdf.PDFArray;
import org.apache.fop.pdf.PDFColor;
import org.apache.fop.pdf.PDFConformanceException;
import org.apache.fop.pdf.PDFDeviceColorSpace;
@@ -50,7 +53,11 @@ public abstract class AbstractImageAdapter implements PDFImage {
/** the image */
protected Image image;
- private PDFICCStream pdfICCStream = null;
+ private PDFICCStream pdfICCStream;
+
+ private static final int MAX_HIVAL = 255;
+
+ private boolean multipleFiltersAllowed = true;
/**
* Creates a new PDFImage from an Image instance.
@@ -203,6 +210,68 @@ public abstract class AbstractImageAdapter implements PDFImage {
}
/**
+ * This is to be used by populateXObjectDictionary() when the image is palette based.
+ * @param dict the dictionary to fill in
+ * @param icm the image color model
+ */
+ protected void populateXObjectDictionaryForIndexColorModel(PDFDictionary dict, IndexColorModel icm) {
+ PDFArray indexed = new PDFArray(dict);
+ indexed.add(new PDFName("Indexed"));
+ if (icm.getColorSpace().getType() != ColorSpace.TYPE_RGB) {
+ log.warn("Indexed color space is not using RGB as base color space."
+ + " The image may not be handled correctly." + " Base color space: "
+ + icm.getColorSpace() + " Image: " + image.getInfo());
+ }
+ indexed.add(new PDFName(toPDFColorSpace(icm.getColorSpace()).getName()));
+ int c = icm.getMapSize();
+ int hival = c - 1;
+ if (hival > MAX_HIVAL) {
+ throw new UnsupportedOperationException("hival must not go beyond " + MAX_HIVAL);
+ }
+ indexed.add(Integer.valueOf(hival));
+ int[] palette = new int[c];
+ icm.getRGBs(palette);
+ ByteArrayOutputStream baout = new ByteArrayOutputStream();
+ for (int i = 0; i < c; i++) {
+ // TODO Probably doesn't work for non RGB based color spaces
+ // See log warning above
+ int entry = palette[i];
+ baout.write((entry & 0xFF0000) >> 16);
+ baout.write((entry & 0xFF00) >> 8);
+ baout.write(entry & 0xFF);
+ }
+ indexed.add(baout.toByteArray());
+
+ dict.put("ColorSpace", indexed);
+ dict.put("BitsPerComponent", icm.getPixelSize());
+
+ Integer index = getIndexOfFirstTransparentColorInPalette(icm);
+ if (index != null) {
+ PDFArray mask = new PDFArray(dict);
+ mask.add(index);
+ mask.add(index);
+ dict.put("Mask", mask);
+ }
+ }
+
+ private static Integer getIndexOfFirstTransparentColorInPalette(IndexColorModel icm) {
+ byte[] alphas = new byte[icm.getMapSize()];
+ byte[] reds = new byte[icm.getMapSize()];
+ byte[] greens = new byte[icm.getMapSize()];
+ byte[] blues = new byte[icm.getMapSize()];
+ icm.getAlphas(alphas);
+ icm.getReds(reds);
+ icm.getGreens(greens);
+ icm.getBlues(blues);
+ for (int i = 0; i < icm.getMapSize(); i++) {
+ if ((alphas[i] & 0xFF) == 0) {
+ return Integer.valueOf(i);
+ }
+ }
+ return null;
+ }
+
+ /**
* Converts a ColorSpace object to a PDFColorSpace object.
* @param cs ColorSpace instance
* @return PDFColorSpace new converted object
@@ -226,5 +295,17 @@ public abstract class AbstractImageAdapter implements PDFImage {
return pdfCS;
}
+ /** {@inheritDoc} */
+ public boolean multipleFiltersAllowed() {
+ return multipleFiltersAllowed;
+ }
+
+ /**
+ * Disallows multiple filters.
+ */
+ public void disallowMultipleFilters() {
+ multipleFiltersAllowed = false;
+ }
+
}
diff --git a/src/java/org/apache/fop/render/pdf/ImageRawPNGAdapter.java b/src/java/org/apache/fop/render/pdf/ImageRawPNGAdapter.java
new file mode 100644
index 000000000..b9f5e1d28
--- /dev/null
+++ b/src/java/org/apache/fop/render/pdf/ImageRawPNGAdapter.java
@@ -0,0 +1,260 @@
+/*
+ * 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$ */
+
+// Original author: Matthias Reichenbacher
+
+package org.apache.fop.render.pdf;
+
+import java.awt.image.ColorModel;
+import java.awt.image.IndexColorModel;
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.zip.Deflater;
+import java.util.zip.DeflaterOutputStream;
+import java.util.zip.Inflater;
+import java.util.zip.InflaterInputStream;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.io.output.ByteArrayOutputStream;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.xmlgraphics.image.loader.impl.ImageRawPNG;
+import org.apache.xmlgraphics.image.loader.impl.ImageRawStream;
+
+import org.apache.fop.pdf.BitmapImage;
+import org.apache.fop.pdf.FlateFilter;
+import org.apache.fop.pdf.PDFColor;
+import org.apache.fop.pdf.PDFDeviceColorSpace;
+import org.apache.fop.pdf.PDFDictionary;
+import org.apache.fop.pdf.PDFDocument;
+import org.apache.fop.pdf.PDFFilter;
+import org.apache.fop.pdf.PDFFilterException;
+import org.apache.fop.pdf.PDFFilterList;
+import org.apache.fop.pdf.PDFICCStream;
+import org.apache.fop.pdf.PDFReference;
+
+public class ImageRawPNGAdapter extends AbstractImageAdapter {
+
+ /** logging instance */
+ private static Log log = LogFactory.getLog(ImageRawPNGAdapter.class);
+
+ private PDFICCStream pdfICCStream;
+ private PDFFilter pdfFilter;
+ private String maskRef;
+ private PDFReference softMask;
+ private int numberOfInterleavedComponents;
+
+ /**
+ * Creates a new PDFImage from an Image instance.
+ * @param image the image
+ * @param key XObject key
+ */
+ public ImageRawPNGAdapter(ImageRawPNG image, String key) {
+ super(image, key);
+ }
+
+ /** {@inheritDoc} */
+ public void setup(PDFDocument doc) {
+ super.setup(doc);
+ ColorModel cm = ((ImageRawPNG) this.image).getColorModel();
+ if (cm instanceof IndexColorModel) {
+ numberOfInterleavedComponents = 1;
+ } else {
+ // this can be 1 (gray), 2 (gray + alpha), 3 (rgb) or 4 (rgb + alpha)
+ // numberOfInterleavedComponents = (cm.hasAlpha() ? 1 : 0) + cm.getNumColorComponents();
+ numberOfInterleavedComponents = cm.getNumComponents();
+ }
+
+ // set up image compression for non-alpha channel
+ FlateFilter flate;
+ try {
+ flate = new FlateFilter();
+ flate.setApplied(true);
+ flate.setPredictor(FlateFilter.PREDICTION_PNG_OPT);
+ if (numberOfInterleavedComponents < 3) {
+ // means palette (1) or gray (1) or gray + alpha (2)
+ flate.setColors(1);
+ } else {
+ // means rgb (3) or rgb + alpha (4)
+ flate.setColors(3);
+ }
+ flate.setColumns(image.getSize().getWidthPx());
+ flate.setBitsPerComponent(this.getBitsPerComponent());
+ } catch (PDFFilterException e) {
+ throw new RuntimeException("FlateFilter configuration error", e);
+ }
+ this.pdfFilter = flate;
+ this.disallowMultipleFilters();
+
+ // Handle transparency channel if applicable; note that for palette images the transparency is
+ // not TRANSLUCENT
+ if (cm.hasAlpha() && cm.getTransparency() == ColorModel.TRANSLUCENT) {
+ doc.getProfile().verifyTransparencyAllowed(image.getInfo().getOriginalURI());
+ // TODO: Implement code to combine image with background color if transparency is not allowed
+ // here we need to inflate the PNG pixel data, which includes alpha, separate the alpha channel
+ // and then deflate it back again
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ DeflaterOutputStream dos = new DeflaterOutputStream(baos, new Deflater());
+ InputStream in = ((ImageRawStream) image).createInputStream();
+ try {
+ InflaterInputStream infStream = new InflaterInputStream(in, new Inflater());
+ DataInputStream dataStream = new DataInputStream(infStream);
+ // offset is the byte offset of the alpha component
+ int offset = numberOfInterleavedComponents - 1; // 1 for GA, 3 for RGBA
+ int numColumns = image.getSize().getWidthPx();
+ int bytesPerRow = numberOfInterleavedComponents * numColumns;
+ int filter;
+ // read line by line; the first byte holds the filter
+ while ((filter = dataStream.read()) != -1) {
+ byte[] bytes = new byte[bytesPerRow];
+ dataStream.readFully(bytes, 0, bytesPerRow);
+ dos.write((byte) filter);
+ for (int j = 0; j < numColumns; j++) {
+ dos.write(bytes, offset, 1);
+ offset += numberOfInterleavedComponents;
+ }
+ offset = numberOfInterleavedComponents - 1;
+ }
+ dos.close();
+ } catch (IOException e) {
+ throw new RuntimeException("Error processing transparency channel:", e);
+ } finally {
+ IOUtils.closeQuietly(in);
+ }
+ // set up alpha channel compression
+ FlateFilter transFlate;
+ try {
+ transFlate = new FlateFilter();
+ transFlate.setApplied(true);
+ transFlate.setPredictor(FlateFilter.PREDICTION_PNG_OPT);
+ transFlate.setColors(1);
+ transFlate.setColumns(image.getSize().getWidthPx());
+ transFlate.setBitsPerComponent(this.getBitsPerComponent());
+ } catch (PDFFilterException e) {
+ throw new RuntimeException("FlateFilter configuration error", e);
+ }
+ BitmapImage alphaMask = new BitmapImage("Mask:" + this.getKey(), image.getSize().getWidthPx(),
+ image.getSize().getHeightPx(), baos.toByteArray(), null);
+ alphaMask.setPDFFilter(transFlate);
+ alphaMask.disallowMultipleFilters();
+ alphaMask.setColorSpace(new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_GRAY));
+ softMask = doc.addImage(null, alphaMask).makeReference();
+ }
+ }
+
+ /** {@inheritDoc} */
+ public PDFDeviceColorSpace getColorSpace() {
+ // DeviceGray, DeviceRGB, or DeviceCMYK
+ return toPDFColorSpace(image.getColorSpace());
+ }
+
+ /** {@inheritDoc} */
+ public int getBitsPerComponent() {
+ return ((ImageRawPNG) this.image).getBitDepth();
+ }
+
+ /** {@inheritDoc} */
+ public boolean isTransparent() {
+ return ((ImageRawPNG) this.image).isTransparent();
+ }
+
+ /** {@inheritDoc} */
+ public PDFColor getTransparentColor() {
+ return new PDFColor(((ImageRawPNG) this.image).getTransparentColor());
+ }
+
+ /** {@inheritDoc} */
+ public String getMask() {
+ return maskRef;
+ }
+
+ /** {@inheritDoc} */
+ public String getSoftMask() {
+ return softMask.toString();
+ }
+
+ /** {@inheritDoc} */
+ public PDFReference getSoftMaskReference() {
+ return softMask;
+ }
+
+ /** {@inheritDoc} */
+ public PDFFilter getPDFFilter() {
+ return pdfFilter;
+ }
+
+ /** {@inheritDoc} */
+ public void outputContents(OutputStream out) throws IOException {
+ InputStream in = ((ImageRawStream) image).createInputStream();
+
+ try {
+ if (numberOfInterleavedComponents == 1 || numberOfInterleavedComponents == 3) {
+ // means we have Gray, RGB, or Palette
+ IOUtils.copy(in, out);
+ } else {
+ // means we have Gray + alpha or RGB + alpha
+ // TODO: since we have alpha here do this when the alpha channel is extracted
+ int numBytes = numberOfInterleavedComponents - 1; // 1 for Gray, 3 for RGB
+ int numColumns = image.getSize().getWidthPx();
+ InflaterInputStream infStream = new InflaterInputStream(in, new Inflater());
+ DataInputStream dataStream = new DataInputStream(infStream);
+ int offset = 0;
+ int bytesPerRow = numberOfInterleavedComponents * numColumns;
+ int filter;
+ // here we need to inflate the PNG pixel data, which includes alpha, separate the alpha
+ // channel and then deflate the RGB channels back again
+ DeflaterOutputStream dos = new DeflaterOutputStream(out, new Deflater());
+ while ((filter = dataStream.read()) != -1) {
+ byte[] bytes = new byte[bytesPerRow];
+ dataStream.readFully(bytes, 0, bytesPerRow);
+ dos.write((byte) filter);
+ for (int j = 0; j < numColumns; j++) {
+ dos.write(bytes, offset, numBytes);
+ offset += numberOfInterleavedComponents;
+ }
+ offset = 0;
+ }
+ dos.close();
+ }
+ } finally {
+ IOUtils.closeQuietly(in);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public PDFICCStream getICCStream() {
+ return pdfICCStream;
+ }
+
+ /** {@inheritDoc} */
+ public String getFilterHint() {
+ return PDFFilterList.PRECOMPRESSED_FILTER;
+ }
+
+ public void populateXObjectDictionary(PDFDictionary dict) {
+ ColorModel cm = ((ImageRawPNG) image).getColorModel();
+ if (cm instanceof IndexColorModel) {
+ IndexColorModel icm = (IndexColorModel) cm;
+ super.populateXObjectDictionaryForIndexColorModel(dict, icm);
+ }
+ }
+}
diff --git a/src/java/org/apache/fop/render/pdf/ImageRenderedAdapter.java b/src/java/org/apache/fop/render/pdf/ImageRenderedAdapter.java
index c38a2edaf..b0b7b79d1 100644
--- a/src/java/org/apache/fop/render/pdf/ImageRenderedAdapter.java
+++ b/src/java/org/apache/fop/render/pdf/ImageRenderedAdapter.java
@@ -27,8 +27,6 @@ import java.awt.image.RenderedImage;
import java.io.IOException;
import java.io.OutputStream;
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.io.output.ByteArrayOutputStream;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -36,14 +34,12 @@ import org.apache.xmlgraphics.image.loader.impl.ImageRendered;
import org.apache.xmlgraphics.ps.ImageEncodingHelper;
import org.apache.fop.pdf.AlphaRasterImage;
-import org.apache.fop.pdf.PDFArray;
import org.apache.fop.pdf.PDFColor;
import org.apache.fop.pdf.PDFDeviceColorSpace;
import org.apache.fop.pdf.PDFDictionary;
import org.apache.fop.pdf.PDFDocument;
import org.apache.fop.pdf.PDFFilter;
import org.apache.fop.pdf.PDFFilterList;
-import org.apache.fop.pdf.PDFName;
import org.apache.fop.pdf.PDFReference;
/**
@@ -162,30 +158,6 @@ public class ImageRenderedAdapter extends AbstractImageAdapter {
return (getImage().getTransparentColor() != null);
}
- private static Integer getIndexOfFirstTransparentColorInPalette(RenderedImage image) {
- ColorModel cm = image.getColorModel();
- if (cm instanceof IndexColorModel) {
- IndexColorModel icm = (IndexColorModel)cm;
- //Identify the transparent color in the palette
- byte[] alphas = new byte[icm.getMapSize()];
- byte[] reds = new byte[icm.getMapSize()];
- byte[] greens = new byte[icm.getMapSize()];
- byte[] blues = new byte[icm.getMapSize()];
- icm.getAlphas(alphas);
- icm.getReds(reds);
- icm.getGreens(greens);
- icm.getBlues(blues);
- for (int i = 0;
- i < ((IndexColorModel) cm).getMapSize();
- i++) {
- if ((alphas[i] & 0xFF) == 0) {
- return Integer.valueOf(i);
- }
- }
- }
- return null;
- }
-
/** {@inheritDoc} */
@Override
public PDFColor getTransparentColor() {
@@ -230,54 +202,13 @@ public class ImageRenderedAdapter extends AbstractImageAdapter {
}
}
- private static final int MAX_HIVAL = 255;
-
/** {@inheritDoc} */
@Override
public void populateXObjectDictionary(PDFDictionary dict) {
ColorModel cm = getEffectiveColorModel();
if (cm instanceof IndexColorModel) {
- IndexColorModel icm = (IndexColorModel)cm;
- PDFArray indexed = new PDFArray(dict);
- indexed.add(new PDFName("Indexed"));
-
- if (icm.getColorSpace().getType() != ColorSpace.TYPE_RGB) {
- log.warn("Indexed color space is not using RGB as base color space."
- + " The image may not be handled correctly."
- + " Base color space: " + icm.getColorSpace()
- + " Image: " + image.getInfo());
- }
- indexed.add(new PDFName(toPDFColorSpace(icm.getColorSpace()).getName()));
- int c = icm.getMapSize();
- int hival = c - 1;
- if (hival > MAX_HIVAL) {
- throw new UnsupportedOperationException("hival must not go beyond " + MAX_HIVAL);
- }
- indexed.add(Integer.valueOf(hival));
- int[] palette = new int[c];
- icm.getRGBs(palette);
- ByteArrayOutputStream baout = new ByteArrayOutputStream();
- for (int i = 0; i < c; i++) {
- //TODO Probably doesn't work for non RGB based color spaces
- //See log warning above
- int entry = palette[i];
- baout.write((entry & 0xFF0000) >> 16);
- baout.write((entry & 0xFF00) >> 8);
- baout.write(entry & 0xFF);
- }
- indexed.add(baout.toByteArray());
- IOUtils.closeQuietly(baout);
-
- dict.put("ColorSpace", indexed);
- dict.put("BitsPerComponent", icm.getPixelSize());
-
- Integer index = getIndexOfFirstTransparentColorInPalette(getImage().getRenderedImage());
- if (index != null) {
- PDFArray mask = new PDFArray(dict);
- mask.add(index);
- mask.add(index);
- dict.put("Mask", mask);
- }
+ IndexColorModel icm = (IndexColorModel) cm;
+ super.populateXObjectDictionaryForIndexColorModel(dict, icm);
}
}
diff --git a/src/java/org/apache/fop/render/pdf/PDFImageHandlerRawPNG.java b/src/java/org/apache/fop/render/pdf/PDFImageHandlerRawPNG.java
new file mode 100644
index 000000000..3475954a8
--- /dev/null
+++ b/src/java/org/apache/fop/render/pdf/PDFImageHandlerRawPNG.java
@@ -0,0 +1,65 @@
+/*
+ * 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$ */
+
+// Original author: Matthias Reichenbacher
+
+package org.apache.fop.render.pdf;
+
+import org.apache.xmlgraphics.image.loader.Image;
+import org.apache.xmlgraphics.image.loader.ImageFlavor;
+import org.apache.xmlgraphics.image.loader.impl.ImageRawPNG;
+
+import org.apache.fop.pdf.PDFImage;
+import org.apache.fop.render.RenderingContext;
+
+/**
+ * Image handler implementation which handles CCITT encoded images (CCITT fax group 3/4)
+ * for PDF output.
+ */
+public class PDFImageHandlerRawPNG extends AbstractPDFImageHandler {
+
+ private static final ImageFlavor[] FLAVORS = new ImageFlavor[] {ImageFlavor.RAW_PNG};
+
+ @Override
+ PDFImage createPDFImage(Image image, String xobjectKey) {
+ return new ImageRawPNGAdapter((ImageRawPNG) image, xobjectKey);
+ }
+
+ /** {@inheritDoc} */
+ public int getPriority() {
+ return 100;
+ }
+
+ /** {@inheritDoc} */
+ public Class<ImageRawPNG> getSupportedImageClass() {
+ return ImageRawPNG.class;
+ }
+
+ /** {@inheritDoc} */
+ public ImageFlavor[] getSupportedImageFlavors() {
+ return FLAVORS;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isCompatible(RenderingContext targetContext, Image image) {
+ return (image == null || image instanceof ImageRawPNG)
+ && targetContext instanceof PDFRenderingContext;
+ }
+
+}
diff --git a/src/java/org/apache/fop/render/ps/FontResourceCache.java b/src/java/org/apache/fop/render/ps/FontResourceCache.java
index 9d4090eed..1514d201e 100644
--- a/src/java/org/apache/fop/render/ps/FontResourceCache.java
+++ b/src/java/org/apache/fop/render/ps/FontResourceCache.java
@@ -42,19 +42,20 @@ class FontResourceCache {
}
/**
- * Returns the PSResource for the given font key.
+ * Returns the PSFontResource for the given font key.
* @param key the font key ("F*")
- * @return the matching PSResource
+ * @return the matching PSFontResource instance
*/
- public PSResource getPSResourceForFontKey(String key) {
- PSResource res = null;
+ public PSFontResource getFontResourceForFontKey(String key) {
+ PSFontResource res = null;
if (this.fontResources != null) {
- res = (PSResource)this.fontResources.get(key);
+ res = (PSFontResource)this.fontResources.get(key);
} else {
this.fontResources = new java.util.HashMap();
}
if (res == null) {
- res = new PSResource(PSResource.TYPE_FONT, getPostScriptNameForFontKey(key));
+ res = PSFontResource.createFontResource(
+ new PSResource(PSResource.TYPE_FONT, getPostScriptNameForFontKey(key)));
this.fontResources.put(key, res);
}
return res;
@@ -76,9 +77,9 @@ class FontResourceCache {
throw new IllegalStateException("Font not available: " + key);
}
if (postFix == null) {
- return tf.getFontName();
+ return tf.getEmbedFontName();
} else {
- return tf.getFontName() + postFix;
+ return tf.getEmbedFontName() + postFix;
}
}
diff --git a/src/java/org/apache/fop/render/ps/ImageEncoderPNG.java b/src/java/org/apache/fop/render/ps/ImageEncoderPNG.java
new file mode 100644
index 000000000..bcda90bda
--- /dev/null
+++ b/src/java/org/apache/fop/render/ps/ImageEncoderPNG.java
@@ -0,0 +1,113 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.ps;
+
+import java.awt.image.ColorModel;
+import java.awt.image.IndexColorModel;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.zip.Deflater;
+import java.util.zip.DeflaterOutputStream;
+import java.util.zip.Inflater;
+import java.util.zip.InflaterInputStream;
+
+import org.apache.commons.io.IOUtils;
+
+import org.apache.xmlgraphics.image.loader.impl.ImageRawPNG;
+import org.apache.xmlgraphics.image.loader.impl.ImageRawStream;
+import org.apache.xmlgraphics.ps.ImageEncoder;
+
+/**
+ * ImageEncoder implementation for PNG images.
+ */
+public class ImageEncoderPNG implements ImageEncoder {
+ private final ImageRawPNG image;
+ private int numberOfInterleavedComponents;
+
+ /**
+ * Main constructor
+ * @param image the PNG image
+ */
+ public ImageEncoderPNG(ImageRawPNG image) {
+ this.image = image;
+ ColorModel cm = ((ImageRawPNG) this.image).getColorModel();
+ if (cm instanceof IndexColorModel) {
+ numberOfInterleavedComponents = 1;
+ } else {
+ // this can be 1 (gray), 2 (gray + alpha), 3 (rgb) or 4 (rgb + alpha)
+ // numberOfInterleavedComponents = (cm.hasAlpha() ? 1 : 0) + cm.getNumColorComponents();
+ numberOfInterleavedComponents = cm.getNumComponents();
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void writeTo(OutputStream out) throws IOException {
+ // TODO: refactor this code with equivalent PDF code
+ InputStream in = ((ImageRawStream) image).createInputStream();
+ try {
+ if (numberOfInterleavedComponents == 1 || numberOfInterleavedComponents == 3) {
+ // means we have Gray, RGB, or Palette
+ IOUtils.copy(in, out);
+ } else {
+ // means we have Gray + alpha or RGB + alpha
+ int numBytes = numberOfInterleavedComponents - 1; // 1 for Gray, 3 for RGB
+ int numColumns = image.getSize().getWidthPx();
+ InflaterInputStream infStream = new InflaterInputStream(in, new Inflater());
+ DataInputStream dataStream = new DataInputStream(infStream);
+ int offset = 0;
+ int bytesPerRow = numberOfInterleavedComponents * numColumns;
+ int filter;
+ // here we need to inflate the PNG pixel data, which includes alpha, separate the alpha
+ // channel and then deflate the RGB channels back again
+ // TODO: not using the baos below and using the original out instead (as happens in PDF)
+ // would be preferable but that does not work with the rest of the postscript code; this
+ // needs to be revisited
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ DeflaterOutputStream dos = new DeflaterOutputStream(/* out */baos, new Deflater());
+ while ((filter = dataStream.read()) != -1) {
+ byte[] bytes = new byte[bytesPerRow];
+ dataStream.readFully(bytes, 0, bytesPerRow);
+ dos.write((byte) filter);
+ for (int j = 0; j < numColumns; j++) {
+ dos.write(bytes, offset, numBytes);
+ offset += numberOfInterleavedComponents;
+ }
+ offset = 0;
+ }
+ dos.close();
+ IOUtils.copy(new ByteArrayInputStream(baos.toByteArray()), out);
+ }
+ } finally {
+ IOUtils.closeQuietly(in);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public String getImplicitFilter() {
+ String filter = "<< /Predictor 15 /Columns " + image.getSize().getWidthPx();
+ filter += " /Colors " + (numberOfInterleavedComponents > 2 ? 3 : 1);
+ filter += " /BitsPerComponent " + image.getBitDepth() + " >> /FlateDecode";
+ return filter;
+ }
+}
diff --git a/src/java/org/apache/fop/render/ps/NativeTextHandler.java b/src/java/org/apache/fop/render/ps/NativeTextHandler.java
index 33adcb8d7..e8f123dc5 100644
--- a/src/java/org/apache/fop/render/ps/NativeTextHandler.java
+++ b/src/java/org/apache/fop/render/ps/NativeTextHandler.java
@@ -100,12 +100,6 @@ public class NativeTextHandler implements PSTextHandler {
//nop
}
- /** {@inheritDoc} */
- public void drawString(String text, float x, float y) throws IOException {
- // TODO Remove me after removing the deprecated method in TextHandler.
- throw new UnsupportedOperationException("Deprecated method!");
- }
-
/**
* Draw a string to the PostScript document. The text is painted using
* text operations.
diff --git a/src/java/org/apache/fop/render/ps/PSDocumentHandler.java b/src/java/org/apache/fop/render/ps/PSDocumentHandler.java
index 490e42b2d..0fe564827 100644
--- a/src/java/org/apache/fop/render/ps/PSDocumentHandler.java
+++ b/src/java/org/apache/fop/render/ps/PSDocumentHandler.java
@@ -26,6 +26,7 @@ import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
@@ -49,6 +50,7 @@ import org.apache.xmlgraphics.ps.dsc.ResourceTracker;
import org.apache.xmlgraphics.ps.dsc.events.DSCCommentBoundingBox;
import org.apache.xmlgraphics.ps.dsc.events.DSCCommentHiResBoundingBox;
+import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.apps.MimeConstants;
import org.apache.fop.render.intermediate.AbstractBinaryWritingIFDocumentHandler;
import org.apache.fop.render.intermediate.IFContext;
@@ -57,11 +59,13 @@ import org.apache.fop.render.intermediate.IFException;
import org.apache.fop.render.intermediate.IFPainter;
import org.apache.fop.render.ps.extensions.PSCommentAfter;
import org.apache.fop.render.ps.extensions.PSCommentBefore;
+import org.apache.fop.render.ps.extensions.PSPageTrailerCodeBefore;
import org.apache.fop.render.ps.extensions.PSSetPageDevice;
import org.apache.fop.render.ps.extensions.PSSetupCode;
/**
- * {@link IFDocumentHandler} implementation that produces PostScript.
+ * {@link org.apache.fop.render.intermediate.IFDocumentHandler} implementation
+ * that produces PostScript.
*/
public class PSDocumentHandler extends AbstractBinaryWritingIFDocumentHandler {
@@ -98,10 +102,13 @@ public class PSDocumentHandler extends AbstractBinaryWritingIFDocumentHandler {
private PSPageDeviceDictionary pageDeviceDictionary;
/** This is a collection holding all document header comments */
- private Collection[] comments = new Collection[3];
+ private Collection[] comments = new Collection[4];
private static final int COMMENT_DOCUMENT_HEADER = 0;
private static final int COMMENT_DOCUMENT_TRAILER = 1;
private static final int COMMENT_PAGE_TRAILER = 2;
+ private static final int PAGE_TRAILER_CODE_BEFORE = 3;
+
+ private PSEventProducer eventProducer;
/**
* Default constructor.
@@ -122,7 +129,9 @@ public class PSDocumentHandler extends AbstractBinaryWritingIFDocumentHandler {
/** {@inheritDoc} */
public void setContext(IFContext context) {
super.setContext(context);
- this.psUtil = new PSRenderingUtil(context.getUserAgent());
+ FOUserAgent userAgent = context.getUserAgent();
+ this.psUtil = new PSRenderingUtil(userAgent);
+ eventProducer = PSEventProducer.Provider.get(userAgent.getEventBroadcaster());
}
/** {@inheritDoc} */
@@ -141,7 +150,7 @@ public class PSDocumentHandler extends AbstractBinaryWritingIFDocumentHandler {
try {
OutputStream out;
if (psUtil.isOptimizeResources()) {
- this.tempFile = File.createTempFile("fop", null);
+ this.tempFile = File.createTempFile("fop", ".ps");
out = new java.io.FileOutputStream(this.tempFile);
out = new java.io.BufferedOutputStream(out);
} else {
@@ -199,7 +208,7 @@ public class PSDocumentHandler extends AbstractBinaryWritingIFDocumentHandler {
gen.writeDSCComment(DSCConstants.BEGIN_SETUP);
PSRenderingUtil.writeSetupCodeList(gen, setupCodeList, "SetupCode");
if (!psUtil.isOptimizeResources()) {
- this.fontResources.addAll(PSFontUtils.writeFontDict(gen, fontInfo));
+ this.fontResources.addAll(PSFontUtils.writeFontDict(gen, fontInfo, eventProducer));
} else {
gen.commentln("%FOPFontSetup"); //Place-holder, will be replaced in the second pass
}
@@ -254,8 +263,8 @@ public class PSDocumentHandler extends AbstractBinaryWritingIFDocumentHandler {
in = new java.io.BufferedInputStream(in);
try {
try {
- ResourceHandler handler = new ResourceHandler(getUserAgent(), this.fontInfo,
- resTracker, this.formResources);
+ ResourceHandler handler = new ResourceHandler(getUserAgent(), eventProducer,
+ this.fontInfo, resTracker, this.formResources);
handler.process(in, this.outputStream,
this.currentPageNumber, this.documentBoundingBox);
this.outputStream.flush();
@@ -443,8 +452,9 @@ public class PSDocumentHandler extends AbstractBinaryWritingIFDocumentHandler {
/** {@inheritDoc} */
public void startPageTrailer() throws IFException {
- super.startPageTrailer();
try {
+ writeExtensions(PAGE_TRAILER_CODE_BEFORE);
+ super.startPageTrailer();
gen.writeDSCComment(DSCConstants.PAGE_TRAILER);
} catch (IOException ioe) {
throw new IFException("I/O error in startPageTrailer()", ioe);
@@ -526,6 +536,11 @@ public class PSDocumentHandler extends AbstractBinaryWritingIFDocumentHandler {
comments[targetCollection] = new java.util.ArrayList();
}
comments[targetCollection].add(extension);
+ } else if (extension instanceof PSPageTrailerCodeBefore) {
+ if (comments[PAGE_TRAILER_CODE_BEFORE] == null) {
+ comments[PAGE_TRAILER_CODE_BEFORE] = new ArrayList();
+ }
+ comments[PAGE_TRAILER_CODE_BEFORE].add(extension);
}
} catch (IOException ioe) {
throw new IFException("I/O error in handleExtensionObject()", ioe);
@@ -537,8 +552,8 @@ public class PSDocumentHandler extends AbstractBinaryWritingIFDocumentHandler {
* @param key the font key ("F*")
* @return the matching PSResource
*/
- protected PSResource getPSResourceForFontKey(String key) {
- return this.fontResources.getPSResourceForFontKey(key);
+ protected PSFontResource getPSResourceForFontKey(String key) {
+ return this.fontResources.getFontResourceForFontKey(key);
}
/**
diff --git a/src/java/org/apache/fop/render/ps/PSEventProducer.java b/src/java/org/apache/fop/render/ps/PSEventProducer.java
index 702380a4d..bffdf2236 100644
--- a/src/java/org/apache/fop/render/ps/PSEventProducer.java
+++ b/src/java/org/apache/fop/render/ps/PSEventProducer.java
@@ -53,4 +53,11 @@ public interface PSEventProducer extends EventProducer {
*/
void postscriptDictionaryParseError(Object source, String content, Exception e);
+ /**
+ * PostScript Level 3 features are necessary.
+ *
+ * @param source the event source
+ * @event.severity FATAL
+ */
+ void postscriptLevel3Needed(Object source);
}
diff --git a/src/java/org/apache/fop/render/ps/PSEventProducer.xml b/src/java/org/apache/fop/render/ps/PSEventProducer.xml
index bcd89ed07..64b22d1a4 100644
--- a/src/java/org/apache/fop/render/ps/PSEventProducer.xml
+++ b/src/java/org/apache/fop/render/ps/PSEventProducer.xml
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<catalogue xml:lang="en">
<message key="postscriptDictionaryParseError">Failed to parse dictionary string. Reason: {e}, content = "{content}"</message>
+ <message key="postscriptLevel3Needed">PostScript Level 3 features are needed to handle this document.</message>
</catalogue>
diff --git a/src/java/org/apache/fop/render/ps/PSFontResource.java b/src/java/org/apache/fop/render/ps/PSFontResource.java
new file mode 100644
index 000000000..8b7b835ed
--- /dev/null
+++ b/src/java/org/apache/fop/render/ps/PSFontResource.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.ps;
+
+import org.apache.xmlgraphics.ps.PSResource;
+import org.apache.xmlgraphics.ps.dsc.ResourceTracker;
+
+/**
+ * A DSC resource corresponding to a font. This class handles the possible other resources
+ * that a font may depend on. For example, a CID-keyed font depends on a CIDFont resource, a
+ * CMap resource, and the ProcSet CIDInit resource.
+ */
+abstract class PSFontResource {
+
+ static PSFontResource createFontResource(final PSResource fontResource) {
+ return new PSFontResource() {
+
+ String getName() {
+ return fontResource.getName();
+ }
+
+ void notifyResourceUsageOnPage(ResourceTracker resourceTracker) {
+ resourceTracker.notifyResourceUsageOnPage(fontResource);
+ }
+ };
+ }
+
+ static PSFontResource createFontResource(final PSResource fontResource,
+ final PSResource procsetCIDInitResource, final PSResource cmapResource,
+ final PSResource cidFontResource) {
+ return new PSFontResource() {
+
+ String getName() {
+ return fontResource.getName();
+ }
+
+ void notifyResourceUsageOnPage(ResourceTracker resourceTracker) {
+ resourceTracker.notifyResourceUsageOnPage(fontResource);
+ resourceTracker.notifyResourceUsageOnPage(procsetCIDInitResource);
+ resourceTracker.notifyResourceUsageOnPage(cmapResource);
+ resourceTracker.notifyResourceUsageOnPage(cidFontResource);
+ }
+ };
+ }
+
+ /**
+ * Returns the name of the font resource.
+ *
+ * @return the name of the font
+ */
+ abstract String getName();
+
+ /**
+ * Notifies the given resource tracker of all the resources needed by this font.
+ *
+ * @param resourceTracker
+ */
+ abstract void notifyResourceUsageOnPage(ResourceTracker resourceTracker);
+
+}
diff --git a/src/java/org/apache/fop/render/ps/PSFontUtils.java b/src/java/org/apache/fop/render/ps/PSFontUtils.java
index 5e95b5ded..d0d75744f 100644
--- a/src/java/org/apache/fop/render/ps/PSFontUtils.java
+++ b/src/java/org/apache/fop/render/ps/PSFontUtils.java
@@ -23,7 +23,11 @@ import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Locale;
import java.util.Map;
+import java.util.Set;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
@@ -38,14 +42,26 @@ import org.apache.xmlgraphics.ps.PSResource;
import org.apache.xmlgraphics.ps.dsc.ResourceTracker;
import org.apache.fop.fonts.Base14Font;
+import org.apache.fop.fonts.CIDFontType;
+import org.apache.fop.fonts.CIDSubset;
+import org.apache.fop.fonts.CMapSegment;
import org.apache.fop.fonts.CustomFont;
+import org.apache.fop.fonts.EmbeddingMode;
import org.apache.fop.fonts.Font;
import org.apache.fop.fonts.FontInfo;
import org.apache.fop.fonts.FontType;
import org.apache.fop.fonts.LazyFont;
+import org.apache.fop.fonts.MultiByteFont;
import org.apache.fop.fonts.SingleByteEncoding;
import org.apache.fop.fonts.SingleByteFont;
import org.apache.fop.fonts.Typeface;
+import org.apache.fop.fonts.truetype.FontFileReader;
+import org.apache.fop.fonts.truetype.TTFFile;
+import org.apache.fop.fonts.truetype.TTFFile.PostScriptVersion;
+import org.apache.fop.fonts.truetype.TTFOutputStream;
+import org.apache.fop.fonts.truetype.TTFSubSetFile;
+import org.apache.fop.render.ps.fonts.PSTTFOutputStream;
+import org.apache.fop.util.HexEncoder;
/**
* Utility code for font handling in PostScript.
@@ -54,7 +70,6 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils {
/** logging instance */
protected static final Log log = LogFactory.getLog(PSFontUtils.class);
-
/**
* Generates the PostScript code for the font dictionary. This method should only be
* used if no "resource optimization" is performed, i.e. when the fonts are not embedded
@@ -66,7 +81,22 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils {
*/
public static Map writeFontDict(PSGenerator gen, FontInfo fontInfo)
throws IOException {
- return writeFontDict(gen, fontInfo, fontInfo.getFonts(), true);
+ return writeFontDict(gen, fontInfo, null);
+ }
+
+ /**
+ * Generates the PostScript code for the font dictionary. This method should only be
+ * used if no "resource optimization" is performed, i.e. when the fonts are not embedded
+ * in a second pass.
+ * @param gen PostScript generator to use for output
+ * @param fontInfo available fonts
+ * @param eventProducer to report events
+ * @return a Map of PSResource instances representing all defined fonts (key: font key)
+ * @throws IOException in case of an I/O problem
+ */
+ public static Map writeFontDict(PSGenerator gen, FontInfo fontInfo,
+ PSEventProducer eventProducer) throws IOException {
+ return writeFontDict(gen, fontInfo, fontInfo.getFonts(), true, eventProducer);
}
/**
@@ -76,13 +106,13 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils {
* @param gen PostScript generator to use for output
* @param fontInfo available fonts
* @param fonts the set of fonts to work with
+ * @param eventProducer the event producer
* @return a Map of PSResource instances representing all defined fonts (key: font key)
* @throws IOException in case of an I/O problem
*/
- public static Map writeFontDict(PSGenerator gen, FontInfo fontInfo,
- Map<String, Typeface> fonts)
- throws IOException {
- return writeFontDict(gen, fontInfo, fonts, false);
+ public static Map writeFontDict(PSGenerator gen, FontInfo fontInfo, Map<String, Typeface> fonts,
+ PSEventProducer eventProducer) throws IOException {
+ return writeFontDict(gen, fontInfo, fonts, false, eventProducer);
}
/**
@@ -96,15 +126,16 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils {
* @throws IOException in case of an I/O problem
*/
private static Map writeFontDict(PSGenerator gen, FontInfo fontInfo,
- Map<String, Typeface> fonts, boolean encodeAllCharacters) throws IOException {
+ Map<String, Typeface> fonts, boolean encodeAllCharacters, PSEventProducer eventProducer)
+ throws IOException {
gen.commentln("%FOPBeginFontDict");
- Map fontResources = new java.util.HashMap();
+ Map fontResources = new HashMap();
for (String key : fonts.keySet()) {
Typeface tf = getTypeFace(fontInfo, fonts, key);
- PSResource fontRes = new PSResource(PSResource.TYPE_FONT, tf.getFontName());
- fontResources.put(key, fontRes);
- embedFont(gen, tf, fontRes);
+ PSResource fontRes = new PSResource(PSResource.TYPE_FONT, tf.getEmbedFontName());
+ PSFontResource fontResource = embedFont(gen, tf, fontRes, eventProducer);
+ fontResources.put(key, fontResource);
if (tf instanceof SingleByteFont) {
SingleByteFont sbf = (SingleByteFont)tf;
@@ -117,9 +148,18 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils {
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);
+ PSResource derivedFontRes;
+ if (tf.getFontType() == FontType.TRUETYPE
+ && sbf.getTrueTypePostScriptVersion() != PostScriptVersion.V2) {
+ derivedFontRes = defineDerivedTrueTypeFont(gen, eventProducer,
+ tf.getEmbedFontName(), tf.getEmbedFontName() + postFix, encoding,
+ sbf.getCMap());
+ } else {
+ derivedFontRes = defineDerivedFont(gen, tf.getEmbedFontName(),
+ tf.getEmbedFontName() + postFix, encoding.getName());
+ }
+ fontResources.put(key + postFix,
+ PSFontResource.createFontResource(derivedFontRes));
}
}
}
@@ -156,12 +196,12 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils {
} else {
if (tf instanceof Base14Font) {
//Our Base 14 fonts don't use the default encoding
- redefineFontEncoding(gen, tf.getFontName(), tf.getEncodingName());
+ redefineFontEncoding(gen, tf.getEmbedFontName(), tf.getEncodingName());
} else if (tf instanceof SingleByteFont) {
SingleByteFont sbf = (SingleByteFont)tf;
if (!sbf.isUsingNativeEncoding()) {
//Font has been configured to use an encoding other than the default one
- redefineFontEncoding(gen, tf.getFontName(), tf.getEncodingName());
+ redefineFontEncoding(gen, tf.getEmbedFontName(), tf.getEncodingName());
}
}
}
@@ -184,39 +224,299 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils {
return tf;
}
- /**
- * Embeds a font in the PostScript file.
- * @param gen the PostScript generator
- * @param tf the font
- * @param fontRes the PSResource associated with the font
- * @throws IOException In case of an I/O error
- */
- public static void embedFont(PSGenerator gen, Typeface tf, PSResource fontRes)
- throws IOException {
- boolean embeddedFont = false;
- if (FontType.TYPE1 == tf.getFontType()) {
- if (tf instanceof CustomFont) {
- CustomFont cf = (CustomFont)tf;
- if (isEmbeddable(cf)) {
- InputStream in = getInputStreamOnFont(gen, cf);
- if (in != null) {
- gen.writeDSCComment(DSCConstants.BEGIN_RESOURCE,
- fontRes);
- embedType1Font(gen, in);
- gen.writeDSCComment(DSCConstants.END_RESOURCE);
- gen.getResourceTracker().registerSuppliedResource(fontRes);
- embeddedFont = true;
- } else {
- gen.commentln("%WARNING: Could not embed font: " + cf.getFontName());
- log.warn("Font " + cf.getFontName() + " is marked as supplied in the"
- + " PostScript file but could not be embedded!");
+ private static PSFontResource embedFont(PSGenerator gen, Typeface tf, PSResource fontRes,
+ PSEventProducer eventProducer) throws IOException {
+ FontType fontType = tf.getFontType();
+ PSFontResource fontResource = null;
+ if (!(fontType == FontType.TYPE1 || fontType == FontType.TRUETYPE
+ || fontType == FontType.TYPE0) || !(tf instanceof CustomFont)) {
+ gen.writeDSCComment(DSCConstants.INCLUDE_RESOURCE, fontRes);
+ fontResource = PSFontResource.createFontResource(fontRes);
+ return fontResource;
+ }
+ CustomFont cf = (CustomFont)tf;
+ if (isEmbeddable(cf)) {
+ InputStream in = getInputStreamOnFont(gen, cf);
+ if (in == null) {
+ gen.commentln("%WARNING: Could not embed font: " + cf.getEmbedFontName());
+ log.warn("Font " + cf.getEmbedFontName() + " is marked as supplied in the"
+ + " PostScript file but could not be embedded!");
+ gen.writeDSCComment(DSCConstants.INCLUDE_RESOURCE, fontRes);
+ fontResource = PSFontResource.createFontResource(fontRes);
+ return fontResource;
+ }
+ if (fontType == FontType.TYPE0) {
+ if (gen.embedIdentityH()) {
+ checkPostScriptLevel3(gen, eventProducer);
+ /*
+ * First CID-keyed font to be embedded; add
+ * %%IncludeResource: comment for ProcSet CIDInit.
+ */
+ gen.includeProcsetCIDInitResource();
+ }
+ PSResource cidFontResource = embedType2CIDFont(gen,
+ (MultiByteFont) tf, in);
+ fontResource = PSFontResource.createFontResource(fontRes,
+ gen.getProcsetCIDInitResource(), gen.getIdentityHCMapResource(),
+ cidFontResource);
+ }
+ gen.writeDSCComment(DSCConstants.BEGIN_RESOURCE, fontRes);
+ if (fontType == FontType.TYPE1) {
+ embedType1Font(gen, in);
+ fontResource = PSFontResource.createFontResource(fontRes);
+ } else if (fontType == FontType.TRUETYPE) {
+ embedTrueTypeFont(gen, (SingleByteFont) tf, in);
+ fontResource = PSFontResource.createFontResource(fontRes);
+ } else {
+ composeType0Font(gen, (MultiByteFont) tf, in);
+ }
+ gen.writeDSCComment(DSCConstants.END_RESOURCE);
+ gen.getResourceTracker().registerSuppliedResource(fontRes);
+ }
+ return fontResource;
+ }
+
+ private static void checkPostScriptLevel3(PSGenerator gen, PSEventProducer eventProducer) {
+ if (gen.getPSLevel() < 3) {
+ if (eventProducer != null) {
+ eventProducer.postscriptLevel3Needed(gen);
+ } else {
+ throw new IllegalStateException("PostScript Level 3 is"
+ + " required to use TrueType fonts,"
+ + " configured level is "
+ + gen.getPSLevel());
+ }
+ }
+ }
+
+ private static void embedTrueTypeFont(PSGenerator gen,
+ SingleByteFont font, InputStream fontStream) throws IOException {
+ /* See Adobe Technical Note #5012, "The Type 42 Font Format Specification" */
+ gen.commentln("%!PS-TrueTypeFont-65536-65536-1"); // TODO TrueType & font versions
+ gen.writeln("11 dict begin");
+ if (font.getEmbeddingMode() == EmbeddingMode.AUTO) {
+ font.setEmbeddingMode(EmbeddingMode.SUBSET);
+ }
+ FontFileReader reader = new FontFileReader(fontStream);
+ TTFFile ttfFile = new TTFFile();
+ ttfFile.readFont(reader, font.getFullName());
+ createType42DictionaryEntries(gen, font, font.getCMap(), ttfFile);
+ gen.writeln("FontName currentdict end definefont pop");
+ }
+
+ private static void createType42DictionaryEntries(PSGenerator gen, CustomFont font,
+ CMapSegment[] cmap, TTFFile ttfFile) throws IOException {
+ gen.write("/FontName /");
+ gen.write(font.getEmbedFontName());
+ gen.writeln(" def");
+ gen.writeln("/PaintType 0 def");
+ gen.writeln("/FontMatrix [1 0 0 1 0 0] def");
+ writeFontBBox(gen, font);
+ gen.writeln("/FontType 42 def");
+ gen.writeln("/Encoding 256 array");
+ gen.writeln("0 1 255{1 index exch/.notdef put}for");
+ boolean buildCharStrings;
+ Set<String> glyphNames = new HashSet<String>();
+ if (font.getFontType() == FontType.TYPE0 && font.getEmbeddingMode() != EmbeddingMode.FULL) {
+ //"/Encoding" is required but ignored for CID fonts
+ //so we keep it minimal to save space
+ buildCharStrings = false;
+ } else {
+ buildCharStrings = true;
+ for (int i = 0; i < Glyphs.WINANSI_ENCODING.length; i++) {
+ gen.write("dup ");
+ gen.write(i);
+ gen.write(" /");
+ String glyphName = Glyphs.charToGlyphName(Glyphs.WINANSI_ENCODING[i]);
+ if (glyphName.equals("")) {
+ gen.write(Glyphs.NOTDEF);
+ } else {
+ gen.write(glyphName);
+ glyphNames.add(glyphName);
+ }
+ gen.writeln(" put");
+ }
+ }
+ gen.writeln("readonly def");
+ TTFOutputStream ttfOut = new PSTTFOutputStream(gen);
+ ttfFile.stream(ttfOut);
+
+ buildCharStrings(gen, buildCharStrings, cmap, glyphNames, font);
+ }
+
+ private static void buildCharStrings(PSGenerator gen, boolean buildCharStrings,
+ CMapSegment[] cmap, Set<String> glyphNames, CustomFont font) throws IOException {
+ gen.write("/CharStrings ");
+ if (!buildCharStrings) {
+ gen.write(1);
+ } else if (font.getEmbeddingMode() != EmbeddingMode.FULL) {
+ int charCount = 1; //1 for .notdef
+ for (CMapSegment segment : cmap) {
+ charCount += segment.getUnicodeEnd() - segment.getUnicodeStart() + 1;
+ }
+ gen.write(charCount);
+ } else {
+ gen.write(font.getCMap().length);
+ }
+ gen.writeln(" dict dup begin");
+ gen.write("/");
+ gen.write(Glyphs.NOTDEF);
+ gen.writeln(" 0 def"); // .notdef always has to be at index 0
+ if (!buildCharStrings) {
+ // If we're not building the full CharStrings we can end here
+ gen.writeln("end readonly def");
+ return;
+ }
+ if (font.getEmbeddingMode() != EmbeddingMode.FULL) {
+ //Only performed in singly-byte mode, ignored for CID fonts
+ for (CMapSegment segment : cmap) {
+ int glyphIndex = segment.getGlyphStartIndex();
+ for (int ch = segment.getUnicodeStart(); ch <= segment.getUnicodeEnd(); ch++) {
+ char ch16 = (char)ch; //TODO Handle Unicode characters beyond 16bit
+ String glyphName = Glyphs.charToGlyphName(ch16);
+ if ("".equals(glyphName)) {
+ glyphName = "u" + Integer.toHexString(ch).toUpperCase(Locale.ENGLISH);
}
+ writeGlyphDefs(gen, glyphName, glyphIndex);
+
+ glyphIndex++;
}
}
+ } else {
+ for (String name : glyphNames) {
+ writeGlyphDefs(gen, name,
+ getGlyphIndex(Glyphs.getUnicodeSequenceForGlyphName(name).charAt(0),
+ font.getCMap()));
+ }
}
- if (!embeddedFont) {
- gen.writeDSCComment(DSCConstants.INCLUDE_RESOURCE, fontRes);
+ gen.writeln("end readonly def");
+ }
+
+ private static void writeGlyphDefs(PSGenerator gen, String glyphName, int glyphIndex)
+ throws IOException {
+ gen.write("/");
+ gen.write(glyphName);
+ gen.write(" ");
+ gen.write(glyphIndex);
+ gen.writeln(" def");
+ }
+
+ private static int getGlyphIndex(char c, CMapSegment[] cmap) {
+ for (CMapSegment segment : cmap) {
+ if (segment.getUnicodeStart() <= c && c <= segment.getUnicodeEnd()) {
+ return segment.getGlyphStartIndex() + c - segment.getUnicodeStart();
+ }
}
+ return 0;
+ }
+
+ private static void composeType0Font(PSGenerator gen, MultiByteFont font,
+ InputStream fontStream) throws IOException {
+ String psName = font.getEmbedFontName();
+ gen.write("/");
+ gen.write(psName);
+ gen.write(" /Identity-H [/");
+ gen.write(psName);
+ gen.writeln("] composefont pop");
+ }
+
+ private static PSResource embedType2CIDFont(PSGenerator gen,
+ MultiByteFont font, InputStream fontStream) throws IOException {
+ assert font.getCIDType() == CIDFontType.CIDTYPE2;
+
+ String psName = font.getEmbedFontName();
+ gen.write("%%BeginResource: CIDFont ");
+ gen.writeln(psName);
+
+ gen.write("%%Title: (");
+ gen.write(psName);
+ gen.writeln(" Adobe Identity 0)");
+
+ gen.writeln("%%Version: 1"); // TODO use font revision?
+ gen.writeln("/CIDInit /ProcSet findresource begin");
+ gen.writeln("20 dict begin");
+
+ gen.write("/CIDFontName /");
+ gen.write(psName);
+ gen.writeln(" def");
+
+ gen.writeln("/CIDFontVersion 1 def"); // TODO same as %%Version above
+
+ gen.write("/CIDFontType ");
+ gen.write(font.getCIDType().getValue());
+ gen.writeln(" def");
+
+ gen.writeln("/CIDSystemInfo 3 dict dup begin");
+ gen.writeln(" /Registry (Adobe) def");
+ gen.writeln(" /Ordering (Identity) def");
+ gen.writeln(" /Supplement 0 def");
+ gen.writeln("end def");
+
+ // TODO UIDBase (and UIDOffset in CMap) necessary if PostScript Level 1 & 2
+ // interpreters are to be supported
+ // (Level 1: with composite font extensions; Level 2: those that do not offer
+ // native mode support for CID-keyed fonts)
+
+ // TODO XUID (optional but strongly recommended)
+
+ // TODO /FontInfo
+
+ gen.write("/CIDCount ");
+ CIDSubset cidSubset = font.getCIDSubset();
+ int subsetSize = cidSubset.getSubsetSize();
+ gen.write(subsetSize);
+ gen.writeln(" def");
+ gen.writeln("/GDBytes 2 def"); // TODO always 2?
+ gen.writeln("/CIDMap [<");
+ int colCount = 0;
+ int lineCount = 1;
+ for (int cid = 0; cid < subsetSize; cid++) {
+ if (colCount++ == 20) {
+ gen.newLine();
+ colCount = 1;
+ if (lineCount++ == 800) {
+ gen.writeln("> <");
+ lineCount = 1;
+ }
+ }
+ String gid;
+ if (font.getEmbeddingMode() != EmbeddingMode.FULL) {
+ gid = HexEncoder.encode(cid, 4);
+ } else {
+ gid = HexEncoder.encode(cidSubset.getGlyphIndexForSubsetIndex(cid), 4);
+ }
+ gen.write(gid);
+ }
+ gen.writeln(">] def");
+ FontFileReader reader = new FontFileReader(fontStream);
+
+ TTFFile ttfFile;
+ if (font.getEmbeddingMode() != EmbeddingMode.FULL) {
+ ttfFile = new TTFSubSetFile();
+ ttfFile.readFont(reader, font.getTTCName(), font.getUsedGlyphs());
+ } else {
+ ttfFile = new TTFFile();
+ ttfFile.readFont(reader, font.getTTCName());
+ }
+
+
+ createType42DictionaryEntries(gen, font, new CMapSegment[0], ttfFile);
+ gen.writeln("CIDFontName currentdict end /CIDFont defineresource pop");
+ gen.writeln("end");
+ gen.writeln("%%EndResource");
+ PSResource cidFontResource = new PSResource(PSResource.TYPE_CIDFONT, psName);
+ gen.getResourceTracker().registerSuppliedResource(cidFontResource);
+ return cidFontResource;
+ }
+
+ private static void writeFontBBox(PSGenerator gen, CustomFont font) throws IOException {
+ int[] bbox = font.getFontBBox();
+ gen.write("/FontBBox[");
+ for (int i = 0; i < 4; i++) {
+ gen.write(" ");
+ gen.write(bbox[i]);
+ }
+ gen.writeln(" ] def");
}
private static boolean isEmbeddable(CustomFont font) {
@@ -273,12 +573,20 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils {
Map fontResources = new java.util.HashMap();
for (String key : fonts.keySet()) {
Typeface tf = getTypeFace(fontInfo, fonts, key);
- PSResource fontRes = new PSResource("font", tf.getFontName());
+ PSResource fontRes = new PSResource("font", tf.getEmbedFontName());
fontResources.put(key, fontRes);
- if (FontType.TYPE1 == tf.getFontType()) {
+ FontType fontType = tf.getFontType();
+ if (fontType == FontType.TYPE1 || fontType == FontType.TRUETYPE
+ || fontType == FontType.TYPE0) {
if (tf instanceof CustomFont) {
CustomFont cf = (CustomFont)tf;
if (isEmbeddable(cf)) {
+ if (fontType == FontType.TYPE0) {
+ resTracker.registerSuppliedResource(
+ new PSResource(PSResource.TYPE_CIDFONT, tf.getEmbedFontName()));
+ resTracker.registerSuppliedResource(
+ new PSResource(PSResource.TYPE_CMAP, "Identity-H"));
+ }
resTracker.registerSuppliedResource(fontRes);
}
if (tf instanceof SingleByteFont) {
@@ -289,7 +597,7 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils {
PSResource.TYPE_ENCODING, encoding.getName());
resTracker.registerSuppliedResource(encodingRes);
PSResource derivedFontRes = new PSResource(
- PSResource.TYPE_FONT, tf.getFontName() + "_" + (i + 1));
+ PSResource.TYPE_FONT, tf.getEmbedFontName() + "_" + (i + 1));
resTracker.registerSuppliedResource(derivedFontRes);
}
}
@@ -366,4 +674,42 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils {
return res;
}
+ private static PSResource defineDerivedTrueTypeFont(PSGenerator gen,
+ PSEventProducer eventProducer, String baseFontName, String fontName,
+ SingleByteEncoding encoding, CMapSegment[] cmap) throws IOException {
+ checkPostScriptLevel3(gen, eventProducer);
+ PSResource res = new PSResource(PSResource.TYPE_FONT, fontName);
+ gen.writeDSCComment(DSCConstants.BEGIN_RESOURCE, res);
+ gen.commentln("%XGCDependencies: font " + baseFontName);
+ gen.commentln("%XGC+ encoding " + encoding.getName());
+ 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.getName() + " def");
+
+ gen.writeln(" /CharStrings 256 dict dup begin");
+ String[] charNameMap = encoding.getCharNameMap();
+ char[] unicodeCharMap = encoding.getUnicodeCharMap();
+ assert charNameMap.length == unicodeCharMap.length;
+ for (int i = 0; i < charNameMap.length; i++) {
+ String glyphName = charNameMap[i];
+ gen.write(" /");
+ gen.write(glyphName);
+ gen.write(" ");
+ if (glyphName.equals(".notdef")) {
+ gen.write(0);
+ } else {
+ gen.write(getGlyphIndex(unicodeCharMap[i], cmap));
+ }
+ gen.writeln(" def");
+ }
+ gen.writeln(" end readonly def");
+
+ gen.writeln(" currentdict");
+ gen.writeln("end");
+ gen.writeln("/" + fontName + " exch definefont pop");
+ gen.writeDSCComment(DSCConstants.END_RESOURCE);
+ gen.getResourceTracker().registerSuppliedResource(res);
+ return res;
+ }
}
diff --git a/src/java/org/apache/fop/render/ps/PSImageHandlerRawPNG.java b/src/java/org/apache/fop/render/ps/PSImageHandlerRawPNG.java
new file mode 100644
index 000000000..2a283e913
--- /dev/null
+++ b/src/java/org/apache/fop/render/ps/PSImageHandlerRawPNG.java
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.ps;
+
+import java.awt.Dimension;
+import java.awt.Rectangle;
+import java.awt.geom.Rectangle2D;
+import java.awt.image.ColorModel;
+import java.io.IOException;
+
+import org.apache.xmlgraphics.image.loader.Image;
+import org.apache.xmlgraphics.image.loader.ImageFlavor;
+import org.apache.xmlgraphics.image.loader.ImageInfo;
+import org.apache.xmlgraphics.image.loader.impl.ImageRawPNG;
+import org.apache.xmlgraphics.ps.FormGenerator;
+import org.apache.xmlgraphics.ps.ImageEncoder;
+import org.apache.xmlgraphics.ps.ImageFormGenerator;
+import org.apache.xmlgraphics.ps.PSGenerator;
+import org.apache.xmlgraphics.ps.PSImageUtils;
+
+import org.apache.fop.render.RenderingContext;
+
+/**
+ * Image handler implementation which handles raw (not decoded) PNG images for PostScript output.
+ */
+public class PSImageHandlerRawPNG implements PSImageHandler {
+
+ private static final ImageFlavor[] FLAVORS = new ImageFlavor[] {ImageFlavor.RAW_PNG};
+
+ /** {@inheritDoc} */
+ public void handleImage(RenderingContext context, Image image, Rectangle pos) throws IOException {
+ PSRenderingContext psContext = (PSRenderingContext) context;
+ PSGenerator gen = psContext.getGenerator();
+ ImageRawPNG png = (ImageRawPNG) image;
+
+ float x = (float) pos.getX() / 1000f;
+ float y = (float) pos.getY() / 1000f;
+ float w = (float) pos.getWidth() / 1000f;
+ float h = (float) pos.getHeight() / 1000f;
+ Rectangle2D targetRect = new Rectangle2D.Float(x, y, w, h);
+
+ ImageEncoder encoder = new ImageEncoderPNG(png);
+ ImageInfo info = image.getInfo();
+ Dimension imgDim = info.getSize().getDimensionPx();
+ String imgDescription = image.getClass().getName();
+ ColorModel cm = png.getColorModel();
+
+ PSImageUtils.writeImage(encoder, imgDim, imgDescription, targetRect, cm, gen);
+ }
+
+ /** {@inheritDoc} */
+ public void generateForm(RenderingContext context, Image image, PSImageFormResource form)
+ throws IOException {
+ PSRenderingContext psContext = (PSRenderingContext) context;
+ PSGenerator gen = psContext.getGenerator();
+ ImageRawPNG png = (ImageRawPNG) image;
+ ImageInfo info = image.getInfo();
+ String imageDescription = info.getMimeType() + " " + info.getOriginalURI();
+
+ ImageEncoder encoder = new ImageEncoderPNG(png);
+ FormGenerator formGen = new ImageFormGenerator(form.getName(), imageDescription, info.getSize()
+ .getDimensionPt(), info.getSize().getDimensionPx(), encoder, png.getColorSpace(),
+ false);
+ formGen.generate(gen);
+ }
+
+ /** {@inheritDoc} */
+ public int getPriority() {
+ return 200;
+ }
+
+ /** {@inheritDoc} */
+ public Class<ImageRawPNG> getSupportedImageClass() {
+ return ImageRawPNG.class;
+ }
+
+ /** {@inheritDoc} */
+ public ImageFlavor[] getSupportedImageFlavors() {
+ return FLAVORS;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isCompatible(RenderingContext targetContext, Image image) {
+ if (targetContext instanceof PSRenderingContext) {
+ PSRenderingContext psContext = (PSRenderingContext) targetContext;
+ // The filters required for this implementation need PS level 2 or higher
+ if (psContext.getGenerator().getPSLevel() >= 2) {
+ return (image == null || image instanceof ImageRawPNG);
+ }
+ }
+ return false;
+ }
+
+}
diff --git a/src/java/org/apache/fop/render/ps/PSImageHandlerRenderedImage.java b/src/java/org/apache/fop/render/ps/PSImageHandlerRenderedImage.java
index 5a13c1c8e..46597e46d 100644
--- a/src/java/org/apache/fop/render/ps/PSImageHandlerRenderedImage.java
+++ b/src/java/org/apache/fop/render/ps/PSImageHandlerRenderedImage.java
@@ -19,7 +19,10 @@
package org.apache.fop.render.ps;
+import java.awt.Dimension;
import java.awt.Rectangle;
+import java.awt.geom.Rectangle2D;
+import java.awt.image.ColorModel;
import java.awt.image.RenderedImage;
import java.io.IOException;
@@ -28,6 +31,8 @@ import org.apache.xmlgraphics.image.loader.ImageFlavor;
import org.apache.xmlgraphics.image.loader.ImageInfo;
import org.apache.xmlgraphics.image.loader.impl.ImageRendered;
import org.apache.xmlgraphics.ps.FormGenerator;
+import org.apache.xmlgraphics.ps.ImageEncoder;
+import org.apache.xmlgraphics.ps.ImageEncodingHelper;
import org.apache.xmlgraphics.ps.ImageFormGenerator;
import org.apache.xmlgraphics.ps.PSGenerator;
import org.apache.xmlgraphics.ps.PSImageUtils;
@@ -47,17 +52,24 @@ public class PSImageHandlerRenderedImage implements PSImageHandler {
/** {@inheritDoc} */
public void handleImage(RenderingContext context, Image image, Rectangle pos)
throws IOException {
- PSRenderingContext psContext = (PSRenderingContext)context;
+ PSRenderingContext psContext = (PSRenderingContext) context;
PSGenerator gen = psContext.getGenerator();
- ImageRendered imageRend = (ImageRendered)image;
+ ImageRendered imageRend = (ImageRendered) image;
- float x = (float)pos.getX() / 1000f;
- float y = (float)pos.getY() / 1000f;
- float w = (float)pos.getWidth() / 1000f;
- float h = (float)pos.getHeight() / 1000f;
+ float x = (float) pos.getX() / 1000f;
+ float y = (float) pos.getY() / 1000f;
+ float w = (float) pos.getWidth() / 1000f;
+ float h = (float) pos.getHeight() / 1000f;
+ Rectangle2D targetRect = new Rectangle2D.Double(x, y, w, h);
RenderedImage ri = imageRend.getRenderedImage();
- PSImageUtils.renderBitmapImage(ri, x, y, w, h, gen);
+ ImageEncoder encoder = ImageEncodingHelper.createRenderedImageEncoder(ri);
+ Dimension imgDim = new Dimension(ri.getWidth(), ri.getHeight());
+ String imgDescription = ri.getClass().getName();
+ ImageEncodingHelper helper = new ImageEncodingHelper(ri);
+ ColorModel cm = helper.getEncodedColorModel();
+
+ PSImageUtils.writeImage(encoder, imgDim, imgDescription, targetRect, cm, gen);
}
/** {@inheritDoc} */
diff --git a/src/java/org/apache/fop/render/ps/PSPainter.java b/src/java/org/apache/fop/render/ps/PSPainter.java
index 9bed2a432..c2288019a 100644
--- a/src/java/org/apache/fop/render/ps/PSPainter.java
+++ b/src/java/org/apache/fop/render/ps/PSPainter.java
@@ -44,6 +44,7 @@ import org.apache.fop.fonts.Font;
import org.apache.fop.fonts.FontInfo;
import org.apache.fop.fonts.FontTriplet;
import org.apache.fop.fonts.LazyFont;
+import org.apache.fop.fonts.MultiByteFont;
import org.apache.fop.fonts.SingleByteFont;
import org.apache.fop.fonts.Typeface;
import org.apache.fop.render.RenderingContext;
@@ -55,6 +56,7 @@ import org.apache.fop.render.intermediate.IFUtil;
import org.apache.fop.traits.BorderProps;
import org.apache.fop.traits.RuleStyle;
import org.apache.fop.util.CharUtilities;
+import org.apache.fop.util.HexEncoder;
/**
* IFPainter implementation that produces PostScript.
@@ -392,7 +394,7 @@ public class PSPainter extends AbstractIFPainter {
if (currentEncoding != encoding) {
if (i > 0) {
writeText(text, start, i - start,
- letterSpacing, wordSpacing, dp, font, tf);
+ letterSpacing, wordSpacing, dp, font, tf, false);
}
if (encoding == 0) {
useFont(fontKey, sizeMillipoints);
@@ -404,19 +406,18 @@ public class PSPainter extends AbstractIFPainter {
}
}
} else {
- //Simple single-font painting
useFont(fontKey, sizeMillipoints);
}
- writeText(text, start, textLen - start, letterSpacing, wordSpacing, dp, font, tf);
+ writeText(text, start, textLen - start, letterSpacing, wordSpacing, dp, font, tf,
+ tf instanceof MultiByteFont);
} catch (IOException ioe) {
throw new IFException("I/O error in drawText()", ioe);
}
}
- private void writeText( // CSOK: ParameterNumber
- String text, int start, int len,
+ private void writeText(String text, int start, int len,
int letterSpacing, int wordSpacing, int[][] dp,
- Font font, Typeface tf) throws IOException {
+ Font font, Typeface tf, boolean multiByte) throws IOException {
PSGenerator generator = getGenerator();
int end = start + len;
int initialSize = len;
@@ -451,8 +452,12 @@ public class PSPainter extends AbstractIFPainter {
if (dx != null && i < dxl - 1) {
glyphAdjust -= dx[i + 1];
}
- char codepoint = (char)(ch % 256);
- PSGenerator.escapeChar(codepoint, accText); //add character to accumulated text
+ if (multiByte) {
+ accText.append(HexEncoder.encode(ch));
+ } else {
+ char codepoint = (char)(ch % 256);
+ PSGenerator.escapeChar(codepoint, accText); //add character to accumulated text
+ }
if (glyphAdjust != 0) {
needTJ = true;
if (sb.length() == 0) {
@@ -463,9 +468,8 @@ public class PSPainter extends AbstractIFPainter {
sb.append(PSGenerator.LF);
lineStart = sb.length();
}
- sb.append('(');
- sb.append(accText);
- sb.append(") ");
+ lineStart = writePostScriptString(sb, accText, multiByte, lineStart);
+ sb.append(' ');
accText.setLength(0); //reset accumulated text
}
sb.append(Integer.toString(glyphAdjust)).append(' ');
@@ -473,9 +477,10 @@ public class PSPainter extends AbstractIFPainter {
}
if (needTJ) {
if (accText.length() > 0) {
- sb.append('(');
- sb.append(accText);
- sb.append(')');
+ if ((sb.length() - lineStart + accText.length()) > 200) {
+ sb.append(PSGenerator.LF);
+ }
+ writePostScriptString(sb, accText, multiByte);
}
if (hasLetterSpacing) {
sb.append("] " + formatMptAsPt(generator, letterSpacing) + " ATJ");
@@ -483,7 +488,7 @@ public class PSPainter extends AbstractIFPainter {
sb.append("] TJ");
}
} else {
- sb.append('(').append(accText).append(")");
+ writePostScriptString(sb, accText, multiByte);
if (hasLetterSpacing) {
StringBuffer spb = new StringBuffer();
spb.append(formatMptAsPt(generator, letterSpacing))
@@ -497,12 +502,37 @@ public class PSPainter extends AbstractIFPainter {
generator.writeln(sb.toString());
}
+ private void writePostScriptString(StringBuffer buffer, StringBuffer string,
+ boolean multiByte) {
+ writePostScriptString(buffer, string, multiByte, 0);
+ }
+
+ private int writePostScriptString(StringBuffer buffer, StringBuffer string, boolean multiByte,
+ int lineStart) {
+ buffer.append(multiByte ? '<' : '(');
+ int l = string.length();
+ int index = 0;
+ int maxCol = 200;
+ buffer.append(string.substring(index, Math.min(index + maxCol, l)));
+ index += maxCol;
+ while (index < l) {
+ if (!multiByte) {
+ buffer.append('\\');
+ }
+ buffer.append(PSGenerator.LF);
+ lineStart = buffer.length();
+ buffer.append(string.substring(index, Math.min(index + maxCol, l)));
+ index += maxCol;
+ }
+ buffer.append(multiByte ? '>' : ')');
+ return lineStart;
+ }
+
private void useFont(String key, int size) throws IOException {
- PSResource res = this.documentHandler.getPSResourceForFontKey(key);
+ PSFontResource res = this.documentHandler.getPSResourceForFontKey(key);
PSGenerator generator = getGenerator();
generator.useFont("/" + res.getName(), size / 1000f);
- generator.getResourceTracker().notifyResourceUsageOnPage(res);
+ res.notifyResourceUsageOnPage(generator.getResourceTracker());
}
-
}
diff --git a/src/java/org/apache/fop/render/ps/PSTextPainter.java b/src/java/org/apache/fop/render/ps/PSTextPainter.java
index acc673491..2b3afaec7 100644
--- a/src/java/org/apache/fop/render/ps/PSTextPainter.java
+++ b/src/java/org/apache/fop/render/ps/PSTextPainter.java
@@ -41,12 +41,15 @@ import org.apache.batik.gvt.text.TextSpanLayout;
import org.apache.xmlgraphics.java2d.ps.PSGraphics2D;
import org.apache.xmlgraphics.ps.PSGenerator;
-import org.apache.xmlgraphics.ps.PSResource;
import org.apache.fop.fonts.Font;
import org.apache.fop.fonts.FontInfo;
+import org.apache.fop.fonts.FontMetrics;
+import org.apache.fop.fonts.LazyFont;
+import org.apache.fop.fonts.MultiByteFont;
import org.apache.fop.svg.NativeTextPainter;
import org.apache.fop.util.CharUtilities;
+import org.apache.fop.util.HexEncoder;
/**
* Renders the attributed character iterator of a text node.
@@ -240,9 +243,9 @@ public class PSTextPainter extends NativeTextPainter {
}
}
- private PSResource getResourceForFont(Font f, String postfix) {
+ private PSFontResource getResourceForFont(Font f, String postfix) {
String key = (postfix != null ? f.getFontName() + '_' + postfix : f.getFontName());
- return this.fontResources.getPSResourceForFontKey(key);
+ return this.fontResources.getFontResourceForFontKey(key);
}
private void clip(PSGraphics2D ps, Shape shape) throws IOException {
@@ -299,9 +302,9 @@ public class PSTextPainter extends NativeTextPainter {
public void selectFont(Font f, char mapped) throws IOException {
int encoding = mapped / 256;
String postfix = (encoding == 0 ? null : Integer.toString(encoding));
- PSResource res = getResourceForFont(f, postfix);
+ PSFontResource res = getResourceForFont(f, postfix);
gen.useFont("/" + res.getName(), f.getFontSize() / 1000f);
- gen.getResourceTracker().notifyResourceUsageOnPage(res);
+ res.notifyResourceUsageOnPage(gen.getResourceTracker());
}
public Font getCurrentFont() {
@@ -427,15 +430,23 @@ public class PSTextPainter extends NativeTextPainter {
textUtil.setCurrentFont(f, mapped);
applyColor(paint, gen);
+ FontMetrics metrics = f.getFontMetrics();
+ boolean multiByte = metrics instanceof MultiByteFont
+ || metrics instanceof LazyFont
+ && ((LazyFont) metrics).getRealFont() instanceof MultiByteFont;
StringBuffer sb = new StringBuffer();
- sb.append('(');
+ sb.append(multiByte ? '<' : '(');
for (int i = 0, c = this.currentChars.length(); i < c; i++) {
char ch = this.currentChars.charAt(i);
mapped = f.mapChar(ch);
- char codepoint = (char) (mapped % 256);
- PSGenerator.escapeChar(codepoint, sb);
+ if (multiByte) {
+ sb.append(HexEncoder.encode(mapped));
+ } else {
+ char codepoint = (char) (mapped % 256);
+ PSGenerator.escapeChar(codepoint, sb);
+ }
}
- sb.append(')');
+ sb.append(multiByte ? '>' : ')');
if (x || y) {
sb.append("\n[");
int idx = 0;
@@ -513,10 +524,20 @@ public class PSTextPainter extends NativeTextPainter {
textUtil.selectFont(f, mapped);
textUtil.setCurrentFont(f, mapped);
}
- mapped = f.mapChar(this.currentChars.charAt(i));
//add glyph outlines to current path
- char codepoint = (char)(mapped % 256);
- gen.write("(" + codepoint + ")");
+ mapped = f.mapChar(this.currentChars.charAt(i));
+ FontMetrics metrics = f.getFontMetrics();
+ boolean multiByte = metrics instanceof MultiByteFont
+ || metrics instanceof LazyFont
+ && ((LazyFont) metrics).getRealFont() instanceof MultiByteFont;
+ if (multiByte) {
+ gen.write('<');
+ gen.write(HexEncoder.encode(mapped));
+ gen.write('>');
+ } else {
+ char codepoint = (char)(mapped % 256);
+ gen.write("(" + codepoint + ")");
+ }
gen.writeln(" false charpath");
if (iter.hasNext()) {
diff --git a/src/java/org/apache/fop/render/ps/ResourceHandler.java b/src/java/org/apache/fop/render/ps/ResourceHandler.java
index 502242c17..5594897ba 100644
--- a/src/java/org/apache/fop/render/ps/ResourceHandler.java
+++ b/src/java/org/apache/fop/render/ps/ResourceHandler.java
@@ -83,6 +83,8 @@ public class ResourceHandler implements DSCParserConstants, PSSupportedFlavors {
private FOUserAgent userAgent;
private FontInfo fontInfo;
+ private PSEventProducer eventProducer;
+
private ResourceTracker resTracker;
//key: URI, values PSImageFormResource
@@ -93,13 +95,15 @@ public class ResourceHandler implements DSCParserConstants, PSSupportedFlavors {
/**
* Main constructor.
* @param userAgent the FO user agent
+ * @param eventProducer the event producer
* @param fontInfo the font information
* @param resTracker the resource tracker to use
* @param formResources Contains all forms used by this document (maintained by PSRenderer)
*/
- public ResourceHandler(FOUserAgent userAgent, FontInfo fontInfo,
- ResourceTracker resTracker, Map formResources) {
+ public ResourceHandler(FOUserAgent userAgent, PSEventProducer eventProducer,
+ FontInfo fontInfo, ResourceTracker resTracker, Map formResources) {
this.userAgent = userAgent;
+ this.eventProducer = eventProducer;
this.fontInfo = fontInfo;
this.resTracker = resTracker;
determineInlineForms(formResources);
@@ -222,7 +226,7 @@ public class ResourceHandler implements DSCParserConstants, PSSupportedFlavors {
if (fontSetupPlaceholder == null) {
throw new DSCException("Didn't find %FOPFontSetup comment in stream");
}
- PSFontUtils.writeFontDict(gen, fontInfo, fontInfo.getUsedFonts());
+ PSFontUtils.writeFontDict(gen, fontInfo, fontInfo.getUsedFonts(), eventProducer);
generateForms(globalFormResources, gen);
//Skip the prolog and to the first page
diff --git a/src/java/org/apache/fop/render/ps/extensions/PSExtensionElementMapping.java b/src/java/org/apache/fop/render/ps/extensions/PSExtensionElementMapping.java
index 00d0594f2..5721afaf6 100644
--- a/src/java/org/apache/fop/render/ps/extensions/PSExtensionElementMapping.java
+++ b/src/java/org/apache/fop/render/ps/extensions/PSExtensionElementMapping.java
@@ -41,6 +41,7 @@ public class PSExtensionElementMapping extends ElementMapping {
foObjs = new java.util.HashMap<String, Maker>();
foObjs.put(PSSetupCodeElement.ELEMENT, new PSSetupCodeMaker());
foObjs.put(PSPageSetupCodeElement.ELEMENT, new PSPageSetupCodeMaker());
+ foObjs.put(PSPageTrailerCodeBefore.ELEMENT, new PSPageTrailerCodeBeforeMaker());
foObjs.put(PSSetPageDeviceElement.ELEMENT, new PSSetPageDeviceMaker());
foObjs.put(PSCommentBefore.ELEMENT, new PSCommentBeforeMaker());
foObjs.put(PSCommentAfter.ELEMENT, new PSCommentAfterMaker());
@@ -59,6 +60,12 @@ public class PSExtensionElementMapping extends ElementMapping {
}
}
+ static class PSPageTrailerCodeBeforeMaker extends ElementMapping.Maker {
+ public FONode make(FONode parent) {
+ return new PSPageTrailerCodeBeforeElement(parent);
+ }
+ }
+
static class PSSetPageDeviceMaker extends ElementMapping.Maker {
public FONode make(FONode parent) {
return new PSSetPageDeviceElement(parent);
diff --git a/src/java/org/apache/fop/render/ps/extensions/PSExtensionHandler.java b/src/java/org/apache/fop/render/ps/extensions/PSExtensionHandler.java
index d499ef6ab..b520d8736 100644
--- a/src/java/org/apache/fop/render/ps/extensions/PSExtensionHandler.java
+++ b/src/java/org/apache/fop/render/ps/extensions/PSExtensionHandler.java
@@ -53,6 +53,7 @@ public class PSExtensionHandler extends DefaultHandler
lastAttributes = new AttributesImpl(attributes);
handled = false;
if (localName.equals(PSSetupCode.ELEMENT)
+ || localName.equals(PSPageTrailerCodeBefore.ELEMENT)
|| localName.equals(PSSetPageDevice.ELEMENT)
|| localName.equals(PSCommentBefore.ELEMENT)
|| localName.equals(PSCommentAfter.ELEMENT)) {
@@ -84,6 +85,8 @@ public class PSExtensionHandler extends DefaultHandler
this.returnedObject = new PSCommentBefore(content.toString());
} else if (PSCommentAfter.ELEMENT.equals(localName)) {
this.returnedObject = new PSCommentAfter(content.toString());
+ } else if (PSPageTrailerCodeBefore.ELEMENT.equals(localName)) {
+ this.returnedObject = new PSPageTrailerCodeBefore(content.toString());
}
}
content.setLength(0); //Reset text buffer (see characters())
diff --git a/src/java/org/apache/fop/render/ps/extensions/PSPageTrailerCodeBefore.java b/src/java/org/apache/fop/render/ps/extensions/PSPageTrailerCodeBefore.java
new file mode 100644
index 000000000..f28f0003e
--- /dev/null
+++ b/src/java/org/apache/fop/render/ps/extensions/PSPageTrailerCodeBefore.java
@@ -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.render.ps.extensions;
+
+
+public class PSPageTrailerCodeBefore extends PSExtensionAttachment {
+
+ /** The element name */
+ protected static final String ELEMENT = "ps-page-trailer-code-before";
+
+ /**
+ * Default constructor
+ * @param content the actual comment
+ */
+ public PSPageTrailerCodeBefore(String content) {
+ super(content);
+ }
+
+ /**
+ * Constructor
+ */
+ public PSPageTrailerCodeBefore() {
+ super();
+ }
+
+ /**
+ * @return element name
+ */
+ protected String getElement() {
+ return ELEMENT;
+ }
+
+}
diff --git a/src/java/org/apache/fop/render/ps/extensions/PSPageTrailerCodeBeforeElement.java b/src/java/org/apache/fop/render/ps/extensions/PSPageTrailerCodeBeforeElement.java
new file mode 100644
index 000000000..65a22eadd
--- /dev/null
+++ b/src/java/org/apache/fop/render/ps/extensions/PSPageTrailerCodeBeforeElement.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.ps.extensions;
+
+import org.apache.fop.fo.FONode;
+import org.apache.fop.fo.extensions.ExtensionAttachment;
+
+/**
+ * Comment before element
+ */
+public class PSPageTrailerCodeBeforeElement extends AbstractPSCommentElement {
+
+ /** the element name */
+ protected static final String ELEMENT = "ps-page-trailer-code-before";
+
+ /**
+ * Main constructor
+ * @param parent parent node
+ */
+ public PSPageTrailerCodeBeforeElement(FONode parent) {
+ super(parent);
+ }
+
+ /**
+ * @return local name
+ * @see org.apache.fop.fo.FONode#getLocalName()
+ */
+ public String getLocalName() {
+ return PSPageTrailerCodeBefore.ELEMENT;
+ }
+
+ /**
+ * @return instance of its extension attachment object
+ */
+ protected ExtensionAttachment instantiateExtensionAttachment() {
+ return new PSPageTrailerCodeBefore();
+ }
+}
diff --git a/src/java/org/apache/fop/render/ps/fonts/PSTTFGenerator.java b/src/java/org/apache/fop/render/ps/fonts/PSTTFGenerator.java
new file mode 100644
index 000000000..556b62457
--- /dev/null
+++ b/src/java/org/apache/fop/render/ps/fonts/PSTTFGenerator.java
@@ -0,0 +1,101 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.ps.fonts;
+
+import java.io.IOException;
+
+import org.apache.xmlgraphics.ps.PSGenerator;
+import org.apache.xmlgraphics.util.io.ASCIIHexOutputStream;
+
+/**
+ * This is a wrapper for {@link PSGenerator} that contains some members specific for streaming
+ * TrueType fonts to a PostScript document.
+ */
+public class PSTTFGenerator {
+ private PSGenerator gen;
+ private ASCIIHexOutputStream hexOut;
+
+ /**
+ * The buffer is used to store the font file in an array of hex-encoded strings. Strings are
+ * limited to 65535 characters, string will start with a newline, 2 characters are needed to
+ * hex-encode each byte.
+ */
+ public static final int MAX_BUFFER_SIZE = 32764;
+
+ /**
+ * Creates a new instance wrapping the given generator.
+ * @param gen the PSGenerator to wrap
+ */
+ public PSTTFGenerator(PSGenerator gen) {
+ this.gen = gen;
+ hexOut = new ASCIIHexOutputStream(gen.getOutputStream());
+ }
+
+ /**
+ * Writes the '&lt;' character that starts a string.
+ */
+ public void startString() throws IOException {
+ // We need to reset the streamer so that it starts a new line in the PS document
+ hexOut = new ASCIIHexOutputStream(gen.getOutputStream());
+ gen.writeln("<");
+ }
+
+ /**
+ * Writes the given string to the output.
+ * @param cmd a string
+ */
+ public void write(String cmd) throws IOException {
+ gen.write(cmd);
+ }
+
+ /**
+ * Writes the given string to the output, followed by a newline.
+ * @param cmd a string
+ */
+ public void writeln(String cmd) throws IOException {
+ gen.writeln(cmd);
+ }
+
+ /**
+ * Writes bytes from the given byte array to the output.
+ *
+ * @param byteArray byte[] a byte array
+ * @param offset the position in the byte array where the streaming must start
+ * @param length the number of bytes to stream. This MUST be less than
+ * {@link #MAX_BUFFER_SIZE} - 1 since strings are suffixed by '00' (see Section 4.2 of
+ * Adobe Technical Note #5012, <em>The Type 42 Font Format Specification</em>.).
+ */
+ public void streamBytes(byte[] byteArray, int offset, int length) throws IOException {
+ if (length > MAX_BUFFER_SIZE) {
+ throw new UnsupportedOperationException("Attempting to write a string to a PostScript"
+ + " file that is greater than the buffer size.");
+ }
+ hexOut.write(byteArray, offset, length);
+ }
+
+ /**
+ * Finishes writing a string by appending '00' and '>' to the end.
+ */
+ public void endString() throws IOException {
+ /* Appends a '00' to the end of the string as specified in the spec */
+ gen.write("00\n> ");
+ }
+
+}
diff --git a/src/java/org/apache/fop/render/ps/fonts/PSTTFGlyphOutputStream.java b/src/java/org/apache/fop/render/ps/fonts/PSTTFGlyphOutputStream.java
new file mode 100644
index 000000000..cc2ae3e82
--- /dev/null
+++ b/src/java/org/apache/fop/render/ps/fonts/PSTTFGlyphOutputStream.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.ps.fonts;
+
+import java.io.IOException;
+
+import org.apache.fop.fonts.truetype.TTFGlyphOutputStream;
+
+/**
+ * Streams glyphs in accordance with the constraints of the PostScript file format.
+ * Mainly, PostScript strings have a limited capacity and the font data may have to be
+ * broken down into several strings; however, this must occur at well-defined places like
+ * table or glyph boundaries. See also Adobe Technical Note #5012, <em>The Type 42 Font
+ * Format Specification</em>.
+ */
+public class PSTTFGlyphOutputStream implements TTFGlyphOutputStream {
+
+ /** Total number of bytes written so far. */
+ private int byteCounter;
+
+ private int lastStringBoundary;
+
+ private PSTTFGenerator ttfGen;
+
+ /**
+ * Constructor
+ * @param ttfGen PSTTFGenerator
+ */
+ public PSTTFGlyphOutputStream(PSTTFGenerator ttfGen) {
+ this.ttfGen = ttfGen;
+ }
+
+ public void startGlyphStream() throws IOException {
+ ttfGen.startString();
+ }
+
+ public void streamGlyph(byte[] glyphData, int offset, int size) throws IOException {
+ if (size > PSTTFGenerator.MAX_BUFFER_SIZE) {
+ throw new UnsupportedOperationException("The glyph is " + size
+ + " bytes. There may be an error in the font file.");
+ }
+
+ if (size + (byteCounter - lastStringBoundary) < PSTTFGenerator.MAX_BUFFER_SIZE) {
+ ttfGen.streamBytes(glyphData, offset, size);
+ } else {
+ ttfGen.endString();
+ lastStringBoundary = byteCounter;
+ ttfGen.startString();
+ ttfGen.streamBytes(glyphData, offset, size);
+ }
+ byteCounter += size;
+ }
+
+ public void endGlyphStream() throws IOException {
+ ttfGen.endString();
+ }
+
+}
diff --git a/src/java/org/apache/fop/render/ps/fonts/PSTTFOutputStream.java b/src/java/org/apache/fop/render/ps/fonts/PSTTFOutputStream.java
new file mode 100644
index 000000000..271d87d1b
--- /dev/null
+++ b/src/java/org/apache/fop/render/ps/fonts/PSTTFOutputStream.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.ps.fonts;
+
+import java.io.IOException;
+
+import org.apache.xmlgraphics.ps.PSGenerator;
+
+import org.apache.fop.fonts.truetype.TTFGlyphOutputStream;
+import org.apache.fop.fonts.truetype.TTFOutputStream;
+import org.apache.fop.fonts.truetype.TTFTableOutputStream;
+
+/**
+ * Streams a TrueType font according to the PostScript format.
+ */
+public class PSTTFOutputStream implements TTFOutputStream {
+
+ private final PSTTFGenerator ttfGen;
+
+ /**
+ * Creates a new instance wrapping the given generator.
+ *
+ * @param gen the generator to wrap
+ */
+ public PSTTFOutputStream(PSGenerator gen) {
+ this.ttfGen = new PSTTFGenerator(gen);
+ }
+
+ public void startFontStream() throws IOException {
+ ttfGen.write("/sfnts[");
+ }
+
+ public TTFTableOutputStream getTableOutputStream() {
+ return new PSTTFTableOutputStream(ttfGen);
+ }
+
+ public TTFGlyphOutputStream getGlyphOutputStream() {
+ return new PSTTFGlyphOutputStream(ttfGen);
+ }
+
+ public void endFontStream() throws IOException {
+ ttfGen.writeln("] def");
+ }
+
+}
diff --git a/src/java/org/apache/fop/render/ps/fonts/PSTTFTableOutputStream.java b/src/java/org/apache/fop/render/ps/fonts/PSTTFTableOutputStream.java
new file mode 100644
index 000000000..2226e11e8
--- /dev/null
+++ b/src/java/org/apache/fop/render/ps/fonts/PSTTFTableOutputStream.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.ps.fonts;
+
+import java.io.IOException;
+
+import org.apache.fop.fonts.truetype.TTFTableOutputStream;
+
+/**
+ * Streams a TrueType table according to the PostScript format.
+ */
+public class PSTTFTableOutputStream implements TTFTableOutputStream {
+
+ private PSTTFGenerator ttfGen;
+
+ /**
+ * Constructor.
+ * @param ttfGen the helper object to stream TrueType data
+ */
+ public PSTTFTableOutputStream(PSTTFGenerator ttfGen) {
+ this.ttfGen = ttfGen;
+ }
+
+ public void streamTable(byte[] ttfData, int offset, int size) throws IOException {
+ int offsetPosition = offset;
+ // Need to split the table into MAX_BUFFER_SIZE chunks
+ for (int i = 0; i < size / PSTTFGenerator.MAX_BUFFER_SIZE; i++) {
+ streamString(ttfData, offsetPosition, PSTTFGenerator.MAX_BUFFER_SIZE);
+ offsetPosition += PSTTFGenerator.MAX_BUFFER_SIZE;
+ }
+ if (size % PSTTFGenerator.MAX_BUFFER_SIZE > 0) {
+ streamString(ttfData, offsetPosition, size % PSTTFGenerator.MAX_BUFFER_SIZE);
+ }
+ }
+
+ private void streamString(byte[] byteArray, int offset, int length) throws IOException {
+ ttfGen.startString();
+ ttfGen.streamBytes(byteArray, offset, length);
+ ttfGen.endString();
+ }
+
+}
diff --git a/src/java/org/apache/fop/render/xml/XMLRenderer.java b/src/java/org/apache/fop/render/xml/XMLRenderer.java
index 4ac650269..cf3cbe5a2 100644
--- a/src/java/org/apache/fop/render/xml/XMLRenderer.java
+++ b/src/java/org/apache/fop/render/xml/XMLRenderer.java
@@ -97,6 +97,15 @@ import org.apache.fop.util.XMLUtil;
*/
public class XMLRenderer extends AbstractXMLRenderer {
+ /**
+ * Area Tree (AT) version, used to express an @version attribute
+ * in the root element of the AT document, the initial value of which
+ * is set to '2.0' to signify that something preceded it (but didn't
+ * happen to be marked as such), and that this version is not necessarily
+ * backwards compatible with the unmarked (<2.0) version.
+ */
+ public static final String VERSION = "2.0";
+
/** XML MIME type */
public static final String XML_MIME_TYPE = MimeConstants.MIME_FOP_AREA_TREE;
@@ -365,7 +374,9 @@ public class XMLRenderer extends AbstractXMLRenderer {
if (userAgent.getProducer() != null) {
comment("Produced by " + userAgent.getProducer());
}
- startElement("areaTree");
+ atts.clear();
+ addAttribute("version", VERSION);
+ startElement("areaTree", atts);
}
/** {@inheritDoc} */
@@ -825,11 +836,10 @@ public class XMLRenderer extends AbstractXMLRenderer {
}
maybeAddLevelAttribute(word);
maybeAddPositionAdjustAttribute(word);
- if ( word.isReversed() ) {
- addAttribute("reversed", "true");
- }
+ String text = word.getWord();
+ maybeAddReversedAttribute(word, text);
startElement("word", atts);
- characters(word.getWord());
+ characters(text);
endElement("word");
super.renderWord(word);
}
@@ -917,5 +927,11 @@ public class XMLRenderer extends AbstractXMLRenderer {
}
}
+ private void maybeAddReversedAttribute ( WordArea w, String text ) {
+ if ( w.isReversed() && ( text.length() > 1 ) ) {
+ addAttribute("reversed", "true");
+ }
+ }
+
}
diff --git a/src/java/org/apache/fop/svg/PDFAElementBridge.java b/src/java/org/apache/fop/svg/PDFAElementBridge.java
index cd70bed34..c2a481af9 100644
--- a/src/java/org/apache/fop/svg/PDFAElementBridge.java
+++ b/src/java/org/apache/fop/svg/PDFAElementBridge.java
@@ -64,7 +64,7 @@ public class PDFAElementBridge extends AbstractGraphicsNodeBridge {
}
/**
- * Creates a {@link CompositeGraphicsNode}.
+ * Creates a {@link org.apache.batik.gvt.CompositeGraphicsNode}.
* @return a new PDFANode
*/
protected GraphicsNode instantiateGraphicsNode() {
diff --git a/src/java/org/apache/fop/svg/PDFGraphics2D.java b/src/java/org/apache/fop/svg/PDFGraphics2D.java
index 67fc5d860..87c12d869 100644
--- a/src/java/org/apache/fop/svg/PDFGraphics2D.java
+++ b/src/java/org/apache/fop/svg/PDFGraphics2D.java
@@ -1201,6 +1201,8 @@ public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHand
currentStream.write("] ");
float offset = bs.getDashPhase();
currentStream.write(PDFNumber.doubleOut(offset) + " d\n");
+ } else {
+ currentStream.write("[] 0 d\n");
}
int ec = bs.getEndCap();
switch (ec) {
diff --git a/src/java/org/apache/fop/traits/MinOptMax.java b/src/java/org/apache/fop/traits/MinOptMax.java
index 022f3c214..84fb0156a 100644
--- a/src/java/org/apache/fop/traits/MinOptMax.java
+++ b/src/java/org/apache/fop/traits/MinOptMax.java
@@ -22,8 +22,9 @@ package org.apache.fop.traits;
import java.io.Serializable;
/**
- * This class holds the resolved (as mpoints) form of a {@link LengthRangeProperty LengthRange} or
- * {@link SpaceProperty Space} type property value.
+ * This class holds the resolved (as mpoints) form of a
+ * {@link org.apache.fop.fo.properties.LengthRangeProperty} or
+ * {@link org.apache.fop.fo.properties.SpaceProperty} type property value.
* <p/>
* Instances of this class are immutable. All arithmetic methods like {@link #plus(MinOptMax) plus},
* {@link #minus(MinOptMax) minus} or {@link #mult(int) mult} return a different instance. So it is
diff --git a/src/java/org/apache/fop/traits/TraitEnum.java b/src/java/org/apache/fop/traits/TraitEnum.java
index 18fdffaab..0d664a6df 100644
--- a/src/java/org/apache/fop/traits/TraitEnum.java
+++ b/src/java/org/apache/fop/traits/TraitEnum.java
@@ -30,7 +30,7 @@ public abstract class TraitEnum implements Serializable {
/**
* Constructor to add a new named item.
* @param name Name of the item.
- * @param enumValue the {@link Constants}.EN_* value
+ * @param enumValue the {@link org.apache.fop.fo.Constants}.EN_* value
*/
protected TraitEnum(String name, int enumValue) {
this.name = name;
@@ -46,7 +46,7 @@ public abstract class TraitEnum implements Serializable {
}
/**
- * Returns the enumeration value (one of {@link Constants}.EN_*).
+ * Returns the enumeration value (one of {@link org.apache.fop.fo.Constants}.EN_*).
* @return the enumeration value
*/
public int getEnumValue() {
diff --git a/src/java/org/apache/fop/util/ColorExt.java b/src/java/org/apache/fop/util/ColorExt.java
index d0ac6767c..f92e7c2a6 100644
--- a/src/java/org/apache/fop/util/ColorExt.java
+++ b/src/java/org/apache/fop/util/ColorExt.java
@@ -28,7 +28,7 @@ import java.util.Arrays;
* <p>
* This class extends java.awt.Color class keeping track of the original color
* property values specified by the fo user in a rgb-icc call.
- * @deprecated Replaced by {@link ColorWithAlternatives}
+ * @deprecated Replaced by {@link org.apache.xmlgraphics.java2d.color.ColorWithAlternatives}
*/
@Deprecated
public final class ColorExt extends Color {
diff --git a/src/java/org/apache/fop/util/HexEncoder.java b/src/java/org/apache/fop/util/HexEncoder.java
new file mode 100644
index 000000000..9ca91f2d2
--- /dev/null
+++ b/src/java/org/apache/fop/util/HexEncoder.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.util;
+
+/**
+ * A helper class to create hex-encoded representations of numbers.
+ */
+public final class HexEncoder {
+
+ private HexEncoder() { }
+
+ /**
+ * Returns an hex encoding of the given number as a string of the given length,
+ * left-padded with zeros if necessary.
+ *
+ * @param n a number
+ * @param width required length of the string
+ * @return an hex-encoded representation of the number
+ */
+ public static String encode(int n, int width) {
+ char[] digits = new char[width];
+ for (int i = width - 1; i >= 0; i--) {
+ int digit = n & 0xF;
+ digits[i] = (char) (digit < 10 ? '0' + digit : 'A' + digit - 10);
+ n >>= 4;
+ }
+ return new String(digits);
+ }
+
+ /**
+ * Returns an hex encoding of the given character as a four-character string.
+ *
+ * @param c a character
+ * @return an hex-encoded representation of the character
+ */
+ public static String encode(char c) {
+ return encode(c, 4);
+ }
+
+}
diff --git a/src/sandbox/org/apache/fop/render/mif/MIFHandler.java b/src/sandbox/org/apache/fop/render/mif/MIFHandler.java
index cfc86edc7..5ef4c0940 100644
--- a/src/sandbox/org/apache/fop/render/mif/MIFHandler.java
+++ b/src/sandbox/org/apache/fop/render/mif/MIFHandler.java
@@ -29,8 +29,10 @@ import org.apache.commons.logging.LogFactory;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.fo.FOEventHandler;
+import org.apache.fop.fo.FOText;
import org.apache.fop.fo.flow.BasicLink;
import org.apache.fop.fo.flow.Block;
+import org.apache.fop.fo.flow.Character;
import org.apache.fop.fo.flow.ExternalGraphic;
import org.apache.fop.fo.flow.Footnote;
import org.apache.fop.fo.flow.FootnoteBody;
@@ -39,6 +41,8 @@ import org.apache.fop.fo.flow.InstreamForeignObject;
import org.apache.fop.fo.flow.Leader;
import org.apache.fop.fo.flow.ListBlock;
import org.apache.fop.fo.flow.ListItem;
+import org.apache.fop.fo.flow.ListItemBody;
+import org.apache.fop.fo.flow.ListItemLabel;
import org.apache.fop.fo.flow.PageNumber;
import org.apache.fop.fo.flow.table.Table;
import org.apache.fop.fo.flow.table.TableBody;
@@ -51,6 +55,7 @@ import org.apache.fop.fo.pagination.Flow;
import org.apache.fop.fo.pagination.PageSequence;
import org.apache.fop.fo.pagination.PageSequenceMaster;
import org.apache.fop.fo.pagination.SimplePageMaster;
+import org.apache.fop.fo.pagination.StaticContent;
import org.apache.fop.fonts.FontSetup;
import org.apache.fop.render.DefaultFontResolver;
@@ -267,27 +272,27 @@ public class MIFHandler extends FOEventHandler {
}
/** {@inheritDoc} */
- public void startListLabel() {
+ public void startListLabel(ListItemLabel listItemLabel) {
}
/** {@inheritDoc} */
- public void endListLabel() {
+ public void endListLabel(ListItemLabel listItemLabel) {
}
/** {@inheritDoc} */
- public void startListBody() {
+ public void startListBody(ListItemBody listItemBody) {
}
/** {@inheritDoc} */
- public void endListBody() {
+ public void endListBody(ListItemBody listItemBody) {
}
/** {@inheritDoc} */
- public void startStatic() {
+ public void startStatic(StaticContent staticContent) {
}
/** {@inheritDoc} */
- public void endStatic() {
+ public void endStatic(StaticContent staticContent) {
}
/** {@inheritDoc} */
@@ -303,7 +308,7 @@ public class MIFHandler extends FOEventHandler {
}
/** {@inheritDoc} */
- public void endLink() {
+ public void endLink(BasicLink basicLink) {
}
/** {@inheritDoc} */
@@ -315,7 +320,11 @@ public class MIFHandler extends FOEventHandler {
}
/** {@inheritDoc} */
- public void foreignObject(InstreamForeignObject ifo) {
+ public void startInstreamForeignObject(InstreamForeignObject ifo) {
+ }
+
+ /** {@inheritDoc} */
+ public void endInstreamForeignObject(InstreamForeignObject ifo) {
}
/** {@inheritDoc} */
@@ -335,19 +344,37 @@ public class MIFHandler extends FOEventHandler {
}
/** {@inheritDoc} */
- public void leader(Leader l) {
+ public void startLeader(Leader l) {
+ }
+
+ /** {@inheritDoc} */
+ public void endLeader(Leader l) {
+ }
+
+ public void character(Character c) {
+ appendCharacters ( new String ( new char[] {c.getCharacter()} ) );
+ }
+
+ /** {@inheritDoc} */
+ public void characters(FOText foText) {
+ appendCharacters ( foText.getCharSequence().toString() );
+ }
+
+ /** {@inheritDoc} */
+ public void startPageNumber(PageNumber pagenum) {
}
/** {@inheritDoc} */
- public void characters(char[] data, int start, int length) {
+ public void endPageNumber(PageNumber pagenum) {
+ }
+
+ private void appendCharacters ( String str ) {
if (para != null) {
- String str = new String(data, start, length);
str = str.trim();
// break into nice length chunks
if (str.length() == 0) {
return;
}
-
MIFElement line = new MIFElement("ParaLine");
MIFElement prop = new MIFElement("TextRectID");
prop.setValue("2");
@@ -355,17 +382,8 @@ public class MIFHandler extends FOEventHandler {
prop = new MIFElement("String");
prop.setValue("\"" + str + "\"");
line.addElement(prop);
-
para.addElement(line);
}
}
-
- /** {@inheritDoc} */
- public void startPageNumber(PageNumber pagenum) {
- }
-
- /** {@inheritDoc} */
- public void endPageNumber(PageNumber pagenum) {
- }
}
diff --git a/src/sandbox/org/apache/fop/render/svg/SVGDocumentHandler.java b/src/sandbox/org/apache/fop/render/svg/SVGDocumentHandler.java
index ef0d9f301..6fd4d3618 100644
--- a/src/sandbox/org/apache/fop/render/svg/SVGDocumentHandler.java
+++ b/src/sandbox/org/apache/fop/render/svg/SVGDocumentHandler.java
@@ -54,7 +54,8 @@ import org.apache.fop.util.GenerationHelperContentHandler;
import org.apache.fop.util.XMLUtil;
/**
- * {@link IFDocumentHandler} implementation that writes SVG 1.1.
+ * {@link org.apache.fop.render.intermediate.IFDocumentHandler} implementation
+ * that writes SVG 1.1.
*/
public class SVGDocumentHandler extends AbstractSVGDocumentHandler {
diff --git a/src/sandbox/org/apache/fop/render/svg/SVGPrintDocumentHandler.java b/src/sandbox/org/apache/fop/render/svg/SVGPrintDocumentHandler.java
index 10be9588e..8ec374731 100644
--- a/src/sandbox/org/apache/fop/render/svg/SVGPrintDocumentHandler.java
+++ b/src/sandbox/org/apache/fop/render/svg/SVGPrintDocumentHandler.java
@@ -32,7 +32,8 @@ import org.apache.fop.render.intermediate.IFPainter;
import org.apache.fop.util.XMLUtil;
/**
- * {@link IFDocumentHandler} implementation that writes SVG Print.
+ * {@link org.apache.fop.render.intermediate.IFDocumentHandler} implementation
+ * that writes SVG Print.
*/
public class SVGPrintDocumentHandler extends AbstractSVGDocumentHandler {
diff --git a/status.xml b/status.xml
index b9fc82dee..a50c358ca 100644
--- a/status.xml
+++ b/status.xml
@@ -62,6 +62,51 @@
documents. Example: the fix of marks layering will be such a case when it's done.
-->
<release version="1.1rc1" date="02 July 2012">
+ <action context="Test" dev="GA" type="fix">
+ Fix errors and warnings in example files. Add build.xml for documentation examples.
+ </action>
+ <action context="Code" dev="GA" type="fix" fixes-bug="53458" due-to="Dieter von Holten">
+ Fix incorrect language and country code key generation in hyphenation tree cache.
+ </action>
+ <action context="Images" dev="GA" type="fix" fixes-bug="53431" due-to="Luis Bernardo">
+ Fix incorrect SVG line dash pattern with PDF output format.
+ </action>
+ <action context="Images" dev="GA" type="update" fixes-bug="40676" due-to="Luis Bernardo">
+ Fix interoperability issue with Adobe reader regarding use of multiple filters.
+ </action>
+ <action context="Code" dev="GA" type="fix">
+ Eliminate javadocs warnings.
+ </action>
+ <action context="Renderers" dev="VH" type="add" fixes-bug="52338" importance="high">
+ Added possibility to embed TrueType fonts in PostScript.
+ </action>
+ <action context="Images" dev="GA" type="update" fixes-bug="40676" due-to="Luis Bernardo">
+ Update site documentation about PNG image loading configuration and support.
+ </action>
+ <action context="Images" dev="GA" type="update" fixes-bug="40676" due-to="Luis Bernardo">
+ Fix newly introduced findbugs warnings.
+ </action>
+ <action context="Images" dev="GA" type="fix" fixes-bug="40676" due-to="Luis Bernardo, Matthias Reischenbacher">
+ Support use of ImageLoaderRawPNG decoder in order to prevent re-encoding of PNG images (and unnecssary output file bloat).
+ </action>
+ <action context="Code" dev="GA" type="fix" fixes-bug="53412" due-to="Alexios Giotis">
+ Eliminate incorrect use of object identity which produces excessive property cache collisions.
+ </action>
+ <action context="Code" dev="GA" type="fix">
+ Eliminate javadocs warnings.
+ </action>
+ <action context="Code" dev="GA" type="update" fixes-bug="53055">
+ Update xmlgraphics common jar to reflect recent fixes in XGC.
+ </action>
+ <action context="Code" dev="GA" type="update" fixes-bug="43940" due-to="Julien Aymé">
+ Fix handling of NaN, {+,-}Infinity, and other edge cases. Submitted by Julien Aymé.
+ </action>
+ <action context="Renderers" dev="GA" type="fix" fixes-bug="53304,53306">
+ Add version attribute to AT and IF intermediate formats. Also eliminate redundant use of reversed attribute in AT format.
+ </action>
+ <action context="Renderers" dev="GA" type="fix" fixes-bug="53295" due-to="Luis Bernardo">
+ Add extension to place code just before PostScript %PageTrailer.
+ </action>
<action context="Renderers" dev="GA" type="fix" fixes-bug="53294" due-to="Robert Meyer">
Fix invalid PostScript file being created when font-size is 0.
</action>
diff --git a/test/java/org/apache/fop/UtilityCodeTestSuite.java b/test/java/org/apache/fop/UtilityCodeTestSuite.java
index cf6b8875d..762b86b14 100644
--- a/test/java/org/apache/fop/UtilityCodeTestSuite.java
+++ b/test/java/org/apache/fop/UtilityCodeTestSuite.java
@@ -28,10 +28,13 @@ import org.apache.fop.pdf.FileIDGeneratorTestCase;
import org.apache.fop.pdf.PDFDocumentGraphics2DTestCase;
import org.apache.fop.pdf.PDFEncryptionJCETestCase;
import org.apache.fop.pdf.PDFFactoryTestCase;
+import org.apache.fop.pdf.PDFNumberTestCase;
+import org.apache.fop.pdf.PDFObjectTestCase;
import org.apache.fop.traits.BorderPropsTestCase;
import org.apache.fop.util.BitmapImageUtilTestCase;
import org.apache.fop.util.ColorUtilTestCase;
import org.apache.fop.util.ElementListUtilsTestCase;
+import org.apache.fop.util.HexEncoderTestCase;
import org.apache.fop.util.XMLResourceBundleTestCase;
/**
@@ -49,7 +52,10 @@ import org.apache.fop.util.XMLResourceBundleTestCase;
PDFFactoryTestCase.class,
PDFEncryptionJCETestCase.class,
BitmapImageUtilTestCase.class,
- PDFDocumentGraphics2DTestCase.class
+ PDFDocumentGraphics2DTestCase.class,
+ PDFNumberTestCase.class,
+ PDFObjectTestCase.class,
+ HexEncoderTestCase.class
})
public class UtilityCodeTestSuite {
}
diff --git a/test/java/org/apache/fop/accessibility/fo/FO2StructureTreeConverterTestCase.java b/test/java/org/apache/fop/accessibility/fo/FO2StructureTreeConverterTestCase.java
index 9c53bdde3..863bfe797 100644
--- a/test/java/org/apache/fop/accessibility/fo/FO2StructureTreeConverterTestCase.java
+++ b/test/java/org/apache/fop/accessibility/fo/FO2StructureTreeConverterTestCase.java
@@ -19,8 +19,6 @@
package org.apache.fop.accessibility.fo;
-import static org.junit.Assert.assertTrue;
-
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -45,6 +43,8 @@ import org.w3c.dom.Document;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;
+import static org.junit.Assert.assertTrue;
+
import org.apache.fop.accessibility.StructureTree2SAXEventAdapter;
import org.apache.fop.accessibility.StructureTreeEventHandler;
import org.apache.fop.apps.FOPException;
@@ -102,6 +102,17 @@ public class FO2StructureTreeConverterTestCase {
testConverter();
}
+ @Test
+ public void testArtifact() throws Exception {
+ foLoader = new FOLoader() {
+
+ public InputStream getFoInputStream() {
+ return getResource("artifact.fo");
+ }
+ };
+ testConverter();
+ }
+
private Transformer createTransformer(Source xslt) throws TransformerFactoryConfigurationError,
TransformerConfigurationException {
TransformerFactory transformerFactory = TransformerFactory.newInstance();
diff --git a/test/java/org/apache/fop/accessibility/fo/artifact.fo b/test/java/org/apache/fop/accessibility/fo/artifact.fo
new file mode 100644
index 000000000..c3d5fadf3
--- /dev/null
+++ b/test/java/org/apache/fop/accessibility/fo/artifact.fo
@@ -0,0 +1,97 @@
+<?xml version="1.0" standalone="no"?>
+<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
+ <fo:layout-master-set>
+ <fo:simple-page-master master-name="page"
+ page-height="500pt" page-width="300pt" margin="20pt">
+ <fo:region-body/>
+ </fo:simple-page-master>
+ </fo:layout-master-set>
+
+ <fo:page-sequence master-reference="page">
+ <fo:flow flow-name="xsl-region-body" text-align="justify">
+
+ <fo:block>This piece of text is normal content that should be read out loud by a screen
+ reader.</fo:block>
+
+ <fo:block space-before="10pt">The following content will be treated as artifact:</fo:block>
+
+ <fo:wrapper role="artifact">
+ <fo:block-container border="1pt solid black" padding="5pt" background-color="#F0F0F0"
+ space-before="10pt" start-indent="6pt" end-indent="6pt" color="#606060">
+ <fo:block start-indent="0" end-indent="0">
+ <fo:block>A block as artifact.</fo:block>
+ <fo:table space-before="5pt" width="100%" table-layout="fixed">
+ <fo:table-column column-width="proportional-column-width(1)"/>
+ <fo:table-column column-width="proportional-column-width(2)"/>
+ <fo:table-header>
+ <fo:table-cell border="1pt solid #606060"><fo:block>Header 1.1</fo:block></fo:table-cell>
+ <fo:table-cell border="1pt solid #606060"><fo:block>Header 1.2</fo:block></fo:table-cell>
+ </fo:table-header>
+ <fo:table-footer>
+ <fo:table-cell border="1pt solid #606060"><fo:block>Footer 1.1</fo:block></fo:table-cell>
+ <fo:table-cell border="1pt solid #606060"><fo:block>Footer 1.2</fo:block></fo:table-cell>
+ </fo:table-footer>
+ <fo:table-body>
+ <fo:table-row>
+ <fo:table-cell border="1pt solid #606060"><fo:block>Cell 1.1</fo:block></fo:table-cell>
+ <fo:table-cell border="1pt solid #606060"><fo:block>Cell 1.2</fo:block></fo:table-cell>
+ </fo:table-row>
+ <fo:table-row>
+ <fo:table-cell border="1pt solid #606060"><fo:block>Cell 2.1</fo:block></fo:table-cell>
+ <fo:table-cell border="1pt solid #606060"><fo:block>Cell 2.2</fo:block></fo:table-cell>
+ </fo:table-row>
+ </fo:table-body>
+ </fo:table>
+ <fo:list-block space-before="7pt" provisional-distance-between-starts="8pt"
+ provisional-label-separation="5pt">
+ <fo:list-item>
+ <fo:list-item-label end-indent="label-end()">
+ <fo:block>•</fo:block>
+ </fo:list-item-label>
+ <fo:list-item-body start-indent="body-start()">
+ <fo:block>Item 1.</fo:block>
+ </fo:list-item-body>
+ </fo:list-item>
+ <fo:list-item>
+ <fo:list-item-label end-indent="label-end()">
+ <fo:block>•</fo:block>
+ </fo:list-item-label>
+ <fo:list-item-body start-indent="body-start()">
+ <fo:block>Item 2.</fo:block>
+ </fo:list-item-body>
+ </fo:list-item>
+ <fo:list-item>
+ <fo:list-item-label end-indent="label-end()">
+ <fo:block>•</fo:block>
+ </fo:list-item-label>
+ <fo:list-item-body start-indent="body-start()">
+ <fo:block>Item 3.</fo:block>
+ </fo:list-item-body>
+ </fo:list-item>
+ </fo:list-block>
+ <fo:wrapper>
+ <fo:block>A block in a nested wrapper.</fo:block>
+ </fo:wrapper>
+ <fo:wrapper role="artifact">
+ <fo:block>A block in a nested artifact wrapper.</fo:block>
+ </fo:wrapper>
+ <fo:wrapper>
+ <fo:block>Inside a nested wrapper.
+ <fo:wrapper role="artifact">An artifact wrapper inside the nested wrapper.
+ <fo:inline><fo:wrapper>Inside a wrapper inside the artifact wrapper that is inside
+ the nested wrapper.</fo:wrapper> Outside the wrapper inside the artifact
+ wrapper that is inside the nested wrapper.</fo:inline>
+ </fo:wrapper> Outside the artifact wrapper.</fo:block>
+ </fo:wrapper>
+ </fo:block>
+ </fo:block-container>
+ </fo:wrapper>
+
+ <fo:block space-before="10pt">Now we are back to normal content that is part of the logical
+ structure, should appear in the structure tree and should be read out loud by the screen
+ reader.</fo:block>
+
+ </fo:flow>
+ </fo:page-sequence>
+
+</fo:root>
diff --git a/test/java/org/apache/fop/accessibility/fo/fo2StructureTree.xsl b/test/java/org/apache/fop/accessibility/fo/fo2StructureTree.xsl
index ce326f3b1..db0dffb14 100644
--- a/test/java/org/apache/fop/accessibility/fo/fo2StructureTree.xsl
+++ b/test/java/org/apache/fop/accessibility/fo/fo2StructureTree.xsl
@@ -105,6 +105,8 @@
<xsl:call-template name="copy"/>
</xsl:template>
+ <xsl:template match="fo:wrapper[translate(normalize-space(@role), 'ARTIFCT', 'artifct') = 'artifact']"/>
+
<!-- Discard descendants of fo:leader -->
<xsl:template match="fo:leader"/>
diff --git a/test/java/org/apache/fop/fonts/DejaVuLGCSerifTestCase.java b/test/java/org/apache/fop/fonts/DejaVuLGCSerifTestCase.java
index 4ac61d893..49c447583 100644
--- a/test/java/org/apache/fop/fonts/DejaVuLGCSerifTestCase.java
+++ b/test/java/org/apache/fop/fonts/DejaVuLGCSerifTestCase.java
@@ -43,7 +43,8 @@ public class DejaVuLGCSerifTestCase {
@Before
public void setUp() throws Exception {
File file = new File("test/resources/fonts/ttf/DejaVuLGCSerif.ttf");
- font = FontLoader.loadFont(file, "", true, EncodingMode.AUTO, fontResolver);
+ font = FontLoader.loadFont(file, "", true, EmbeddingMode.AUTO, EncodingMode.AUTO,
+ fontResolver);
}
/**
diff --git a/test/java/org/apache/fop/fonts/EncodingModeTestCase.java b/test/java/org/apache/fop/fonts/EncodingModeTestCase.java
index 1ec22e1ef..8cab9eb8b 100644
--- a/test/java/org/apache/fop/fonts/EncodingModeTestCase.java
+++ b/test/java/org/apache/fop/fonts/EncodingModeTestCase.java
@@ -19,10 +19,13 @@
package org.apache.fop.fonts;
-import static org.junit.Assert.assertEquals;
-
import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Tests {@link EncodingMode}.
+ */
public class EncodingModeTestCase {
@Test
@@ -34,8 +37,13 @@ public class EncodingModeTestCase {
@Test
public void testGetValue() {
- assertEquals(EncodingMode.AUTO, EncodingMode.getEncodingMode("auto"));
- assertEquals(EncodingMode.SINGLE_BYTE, EncodingMode.getEncodingMode("single-byte"));
- assertEquals(EncodingMode.CID, EncodingMode.getEncodingMode("cid"));
+ assertEquals(EncodingMode.AUTO, EncodingMode.getValue("auto"));
+ assertEquals(EncodingMode.SINGLE_BYTE, EncodingMode.getValue("single-byte"));
+ assertEquals(EncodingMode.CID, EncodingMode.getValue("cid"));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void getValueMustCheckForIllegalArguments() {
+ EncodingMode.getValue("fail");
}
}
diff --git a/test/java/org/apache/fop/fonts/FOPFontsTestSuite.java b/test/java/org/apache/fop/fonts/FOPFontsTestSuite.java
new file mode 100644
index 000000000..8e1a0040d
--- /dev/null
+++ b/test/java/org/apache/fop/fonts/FOPFontsTestSuite.java
@@ -0,0 +1,42 @@
+/*
+ * 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.junit.runner.RunWith;
+import org.junit.runners.Suite;
+import org.junit.runners.Suite.SuiteClasses;
+
+import org.apache.fop.fonts.truetype.FontFileReaderTestCase;
+import org.apache.fop.fonts.truetype.TTFFileTestCase;
+import org.apache.fop.fonts.truetype.TTFSubSetFileTestCase;
+import org.apache.fop.fonts.truetype.TTFTableNameTestCase;
+
+/**
+ * A test suite designed for org.apache.fop.fonts.*
+ */
+@RunWith(Suite.class)
+@SuiteClasses({
+ EncodingModeTestCase.class,
+ FontFileReaderTestCase.class,
+ TTFFileTestCase.class,
+ TTFSubSetFileTestCase.class,
+ TTFTableNameTestCase.class })
+public final class FOPFontsTestSuite {
+}
diff --git a/test/java/org/apache/fop/fonts/truetype/FontFileReaderTestCase.java b/test/java/org/apache/fop/fonts/truetype/FontFileReaderTestCase.java
new file mode 100644
index 000000000..5c1fec175
--- /dev/null
+++ b/test/java/org/apache/fop/fonts/truetype/FontFileReaderTestCase.java
@@ -0,0 +1,304 @@
+/*
+ * 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.truetype;
+
+import java.io.ByteArrayInputStream;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+/**
+ * A test class for org.apache.fop.truetype.FontFileReader
+ */
+public class FontFileReaderTestCase {
+ private FontFileReader fontReader;
+ private final InputStream in;
+ private final byte[] byteArray;
+
+ /**
+ * Constructor - initialises an array that only needs to be created once. It creates a byte[]
+ * of form { 0x00, 0x01, 0x02, 0x03..., 0xff};
+ */
+ public FontFileReaderTestCase() {
+ byteArray = new byte[256];
+ for (int i = 0; i < 256; i++) {
+ byteArray[i] = (byte) i;
+ }
+ in = new ByteArrayInputStream(byteArray);
+ }
+
+ /**
+ * sets up the test subject object for testing.
+ */
+ @Before
+ public void setUp() {
+ try {
+ fontReader = new FontFileReader(in);
+ } catch (Exception e) {
+ fail("Error: " + e.getMessage());
+ }
+ }
+
+ /**
+ * the "destructor" method.
+ *
+ */
+ public void tearDown() {
+ fontReader = null;
+ }
+
+ /**
+ * Test readTTFByte()
+ * @throws IOException exception
+ */
+ @Test
+ public void testReadTTFByte() throws IOException {
+ for (int i = 0; i < 256; i++) {
+ assertEquals((byte) i, fontReader.readTTFByte());
+ }
+ }
+
+ /**
+ * Test seekSet() - check that it moves to the correct position and enforce a failure case.
+ * @throws IOException exception
+ */
+ @Test
+ public void testSeekSet() throws IOException {
+ fontReader.seekSet(10);
+ assertEquals(10, fontReader.readTTFByte());
+ try {
+ fontReader.seekSet(257);
+ fail("FileFontReaderTest Failed testSeekSet");
+ } catch (IOException e) {
+ // Passed
+ }
+ }
+
+ /**
+ * Test skip() - check that it moves to the correct position and enforce a failure case.
+ * @throws IOException exception
+ */
+ @Test
+ public void testSkip() throws IOException {
+ fontReader.skip(100);
+ assertEquals(100, fontReader.readTTFByte());
+ try {
+ // 100 (seekAdd) + 1 (read() = 1 byte) + 156 = 257
+ fontReader.skip(156);
+ fail("FileFontReaderTest Failed testSkip");
+ } catch (IOException e) {
+ // Passed
+ }
+ }
+
+ /**
+ * Test getCurrentPos() - 3 checks:
+ * 1) test with seekSet(int)
+ * 2) test with skip(int)
+ * 3) test with a readTTFByte() (this moves the position by the size of the data being read)
+ * @throws IOException exception
+ */
+ @Test
+ public void testGetCurrentPos() throws IOException {
+ fontReader.seekSet(10);
+ fontReader.skip(100);
+ assertEquals(110, fontReader.getCurrentPos());
+ fontReader.readTTFByte();
+ assertEquals(111, fontReader.getCurrentPos());
+ }
+
+ /**
+ * Test getFileSize()
+ */
+ @Test
+ public void testGetFileSize() {
+ assertEquals(256, fontReader.getFileSize());
+ }
+
+ /**
+ * Test readTTFUByte()
+ * @throws IOException exception
+ */
+ @Test
+ public void testReadTTFUByte() throws IOException {
+ for (int i = 0; i < 256; i++) {
+ assertEquals(i, fontReader.readTTFUByte());
+ }
+ }
+
+ /**
+ * Test readTTFShort() - Test positive and negative numbers (two's compliment).
+ * @throws IOException exception
+ */
+ @Test
+ public void testReadTTFShort() throws IOException {
+ // 0x0001 = 1
+ assertEquals("Should have been 1 (0x0001)", 1, fontReader.readTTFShort());
+ // 0x0203 = 515
+ assertEquals(515, fontReader.readTTFShort());
+ // now test negative numbers
+ fontReader.seekSet(250);
+ // 0xfafb
+ assertEquals(-1285, fontReader.readTTFShort());
+ }
+
+ /**
+ * Test readTTFUShort() - Test positive and potentially negative numbers (two's compliment).
+ * @throws IOException exception
+ */
+ @Test
+ public void testReadTTFUShort() throws IOException {
+ // 0x0001
+ assertEquals(1, fontReader.readTTFUShort());
+ // 0x0203
+ assertEquals(515, fontReader.readTTFUShort());
+ // test potential negatives
+ fontReader.seekSet(250);
+ // 0xfafb
+ assertEquals((250 << 8) + 251, fontReader.readTTFUShort());
+ }
+
+ /**
+ * Test readTTFShort(int) - test reading ahead of current position and behind current position
+ * and in both cases ensure that our current position isn't changed.
+ * @throws IOException exception
+ */
+ @Test
+ public void testReadTTFShortWithArg() throws IOException {
+ // 0x6465
+ assertEquals(25701, fontReader.readTTFShort(100));
+ assertEquals(0, fontReader.getCurrentPos());
+ // read behind current position (and negative)
+ fontReader.seekSet(255);
+ // 0xfafb
+ assertEquals(-1285, fontReader.readTTFShort(250));
+ assertEquals(255, fontReader.getCurrentPos());
+ }
+
+ /**
+ * Test readTTFUShort(int arg) - test reading ahead of current position and behind current
+ * position and in both cases ensure that our current position isn't changed.
+ * @throws IOException exception
+ */
+ @Test
+ public void testReadTTFUShortWithArg() throws IOException {
+ // 0x6465
+ assertEquals(25701, fontReader.readTTFUShort(100));
+ assertEquals(0, fontReader.getCurrentPos());
+ // read behind current position (and potential negative)
+ fontReader.seekSet(255);
+ // 0xfafb
+ assertEquals(64251, fontReader.readTTFUShort(250));
+ assertEquals(255, fontReader.getCurrentPos());
+ }
+
+ /**
+ * Test readTTFLong()
+ * @throws IOException exception
+ */
+ @Test
+ public void testReadTTFLong() throws IOException {
+ // 0x00010203
+ assertEquals(66051, fontReader.readTTFLong());
+ // test negative numbers
+ fontReader.seekSet(250);
+ // 0xf0f1f2f3
+ assertEquals(-84148995, fontReader.readTTFLong());
+ }
+
+ /**
+ * Test readTTFULong()
+ * @throws IOException exception
+ */
+ @Test
+ public void testReadTTFULong() throws IOException {
+ // 0x00010203
+ assertEquals(66051, fontReader.readTTFULong());
+ // test negative numbers
+ fontReader.seekSet(250);
+ // 0xfafbfcfd
+ assertEquals(4210818301L, fontReader.readTTFULong());
+ }
+
+ /**
+ * Test readTTFString() - there are two paths to test here:
+ * 1) A null terminated string
+ * 2) A string not terminated with a null (we expect this to throw an EOFException)
+ * @throws IOException exception
+ */
+ @Test
+ public void testReadTTFString() throws IOException {
+ byte[] strByte = {(byte)'t', (byte)'e', (byte)'s', (byte)'t', 0x00};
+ fontReader = new FontFileReader(new ByteArrayInputStream(strByte));
+ assertEquals("test", fontReader.readTTFString());
+ try {
+ // not NUL terminated
+ byte[] strByteNoNull = {(byte)'t', (byte)'e', (byte)'s', (byte)'t'};
+ fontReader = new FontFileReader(new ByteArrayInputStream(strByteNoNull));
+ assertEquals("test", fontReader.readTTFString());
+ fail("FontFileReaderTest testReadTTFString Fails.");
+ } catch (EOFException e) {
+ // Pass
+ }
+ }
+
+ /**
+ * Test readTTFString(int arg)
+ * @throws IOException exception
+ */
+ @Test
+ public void testReadTTFStringIntArg() throws IOException {
+ byte[] strByte = {(byte)'t', (byte)'e', (byte)'s', (byte)'t'};
+ fontReader = new FontFileReader(new ByteArrayInputStream(strByte));
+ assertEquals("test", fontReader.readTTFString(4));
+ try {
+ fontReader = new FontFileReader(new ByteArrayInputStream(strByte));
+ assertEquals("test", fontReader.readTTFString(5));
+ fail("FontFileReaderTest testReadTTFStringIntArg Fails.");
+ } catch (EOFException e) {
+ // Pass
+ }
+ }
+
+ /**
+ * Test readTTFString(int arg1, int arg2)
+ */
+ public void testReadTTFString2IntArgs() {
+ // currently the same as above
+ }
+
+ /**
+ * Test getBytes()
+ * @throws IOException exception
+ */
+ @Test
+ public void testGetBytes() throws IOException {
+ byte[] retrievedBytes = fontReader.getBytes(0, 256);
+ assertTrue(Arrays.equals(byteArray, retrievedBytes));
+ }
+}
diff --git a/test/java/org/apache/fop/fonts/truetype/GlyfTableTestCase.java b/test/java/org/apache/fop/fonts/truetype/GlyfTableTestCase.java
index 67191accc..825f71ac1 100644
--- a/test/java/org/apache/fop/fonts/truetype/GlyfTableTestCase.java
+++ b/test/java/org/apache/fop/fonts/truetype/GlyfTableTestCase.java
@@ -19,8 +19,6 @@
package org.apache.fop.fonts.truetype;
-import static org.junit.Assert.assertTrue;
-
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -31,6 +29,8 @@ import java.util.Map;
import org.junit.Before;
import org.junit.Test;
+import static org.junit.Assert.assertTrue;
+
/**
* Tests {@link GlyfTable}.
*/
@@ -141,7 +141,8 @@ public class GlyfTableTestCase {
private void setupSubsetReader(Map<Integer, Integer> glyphs) throws IOException {
TTFSubSetFile fontFile = new TTFSubSetFile();
- byte[] subsetFont = fontFile.readFont(originalFontReader, "Deja", glyphs);
+ fontFile.readFont(originalFontReader, "Deja", glyphs);
+ byte[] subsetFont = fontFile.getFontSubset();
InputStream intputStream = new ByteArrayInputStream(subsetFont);
subsetReader = new FontFileReader(intputStream);
}
diff --git a/test/java/org/apache/fop/fonts/truetype/TTFFileTestCase.java b/test/java/org/apache/fop/fonts/truetype/TTFFileTestCase.java
new file mode 100644
index 000000000..d490a3d5d
--- /dev/null
+++ b/test/java/org/apache/fop/fonts/truetype/TTFFileTestCase.java
@@ -0,0 +1,427 @@
+/*
+ * 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.truetype;
+
+import java.io.IOException;
+import java.util.Map;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import org.apache.fop.fonts.truetype.TTFFile.PostScriptVersion;
+
+/**
+ * Class for testing org.apache.fop.fonts.truetype.TTFFile
+ */
+public class TTFFileTestCase {
+ // We only want to initialize the FontFileReader once (for performance reasons)
+ /** The truetype font file (DejaVuLGCSerif) */
+ protected final TTFFile dejavuTTFFile;
+ /** The FontFileReader for ttfFile (DejaVuLGCSerif) */
+ protected final FontFileReader dejavuReader;
+ /** The truetype font file (DroidSansMono) */
+ protected final TTFFile droidmonoTTFFile;
+ /** The FontFileReader for ttfFile (DroidSansMono) */
+ protected final FontFileReader droidmonoReader;
+
+
+ /**
+ * Constructor initialises FileFontReader to
+ * @throws IOException exception
+ */
+ public TTFFileTestCase() throws IOException {
+ dejavuTTFFile = new TTFFile();
+ dejavuReader = new FontFileReader("test/resources/fonts/ttf/DejaVuLGCSerif.ttf");
+ dejavuTTFFile.readFont(dejavuReader);
+ droidmonoTTFFile = new TTFFile();
+ droidmonoReader = new FontFileReader("test/resources/fonts/ttf/DroidSansMono.ttf");
+ droidmonoTTFFile.readFont(droidmonoReader);
+ }
+
+ /**
+ * Test convertTTFUnit2PDFUnit() - The units per em retrieved reading the HEAD table from
+ * the font file. (DroidSansMono has the same units per em as DejaVu so no point testing it)
+ */
+ @Test
+ public void testConvertTTFUnit2PDFUnit() {
+ // DejaVu has 2048 units per em (PDF works in millipts, thus the 1000)
+ // test rational number
+ assertEquals(1000, dejavuTTFFile.convertTTFUnit2PDFUnit(2048));
+ // test smallest case, this should = 0.488 (round down to 0)
+ assertEquals(0, dejavuTTFFile.convertTTFUnit2PDFUnit(1));
+ // this should round up, but since it's millipts...
+ assertEquals(0, dejavuTTFFile.convertTTFUnit2PDFUnit(2));
+ // ensure behaviour is the same for negative numbers
+ assertEquals(0, dejavuTTFFile.convertTTFUnit2PDFUnit(-0));
+ assertEquals(-1000, dejavuTTFFile.convertTTFUnit2PDFUnit(-2048));
+ assertEquals(0, dejavuTTFFile.convertTTFUnit2PDFUnit(-1));
+ assertEquals(0, dejavuTTFFile.convertTTFUnit2PDFUnit(-2));
+ }
+
+ /**
+ * Test checkTTC()
+ * @throws IOException exception
+ */
+ @Test
+ public void testCheckTTC() throws IOException {
+ // DejaVu is not a TTC, thus this returns true
+ assertTrue(dejavuTTFFile.checkTTC(""));
+ assertTrue(droidmonoTTFFile.checkTTC(""));
+ /*
+ * Cannot reasonably test the rest of this method without an actual truetype collection
+ * because all methods in FontFileReader are "final" and thus mocking isn't possible.
+ */
+ }
+
+ /**
+ * Test getAnsiKerning() - Tests values retrieved from the kern table in the font file.
+ */
+ @Test
+ public void testGetAnsiKerning() {
+ Map<Integer, Map<Integer, Integer>> ansiKerning = dejavuTTFFile.getKerning();
+ if (ansiKerning.isEmpty()) {
+ fail();
+ }
+ Integer k1 = ansiKerning.get(Integer.valueOf('A')).get(
+ Integer.valueOf('T'));
+ assertEquals(dejavuTTFFile.convertTTFUnit2PDFUnit(-112), k1.intValue());
+ Integer k2 = ansiKerning.get(Integer.valueOf('Y')).get(Integer.valueOf('u'));
+ assertEquals(dejavuTTFFile.convertTTFUnit2PDFUnit(-178), k2.intValue());
+
+ // DroidSansMono doens't have kerning (it's mono-spaced)
+ ansiKerning = droidmonoTTFFile.getAnsiKerning();
+ if (!ansiKerning.isEmpty()) {
+ fail("DroidSansMono shouldn't have any kerning data.");
+ }
+ }
+
+ /**
+ * Test getCapHeight - there are several paths to test:
+ * 1) The PCLT table (if present)
+ * 2) The yMax (3rd) value, for the bounding box, for 'H' in the glyf table.
+ * if not the above:
+ * 3) The caps height in the OS/2 table
+ * Tests values retrieved from analysing the font file.
+ */
+ @Test
+ public void testGetCapHeight() {
+ // DejaVu doesn't have the PCLT table and so these have to be guessed
+ // The height is approximated to be the height of the "H" which for
+ // Deja = 1493 TTFunits
+ assertEquals(dejavuTTFFile.convertTTFUnit2PDFUnit(1493), dejavuTTFFile.getCapHeight());
+ // DroidSansMono doesn't have a PCLT table either
+ // height of "H" = 1462
+ assertEquals(droidmonoTTFFile.convertTTFUnit2PDFUnit(1462),
+ droidmonoTTFFile.getCapHeight());
+ }
+
+ /**
+ * Test getCharSetName() - check that it returns "WinAnsiEncoding".
+ */
+ @Test
+ public void testGetCharSetName() {
+ assertTrue("WinAnsiEncoding".equals(dejavuTTFFile.getCharSetName()));
+ assertTrue("WinAnsiEncoding".equals(droidmonoTTFFile.getCharSetName()));
+ }
+
+ /**
+ * Test getCharWidth() - Test values retrieved from the metrics in the glyf table in
+ * the font file.
+ */
+ @Test
+ public void testGetCharWidth() {
+ // Arbitrarily test a few values:
+ // The width of "H" (Unicode index 0x0048) is 1786
+ assertEquals(dejavuTTFFile.convertTTFUnit2PDFUnit(1786), dejavuTTFFile.getCharWidth(0x48));
+ // The width of "i" (unicode index 0x0069) is 655 TTFunits
+ assertEquals(dejavuTTFFile.convertTTFUnit2PDFUnit(655), dejavuTTFFile.getCharWidth(0x69));
+ // final check, "!" (unicode index 0x0021) is 823 TTFunits
+ assertEquals(dejavuTTFFile.convertTTFUnit2PDFUnit(823), dejavuTTFFile.getCharWidth(0x21));
+
+ // All the glyphs should be the same width in DroidSansMono (mono-spaced)
+ int charWidth = droidmonoTTFFile.convertTTFUnit2PDFUnit(1229);
+ for (int i = 0; i < 255; i++) {
+ assertEquals(charWidth, droidmonoTTFFile.getCharWidth(i));
+ }
+ }
+
+ /**
+ * TODO: add implementation to this test
+ */
+ public void testGetCMaps() {
+ }
+
+ /**
+ * Test getFamilyNames() - Test value retrieved from the name table in the font file.
+ */
+ @Test
+ public void testGetFamilyNames() {
+ assertEquals(1, dejavuTTFFile.getFamilyNames().size());
+ for (String name : dejavuTTFFile.getFamilyNames()) {
+ assertEquals("DejaVu LGC Serif", name);
+ }
+ assertEquals(1, droidmonoTTFFile.getFamilyNames().size());
+ for (String name : droidmonoTTFFile.getFamilyNames()) {
+ assertEquals("Droid Sans Mono", name);
+ }
+ }
+
+ /**
+ * Test getFirstChar() - TODO: implement a more intelligent test here.
+ */
+ @Test
+ public void testGetFirstChar() {
+ // Not really sure how to test this intelligently
+ assertEquals(0, dejavuTTFFile.getFirstChar());
+ assertEquals(0, droidmonoTTFFile.getFirstChar());
+ }
+
+ /**
+ * Test getFlags() - Test values retrieved from the POST table in the font file.
+ */
+ @Test
+ public void testGetFlags() {
+ /* DejaVu flags are:
+ * italic angle = 0
+ * fixed pitch = 0
+ * has serifs = true (default value; this font doesn't have a PCLT table)
+ */
+ int flags = dejavuTTFFile.getFlags();
+ assertEquals(0, flags & 64); // Italics angle = 0
+ assertEquals(32, flags & 32); // Adobe standard charset
+ assertEquals(0, flags & 2); // fixed pitch = 0
+ assertEquals(1, flags & 1); // has serifs = 1 (true)
+ /*
+ * Droid flags are:
+ * italic angle = 0
+ * fixed pitch = 1
+ * has serifs = true (default value; this font doesn't have a PCLT table)
+ */
+ flags = droidmonoTTFFile.getFlags();
+ assertEquals(0, flags & 64);
+ assertEquals(32, flags & 32);
+ assertEquals(2, flags & 2);
+ assertEquals(1, flags & 1);
+ }
+
+ /**
+ * Test getFontBBox() - Test values retrieved from values in the HEAD table in the font file.
+ */
+ @Test
+ public void testGetFontBBox() {
+ int[] bBox = dejavuTTFFile.getFontBBox();
+ /*
+ * The head table has the following values(DejaVu):
+ * xmin = -1576, ymin = -710, xmax = 3439, ymax = 2544
+ */
+ assertEquals(dejavuTTFFile.convertTTFUnit2PDFUnit(-1576), bBox[0]);
+ assertEquals(dejavuTTFFile.convertTTFUnit2PDFUnit(-710), bBox[1]);
+ assertEquals(dejavuTTFFile.convertTTFUnit2PDFUnit(3439), bBox[2]);
+ assertEquals(dejavuTTFFile.convertTTFUnit2PDFUnit(2544), bBox[3]);
+ /*
+ * The head table has the following values (DroidSansMono):
+ * xmin = -312, ymin= -555, xmax = 1315, ymax = 2163
+ */
+ bBox = droidmonoTTFFile.getFontBBox();
+ assertEquals(droidmonoTTFFile.convertTTFUnit2PDFUnit(-312), bBox[0]);
+ assertEquals(droidmonoTTFFile.convertTTFUnit2PDFUnit(-555), bBox[1]);
+ assertEquals(droidmonoTTFFile.convertTTFUnit2PDFUnit(1315), bBox[2]);
+ assertEquals(droidmonoTTFFile.convertTTFUnit2PDFUnit(2163), bBox[3]);
+ }
+
+ /**
+ * Test getFullName() - Test value retrieved from the name table in the font file.
+ */
+ @Test
+ public void testGetFullName() {
+ assertEquals("DejaVu LGC Serif", dejavuTTFFile.getFullName());
+ assertEquals("Droid Sans Mono", droidmonoTTFFile.getFullName());
+ }
+
+ /**
+ * Test getGlyphName - Test value retrieved from the POST table in the font file.
+ */
+ @Test
+ public void testGetGlyphName() {
+ assertEquals("H", dejavuTTFFile.getGlyphName(43));
+ assertEquals("H", droidmonoTTFFile.getGlyphName(43));
+ }
+
+ /**
+ * Test getItalicAngle() - Test value retrieved from the POST table in the font file.
+ */
+ @Test
+ public void testGetItalicAngle() {
+ assertEquals("0", dejavuTTFFile.getItalicAngle());
+ assertEquals("0", droidmonoTTFFile.getItalicAngle());
+ }
+
+ /**
+ * Test getKerning() - Test values retrieved from the kern table in the font file.
+ */
+ @Test
+ public void testGetKerning() {
+ Map<Integer, Map<Integer, Integer>> kerning = dejavuTTFFile.getKerning();
+ if (kerning.isEmpty()) {
+ fail();
+ }
+ Integer k1 = kerning.get(Integer.valueOf('A')).get(Integer.valueOf('T'));
+ assertEquals(dejavuTTFFile.convertTTFUnit2PDFUnit(-112), k1.intValue());
+ Integer k2 = kerning.get(Integer.valueOf('K')).get(Integer.valueOf('u'));
+ assertEquals(dejavuTTFFile.convertTTFUnit2PDFUnit(-45), k2.intValue());
+
+ // DroidSansMono has no kerning data (mono-spaced)
+ kerning = droidmonoTTFFile.getKerning();
+ if (!kerning.isEmpty()) {
+ fail("DroidSansMono shouldn't have any kerning data");
+ }
+ }
+
+ /**
+ * Test lastChar() - TODO: implement a more intelligent test
+ */
+ @Test
+ public void testLastChar() {
+ assertEquals(0xff, dejavuTTFFile.getLastChar());
+ assertEquals(0xff, droidmonoTTFFile.getLastChar());
+ }
+
+ /**
+ * Test getLowerCaseAscent() - There are several paths to test:
+ * 1) The values in the HHEA table (see code)
+ * 2) Fall back to values from the OS/2 table
+ * Test values retrieved from the font file.
+ */
+ @Test
+ public void testGetLowerCaseAscent() {
+ assertEquals(dejavuTTFFile.convertTTFUnit2PDFUnit(1556),
+ dejavuTTFFile.getLowerCaseAscent());
+ // Curiously the same value
+ assertEquals(droidmonoTTFFile.convertTTFUnit2PDFUnit(1556),
+ droidmonoTTFFile.getLowerCaseAscent());
+ }
+
+ /**
+ * Test getPostScriptName() - Test values retrieved from the post table in the font file.
+ */
+ @Test
+ public void testGetPostScriptName() {
+ assertEquals(PostScriptVersion.V2, dejavuTTFFile.getPostScriptVersion());
+ assertEquals(PostScriptVersion.V2, droidmonoTTFFile.getPostScriptVersion());
+ }
+
+ /**
+ * Test getStemV() - Undefined.
+ */
+ @Test
+ public void testGetStemV() {
+ // Undefined
+ assertEquals("0", dejavuTTFFile.getStemV());
+ assertEquals("0", droidmonoTTFFile.getStemV());
+ }
+
+ /**
+ * Test getSubFamilyName() - Test values retrieved from the name table in the font file.
+ */
+ @Test
+ public void testGetSubFamilyName() {
+ assertEquals("Book", dejavuTTFFile.getSubFamilyName());
+ assertEquals("Regular", droidmonoTTFFile.getSubFamilyName());
+ }
+
+ /**
+ * Test getTTCnames() - TODO: add implementation with TTC font.
+ */
+ public void testGetTTCnames() {
+ // Can't test with with DejaVu since it's not a TrueType Collection
+ }
+
+ /**
+ * Test getWeightClass() - Test value retrieved from the OS/2 table in the font file.
+ */
+ @Test
+ public void testGetWeightClass() {
+ // Retrieved from OS/2 table
+ assertEquals(400, dejavuTTFFile.getWeightClass());
+ assertEquals(400, droidmonoTTFFile.getWeightClass());
+ }
+
+ /**
+ * Test getWidths() - Test values retrieved from the hmtx table in the font file.
+ */
+ @Test
+ public void testGetWidths() {
+ int[] widths = dejavuTTFFile.getWidths();
+ // using the width of 'A' index = 36
+ assertEquals(dejavuTTFFile.convertTTFUnit2PDFUnit(1479), widths[36]);
+ // using the width of '|' index = 95
+ assertEquals(dejavuTTFFile.convertTTFUnit2PDFUnit(690), widths[95]);
+ widths = droidmonoTTFFile.getWidths();
+ // DroidSansMono should have all widths the same size (mono-spaced)
+ int width = droidmonoTTFFile.convertTTFUnit2PDFUnit(1229);
+ for (int i = 0; i < 255; i++) {
+ assertEquals(width, widths[i]);
+ }
+ }
+
+ /**
+ * Test getXHeight() - There are several paths to test:
+ * 1) The PCLT table (if available)
+ * 2) The yMax for the bounding box for 'x' in the glyf table.
+ * Fall back:
+ * 3) The xheight in the OS/2 table.
+ */
+ @Test
+ public void testGetXHeight() {
+ // Since there's no PCLT table, the height of 'x' is used for both DejaVu and DroidSansMono
+ assertEquals(dejavuTTFFile.convertTTFUnit2PDFUnit(1064), dejavuTTFFile.getXHeight());
+ assertEquals(droidmonoTTFFile.convertTTFUnit2PDFUnit(1098), droidmonoTTFFile.getXHeight());
+ }
+
+ /**
+ * Test isCFF() - TODO: add test for a CFF font.
+ */
+ @Test
+ public void testIsCFF() {
+ // Neither DejaVu nor DroidSansMono are a compact format font
+ assertEquals(false, dejavuTTFFile.isCFF());
+ assertEquals(false, droidmonoTTFFile.isCFF());
+ }
+
+ /**
+ * Test isEmbeddable() - Test value retrieved from the OS/2 table in the font file.
+ */
+ @Test
+ public void testIsEmbeddable() {
+ // Dejavu and DroidSansMono are both embeddable
+ assertEquals(true, dejavuTTFFile.isEmbeddable());
+ assertEquals(true, droidmonoTTFFile.isEmbeddable());
+ }
+
+ /**
+ * Test readFont() - Add implementation if necessary.
+ */
+ public void testReadFont() {
+ // I'm pretty sure we've tested this with all the other tests
+ }
+}
diff --git a/test/java/org/apache/fop/fonts/truetype/TTFFontLoaderTestCase.java b/test/java/org/apache/fop/fonts/truetype/TTFFontLoaderTestCase.java
index 35c865cd7..d6555c32e 100644
--- a/test/java/org/apache/fop/fonts/truetype/TTFFontLoaderTestCase.java
+++ b/test/java/org/apache/fop/fonts/truetype/TTFFontLoaderTestCase.java
@@ -19,18 +19,19 @@
package org.apache.fop.fonts.truetype;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
import java.io.File;
import java.io.IOException;
import org.junit.Test;
+import org.apache.fop.fonts.EmbeddingMode;
import org.apache.fop.fonts.EncodingMode;
import org.apache.fop.fonts.FontManager;
import org.apache.fop.fonts.FontResolver;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
/**
* Test case for {@link TTFFontLoader}.
*/
@@ -47,12 +48,12 @@ public class TTFFontLoaderTestCase {
boolean useKerning = true;
TTFFontLoader fontLoader = new TTFFontLoader(absoluteFilePath, fontName, embedded,
- EncodingMode.AUTO, useKerning, useComplexScriptFeatures, resolver);
+ EmbeddingMode.AUTO, EncodingMode.AUTO, useKerning, useComplexScriptFeatures, resolver);
assertTrue(fontLoader.getFont().hasKerningInfo());
useKerning = false;
- fontLoader = new TTFFontLoader(absoluteFilePath, fontName, embedded, EncodingMode.AUTO,
- useKerning, useComplexScriptFeatures, resolver);
+ fontLoader = new TTFFontLoader(absoluteFilePath, fontName, embedded, EmbeddingMode.AUTO,
+ EncodingMode.AUTO, useKerning, useComplexScriptFeatures, resolver);
assertFalse(fontLoader.getFont().hasKerningInfo());
}
}
diff --git a/test/java/org/apache/fop/fonts/truetype/TTFSubSetFileTestCase.java b/test/java/org/apache/fop/fonts/truetype/TTFSubSetFileTestCase.java
new file mode 100644
index 000000000..16bedad8d
--- /dev/null
+++ b/test/java/org/apache/fop/fonts/truetype/TTFSubSetFileTestCase.java
@@ -0,0 +1,76 @@
+/*
+ * 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.truetype;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * This class tests TTFSubSetFile
+ * TODO: Test with more than just a single font
+ */
+public class TTFSubSetFileTestCase extends TTFFileTestCase {
+ private TTFSubSetFile ttfSubset;
+ private byte[] subset;
+ /**
+ * Constructor
+ * @throws IOException exception
+ */
+ public TTFSubSetFileTestCase() throws IOException {
+ super();
+ }
+
+ /**
+ * setUp()
+ * @exception IOException file read error
+ */
+ @Before
+ public void setUp() throws IOException {
+ ttfSubset = new TTFSubSetFile();
+ Map<Integer, Integer> glyphs = new HashMap<Integer, Integer>();
+ for (int i = 0; i < 255; i++) {
+ glyphs.put(i, i);
+ }
+ ttfSubset.readFont(dejavuReader, "DejaVu", glyphs);
+ subset = ttfSubset.getFontSubset();
+ }
+ /**
+ * Test readFont(FontFileReader, String, Map) - Reads the font and tests the output by injecting
+ * it into a TTFFile object to check the validity of the file as a font. This currently doesn't
+ * create a cmap table, and so the font doesn't contain ALL of the mandatory tables.
+ * @throws IOException exception
+ */
+ @Test
+ public void testReadFont3Args() throws IOException {
+
+ ByteArrayInputStream byteArray = new ByteArrayInputStream(subset);
+ dejavuTTFFile.readFont(new FontFileReader(byteArray));
+ // Test a couple arbitrary values
+ assertEquals(dejavuTTFFile.convertTTFUnit2PDFUnit(-1576), dejavuTTFFile.getFontBBox()[0]);
+ assertEquals(dejavuTTFFile.getFullName(), "DejaVu LGC Serif");
+ }
+}
diff --git a/test/java/org/apache/fop/fonts/truetype/TTFTableNameTestCase.java b/test/java/org/apache/fop/fonts/truetype/TTFTableNameTestCase.java
new file mode 100644
index 000000000..b9066dc2d
--- /dev/null
+++ b/test/java/org/apache/fop/fonts/truetype/TTFTableNameTestCase.java
@@ -0,0 +1,153 @@
+/*
+ * 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.truetype;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * This class tests the enum org.apache.fop.fonts.truetype.TTFTableName
+ *
+ */
+public class TTFTableNameTestCase {
+ /**
+ * Test getName() - tests that the getName() method returns the expected String as expected in
+ * the Directory Table.
+ * @exception IllegalAccessException error
+ */
+ @Test
+ public void testGetName() throws IllegalAccessException {
+ assertEquals("tableDirectory", TTFTableName.TABLE_DIRECTORY.getName());
+ assertEquals("EBDT", TTFTableName.EBDT.getName());
+ assertEquals("EBLC", TTFTableName.EBLC.getName());
+ assertEquals("EBSC", TTFTableName.EBSC.getName());
+ assertEquals("FFTM", TTFTableName.FFTM.getName());
+ assertEquals("GDEF", TTFTableName.GDEF.getName());
+ assertEquals("GPOS", TTFTableName.GPOS.getName());
+ assertEquals("GSUB", TTFTableName.GSUB.getName());
+ assertEquals("LTSH", TTFTableName.LTSH.getName());
+ assertEquals("OS/2", TTFTableName.OS2.getName());
+ assertEquals("PCLT", TTFTableName.PCLT.getName());
+ assertEquals("VDMX", TTFTableName.VDMX.getName());
+ assertEquals("cmap", TTFTableName.CMAP.getName());
+ assertEquals("cvt ", TTFTableName.CVT.getName());
+ assertEquals("fpgm", TTFTableName.FPGM.getName());
+ assertEquals("gasp", TTFTableName.GASP.getName());
+ assertEquals("glyf", TTFTableName.GLYF.getName());
+ assertEquals("hdmx", TTFTableName.HDMX.getName());
+ assertEquals("head", TTFTableName.HEAD.getName());
+ assertEquals("hhea", TTFTableName.HHEA.getName());
+ assertEquals("hmtx", TTFTableName.HMTX.getName());
+ assertEquals("kern", TTFTableName.KERN.getName());
+ assertEquals("loca", TTFTableName.LOCA.getName());
+ assertEquals("maxp", TTFTableName.MAXP.getName());
+ assertEquals("name", TTFTableName.NAME.getName());
+ assertEquals("post", TTFTableName.POST.getName());
+ assertEquals("prep", TTFTableName.PREP.getName());
+ assertEquals("vhea", TTFTableName.VHEA.getName());
+ assertEquals("vmtx", TTFTableName.VMTX.getName());
+ // make sure it works with other table names
+ TTFTableName test = TTFTableName.getValue("test");
+ assertEquals("test", test.getName());
+ }
+
+ /**
+ * Test getValue(String) - tests that the getValue(String) method returns the expected
+ * TTFTableNames value when it is given a String (name of a table).
+ * @exception IllegalAccessException error
+ */
+ @Test
+ public void testGetValue() throws IllegalAccessException {
+ assertEquals(TTFTableName.EBDT, TTFTableName.getValue("EBDT"));
+ assertEquals(TTFTableName.EBLC, TTFTableName.getValue("EBLC"));
+ assertEquals(TTFTableName.EBSC, TTFTableName.getValue("EBSC"));
+ assertEquals(TTFTableName.FFTM, TTFTableName.getValue("FFTM"));
+ assertEquals(TTFTableName.LTSH, TTFTableName.getValue("LTSH"));
+ assertEquals(TTFTableName.OS2, TTFTableName.getValue("OS/2"));
+ assertEquals(TTFTableName.PCLT, TTFTableName.getValue("PCLT"));
+ assertEquals(TTFTableName.VDMX, TTFTableName.getValue("VDMX"));
+ assertEquals(TTFTableName.CMAP, TTFTableName.getValue("cmap"));
+ assertEquals(TTFTableName.CVT, TTFTableName.getValue("cvt "));
+ assertEquals(TTFTableName.FPGM, TTFTableName.getValue("fpgm"));
+ assertEquals(TTFTableName.GASP, TTFTableName.getValue("gasp"));
+ assertEquals(TTFTableName.GLYF, TTFTableName.getValue("glyf"));
+ assertEquals(TTFTableName.HDMX, TTFTableName.getValue("hdmx"));
+ assertEquals(TTFTableName.HEAD, TTFTableName.getValue("head"));
+ assertEquals(TTFTableName.HHEA, TTFTableName.getValue("hhea"));
+ assertEquals(TTFTableName.HMTX, TTFTableName.getValue("hmtx"));
+ assertEquals(TTFTableName.KERN, TTFTableName.getValue("kern"));
+ assertEquals(TTFTableName.LOCA, TTFTableName.getValue("loca"));
+ assertEquals(TTFTableName.MAXP, TTFTableName.getValue("maxp"));
+ assertEquals(TTFTableName.NAME, TTFTableName.getValue("name"));
+ assertEquals(TTFTableName.POST, TTFTableName.getValue("post"));
+ assertEquals(TTFTableName.PREP, TTFTableName.getValue("prep"));
+ assertEquals(TTFTableName.VHEA, TTFTableName.getValue("vhea"));
+ assertEquals(TTFTableName.VMTX, TTFTableName.getValue("vmtx"));
+ // Test that we can store a random table name and it will not fail or throw an error.
+ TTFTableName test = TTFTableName.getValue("random");
+ assertTrue(test instanceof TTFTableName);
+ }
+
+ /**
+ * This class overrides hashCode() - we need to ensure it works properly by instantiating two
+ * objects and comparing their hash-codes.
+ * @exception IllegalAccessException error
+ */
+ @Test
+ public void testHashCode() throws IllegalAccessException {
+ TTFTableName a = TTFTableName.getValue("testObject");
+ TTFTableName b = TTFTableName.getValue("testObject");
+ assertTrue(a.hashCode() == b.hashCode());
+ TTFTableName c = TTFTableName.getValue("fail");
+ assertFalse(a.hashCode() == c.hashCode());
+ }
+
+ /**
+ * This class overrides equals(object) - we need to test:
+ * 1) Reflexivity
+ * 2) Symmetry
+ * 3) Transitivity
+ * 4) Consistency
+ * 5) check it fails if you put in a null value
+ * @throws IllegalAccessException error
+ */
+ @Test
+ public void testEquals() throws IllegalAccessException {
+ // Reflexivity
+ TTFTableName a = TTFTableName.getValue("test");
+ assertTrue(a.equals(a));
+ // Symmetry
+ TTFTableName b = TTFTableName.getValue("test");
+ assertTrue(a.equals(b));
+ assertTrue(b.equals(a));
+ // Transitivity (tested with symmetry)
+ // Consistency (test that a == b is true and that a == c fails)
+ TTFTableName c = TTFTableName.getValue("fail");
+ for (int i = 0; i < 100; i++) {
+ assertTrue(a.equals(b));
+ assertFalse(a.equals(c));
+ }
+ // check with null value
+ assertFalse(a.equals(null));
+ }
+}
diff --git a/test/java/org/apache/fop/render/RawPNGTestUtil.java b/test/java/org/apache/fop/render/RawPNGTestUtil.java
new file mode 100644
index 000000000..e6660bb42
--- /dev/null
+++ b/test/java/org/apache/fop/render/RawPNGTestUtil.java
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.zip.Deflater;
+import java.util.zip.DeflaterOutputStream;
+
+import org.apache.xmlgraphics.image.loader.ImageSize;
+
+public final class RawPNGTestUtil {
+
+ private static int NUM_ROWS = 32;
+ private static int NUM_COLUMNS = 32;
+ private static int DPI = 72;
+
+ private RawPNGTestUtil() {
+
+ }
+
+ /**
+ * Builds a PNG IDAT section for a square of a given color and alpha; the filter is fixed.
+ * @param gray the gray color; set to -1 if using RGB
+ * @param red the red color; ignored if gray > -1
+ * @param green the green color; ignored if gray > -1
+ * @param blue the blue color; ignored if gray > -1
+ * @param alpha the alpha color; set to -1 if not present
+ * @return the PNG IDAT byte array
+ * @throws IOException
+ */
+ public static byte[] buildGRGBAData(int gray, int red, int green, int blue, int alpha) throws IOException {
+ // build an image, 32x32, Gray or RGB, with or without alpha, and with filter
+ int filter = 0;
+ int numRows = NUM_ROWS;
+ int numColumns = NUM_COLUMNS;
+ int numComponents = (gray > -1 ? 1 : 3) + (alpha > -1 ? 1 : 0);
+ int numBytesPerRow = numColumns * numComponents + 1; // 1 for filter
+ int numBytes = numRows * numBytesPerRow;
+ byte[] data = new byte[numBytes];
+ for (int r = 0; r < numRows; r++) {
+ data[r * numBytesPerRow] = (byte) filter;
+ for (int c = 0; c < numColumns; c++) {
+ if (numComponents == 1) {
+ data[r * numBytesPerRow + numComponents * c + 1] = (byte) gray;
+ } else if (numComponents == 2) {
+ data[r * numBytesPerRow + numComponents * c + 1] = (byte) gray;
+ data[r * numBytesPerRow + numComponents * c + 2] = (byte) alpha;
+ } else if (numComponents == 3) {
+ data[r * numBytesPerRow + numComponents * c + 1] = (byte) red;
+ data[r * numBytesPerRow + numComponents * c + 2] = (byte) green;
+ data[r * numBytesPerRow + numComponents * c + 3] = (byte) blue;
+ } else if (numComponents == 4) {
+ data[r * numBytesPerRow + numComponents * c + 1] = (byte) red;
+ data[r * numBytesPerRow + numComponents * c + 2] = (byte) green;
+ data[r * numBytesPerRow + numComponents * c + 3] = (byte) blue;
+ data[r * numBytesPerRow + numComponents * c + 4] = (byte) alpha;
+ }
+ }
+ }
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ DeflaterOutputStream dos = new DeflaterOutputStream(baos, new Deflater());
+ dos.write(data);
+ dos.close();
+ return baos.toByteArray();
+ }
+
+ /**
+ *
+ * @return a default ImageSize
+ */
+ public static ImageSize getImageSize() {
+ return new ImageSize(NUM_ROWS, NUM_COLUMNS, DPI);
+ }
+}
diff --git a/test/java/org/apache/fop/render/pdf/ImageRawPNGAdapterTestCase.java b/test/java/org/apache/fop/render/pdf/ImageRawPNGAdapterTestCase.java
new file mode 100644
index 000000000..885821f66
--- /dev/null
+++ b/test/java/org/apache/fop/render/pdf/ImageRawPNGAdapterTestCase.java
@@ -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.render.pdf;
+
+import java.awt.image.ComponentColorModel;
+import java.awt.image.IndexColorModel;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.zip.Deflater;
+import java.util.zip.DeflaterOutputStream;
+
+import org.junit.Test;
+
+import org.apache.xmlgraphics.image.loader.ImageSize;
+import org.apache.xmlgraphics.image.loader.impl.ImageRawPNG;
+
+import org.apache.fop.pdf.FlateFilter;
+import org.apache.fop.pdf.PDFAMode;
+import org.apache.fop.pdf.PDFDocument;
+import org.apache.fop.pdf.PDFProfile;
+import org.apache.fop.render.RawPNGTestUtil;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class ImageRawPNGAdapterTestCase {
+
+ @Test
+ public void testSetupWithIndexColorModel() {
+ IndexColorModel cm = mock(IndexColorModel.class);
+ ImageRawPNG irpng = mock(ImageRawPNG.class);
+ PDFDocument doc = mock(PDFDocument.class);
+ PDFProfile profile = mock(PDFProfile.class);
+ ImageRawPNGAdapter irpnga = new ImageRawPNGAdapter(irpng, "mock");
+ ImageSize is = RawPNGTestUtil.getImageSize();
+
+ when(irpng.getColorModel()).thenReturn(cm);
+ // when(cm.hasAlpha()).thenReturn(false);
+ when(doc.getProfile()).thenReturn(profile);
+ when(profile.getPDFAMode()).thenReturn(PDFAMode.PDFA_1A);
+ when(irpng.getSize()).thenReturn(is);
+ irpnga.setup(doc);
+ FlateFilter filter = (FlateFilter) irpnga.getPDFFilter();
+ assertEquals(1, filter.getColors());
+ }
+
+ @Test
+ public void testSetupWithComponentColorModel() throws IOException {
+ ComponentColorModel cm = mock(ComponentColorModel.class);
+ ImageRawPNG irpng = mock(ImageRawPNG.class);
+ PDFDocument doc = mock(PDFDocument.class);
+ PDFProfile profile = mock(PDFProfile.class);
+ ImageRawPNGAdapter irpnga = new ImageRawPNGAdapter(irpng, "mock");
+ ImageSize is = RawPNGTestUtil.getImageSize();
+
+ when(irpng.getColorModel()).thenReturn(cm);
+ when(cm.getNumComponents()).thenReturn(3);
+ // when(cm.hasAlpha()).thenReturn(false);
+ when(doc.getProfile()).thenReturn(profile);
+ when(profile.getPDFAMode()).thenReturn(PDFAMode.PDFA_1A);
+ when(irpng.getSize()).thenReturn(is);
+ irpnga.setup(doc);
+ FlateFilter filter = (FlateFilter) irpnga.getPDFFilter();
+ assertEquals(3, filter.getColors());
+ }
+
+ @Test
+ public void testOutputContentsWithRGBPNG() throws IOException {
+ testOutputContentsWithGRGBAPNG(-1, 128, 128, 128, -1);
+ }
+
+ @Test
+ public void testOutputContentsWithRGBAPNG() throws IOException {
+ testOutputContentsWithGRGBAPNG(-1, 128, 128, 128, 128);
+ }
+
+ @Test
+ public void testOutputContentsWithGPNG() throws IOException {
+ testOutputContentsWithGRGBAPNG(128, -1, -1, -1, -1);
+ }
+
+ @Test
+ public void testOutputContentsWithGAPNG() throws IOException {
+ testOutputContentsWithGRGBAPNG(128, -1, -1, -1, 128);
+ }
+
+ private void testOutputContentsWithGRGBAPNG(int gray, int red, int green, int blue, int alpha)
+ throws IOException {
+ int numColorComponents = gray > -1 ? 1 : 3;
+ int numComponents = numColorComponents + (alpha > -1 ? 1 : 0);
+ ComponentColorModel cm = mock(ComponentColorModel.class);
+ ImageRawPNG irpng = mock(ImageRawPNG.class);
+ PDFDocument doc = mock(PDFDocument.class);
+ PDFProfile profile = mock(PDFProfile.class);
+ ImageRawPNGAdapter irpnga = new ImageRawPNGAdapter(irpng, "mock");
+ ImageSize is = RawPNGTestUtil.getImageSize();
+
+ when(irpng.getColorModel()).thenReturn(cm);
+ when(cm.getNumComponents()).thenReturn(numComponents);
+ // when(cm.hasAlpha()).thenReturn(false);
+ when(doc.getProfile()).thenReturn(profile);
+ when(profile.getPDFAMode()).thenReturn(PDFAMode.PDFA_1A);
+ when(irpng.getSize()).thenReturn(is);
+ irpnga.setup(doc);
+ FlateFilter filter = (FlateFilter) irpnga.getPDFFilter();
+ assertEquals(numColorComponents, filter.getColors());
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ byte[] data = RawPNGTestUtil.buildGRGBAData(gray, red, green, blue, alpha);
+ ByteArrayInputStream bais = new ByteArrayInputStream(data);
+ when(irpng.createInputStream()).thenReturn(bais);
+ irpnga.outputContents(baos);
+ if (alpha > -1) {
+ byte[] expected = RawPNGTestUtil.buildGRGBAData(gray, red, green, blue, -1);
+ assertArrayEquals(expected, baos.toByteArray());
+ } else {
+ assertArrayEquals(data, baos.toByteArray());
+ }
+ }
+
+}
diff --git a/test/java/org/apache/fop/render/ps/ImageEncoderPNGTestCase.java b/test/java/org/apache/fop/render/ps/ImageEncoderPNGTestCase.java
new file mode 100644
index 000000000..458033dad
--- /dev/null
+++ b/test/java/org/apache/fop/render/ps/ImageEncoderPNGTestCase.java
@@ -0,0 +1,133 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.ps;
+
+import java.awt.image.ComponentColorModel;
+import java.awt.image.IndexColorModel;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import org.junit.Test;
+
+import org.apache.xmlgraphics.image.loader.ImageSize;
+import org.apache.xmlgraphics.image.loader.impl.ImageRawPNG;
+
+import org.apache.fop.render.RawPNGTestUtil;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class ImageEncoderPNGTestCase {
+
+ @Test
+ public void testWriteToWithRGBPNG() throws IOException {
+ testWriteToWithGRGBAPNG(-1, 128, 128, 128, -1);
+ }
+
+ @Test
+ public void testWriteToWithGPNG() throws IOException {
+ testWriteToWithGRGBAPNG(128, -1, -1, -1, -1);
+ }
+
+ @Test
+ public void testWriteToWithRGBAPNG() throws IOException {
+ testWriteToWithGRGBAPNG(-1, 128, 128, 128, 128);
+ }
+
+ @Test
+ public void testWriteToWithGAPNG() throws IOException {
+ testWriteToWithGRGBAPNG(128, -1, -1, -1, 128);
+ }
+
+ private void testWriteToWithGRGBAPNG(int gray, int red, int green, int blue, int alpha)
+ throws IOException {
+ int numComponents = (gray > -1 ? 1 : 3) + (alpha > -1 ? 1 : 0);
+ ImageSize is = RawPNGTestUtil.getImageSize();
+ ComponentColorModel cm = mock(ComponentColorModel.class);
+ when(cm.getNumComponents()).thenReturn(numComponents);
+ ImageRawPNG irpng = mock(ImageRawPNG.class);
+ when(irpng.getColorModel()).thenReturn(cm);
+ when(irpng.getSize()).thenReturn(is);
+ ImageEncoderPNG iepng = new ImageEncoderPNG(irpng);
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ byte[] data = RawPNGTestUtil.buildGRGBAData(gray, red, green, blue, alpha);
+ ByteArrayInputStream bais = new ByteArrayInputStream(data);
+ when(irpng.createInputStream()).thenReturn(bais);
+ iepng.writeTo(baos);
+ if (alpha > -1) {
+ byte[] expected = RawPNGTestUtil.buildGRGBAData(gray, red, green, blue, -1);
+ assertArrayEquals(expected, baos.toByteArray());
+ } else {
+ assertArrayEquals(data, baos.toByteArray());
+ }
+ }
+
+ @Test
+ public void testWriteToWithPalettePNG() throws IOException {
+ ImageSize is = RawPNGTestUtil.getImageSize();
+ IndexColorModel cm = mock(IndexColorModel.class);
+ ImageRawPNG irpng = mock(ImageRawPNG.class);
+ when(irpng.getColorModel()).thenReturn(cm);
+ when(irpng.getSize()).thenReturn(is);
+ ImageEncoderPNG iepng = new ImageEncoderPNG(irpng);
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ byte[] data = RawPNGTestUtil.buildGRGBAData(128, -1, -1, -1, -1);
+ ByteArrayInputStream bais = new ByteArrayInputStream(data);
+ when(irpng.createInputStream()).thenReturn(bais);
+ iepng.writeTo(baos);
+ assertArrayEquals(data, baos.toByteArray());
+ }
+
+ @Test
+ public void testGetImplicitFilterWithIndexColorModel() {
+ ImageSize is = RawPNGTestUtil.getImageSize();
+ IndexColorModel cm = mock(IndexColorModel.class);
+ ImageRawPNG irpng = mock(ImageRawPNG.class);
+ when(irpng.getColorModel()).thenReturn(cm);
+ when(irpng.getBitDepth()).thenReturn(8);
+ when(irpng.getSize()).thenReturn(is);
+ ImageEncoderPNG iepng = new ImageEncoderPNG(irpng);
+
+ String expectedFilter = "<< /Predictor 15 /Columns 32 /Colors 1 /BitsPerComponent 8 >> /FlateDecode";
+ assertEquals(expectedFilter, iepng.getImplicitFilter());
+ }
+
+ @Test
+ public void testGetImplicitFilterWithComponentColorModel() {
+ ImageSize is = RawPNGTestUtil.getImageSize();
+ ComponentColorModel cm = mock(ComponentColorModel.class);
+ when(cm.getNumComponents()).thenReturn(3);
+ ImageRawPNG irpng = mock(ImageRawPNG.class);
+ when(irpng.getColorModel()).thenReturn(cm);
+ when(irpng.getBitDepth()).thenReturn(8);
+ when(irpng.getSize()).thenReturn(is);
+ ImageEncoderPNG iepng = new ImageEncoderPNG(irpng);
+
+ String expectedFilter = "<< /Predictor 15 /Columns 32 /Colors 3 /BitsPerComponent 8 >> /FlateDecode";
+ assertEquals(expectedFilter, iepng.getImplicitFilter());
+ }
+
+}
diff --git a/test/java/org/apache/fop/render/ps/PSRenderingUtilTestCase.java b/test/java/org/apache/fop/render/ps/PSRenderingUtilTestCase.java
new file mode 100644
index 000000000..3f1088fe1
--- /dev/null
+++ b/test/java/org/apache/fop/render/ps/PSRenderingUtilTestCase.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.ps;
+
+import java.io.IOException;
+
+import org.junit.Test;
+
+import org.apache.xmlgraphics.ps.PSGenerator;
+
+import org.apache.fop.render.ps.extensions.PSPageTrailerCodeBefore;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+public class PSRenderingUtilTestCase {
+
+ private final String content = "<< /MyEntry 0 >> command";
+ private final PSPageTrailerCodeBefore ptcb = new PSPageTrailerCodeBefore(content);
+ private final PSGenerator gen = mock(PSGenerator.class);
+
+ @Test
+ public void testWriteEnclosedExtensionAttachment() throws IOException {
+ PSRenderingUtil.writeEnclosedExtensionAttachment(gen, ptcb);
+ verify(gen).writeln(content);
+ }
+
+}
diff --git a/test/java/org/apache/fop/render/ps/RenderPSTestSuite.java b/test/java/org/apache/fop/render/ps/RenderPSTestSuite.java
new file mode 100644
index 000000000..2e15bf91f
--- /dev/null
+++ b/test/java/org/apache/fop/render/ps/RenderPSTestSuite.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.ps;
+
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+import org.junit.runners.Suite.SuiteClasses;
+
+import org.apache.fop.render.ps.fonts.PSTTFGeneratorTestCase;
+import org.apache.fop.render.ps.fonts.PSTTFGlyphOutputStreamTestCase;
+import org.apache.fop.render.ps.fonts.PSTTFOutputStreamTestCase;
+import org.apache.fop.render.ps.fonts.PSTTFTableOutputStreamTestCase;
+
+
+/**
+ * A test Suite for org.apache.fop.render.ps.*
+ */
+@RunWith(Suite.class)
+@SuiteClasses({
+ PSTTFGeneratorTestCase.class,
+ PSTTFOutputStreamTestCase.class,
+ PSTTFGlyphOutputStreamTestCase.class,
+ PSTTFTableOutputStreamTestCase.class
+})
+public final class RenderPSTestSuite {
+}
diff --git a/test/java/org/apache/fop/render/ps/fonts/PSTTFGeneratorTestCase.java b/test/java/org/apache/fop/render/ps/fonts/PSTTFGeneratorTestCase.java
new file mode 100644
index 000000000..f7f311ff8
--- /dev/null
+++ b/test/java/org/apache/fop/render/ps/fonts/PSTTFGeneratorTestCase.java
@@ -0,0 +1,120 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.ps.fonts;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import org.apache.xmlgraphics.ps.PSGenerator;
+
+/**
+ * The test class for org.apache.fop.render.ps.fonts.PSGenerator
+ */
+public class PSTTFGeneratorTestCase {
+ private PSTTFGenerator ttfGen;
+ private ByteArrayOutputStream out = new ByteArrayOutputStream();
+ private PSGenerator gen = new PSGenerator(out);
+ private byte[] byteArray;
+
+ /**
+ * Constructor
+ */
+ public PSTTFGeneratorTestCase() {
+ byteArray = new byte[65536];
+ for (int i = 0; i < 65536; i++) {
+ byteArray[i] = (byte) i;
+ }
+ }
+
+ @Before
+ public void setUp() {
+ ttfGen = new PSTTFGenerator(gen);
+ }
+
+ /**
+ * Tests startString() - starts the string in an appropriate way for a PostScript file.
+ * @exception IOException write error
+ */
+ @Test
+ public void testStartString() throws IOException {
+ ttfGen.startString();
+ assertEquals("<\n", out.toString());
+ }
+
+ /**
+ * Test streamBytes() - tests that strings are written to file in the proper format.
+ * @throws IOException write error.
+ */
+ @Test
+ public void testStreamBytes() throws IOException {
+ ttfGen.streamBytes(byteArray, 0, 16);
+ assertEquals("000102030405060708090A0B0C0D0E0F", out.toString());
+ /*
+ * 65520 is the closes multiple of 80 to 65535 (max string size in PS document) and since
+ * one byte takes up two characters, 65520 / 2 - 16 (16 bytes already written)= 32744.
+ */
+ ttfGen.streamBytes(byteArray, 0, 32744);
+ // Using a regex to ensure that the format is correct
+ assertTrue(out.toString().matches("([0-9A-F]{80}\n){819}"));
+ try {
+ ttfGen.streamBytes(byteArray, 0, PSTTFGenerator.MAX_BUFFER_SIZE + 1);
+ fail("Shouldn't be able to write more than MAX_BUFFER_SIZE to a PS document");
+ } catch (UnsupportedOperationException e) {
+ // PASS
+ }
+ }
+
+ /**
+ * Test reset() - reset should reset the line counter such that when reset() is invoked the
+ * following string streamed to the PS document should be 80 chars long.
+ * @throws IOException file write error.
+ */
+ @Test
+ public void testReset() throws IOException {
+ ttfGen.streamBytes(byteArray, 0, 40);
+ assertTrue(out.toString().matches("([0-9A-F]{80}\n)"));
+ ttfGen.streamBytes(byteArray, 0, 40);
+ assertTrue(out.toString().matches("([0-9A-F]{80}\n){2}"));
+
+ }
+
+ /**
+ * Test endString() - ensures strings are ended in the PostScript document in the correct
+ * format, a "00" needs to be appended to the end of a string.
+ * @throws IOException file write error
+ */
+ @Test
+ public void testEndString() throws IOException {
+ ttfGen.endString();
+ assertEquals("00\n> ", out.toString());
+ out.reset();
+ // we need to check that this doesn't write more than 80 chars per line
+ ttfGen.streamBytes(byteArray, 0, 40);
+ ttfGen.endString();
+ assertTrue(out.toString().matches("([0-9A-F]{80}\n)00\n> "));
+ }
+}
diff --git a/test/java/org/apache/fop/render/ps/fonts/PSTTFGlyphOutputStreamTestCase.java b/test/java/org/apache/fop/render/ps/fonts/PSTTFGlyphOutputStreamTestCase.java
new file mode 100644
index 000000000..82b4364c3
--- /dev/null
+++ b/test/java/org/apache/fop/render/ps/fonts/PSTTFGlyphOutputStreamTestCase.java
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.ps.fonts;
+
+import java.io.IOException;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.InOrder;
+
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+/**
+ * Test class for PSTTFGlyphOutputStream
+ */
+public class PSTTFGlyphOutputStreamTestCase {
+ private PSTTFGenerator mockGen;
+ private PSTTFGlyphOutputStream glyphOut;
+
+ @Before
+ public void setUp() {
+ mockGen = mock(PSTTFGenerator.class);
+ glyphOut = new PSTTFGlyphOutputStream(mockGen);
+ }
+
+ /**
+ * Test startGlyphStream() - test that startGlyphStream() invokes reset() and startString() in
+ * PSTTFGenerator.
+ * @exception IOException file write error
+ */
+ @Test
+ public void testStartGlyphStream() throws IOException {
+ glyphOut.startGlyphStream();
+ verify(mockGen).startString();
+ }
+
+ /**
+ * Test streamGlyph(byte[],int,int) - tests several paths:
+ * 1) strings are properly appended
+ * 2) when total strings size > PSTTFGenerator.MAX_BUFFER_SIZE, the strings is closed and a new
+ * strings is started.
+ * 3) if a glyph of size > PSTTFGenerator.MAX_BUFFER_SIZE is attempted, an exception is thrown.
+ * @throws IOException file write error.
+ */
+ @Test
+ public void testStreamGlyph() throws IOException {
+ int byteArraySize = 10;
+ byte[] byteArray = new byte[byteArraySize];
+ int runs = 100;
+ for (int i = 0; i < runs; i++) {
+ glyphOut.streamGlyph(byteArray, 0, byteArraySize);
+ }
+ verify(mockGen, times(runs)).streamBytes(byteArray, 0, byteArraySize);
+
+ /*
+ * We want to run this for MAX_BUFFER_SIZE / byteArraySize so that go over the string
+ * boundary and enforce the ending and starting of a new string. Using mockito to ensure
+ * that this behaviour is performed in order (since this is an integral behavioural aspect)
+ */
+ int stringLimit = PSTTFGenerator.MAX_BUFFER_SIZE / byteArraySize;
+ for (int i = 0; i < stringLimit; i++) {
+ glyphOut.streamGlyph(byteArray, 0, byteArraySize);
+ }
+ InOrder inOrder = inOrder(mockGen);
+ inOrder.verify(mockGen, times(stringLimit)).streamBytes(byteArray, 0, byteArraySize);
+ inOrder.verify(mockGen).endString();
+ inOrder.verify(mockGen).startString();
+ inOrder.verify(mockGen, times(runs)).streamBytes(byteArray, 0, byteArraySize);
+
+ try {
+ glyphOut.streamGlyph(byteArray, 0, PSTTFGenerator.MAX_BUFFER_SIZE + 1);
+ fail("Shouldn't allow a length > PSTTFGenerator.MAX_BUFFER_SIZE");
+ } catch (UnsupportedOperationException e) {
+ // PASS
+ }
+ }
+
+ /**
+ * Test endGlyphStream() - tests that PSTTFGenerator.endString() is invoked when this method
+ * is called.
+ * @throws IOException file write exception
+ */
+ @Test
+ public void testEndGlyphStream() throws IOException {
+ glyphOut.endGlyphStream();
+ verify(mockGen).endString();
+ }
+}
diff --git a/test/java/org/apache/fop/render/ps/fonts/PSTTFOutputStreamTestCase.java b/test/java/org/apache/fop/render/ps/fonts/PSTTFOutputStreamTestCase.java
new file mode 100644
index 000000000..744f17f64
--- /dev/null
+++ b/test/java/org/apache/fop/render/ps/fonts/PSTTFOutputStreamTestCase.java
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.ps.fonts;
+
+import java.io.IOException;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import org.apache.xmlgraphics.ps.PSGenerator;
+
+import org.apache.fop.fonts.truetype.TTFGlyphOutputStream;
+import org.apache.fop.fonts.truetype.TTFTableOutputStream;
+
+/**
+ * Tests PSTTFOuputStream
+ */
+public class PSTTFOutputStreamTestCase {
+ private PSGenerator gen;
+ private PSTTFOutputStream out;
+
+ /**
+ * Assigns an OutputStream to the PSGenerator.
+ */
+ @Before
+ public void setUp() {
+ gen = mock(PSGenerator.class);
+ out = new PSTTFOutputStream(gen);
+ }
+
+ /**
+ * Test startFontStream() - Just tests that the font is properly initiated in the PostScript
+ * document (in this case with "/sfnts[")
+ * @throws IOException write exception.
+ */
+ @Test
+ public void testStartFontStream() throws IOException {
+ out.startFontStream();
+ verify(gen).write("/sfnts[");
+ }
+
+ /**
+ * Test getTableOutputStream() - we need to test that the inheritance model is properly obeyed.
+ */
+ @Test
+ public void testGetTableOutputStream() {
+ TTFTableOutputStream tableOut = out.getTableOutputStream();
+ assertTrue(tableOut instanceof PSTTFTableOutputStream);
+ }
+
+ /**
+ * Test getGlyphOutputStream() - we need to test that the inheritance model is properly obeyed.
+ */
+ @Test
+ public void testGetGlyphOutputStream() {
+ TTFGlyphOutputStream glyphOut = out.getGlyphOutputStream();
+ assertTrue(glyphOut instanceof PSTTFGlyphOutputStream);
+ }
+
+ /**
+ * Test endFontStream()
+ * @exception IOException write error.
+ */
+ @Test
+ public void testEndFontStream() throws IOException {
+ out.endFontStream();
+ verify(gen).writeln("] def");
+ }
+}
diff --git a/test/java/org/apache/fop/render/ps/fonts/PSTTFTableOutputStreamTestCase.java b/test/java/org/apache/fop/render/ps/fonts/PSTTFTableOutputStreamTestCase.java
new file mode 100644
index 000000000..c20c3d8b1
--- /dev/null
+++ b/test/java/org/apache/fop/render/ps/fonts/PSTTFTableOutputStreamTestCase.java
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.ps.fonts;
+
+import java.io.IOException;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.InOrder;
+
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
+
+/**
+ * Test class for unit testing PSTTFTableOutputStream
+ */
+public class PSTTFTableOutputStreamTestCase {
+ private PSTTFGenerator mockGen;
+ private PSTTFTableOutputStream tableOut;
+
+ @Before
+ public void setUp() {
+ mockGen = mock(PSTTFGenerator.class);
+ tableOut = new PSTTFTableOutputStream(mockGen);
+ }
+
+ /**
+ * Test streamTable() - several paths to test (2. and 3. test corner cases):
+ * 1) that a table of length < PSTTFGenerator.MAX_BUFFER_SIZE invokes the correct methods in
+ * PSTTFGenerator.
+ * 2) that a table of length > PSTTFGenerator.MAX_BUFFER_SIZE and
+ * length == n * PSTTFGenerator.MAX_BUFFER_SIZE is split up and the methods in PSTTFGenerator
+ * are invoked.
+ * 3) that a table of length > PSTTFGenerator.MAX_BUFFER_SIZE but
+ * length != n * PSTTFGenerator.MAX_BUFFER_SIZE is split up and the methods in PSTTFGenerator
+ * are invoked.
+ * @throws IOException file write error.
+ */
+ @Test
+ public void testStreamTable() throws IOException {
+ byte[] byteArray = new byte[PSTTFGenerator.MAX_BUFFER_SIZE * 3];
+ tableOut.streamTable(byteArray, 0, 10);
+ InOrder inOrder = inOrder(mockGen);
+ inOrder.verify(mockGen).startString();
+ inOrder.verify(mockGen).streamBytes(byteArray, 0, 10);
+ inOrder.verify(mockGen).endString();
+
+ setUp(); // reset all all the method calls
+ /* We're going to run this 3 times to ensure the proper method calls are invoked and all
+ * the bytes are streamed */
+ tableOut.streamTable(byteArray, 0, byteArray.length);
+ inOrder = inOrder(mockGen);
+ for (int i = 0; i < 3; i++) {
+ int offset = PSTTFGenerator.MAX_BUFFER_SIZE * i;
+ inOrder.verify(mockGen).startString();
+ inOrder.verify(mockGen).streamBytes(byteArray, offset, PSTTFGenerator.MAX_BUFFER_SIZE);
+ inOrder.verify(mockGen).endString();
+ }
+
+ setUp(); // reset all the method calls
+ tableOut.streamTable(byteArray, 0, PSTTFGenerator.MAX_BUFFER_SIZE + 1);
+ inOrder = inOrder(mockGen);
+ inOrder.verify(mockGen).startString();
+ inOrder.verify(mockGen).streamBytes(byteArray, 0, PSTTFGenerator.MAX_BUFFER_SIZE);
+ inOrder.verify(mockGen).endString();
+ inOrder.verify(mockGen).startString();
+ inOrder.verify(mockGen).streamBytes(byteArray, PSTTFGenerator.MAX_BUFFER_SIZE, 1);
+ inOrder.verify(mockGen).endString();
+ }
+}
diff --git a/test/java/org/apache/fop/svg/PDFGraphics2DTestCase.java b/test/java/org/apache/fop/svg/PDFGraphics2DTestCase.java
new file mode 100644
index 000000000..3f610c56f
--- /dev/null
+++ b/test/java/org/apache/fop/svg/PDFGraphics2DTestCase.java
@@ -0,0 +1,48 @@
+/*
+ * 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.svg;
+
+import java.awt.BasicStroke;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertTrue;
+
+public class PDFGraphics2DTestCase {
+
+ @Test
+ public void testApplyStrokeNullDash() {
+ PDFGraphics2D g2d = new PDFGraphics2D(false);
+ BasicStroke stroke = new BasicStroke();
+ g2d.applyStroke(stroke);
+ assertTrue(g2d.getString().contains("[] 0 d\n"));
+ }
+
+ @Test
+ public void testApplyStrokeNonNullDash() {
+ PDFGraphics2D g2d = new PDFGraphics2D(false);
+ float[] dashArray = {3.0f, 5.0f};
+ BasicStroke stroke = new BasicStroke(1.0f, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER, 10.0f,
+ dashArray, 0.0f);
+ g2d.applyStroke(stroke);
+ assertTrue(g2d.getString().contains("[3 5] 0 d\n"));
+ }
+
+}
diff --git a/test/java/org/apache/fop/util/HexEncoderTestCase.java b/test/java/org/apache/fop/util/HexEncoderTestCase.java
new file mode 100644
index 000000000..cb366abdf
--- /dev/null
+++ b/test/java/org/apache/fop/util/HexEncoderTestCase.java
@@ -0,0 +1,61 @@
+/*
+ * 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.util;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Test case for the conversion of characters into hex-encoded strings.
+ */
+public class HexEncoderTestCase {
+
+ /**
+ * Tests that characters are properly encoded into hex strings.
+ */
+ @Test
+ public void testEncodeChar() {
+ char[] digits = new char[] {'0', '0', '0', '0'};
+ for (int c = 0; c <= 0xFFFF; c++) {
+ assertEquals(new String(digits), HexEncoder.encode((char) c));
+ increment(digits);
+ }
+ }
+
+ private static void increment(char[] digits) {
+ int d = 4;
+ do {
+ d--;
+ digits[d] = successor(digits[d]);
+ } while (digits[d] == '0' && d > 0);
+ }
+
+ private static char successor(char d) {
+ if (d == '9') {
+ return 'A';
+ } else if (d == 'F') {
+ return '0';
+ } else {
+ return (char) (d + 1);
+ }
+ }
+
+}
diff --git a/test/layoutengine/standard-testcases/bidi_propagation_1.xml b/test/layoutengine/standard-testcases/bidi_propagation_1.xml
index 947d9dac5..587bc6d2f 100644
--- a/test/layoutengine/standard-testcases/bidi_propagation_1.xml
+++ b/test/layoutengine/standard-testcases/bidi_propagation_1.xml
@@ -66,7 +66,7 @@
<fo:table-cell margin-left="0" text-align="right">
<fo:block>
<fo:inline>
- <fo:inline><fo:bidi-override unicode-bidi="bidi-override" direction="rtl">X</fo:bidi-override></fo:inline>
+ <fo:inline><fo:bidi-override unicode-bidi="bidi-override" direction="rtl">XY</fo:bidi-override></fo:inline>
</fo:inline>
</fo:block>
</fo:table-cell>
@@ -93,7 +93,7 @@
<fo:table-cell margin-left="0" text-align="right">
<fo:block>
<fo:inline>
- <fo:inline><fo:bidi-override unicode-bidi="bidi-override" direction="rtl">X</fo:bidi-override></fo:inline>
+ <fo:inline><fo:bidi-override unicode-bidi="bidi-override" direction="rtl">XY</fo:bidi-override></fo:inline>
</fo:inline>
</fo:block>
</fo:table-cell>
diff --git a/test/layoutengine/standard-testcases/character_writing-mode_rl.xml b/test/layoutengine/standard-testcases/character_writing-mode_rl.xml
index adc126ec8..db65238c0 100644
--- a/test/layoutengine/standard-testcases/character_writing-mode_rl.xml
+++ b/test/layoutengine/standard-testcases/character_writing-mode_rl.xml
@@ -59,18 +59,18 @@
<eval expected="6672" xpath="//flow/block[2]/block[1]/block[1]/lineArea/inlineparent/text[1]/@ipd"/>
<eval expected="4" xpath="//flow/block[2]/block[1]/block[1]/lineArea/inlineparent/text[1]/word[1]"/>
<eval expected="3" xpath="//flow/block[2]/block[1]/block[1]/lineArea/inlineparent/text[1]/word[1]/@level"/>
- <true xpath="boolean(//flow/block[2]/block[1]/block[1]/lineArea/inlineparent/text[1]/word[1]/@reversed)"/>
+ <eval expected="" xpath="//flow/block[2]/block[1]/block[1]/lineArea/inlineparent/text[1]/word[1]/@reversed"/>
<eval expected="6672" xpath="//flow/block[2]/block[1]/block[1]/lineArea/inlineparent/text[2]/@ipd"/>
<eval expected="3" xpath="//flow/block[2]/block[1]/block[1]/lineArea/inlineparent/text[2]/word[1]"/>
- <eval expected="3" xpath="//flow/block[2]/block[1]/block[1]/lineArea/inlineparent/text[1]/word[1]/@level"/>
- <true xpath="boolean(//flow/block[2]/block[1]/block[1]/lineArea/inlineparent/text[1]/word[1]/@reversed)"/>
+ <eval expected="3" xpath="//flow/block[2]/block[1]/block[1]/lineArea/inlineparent/text[2]/word[1]/@level"/>
+ <eval expected="" xpath="//flow/block[2]/block[1]/block[1]/lineArea/inlineparent/text[2]/word[1]/@reversed"/>
<eval expected="6672" xpath="//flow/block[2]/block[1]/block[1]/lineArea/inlineparent/text[3]/@ipd"/>
<eval expected="2" xpath="//flow/block[2]/block[1]/block[1]/lineArea/inlineparent/text[3]/word[1]"/>
<eval expected="3" xpath="//flow/block[2]/block[1]/block[1]/lineArea/inlineparent/text[3]/word[1]/@level"/>
- <true xpath="boolean(//flow/block[2]/block[1]/block[1]/lineArea/inlineparent/text[3]/word[1]/@reversed)"/>
+ <eval expected="" xpath="//flow/block[2]/block[1]/block[1]/lineArea/inlineparent/text[3]/word[1]/@reversed"/>
<eval expected="6672" xpath="//flow/block[2]/block[1]/block[1]/lineArea/inlineparent/text[4]/@ipd"/>
<eval expected="1" xpath="//flow/block[2]/block[1]/block[1]/lineArea/inlineparent/text[4]/word[1]"/>
<eval expected="3" xpath="//flow/block[2]/block[1]/block[1]/lineArea/inlineparent/text[4]/word[1]/@level"/>
- <true xpath="boolean(//flow/block[2]/block[1]/block[1]/lineArea/inlineparent/text[4]/word[1]/@reversed)"/>
+ <eval expected="" xpath="//flow/block[2]/block[1]/block[1]/lineArea/inlineparent/text[4]/word[1]/@reversed"/>
</checks>
</testcase>
diff --git a/test/pdf/accessibility/pdf/role.pdf b/test/pdf/accessibility/pdf/role.pdf
index 38fcf1bde..4dfb686c9 100644
--- a/test/pdf/accessibility/pdf/role.pdf
+++ b/test/pdf/accessibility/pdf/role.pdf
Binary files differ
diff --git a/test/pdf/accessibility/role.fo b/test/pdf/accessibility/role.fo
index 885638592..b94c6ab67 100644
--- a/test/pdf/accessibility/role.fo
+++ b/test/pdf/accessibility/role.fo
@@ -24,8 +24,8 @@
<fo:region-after extent="10pt"/>
</fo:simple-page-master>
</fo:layout-master-set>
- <fo:page-sequence master-reference="page" role="Art">
- <fo:static-content flow-name="xsl-region-after" role="NonStruct" font-size="8pt">
+ <fo:page-sequence master-reference="page" language="en" country="GB" role="Art">
+ <fo:static-content flow-name="xsl-region-after" role="artifact" font-size="8pt">
<fo:block text-align="center"><fo:page-number/></fo:block>
</fo:static-content>
<fo:flow flow-name="xsl-region-body" role="NonStruct" hyphenate="true"
diff --git a/test/resources/fonts/ttf/DroidSansMono.LICENSE b/test/resources/fonts/ttf/DroidSansMono.LICENSE
new file mode 100644
index 000000000..1a96dfde6
--- /dev/null
+++ b/test/resources/fonts/ttf/DroidSansMono.LICENSE
@@ -0,0 +1,18 @@
+Copyright (C) 2008 The Android Open Source Project
+
+Licensed 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.
+
+##########
+
+This directory contains the fonts for the platform. They are licensed
+under the Apache 2 license.
diff --git a/test/resources/fonts/ttf/DroidSansMono.ttf b/test/resources/fonts/ttf/DroidSansMono.ttf
new file mode 100644
index 000000000..4546611d4
--- /dev/null
+++ b/test/resources/fonts/ttf/DroidSansMono.ttf
Binary files differ