diff options
author | Glenn Adams <gadams@apache.org> | 2012-07-05 20:29:53 +0000 |
---|---|---|
committer | Glenn Adams <gadams@apache.org> | 2012-07-05 20:29:53 +0000 |
commit | e2ef36f857d24816dbc856792fdb811778bf8df6 (patch) | |
tree | 357409807d667bd7e36b662aef400d195aed0048 | |
parent | ec0cb9611cdc2faf759010395f0a4dbe328c6994 (diff) | |
parent | 015538e0f11f031e3d7bd05db8c29e2a40365678 (diff) | |
download | xmlgraphics-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
174 files changed, 5889 insertions, 1596 deletions
@@ -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="10cm", + inline-progression-dimension.maximum="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 Binary files differindex d8c5e0989..be61b0b6e 100644 --- a/lib/xmlgraphics-commons-1.5svn.jar +++ b/lib/xmlgraphics-commons-1.5svn.jar 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 Binary files differdeleted file mode 100644 index 9e5393e1e..000000000 --- a/src/documentation/content/xdocs/fo/align.fo.pdf +++ /dev/null diff --git a/src/documentation/content/xdocs/fo/align.pdf b/src/documentation/content/xdocs/fo/align.pdf Binary files differnew file mode 100644 index 000000000..4b9413b1c --- /dev/null +++ b/src/documentation/content/xdocs/fo/align.pdf 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 Binary files differdeleted file mode 100644 index f5a02666b..000000000 --- a/src/documentation/content/xdocs/fo/align2.fo.pdf +++ /dev/null diff --git a/src/documentation/content/xdocs/fo/align2.pdf b/src/documentation/content/xdocs/fo/align2.pdf Binary files differnew file mode 100644 index 000000000..01b8d1bfb --- /dev/null +++ b/src/documentation/content/xdocs/fo/align2.pdf 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 Binary files differnew file mode 100644 index 000000000..59dec7763 --- /dev/null +++ b/src/documentation/content/xdocs/fo/embed.pdf 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^Tu<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 Binary files differnew file mode 100644 index 000000000..5c84a41b2 --- /dev/null +++ b/src/documentation/content/xdocs/fo/fonts.pdf 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 Binary files differdeleted file mode 100644 index 177160580..000000000 --- a/src/documentation/content/xdocs/fo/size.fo.pdf +++ /dev/null diff --git a/src/documentation/content/xdocs/fo/size.pdf b/src/documentation/content/xdocs/fo/size.pdf Binary files differnew file mode 100644 index 000000000..add8fe871 --- /dev/null +++ b/src/documentation/content/xdocs/fo/size.pdf 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&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 '<' 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 Binary files differindex 38fcf1bde..4dfb686c9 100644 --- a/test/pdf/accessibility/pdf/role.pdf +++ b/test/pdf/accessibility/pdf/role.pdf 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 Binary files differnew file mode 100644 index 000000000..4546611d4 --- /dev/null +++ b/test/resources/fonts/ttf/DroidSansMono.ttf |