aboutsummaryrefslogtreecommitdiffstats
path: root/src/java
diff options
context:
space:
mode:
Diffstat (limited to 'src/java')
-rw-r--r--src/java/META-INF/services/org.apache.fop.util.ContentHandlerFactory1
-rw-r--r--src/java/org/apache/fop/Version.java5
-rw-r--r--src/java/org/apache/fop/apps/FOUserAgent.java2
-rw-r--r--src/java/org/apache/fop/apps/FopConfParser.java53
-rw-r--r--src/java/org/apache/fop/area/AreaTreeParser.java19
-rw-r--r--src/java/org/apache/fop/area/Footnote.java2
-rw-r--r--src/java/org/apache/fop/area/Trait.java5
-rw-r--r--src/java/org/apache/fop/area/inline/Container.java15
-rw-r--r--src/java/org/apache/fop/cli/CommandLineOptions.java44
-rw-r--r--src/java/org/apache/fop/complexscripts/bidi/BidiClass.java1
-rw-r--r--src/java/org/apache/fop/complexscripts/bidi/BidiResolver.java10
-rw-r--r--src/java/org/apache/fop/complexscripts/bidi/DelimitedTextRange.java5
-rw-r--r--src/java/org/apache/fop/complexscripts/bidi/InlineRun.java11
-rw-r--r--src/java/org/apache/fop/complexscripts/bidi/TextInterval.java5
-rw-r--r--src/java/org/apache/fop/complexscripts/bidi/UnflattenProcessor.java29
-rw-r--r--src/java/org/apache/fop/complexscripts/bidi/UnicodeBidiAlgorithm.java69
-rw-r--r--src/java/org/apache/fop/complexscripts/fonts/GlyphClassTable.java11
-rw-r--r--src/java/org/apache/fop/complexscripts/fonts/GlyphCoverageTable.java8
-rw-r--r--src/java/org/apache/fop/complexscripts/fonts/GlyphDefinitionSubtable.java1
-rw-r--r--src/java/org/apache/fop/complexscripts/fonts/GlyphDefinitionTable.java1
-rw-r--r--src/java/org/apache/fop/complexscripts/fonts/GlyphMappingTable.java4
-rw-r--r--src/java/org/apache/fop/complexscripts/fonts/GlyphPositioningState.java1
-rw-r--r--src/java/org/apache/fop/complexscripts/fonts/GlyphPositioningSubtable.java8
-rw-r--r--src/java/org/apache/fop/complexscripts/fonts/GlyphPositioningTable.java107
-rw-r--r--src/java/org/apache/fop/complexscripts/fonts/GlyphProcessingState.java21
-rw-r--r--src/java/org/apache/fop/complexscripts/fonts/GlyphSubstitutionState.java3
-rw-r--r--src/java/org/apache/fop/complexscripts/fonts/GlyphSubstitutionSubtable.java7
-rw-r--r--src/java/org/apache/fop/complexscripts/fonts/GlyphSubstitutionTable.java30
-rw-r--r--src/java/org/apache/fop/complexscripts/fonts/GlyphSubtable.java1
-rw-r--r--src/java/org/apache/fop/complexscripts/fonts/GlyphTable.java49
-rw-r--r--src/java/org/apache/fop/complexscripts/fonts/OTFAdvancedTypographicTableReader.java93
-rw-r--r--src/java/org/apache/fop/complexscripts/scripts/ArabicScriptProcessor.java22
-rw-r--r--src/java/org/apache/fop/complexscripts/scripts/DevanagariScriptProcessor.java27
-rw-r--r--src/java/org/apache/fop/complexscripts/scripts/GujaratiScriptProcessor.java27
-rw-r--r--src/java/org/apache/fop/complexscripts/scripts/GurmukhiScriptProcessor.java27
-rw-r--r--src/java/org/apache/fop/complexscripts/scripts/IndicScriptProcessor.java17
-rw-r--r--src/java/org/apache/fop/complexscripts/scripts/ScriptProcessor.java12
-rw-r--r--src/java/org/apache/fop/complexscripts/util/CharScript.java40
-rw-r--r--src/java/org/apache/fop/complexscripts/util/GlyphSequence.java19
-rw-r--r--src/java/org/apache/fop/complexscripts/util/NumberConverter.java39
-rw-r--r--src/java/org/apache/fop/complexscripts/util/UTF32.java1
-rw-r--r--src/java/org/apache/fop/fo/Constants.java5
-rw-r--r--src/java/org/apache/fop/fo/FOPropertyMapping.java7
-rw-r--r--src/java/org/apache/fop/fo/FObj.java33
-rw-r--r--src/java/org/apache/fop/fo/expr/FunctionBase.java3
-rw-r--r--src/java/org/apache/fop/fo/expr/PropertyParser.java3
-rw-r--r--src/java/org/apache/fop/fo/extensions/ExtensionElementMapping.java2
-rw-r--r--src/java/org/apache/fop/fo/flow/BlockContainer.java18
-rw-r--r--src/java/org/apache/fop/fo/flow/InlineContainer.java172
-rw-r--r--src/java/org/apache/fop/fo/flow/table/Table.java13
-rw-r--r--src/java/org/apache/fop/fo/pagination/PageSequence.java18
-rw-r--r--src/java/org/apache/fop/fo/properties/GenericShorthandParser.java6
-rw-r--r--src/java/org/apache/fop/fonts/FontLoader.java4
-rw-r--r--src/java/org/apache/fop/fonts/FontManagerConfigurator.java16
-rw-r--r--src/java/org/apache/fop/fonts/FontTriplet.java3
-rw-r--r--src/java/org/apache/fop/fonts/MultiByteFont.java69
-rw-r--r--src/java/org/apache/fop/fonts/SingleByteFont.java2
-rw-r--r--src/java/org/apache/fop/fonts/apps/AbstractFontReader.java6
-rw-r--r--src/java/org/apache/fop/fonts/apps/PFMReader.java11
-rw-r--r--src/java/org/apache/fop/fonts/apps/TTFReader.java16
-rw-r--r--src/java/org/apache/fop/fonts/autodetect/FontInfoFinder.java4
-rw-r--r--src/java/org/apache/fop/fonts/cff/CFFDataReader.java927
-rw-r--r--src/java/org/apache/fop/fonts/truetype/GlyfTable.java4
-rw-r--r--src/java/org/apache/fop/fonts/truetype/OFDirTabEntry.java (renamed from src/java/org/apache/fop/fonts/truetype/TTFDirTabEntry.java)6
-rw-r--r--src/java/org/apache/fop/fonts/truetype/OFFontLoader.java (renamed from src/java/org/apache/fop/fonts/truetype/TTFFontLoader.java)136
-rw-r--r--src/java/org/apache/fop/fonts/truetype/OFMtxEntry.java (renamed from src/java/org/apache/fop/fonts/truetype/TTFMtxEntry.java)2
-rw-r--r--src/java/org/apache/fop/fonts/truetype/OFTableName.java (renamed from src/java/org/apache/fop/fonts/truetype/TTFTableName.java)76
-rw-r--r--src/java/org/apache/fop/fonts/truetype/OTFFile.java109
-rw-r--r--src/java/org/apache/fop/fonts/truetype/OTFSubSetFile.java1097
-rw-r--r--src/java/org/apache/fop/fonts/truetype/OpenFont.java1971
-rw-r--r--src/java/org/apache/fop/fonts/truetype/TTFFile.java2021
-rw-r--r--src/java/org/apache/fop/fonts/truetype/TTFSubSetFile.java106
-rw-r--r--src/java/org/apache/fop/fonts/type1/AFMFile.java2
-rw-r--r--src/java/org/apache/fop/fonts/type1/CharMetricsHandler.java9
-rw-r--r--src/java/org/apache/fop/fonts/type1/Type1FontLoader.java25
-rw-r--r--src/java/org/apache/fop/layoutmgr/AbstractBaseLayoutManager.java3
-rw-r--r--src/java/org/apache/fop/layoutmgr/AbstractLayoutManager.java28
-rw-r--r--src/java/org/apache/fop/layoutmgr/AreaAdditionUtil.java24
-rw-r--r--src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java64
-rw-r--r--src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java65
-rw-r--r--src/java/org/apache/fop/layoutmgr/BlockStackingLayoutManager.java3
-rw-r--r--src/java/org/apache/fop/layoutmgr/ExternalDocumentLayoutManager.java10
-rw-r--r--src/java/org/apache/fop/layoutmgr/FlowLayoutManager.java1
-rw-r--r--src/java/org/apache/fop/layoutmgr/LayoutManager.java19
-rw-r--r--src/java/org/apache/fop/layoutmgr/LayoutManagerMapping.java12
-rw-r--r--src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java78
-rw-r--r--src/java/org/apache/fop/layoutmgr/SpacedBorderedPaddedBlockLayoutManager.java112
-rw-r--r--src/java/org/apache/fop/layoutmgr/TraitSetter.java11
-rw-r--r--src/java/org/apache/fop/layoutmgr/inline/AlignmentContext.java2
-rw-r--r--src/java/org/apache/fop/layoutmgr/inline/ContentLayoutManager.java9
-rw-r--r--src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java326
-rw-r--r--src/java/org/apache/fop/layoutmgr/inline/InlineLayoutManager.java1
-rw-r--r--src/java/org/apache/fop/layoutmgr/inline/InlineLevelEventProducer.java9
-rw-r--r--src/java/org/apache/fop/layoutmgr/inline/InlineLevelEventProducer.xml1
-rw-r--r--src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java22
-rw-r--r--src/java/org/apache/fop/layoutmgr/list/ListBlockLayoutManager.java67
-rw-r--r--src/java/org/apache/fop/layoutmgr/list/ListItemContentLayoutManager.java1
-rw-r--r--src/java/org/apache/fop/layoutmgr/list/ListItemLayoutManager.java86
-rw-r--r--src/java/org/apache/fop/layoutmgr/table/TableAndCaptionLayoutManager.java1
-rw-r--r--src/java/org/apache/fop/layoutmgr/table/TableCaptionLayoutManager.java1
-rw-r--r--src/java/org/apache/fop/layoutmgr/table/TableCellLayoutManager.java20
-rw-r--r--src/java/org/apache/fop/layoutmgr/table/TableLayoutManager.java68
-rw-r--r--src/java/org/apache/fop/pdf/AbstractPDFStream.java4
-rw-r--r--src/java/org/apache/fop/pdf/PDFCFFStreamType0C.java74
-rw-r--r--src/java/org/apache/fop/pdf/PDFDictionary.java2
-rw-r--r--src/java/org/apache/fop/pdf/PDFDocument.java54
-rw-r--r--src/java/org/apache/fop/pdf/PDFFactory.java187
-rw-r--r--src/java/org/apache/fop/pdf/PDFFontDescriptor.java2
-rw-r--r--src/java/org/apache/fop/pdf/PDFFunction.java651
-rw-r--r--src/java/org/apache/fop/pdf/PDFIdentifiedDictionary.java (renamed from src/java/org/apache/fop/layoutmgr/inline/ICLayoutManager.java)42
-rw-r--r--src/java/org/apache/fop/pdf/PDFLayer.java86
-rw-r--r--src/java/org/apache/fop/pdf/PDFNavigator.java93
-rw-r--r--src/java/org/apache/fop/pdf/PDFNavigatorAction.java28
-rw-r--r--src/java/org/apache/fop/pdf/PDFNumber.java8
-rw-r--r--src/java/org/apache/fop/pdf/PDFPaintingState.java34
-rw-r--r--src/java/org/apache/fop/pdf/PDFPattern.java19
-rw-r--r--src/java/org/apache/fop/pdf/PDFResources.java34
-rw-r--r--src/java/org/apache/fop/pdf/PDFSetOCGStateAction.java82
-rw-r--r--src/java/org/apache/fop/pdf/PDFShading.java373
-rw-r--r--src/java/org/apache/fop/pdf/PDFTransitionAction.java79
-rw-r--r--src/java/org/apache/fop/render/AbstractRenderer.java58
-rw-r--r--src/java/org/apache/fop/render/afp/AFPFontConfig.java4
-rw-r--r--src/java/org/apache/fop/render/afp/AFPPainter.java2
-rw-r--r--src/java/org/apache/fop/render/afp/AFPRendererConfig.java3
-rw-r--r--src/java/org/apache/fop/render/intermediate/AbstractIFPainter.java4
-rw-r--r--src/java/org/apache/fop/render/intermediate/AbstractXMLWritingIFDocumentHandler.java4
-rw-r--r--src/java/org/apache/fop/render/intermediate/IFContext.java18
-rw-r--r--src/java/org/apache/fop/render/intermediate/IFGraphicContext.java23
-rw-r--r--src/java/org/apache/fop/render/intermediate/IFPainter.java8
-rw-r--r--src/java/org/apache/fop/render/intermediate/IFParser.java5
-rw-r--r--src/java/org/apache/fop/render/intermediate/IFRenderer.java34
-rw-r--r--src/java/org/apache/fop/render/intermediate/IFSerializer.java15
-rw-r--r--src/java/org/apache/fop/render/java2d/Java2DPainter.java2
-rw-r--r--src/java/org/apache/fop/render/java2d/Java2DRenderer.java8
-rw-r--r--src/java/org/apache/fop/render/pcl/PCLPainter.java2
-rw-r--r--src/java/org/apache/fop/render/pdf/AbstractImageAdapter.java38
-rw-r--r--src/java/org/apache/fop/render/pdf/PDFContentGenerator.java81
-rw-r--r--src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java15
-rw-r--r--src/java/org/apache/fop/render/pdf/PDFPainter.java4
-rw-r--r--src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java290
-rw-r--r--src/java/org/apache/fop/render/pdf/PDFStructureTreeBuilder.java1
-rw-r--r--src/java/org/apache/fop/render/pdf/extensions/AbstractPDFExtensionElement.java7
-rw-r--r--src/java/org/apache/fop/render/pdf/extensions/PDFActionElement.java64
-rw-r--r--src/java/org/apache/fop/render/pdf/extensions/PDFActionExtension.java32
-rw-r--r--src/java/org/apache/fop/render/pdf/extensions/PDFArrayElement.java82
-rw-r--r--src/java/org/apache/fop/render/pdf/extensions/PDFArrayExtension.java84
-rw-r--r--src/java/org/apache/fop/render/pdf/extensions/PDFCatalogElement.java45
-rw-r--r--src/java/org/apache/fop/render/pdf/extensions/PDFCatalogExtension.java29
-rw-r--r--src/java/org/apache/fop/render/pdf/extensions/PDFCollectionEntryElement.java146
-rw-r--r--src/java/org/apache/fop/render/pdf/extensions/PDFCollectionEntryExtension.java43
-rw-r--r--src/java/org/apache/fop/render/pdf/extensions/PDFCollectionExtension.java34
-rw-r--r--src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryAttachment.java166
-rw-r--r--src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryElement.java145
-rw-r--r--src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryExtension.java121
-rw-r--r--src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryType.java65
-rw-r--r--src/java/org/apache/fop/render/pdf/extensions/PDFElementMapping.java90
-rw-r--r--src/java/org/apache/fop/render/pdf/extensions/PDFEmbeddedFileAttachment.java (renamed from src/java/org/apache/fop/render/pdf/extensions/PDFEmbeddedFileExtensionAttachment.java)8
-rw-r--r--src/java/org/apache/fop/render/pdf/extensions/PDFEmbeddedFileElement.java18
-rw-r--r--src/java/org/apache/fop/render/pdf/extensions/PDFExtensionAttachment.java34
-rw-r--r--src/java/org/apache/fop/render/pdf/extensions/PDFExtensionHandler.java175
-rw-r--r--src/java/org/apache/fop/render/pdf/extensions/PDFExtensionHandlerFactory.java44
-rw-r--r--src/java/org/apache/fop/render/pdf/extensions/PDFLayerElement.java45
-rw-r--r--src/java/org/apache/fop/render/pdf/extensions/PDFLayerExtension.java29
-rw-r--r--src/java/org/apache/fop/render/pdf/extensions/PDFNavigatorElement.java45
-rw-r--r--src/java/org/apache/fop/render/pdf/extensions/PDFNavigatorExtension.java30
-rw-r--r--src/java/org/apache/fop/render/pdf/extensions/PDFObjectExtension.java101
-rw-r--r--src/java/org/apache/fop/render/pdf/extensions/PDFObjectType.java60
-rw-r--r--src/java/org/apache/fop/render/pdf/extensions/PDFPageElement.java64
-rw-r--r--src/java/org/apache/fop/render/pdf/extensions/PDFPageExtension.java73
-rw-r--r--src/java/org/apache/fop/render/pdf/extensions/PDFReferenceElement.java (renamed from src/java/org/apache/fop/fo/extensions/ExtensionObj.java)49
-rw-r--r--src/java/org/apache/fop/render/pdf/extensions/PDFReferenceExtension.java61
-rw-r--r--src/java/org/apache/fop/render/ps/PSFontUtils.java192
-rw-r--r--src/java/org/apache/fop/render/ps/PSImageHandlerSVG.java327
-rw-r--r--src/java/org/apache/fop/render/ps/PSPainter.java51
-rw-r--r--src/java/org/apache/fop/render/ps/svg/PSFunction.java143
-rw-r--r--src/java/org/apache/fop/render/ps/svg/PSPattern.java103
-rw-r--r--src/java/org/apache/fop/render/ps/svg/PSSVGGraphics2D.java294
-rw-r--r--src/java/org/apache/fop/render/ps/svg/PSShading.java228
-rw-r--r--src/java/org/apache/fop/render/rtf/RTFHandler.java42
-rw-r--r--src/java/org/apache/fop/render/rtf/RTFPlaceHolderHelper.java74
-rw-r--r--src/java/org/apache/fop/render/rtf/rtflib/tools/BuilderContext.java71
-rw-r--r--src/java/org/apache/fop/render/shading/Function.java39
-rw-r--r--src/java/org/apache/fop/render/shading/FunctionDelegate.java451
-rw-r--r--src/java/org/apache/fop/render/shading/FunctionPattern.java363
-rw-r--r--src/java/org/apache/fop/render/shading/GradientFactory.java162
-rw-r--r--src/java/org/apache/fop/render/shading/GradientRegistrar.java45
-rw-r--r--src/java/org/apache/fop/render/shading/PDFGradientFactory.java76
-rw-r--r--src/java/org/apache/fop/render/shading/PSGradientFactory.java70
-rw-r--r--src/java/org/apache/fop/render/shading/Pattern.java22
-rw-r--r--src/java/org/apache/fop/render/shading/Shading.java26
-rw-r--r--src/java/org/apache/fop/render/shading/ShadingPattern.java105
-rw-r--r--src/java/org/apache/fop/render/txt/TXTRenderer.java8
-rw-r--r--src/java/org/apache/fop/render/xml/XMLRenderer.java10
-rw-r--r--src/java/org/apache/fop/svg/PDFDocumentGraphics2D.java1
-rw-r--r--src/java/org/apache/fop/svg/PDFDocumentGraphics2DConfigurator.java3
-rw-r--r--src/java/org/apache/fop/svg/PDFGraphics2D.java86
-rw-r--r--src/java/org/apache/fop/svg/SimpleSVGUserAgent.java26
-rw-r--r--src/java/org/apache/fop/text/linebreak/LineBreakUtils.java11
-rw-r--r--src/java/org/apache/fop/tools/fontlist/FontListMain.java2
-rw-r--r--src/java/org/apache/fop/traits/WritingMode.java5
-rw-r--r--src/java/org/apache/fop/traits/WritingModeTraits.java21
-rw-r--r--src/java/org/apache/fop/traits/WritingModeTraitsGetter.java5
-rw-r--r--src/java/org/apache/fop/traits/WritingModeTraitsSetter.java5
-rw-r--r--src/java/org/apache/fop/util/AbstractPaintingState.java67
-rw-r--r--src/java/org/apache/fop/util/CommandLineLogger.java263
-rw-r--r--src/java/org/apache/fop/util/GenerationHelperContentHandler.java13
-rw-r--r--src/java/org/apache/fop/util/XMLUtil.java31
207 files changed, 12133 insertions, 4566 deletions
diff --git a/src/java/META-INF/services/org.apache.fop.util.ContentHandlerFactory b/src/java/META-INF/services/org.apache.fop.util.ContentHandlerFactory
index 76d6ebfbc..cabf917eb 100644
--- a/src/java/META-INF/services/org.apache.fop.util.ContentHandlerFactory
+++ b/src/java/META-INF/services/org.apache.fop.util.ContentHandlerFactory
@@ -1,3 +1,4 @@
org.apache.fop.render.afp.extensions.AFPExtensionHandlerFactory
+org.apache.fop.render.pdf.extensions.PDFExtensionHandlerFactory
org.apache.fop.render.ps.extensions.PSExtensionHandlerFactory
org.apache.fop.fo.extensions.xmp.XMPContentHandlerFactory
diff --git a/src/java/org/apache/fop/Version.java b/src/java/org/apache/fop/Version.java
index 86861a60b..a8903c74e 100644
--- a/src/java/org/apache/fop/Version.java
+++ b/src/java/org/apache/fop/Version.java
@@ -40,8 +40,9 @@ public final class Version {
}
if (version == null) {
//Fallback if FOP is used in a development environment
- String headURL
- = "$HeadURL$";
+ // CSOFF: LineLength
+ String headURL = "$HeadURL$";
+ // CSON: LineLength
version = headURL;
final String pathPrefix = "/xmlgraphics/fop/";
int pos = version.indexOf(pathPrefix);
diff --git a/src/java/org/apache/fop/apps/FOUserAgent.java b/src/java/org/apache/fop/apps/FOUserAgent.java
index 68064343e..8b9e079ab 100644
--- a/src/java/org/apache/fop/apps/FOUserAgent.java
+++ b/src/java/org/apache/fop/apps/FOUserAgent.java
@@ -408,7 +408,7 @@ public class FOUserAgent {
try {
// Have to do this so we can resolve data URIs
StreamSource src = new StreamSource(resourceResolver.getResource(uri));
- src.setSystemId(uri);
+ src.setSystemId(new File(uri).toURI().toURL().toExternalForm());
return src;
} catch (URISyntaxException use) {
return null;
diff --git a/src/java/org/apache/fop/apps/FopConfParser.java b/src/java/org/apache/fop/apps/FopConfParser.java
index b4918ef30..b0fa40f45 100644
--- a/src/java/org/apache/fop/apps/FopConfParser.java
+++ b/src/java/org/apache/fop/apps/FopConfParser.java
@@ -70,16 +70,7 @@ public class FopConfParser {
*/
public FopConfParser(InputStream fopConfStream, EnvironmentProfile enviro)
throws SAXException, IOException {
- DefaultConfigurationBuilder cfgBuilder = new DefaultConfigurationBuilder();
- Configuration cfg;
- try {
- cfg = cfgBuilder.build(fopConfStream);
- } catch (ConfigurationException e) {
- throw new FOPException(e);
- }
- // The default base URI is taken from the directory in which the fopConf resides
- fopFactoryBuilder = new FopFactoryBuilder(enviro).setConfiguration(cfg);
- configure(enviro.getDefaultBaseURI(), enviro.getResourceResolver(), cfg);
+ this(fopConfStream, enviro.getDefaultBaseURI(), enviro);
}
/**
@@ -94,7 +85,8 @@ public class FopConfParser {
*/
public FopConfParser(InputStream fopConfStream, URI defaultBaseURI,
ResourceResolver resourceResolver) throws SAXException, IOException {
- this(fopConfStream, EnvironmentalProfileFactory.createDefault(defaultBaseURI, resourceResolver));
+ this(fopConfStream, defaultBaseURI,
+ EnvironmentalProfileFactory.createDefault(defaultBaseURI, resourceResolver));
}
/**
@@ -123,6 +115,20 @@ public class FopConfParser {
}
/**
+ * Constructor that takes the FOP conf and a default base URI and uses the default URI resolver.
+ *
+ * @param fopConfFile the FOP conf file
+ * @param defaultBaseURI the default base URI
+ * @throws SAXException if a SAX error was thrown parsing the FOP conf
+ * @throws IOException if an I/O error is thrown while parsing the FOP conf
+ */
+ public FopConfParser(File fopConfFile, URI defaultBaseURI) throws SAXException, IOException {
+ this(new FileInputStream(fopConfFile), fopConfFile.toURI(),
+ EnvironmentalProfileFactory.createDefault(defaultBaseURI,
+ ResourceResolverFactory.createDefaultResourceResolver()));
+ }
+
+ /**
* Constructor that parses the FOP conf and uses the URI resolver given.
*
* @param fopConfFile the FOP conf file
@@ -132,11 +138,24 @@ public class FopConfParser {
*/
public FopConfParser(File fopConfFile, ResourceResolver resourceResolver)
throws SAXException, IOException {
- this(new FileInputStream(fopConfFile),
- fopConfFile.getAbsoluteFile().getParentFile().toURI(), resourceResolver);
+ this(new FileInputStream(fopConfFile), fopConfFile.toURI(), resourceResolver);
+ }
+
+ private FopConfParser(InputStream fopConfStream, URI baseURI, EnvironmentProfile enviro)
+ throws SAXException, IOException {
+ DefaultConfigurationBuilder cfgBuilder = new DefaultConfigurationBuilder();
+ Configuration cfg;
+ try {
+ cfg = cfgBuilder.build(fopConfStream);
+ } catch (ConfigurationException e) {
+ throw new FOPException(e);
+ }
+ // The default base URI is taken from the directory in which the fopConf resides
+ fopFactoryBuilder = new FopFactoryBuilder(enviro).setConfiguration(cfg);
+ configure(baseURI, enviro.getResourceResolver(), cfg);
}
- private void configure(final URI defaultBaseURI, final ResourceResolver resourceResolver,
+ private void configure(final URI baseURI, final ResourceResolver resourceResolver,
Configuration cfg) throws FOPException {
if (log.isDebugEnabled()) {
log.debug("Initializing FopFactory Configuration");
@@ -174,7 +193,7 @@ public class FopConfParser {
if (cfg.getChild("base", false) != null) {
try {
URI confUri = InternalResourceResolver.getBaseURI(cfg.getChild("base").getValue(null));
- fopFactoryBuilder.setBaseURI(defaultBaseURI.resolve(confUri));
+ fopFactoryBuilder.setBaseURI(baseURI.resolve(confUri));
} catch (URISyntaxException use) {
LogUtil.handleException(log, use, strict);
}
@@ -242,8 +261,8 @@ public class FopConfParser {
}
// configure font manager
- new FontManagerConfigurator(cfg, fopFactoryBuilder.getBaseURI(), resourceResolver).configure(
- fopFactoryBuilder.getFontManager(), strict);
+ new FontManagerConfigurator(cfg, baseURI, fopFactoryBuilder.getBaseURI(), resourceResolver)
+ .configure(fopFactoryBuilder.getFontManager(), strict);
// configure image loader framework
configureImageLoading(cfg.getChild("image-loading", false), strict);
diff --git a/src/java/org/apache/fop/area/AreaTreeParser.java b/src/java/org/apache/fop/area/AreaTreeParser.java
index c79f975c1..640f04335 100644
--- a/src/java/org/apache/fop/area/AreaTreeParser.java
+++ b/src/java/org/apache/fop/area/AreaTreeParser.java
@@ -62,6 +62,7 @@ import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.area.Trait.Background;
import org.apache.fop.area.Trait.InternalLink;
import org.apache.fop.area.inline.AbstractTextArea;
+import org.apache.fop.area.inline.Container;
import org.apache.fop.area.inline.ForeignObject;
import org.apache.fop.area.inline.Image;
import org.apache.fop.area.inline.InlineArea;
@@ -195,6 +196,7 @@ public class AreaTreeParser {
makers.put("space", new SpaceMaker());
makers.put("leader", new LeaderMaker());
makers.put("viewport", new InlineViewportMaker());
+ makers.put("container", new ContainerMaker());
makers.put("image", new ImageMaker());
makers.put("foreignObject", new ForeignObjectMaker());
makers.put("bookmarkTree", new BookmarkTreeMaker());
@@ -863,6 +865,21 @@ public class AreaTreeParser {
}
}
+ private class ContainerMaker extends AbstractMaker {
+
+ public void startElement(Attributes attributes) {
+ Container container = new Container();
+ transferForeignObjects(attributes, container);
+ InlineViewport parent = (InlineViewport) areaStack.peek();
+ parent.setContent(container);
+ areaStack.push(container);
+ }
+
+ public void endElement() {
+ assertObjectOfClass(areaStack.pop(), Container.class);
+ }
+ }
+
private class InlineViewportMaker extends AbstractMaker {
public void startElement(Attributes attributes) {
@@ -1040,7 +1057,7 @@ public class AreaTreeParser {
}
private static final Object[] SUBSET_COMMON = new Object[] {
- Trait.PROD_ID};
+ Trait.PROD_ID, Trait.LAYER};
private static final Object[] SUBSET_LINK = new Object[] {
Trait.INTERNAL_LINK, Trait.EXTERNAL_LINK};
private static final Object[] SUBSET_COLOR = new Object[] {
diff --git a/src/java/org/apache/fop/area/Footnote.java b/src/java/org/apache/fop/area/Footnote.java
index bc9f27b24..28f3ee523 100644
--- a/src/java/org/apache/fop/area/Footnote.java
+++ b/src/java/org/apache/fop/area/Footnote.java
@@ -83,7 +83,7 @@ public class Footnote extends BlockParent {
@Override
public void addBlock(Block child) {
addChildArea(child);
- this.setBPD(this.getBPD() + child.getBPD());
+ setBPD(getBPD() + child.getAllocBPD());
}
}
diff --git a/src/java/org/apache/fop/area/Trait.java b/src/java/org/apache/fop/area/Trait.java
index cd0d4becf..eac9d440d 100644
--- a/src/java/org/apache/fop/area/Trait.java
+++ b/src/java/org/apache/fop/area/Trait.java
@@ -169,9 +169,11 @@ public final class Trait implements Serializable {
/** shift direction trait */
public static final Integer SHIFT_DIRECTION = 42;
+ /** For optional content groups. */
+ public static final Integer LAYER = 43;
/** Maximum value used by trait keys */
- public static final int MAX_TRAIT_KEY = 42;
+ public static final int MAX_TRAIT_KEY = 43;
private static final TraitInfo[] TRAIT_INFO = new TraitInfo[MAX_TRAIT_KEY + 1];
@@ -243,6 +245,7 @@ public final class Trait implements Serializable {
new TraitInfo("block-progression-direction", Direction.class));
put(SHIFT_DIRECTION,
new TraitInfo("shift-direction", Direction.class));
+ put(LAYER, new TraitInfo("layer", String.class));
}
diff --git a/src/java/org/apache/fop/area/inline/Container.java b/src/java/org/apache/fop/area/inline/Container.java
index bc2acaa28..3d0060007 100644
--- a/src/java/org/apache/fop/area/inline/Container.java
+++ b/src/java/org/apache/fop/area/inline/Container.java
@@ -51,13 +51,12 @@ public class Container extends Area {
public Container() {
}
- /**
- * Add the block to this area.
- *
- * @param block the block area to add
- */
- public void addBlock(Block block) {
- blocks.add(block);
+ @Override
+ public void addChildArea(Area child) {
+ if (!(child instanceof Block)) {
+ throw new IllegalArgumentException("Container only accepts block areas");
+ }
+ blocks.add((Block) child);
}
/**
@@ -65,7 +64,7 @@ public class Container extends Area {
*
* @return the list of block areas
*/
- public List getBlocks() {
+ public List<Block> getBlocks() {
return blocks;
}
diff --git a/src/java/org/apache/fop/cli/CommandLineOptions.java b/src/java/org/apache/fop/cli/CommandLineOptions.java
index 080fe7930..ad8019a7d 100644
--- a/src/java/org/apache/fop/cli/CommandLineOptions.java
+++ b/src/java/org/apache/fop/cli/CommandLineOptions.java
@@ -58,7 +58,6 @@ import org.apache.fop.render.pdf.PDFEncryptionOption;
import org.apache.fop.render.print.PagesMode;
import org.apache.fop.render.print.PrintRenderer;
import org.apache.fop.render.xml.XMLRenderer;
-import org.apache.fop.util.CommandLineLogger;
/**
* Options parses the commandline arguments
@@ -145,16 +144,6 @@ public class CommandLineOptions {
* Construct a command line option object.
*/
public CommandLineOptions() {
- LogFactory logFactory = LogFactory.getFactory();
-
- // Enable the simple command line logging when no other logger is
- // defined.
- if (System.getProperty("org.apache.commons.logging.Log") == null) {
- logFactory.setAttribute("org.apache.commons.logging.Log",
- CommandLineLogger.class.getName());
- setLogLevel("info");
- }
-
log = LogFactory.getLog("FOP");
}
@@ -184,9 +173,6 @@ public class CommandLineOptions {
//Factory config is set up, now we can create the user agent
foUserAgent = factory.newFOUserAgent();
foUserAgent.getRendererOptions().putAll(renderingOptions);
- if (targetResolution != 0) {
- foUserAgent.setTargetResolution(targetResolution);
- }
addXSLTParameter("fop-output-format", getOutputFormat());
addXSLTParameter("fop-version", Version.getVersion());
foUserAgent.setConserveMemoryPolicy(conserveMemoryPolicy);
@@ -292,7 +278,7 @@ public class CommandLineOptions {
} else if (args[i].equals("-s")) {
suppressLowLevelAreas = Boolean.TRUE;
} else if (args[i].equals("-d")) {
- setLogOption("debug", "debug");
+ // nop. Left there for backwards compatibility
} else if (args[i].equals("-r")) {
strictValidation = false;
} else if (args[i].equals("-conserve")) {
@@ -304,7 +290,7 @@ public class CommandLineOptions {
} else if (args[i].equals("-dpi")) {
i = i + parseResolution(args, i);
} else if (args[i].equals("-q") || args[i].equals("--quiet")) {
- setLogOption("quiet", "error");
+ // nop. Left there for backwards compatibility
} else if (args[i].equals("-fo")) {
i = i + parseFOInputOption(args, i);
} else if (args[i].equals("-xsl")) {
@@ -904,27 +890,6 @@ public class CommandLineOptions {
}
}
- private void setLogOption(String option, String level) {
- if (log instanceof CommandLineLogger
- || System.getProperty("org.apache.commons.logging.Log") == null) {
- setLogLevel(level);
- } else if (log != null) {
- log.warn("The option " + option + " can only be used");
- log.warn("with FOP's command line logger,");
- log.warn("which is the default on the command line.");
- log.warn("Configure other loggers using Java system properties.");
- }
- }
-
- private void setLogLevel(String level) {
- // Set the level for future loggers.
- LogFactory.getFactory().setAttribute("level", level);
- if (log instanceof CommandLineLogger) {
- // Set the level for the logger created already.
- ((CommandLineLogger) log).setLogLevel(level);
- }
- }
-
private void setInputFormat(int format) throws FOPException {
if (inputmode == NOT_SET || inputmode == format) {
inputmode = format;
@@ -1050,7 +1015,8 @@ public class CommandLineOptions {
fopFactoryBuilder.setComplexScriptFeatures(useComplexScriptFeatures);
} else {
try {
- fopFactoryBuilder = new FopConfParser(userConfigFile).getFopFactoryBuilder();
+ FopConfParser fopConfParser = new FopConfParser(userConfigFile, baseURI);
+ fopFactoryBuilder = fopConfParser.getFopFactoryBuilder();
} catch (SAXException e) {
throw new FOPException(e);
}
@@ -1208,9 +1174,7 @@ public class CommandLineOptions {
+ "[-awt|-pdf|-mif|-rtf|-tiff|-png|-pcl|-ps|-txt|-at [mime]|-print] <outfile>\n"
+ " [OPTIONS] \n"
+ " -version print FOP version and exit\n"
- + " -d debug mode \n"
+ " -x dump configuration settings \n"
- + " -q quiet mode \n"
+ " -c cfg.xml use additional configuration file cfg.xml\n"
+ " -l lang the language to use for user information \n"
+ " -nocs disable complex script features\n"
diff --git a/src/java/org/apache/fop/complexscripts/bidi/BidiClass.java b/src/java/org/apache/fop/complexscripts/bidi/BidiClass.java
index eed7b983b..4267e275a 100644
--- a/src/java/org/apache/fop/complexscripts/bidi/BidiClass.java
+++ b/src/java/org/apache/fop/complexscripts/bidi/BidiClass.java
@@ -21,7 +21,6 @@ package org.apache.fop.complexscripts.bidi;
import java.util.Arrays;
-// CSOFF: WhitespaceAfterCheck
// CSOFF: LineLengthCheck
/*
diff --git a/src/java/org/apache/fop/complexscripts/bidi/BidiResolver.java b/src/java/org/apache/fop/complexscripts/bidi/BidiResolver.java
index 8b78615d4..f1a4d2a69 100644
--- a/src/java/org/apache/fop/complexscripts/bidi/BidiResolver.java
+++ b/src/java/org/apache/fop/complexscripts/bidi/BidiResolver.java
@@ -32,11 +32,7 @@ import org.apache.fop.area.LineArea;
import org.apache.fop.area.inline.InlineArea;
import org.apache.fop.fo.pagination.PageSequence;
-// CSOFF: EmptyForIteratorPadCheck
-// CSOFF: InnerAssignmentCheck
// CSOFF: LineLengthCheck
-// CSOFF: NoWhitespaceAfterCheck
-// CSOFF: SimplifyBooleanReturnCheck
/**
* <p>A utility class for performing bidirectional resolution processing.</p>
@@ -140,7 +136,7 @@ public final class BidiResolver {
runsNew.addAll(ir.split());
}
}
- if (! runsNew.equals(runs)) {
+ if (!runsNew.equals(runs)) {
runs = runsNew;
}
return runs;
@@ -180,7 +176,7 @@ public final class BidiResolver {
i = e - 1;
}
}
- if (! runsNew.equals(runs)) {
+ if (!runsNew.equals(runs)) {
runs = runsNew;
}
return runs;
@@ -232,7 +228,7 @@ public final class BidiResolver {
Vector rv = new Vector();
for (Iterator it = ranges.iterator(); it.hasNext(); ) {
DelimitedTextRange r = (DelimitedTextRange) it.next();
- if (! r.isEmpty()) {
+ if (!r.isEmpty()) {
rv.add(r);
}
}
diff --git a/src/java/org/apache/fop/complexscripts/bidi/DelimitedTextRange.java b/src/java/org/apache/fop/complexscripts/bidi/DelimitedTextRange.java
index 2f69eb73b..67bb0aae1 100644
--- a/src/java/org/apache/fop/complexscripts/bidi/DelimitedTextRange.java
+++ b/src/java/org/apache/fop/complexscripts/bidi/DelimitedTextRange.java
@@ -34,10 +34,7 @@ import org.apache.fop.traits.WritingModeTraits;
import org.apache.fop.traits.WritingModeTraitsGetter;
import org.apache.fop.util.CharUtilities;
-// CSOFF: EmptyForIteratorPadCheck
-// CSOFF: InnerAssignmentCheck
// CSOFF: LineLengthCheck
-// CSOFF: NoWhitespaceAfterCheck
/**
* The <code>DelimitedTextRange</code> class implements the "delimited text range" as described
@@ -151,7 +148,7 @@ public class DelimitedTextRange {
TextInterval ti = (TextInterval) it.next();
intervalsNew.addAll(assignLevels(ti, levels));
}
- if (! intervalsNew.equals(intervals)) {
+ if (!intervalsNew.equals(intervals)) {
intervals = intervalsNew;
}
}
diff --git a/src/java/org/apache/fop/complexscripts/bidi/InlineRun.java b/src/java/org/apache/fop/complexscripts/bidi/InlineRun.java
index c51d95c6e..f8f67f953 100644
--- a/src/java/org/apache/fop/complexscripts/bidi/InlineRun.java
+++ b/src/java/org/apache/fop/complexscripts/bidi/InlineRun.java
@@ -35,11 +35,6 @@ import org.apache.fop.area.inline.UnresolvedPageNumber;
import org.apache.fop.area.inline.WordArea;
import org.apache.fop.util.CharUtilities;
-// CSOFF: EmptyForIteratorPadCheck
-// CSOFF: InnerAssignmentCheck
-// CSOFF: NoWhitespaceAfterCheck
-// CSOFF: SimplifyBooleanReturnCheck
-
/**
* The <code>InlineRun</code> class is a utility class, the instances of which are used
* to capture a sequence of reordering levels associated with an inline area.
@@ -179,7 +174,7 @@ public class InlineRun {
if (inline instanceof WordArea) {
WordArea w = (WordArea) inline;
// if not already reversed, then reverse now
- if (! w.isReversed()) {
+ if (!w.isReversed()) {
if ((reversals & 1) != 0) {
w.reverse(mirror);
} else if (mirror && maybeNeedsMirroring()) {
@@ -209,10 +204,8 @@ public class InlineRun {
}
return true;
}
- } else if ((ir.levels == null) && (levels == null)) {
- return true;
} else {
- return false;
+ return (ir.levels == null) && (levels == null);
}
} else {
return false;
diff --git a/src/java/org/apache/fop/complexscripts/bidi/TextInterval.java b/src/java/org/apache/fop/complexscripts/bidi/TextInterval.java
index d904aedbb..e1a42a473 100644
--- a/src/java/org/apache/fop/complexscripts/bidi/TextInterval.java
+++ b/src/java/org/apache/fop/complexscripts/bidi/TextInterval.java
@@ -28,7 +28,6 @@ import org.apache.fop.fo.flow.Character;
import org.apache.fop.fo.flow.Leader;
// CSOFF: LineLengthCheck
-// CSOFF: SimplifyBooleanReturnCheck
/**
* <p>The <code>TextInterval</code> class is a utility class, the instances of which are used
@@ -102,10 +101,8 @@ class TextInterval {
return false;
} else if (ti.getStart() != start) {
return false;
- } else if (ti.getEnd() != end) {
- return false;
} else {
- return true;
+ return ti.getEnd() == end;
}
} else {
return false;
diff --git a/src/java/org/apache/fop/complexscripts/bidi/UnflattenProcessor.java b/src/java/org/apache/fop/complexscripts/bidi/UnflattenProcessor.java
index 60ad7ba5b..c511dbd70 100644
--- a/src/java/org/apache/fop/complexscripts/bidi/UnflattenProcessor.java
+++ b/src/java/org/apache/fop/complexscripts/bidi/UnflattenProcessor.java
@@ -35,10 +35,7 @@ import org.apache.fop.area.inline.SpaceArea;
import org.apache.fop.area.inline.TextArea;
import org.apache.fop.area.inline.UnresolvedPageNumber;
-// CSOFF: EmptyForIteratorPadCheck
// CSOFF: LineLengthCheck
-// CSOFF: NoWhitespaceAfterCheck
-// CSOFF: SimplifyBooleanReturnCheck
/**
* <p>The <code>UnflattenProcessor</code> class is used to reconstruct (by unflattening) a line
@@ -86,10 +83,8 @@ class UnflattenProcessor {
private boolean shouldFinishTextContainer(TextArea tc, InlineArea ia) {
if ((tcOrig != null) && (tc != tcOrig)) {
return true;
- } else if ((iaLevelLast != -1) && (ia.getBidiLevel() != iaLevelLast)) {
- return true;
} else {
- return false;
+ return (iaLevelLast != -1) && (ia.getBidiLevel() != iaLevelLast);
}
}
private void finishTextContainer() {
@@ -98,7 +93,7 @@ class UnflattenProcessor {
private void finishTextContainer(TextArea tc, InlineArea ia) {
if (tcNew != null) {
updateIPD(tcNew);
- if (! icNew.empty()) {
+ if (!icNew.empty()) {
icNew.peek().addChildArea(tcNew);
} else {
ilNew.add(tcNew);
@@ -113,12 +108,12 @@ class UnflattenProcessor {
}
private boolean shouldFinishInlineContainer(List<InlineParent> ich, TextArea tc, InlineArea ia) {
if ((ich == null) || ich.isEmpty()) {
- return ! icOrig.empty();
+ return !icOrig.empty();
} else {
- if (! icOrig.empty()) {
+ if (!icOrig.empty()) {
InlineParent ic = ich.get(0);
InlineParent ic0 = icOrig.peek();
- return (ic != ic0) && ! isInlineParentOf(ic, ic0);
+ return (ic != ic0) && !isInlineParentOf(ic, ic0);
} else {
return false;
}
@@ -128,14 +123,14 @@ class UnflattenProcessor {
finishInlineContainer(null, null, null);
}
private void finishInlineContainer(List<InlineParent> ich, TextArea tc, InlineArea ia) {
- if ((ich != null) && ! ich.isEmpty()) { // finish non-matching inner inline container(s)
+ if ((ich != null) && !ich.isEmpty()) { // finish non-matching inner inline container(s)
for (Iterator<InlineParent> it = ich.iterator(); it.hasNext(); ) {
InlineParent ic = it.next();
InlineParent ic0 = icOrig.empty() ? null : icOrig.peek();
if (ic0 == null) {
assert icNew.empty();
} else if (ic != ic0) {
- assert ! icNew.empty();
+ assert !icNew.empty();
InlineParent icO0 = icOrig.pop();
InlineParent icN0 = icNew.pop();
assert icO0 != null;
@@ -145,7 +140,7 @@ class UnflattenProcessor {
} else {
icNew.peek().addChildArea(icN0);
}
- if (! icOrig.empty() && (icOrig.peek() == ic)) {
+ if (!icOrig.empty() && (icOrig.peek() == ic)) {
break;
}
} else {
@@ -153,7 +148,7 @@ class UnflattenProcessor {
}
}
} else { // finish all inline containers
- while (! icNew.empty()) {
+ while (!icNew.empty()) {
InlineParent icO0 = icOrig.pop();
InlineParent icN0 = icNew.pop();
assert icO0 != null;
@@ -176,8 +171,8 @@ class UnflattenProcessor {
finishInlineContainer();
}
private void update(List<InlineParent> ich, TextArea tc, InlineArea ia) {
- if (! alreadyUnflattened(ia)) {
- if ((ich != null) && ! ich.isEmpty()) {
+ if (!alreadyUnflattened(ia)) {
+ if ((ich != null) && !ich.isEmpty()) {
pushInlineContainers(ich);
}
if (tc != null) {
@@ -345,7 +340,7 @@ class UnflattenProcessor {
Area a = ia.getParentArea();
while (a != null) {
if (a instanceof InlineArea) {
- if ((a instanceof InlineParent) && ! (a instanceof TextArea)) {
+ if ((a instanceof InlineParent) && !(a instanceof TextArea)) {
ich.add((InlineParent) a);
}
a = ((InlineArea) a) .getParentArea();
diff --git a/src/java/org/apache/fop/complexscripts/bidi/UnicodeBidiAlgorithm.java b/src/java/org/apache/fop/complexscripts/bidi/UnicodeBidiAlgorithm.java
index b1234d323..694cc9245 100644
--- a/src/java/org/apache/fop/complexscripts/bidi/UnicodeBidiAlgorithm.java
+++ b/src/java/org/apache/fop/complexscripts/bidi/UnicodeBidiAlgorithm.java
@@ -25,12 +25,7 @@ import org.apache.commons.logging.LogFactory;
import org.apache.fop.traits.Direction;
import org.apache.fop.util.CharUtilities;
-// CSOFF: AvoidNestedBlocksCheck
-// CSOFF: EmptyForIteratorPadCheck
-// CSOFF: InnerAssignmentCheck
// CSOFF: LineLengthCheck
-// CSOFF: NoWhitespaceAfterCheck
-// CSOFF: ParameterNumberCheck
/**
* <p>The <code>UnicodeBidiAlgorithm</code> class implements functionality prescribed by
@@ -125,47 +120,39 @@ public final class UnicodeBidiAlgorithm implements BidiConstants {
case RLE: // start right-to-left embedding
case LRO: // start left-to-right override
case RLO: // start right-to-left override
- {
- int en; /* new embedding level */
- if ((bc == RLE) || (bc == RLO)) {
- en = ((ec & ~OVERRIDE) + 1) | 1;
- } else {
- en = ((ec & ~OVERRIDE) + 2) & ~1;
- }
- if (en < (MAX_LEVELS + 1)) {
- es [ ei++ ] = ec;
- if ((bc == LRO) || (bc == RLO)) {
- ec = en | OVERRIDE;
- } else {
- ec = en & ~OVERRIDE;
- }
+ int en; /* new embedding level */
+ if ((bc == RLE) || (bc == RLO)) {
+ en = ((ec & ~OVERRIDE) + 1) | 1;
+ } else {
+ en = ((ec & ~OVERRIDE) + 2) & ~1;
+ }
+ if (en < (MAX_LEVELS + 1)) {
+ es [ ei++ ] = ec;
+ if ((bc == LRO) || (bc == RLO)) {
+ ec = en | OVERRIDE;
} else {
- // max levels exceeded, so don't change level or override
+ ec = en & ~OVERRIDE;
}
- el = ec;
- break;
+ } else {
+ // max levels exceeded, so don't change level or override
}
+ el = ec;
+ break;
case PDF: // pop directional formatting
- {
- el = ec;
- if (ei > 0) {
- ec = es [ --ei ];
- } else {
- // ignore isolated PDF
- }
- break;
+ el = ec;
+ if (ei > 0) {
+ ec = es [ --ei ];
+ } else {
+ // ignore isolated PDF
}
+ break;
case B: // paragraph separator
- {
- el = ec = defaultLevel;
- ei = 0;
- break;
- }
+ el = ec = defaultLevel;
+ ei = 0;
+ break;
default:
- {
- el = ec;
- break;
- }
+ el = ec;
+ break;
}
switch (bc) {
case BN:
@@ -608,7 +595,7 @@ public final class UnicodeBidiAlgorithm implements BidiConstants {
private static boolean isRetainedFormatting(int[] ca, int s, int e) {
for (int i = s; i < e; i++) {
- if (! isRetainedFormatting(ca[i])) {
+ if (!isRetainedFormatting(ca[i])) {
return false;
}
}
@@ -680,7 +667,7 @@ public final class UnicodeBidiAlgorithm implements BidiConstants {
} else {
chOut = chIn;
}
- if (! triggered && triggersBidi(chOut)) {
+ if (!triggered && triggersBidi(chOut)) {
triggered = true;
}
if ((chOut & 0xFF0000) == 0) {
diff --git a/src/java/org/apache/fop/complexscripts/fonts/GlyphClassTable.java b/src/java/org/apache/fop/complexscripts/fonts/GlyphClassTable.java
index 62926dc5e..301ab846e 100644
--- a/src/java/org/apache/fop/complexscripts/fonts/GlyphClassTable.java
+++ b/src/java/org/apache/fop/complexscripts/fonts/GlyphClassTable.java
@@ -23,7 +23,6 @@ import java.util.Iterator;
import java.util.List;
// CSOFF: LineLengthCheck
-// CSOFF: NoWhitespaceAfterCheck
/**
* <p>Base class implementation of glyph class table.</p>
@@ -100,7 +99,7 @@ public final class GlyphClassTable extends GlyphMappingTable implements GlyphCla
} else {
for (Iterator it = entries.iterator(); it.hasNext();) {
Object o = it.next();
- if (! (o instanceof Integer)) {
+ if (!(o instanceof Integer)) {
return false;
}
}
@@ -114,7 +113,7 @@ public final class GlyphClassTable extends GlyphMappingTable implements GlyphCla
} else {
for (Iterator it = entries.iterator(); it.hasNext();) {
Object o = it.next();
- if (! (o instanceof MappingRange)) {
+ if (!(o instanceof MappingRange)) {
return false;
}
}
@@ -128,7 +127,7 @@ public final class GlyphClassTable extends GlyphMappingTable implements GlyphCla
} else {
for (Iterator it = entries.iterator(); it.hasNext();) {
Object o = it.next();
- if (! (o instanceof GlyphCoverageTable)) {
+ if (!(o instanceof GlyphCoverageTable)) {
return false;
}
}
@@ -197,7 +196,7 @@ public final class GlyphClassTable extends GlyphMappingTable implements GlyphCla
if (it.hasNext()) {
Object o = it.next();
if (o instanceof Integer) {
- firstGlyph = ((Integer) o) . intValue();
+ firstGlyph = ((Integer) o) .intValue();
} else {
throw new AdvancedTypographicTableFormatException("illegal entry, first entry must be Integer denoting first glyph value, but is: " + o);
}
@@ -210,7 +209,7 @@ public final class GlyphClassTable extends GlyphMappingTable implements GlyphCla
while (it.hasNext()) {
Object o = it.next();
if (o instanceof Integer) {
- int gc = ((Integer) o) . intValue();
+ int gc = ((Integer) o) .intValue();
gca [ i++ ] = gc;
if (gc > gcMax) {
gcMax = gc;
diff --git a/src/java/org/apache/fop/complexscripts/fonts/GlyphCoverageTable.java b/src/java/org/apache/fop/complexscripts/fonts/GlyphCoverageTable.java
index 7e2d0e818..699672439 100644
--- a/src/java/org/apache/fop/complexscripts/fonts/GlyphCoverageTable.java
+++ b/src/java/org/apache/fop/complexscripts/fonts/GlyphCoverageTable.java
@@ -27,8 +27,6 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
// CSOFF: LineLengthCheck
-// CSOFF: InnerAssignmentCheck
-// CSOFF: NoWhitespaceAfterCheck
/**
* <p>.Base class implementation of glyph coverage table.</p>
@@ -103,7 +101,7 @@ public final class GlyphCoverageTable extends GlyphMappingTable implements Glyph
} else {
for (Iterator it = entries.iterator(); it.hasNext();) {
Object o = it.next();
- if (! (o instanceof Integer)) {
+ if (!(o instanceof Integer)) {
return false;
}
}
@@ -117,7 +115,7 @@ public final class GlyphCoverageTable extends GlyphMappingTable implements Glyph
} else {
for (Iterator it = entries.iterator(); it.hasNext();) {
Object o = it.next();
- if (! (o instanceof MappingRange)) {
+ if (!(o instanceof MappingRange)) {
return false;
}
}
@@ -183,7 +181,7 @@ public final class GlyphCoverageTable extends GlyphMappingTable implements Glyph
for (Iterator it = entries.iterator(); it.hasNext();) {
Object o = it.next();
if (o instanceof Integer) {
- int gid = ((Integer) o) . intValue();
+ int gid = ((Integer) o) .intValue();
if ((gid >= 0) && (gid < 65536)) {
if (gid > gidMax) {
map [ i++ ] = gidMax = gid;
diff --git a/src/java/org/apache/fop/complexscripts/fonts/GlyphDefinitionSubtable.java b/src/java/org/apache/fop/complexscripts/fonts/GlyphDefinitionSubtable.java
index eaa16146a..3ef9a0152 100644
--- a/src/java/org/apache/fop/complexscripts/fonts/GlyphDefinitionSubtable.java
+++ b/src/java/org/apache/fop/complexscripts/fonts/GlyphDefinitionSubtable.java
@@ -20,7 +20,6 @@
package org.apache.fop.complexscripts.fonts;
// CSOFF: LineLengthCheck
-// CSOFF: InnerAssignmentCheck
/**
* <p>The <code>GlyphDefinitionSubtable</code> implements an abstract base of a glyph definition subtable,
diff --git a/src/java/org/apache/fop/complexscripts/fonts/GlyphDefinitionTable.java b/src/java/org/apache/fop/complexscripts/fonts/GlyphDefinitionTable.java
index 0e98e4588..a5942536c 100644
--- a/src/java/org/apache/fop/complexscripts/fonts/GlyphDefinitionTable.java
+++ b/src/java/org/apache/fop/complexscripts/fonts/GlyphDefinitionTable.java
@@ -29,7 +29,6 @@ import org.apache.commons.logging.LogFactory;
import org.apache.fop.complexscripts.scripts.ScriptProcessor;
import org.apache.fop.complexscripts.util.GlyphSequence;
-// CSOFF: InnerAssignmentCheck
// CSOFF: LineLengthCheck
/**
diff --git a/src/java/org/apache/fop/complexscripts/fonts/GlyphMappingTable.java b/src/java/org/apache/fop/complexscripts/fonts/GlyphMappingTable.java
index eef49c399..410f5e544 100644
--- a/src/java/org/apache/fop/complexscripts/fonts/GlyphMappingTable.java
+++ b/src/java/org/apache/fop/complexscripts/fonts/GlyphMappingTable.java
@@ -23,8 +23,6 @@ import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
-// CSOFF: NoWhitespaceAfterCheck
-// CSOFF: InnerAssignmentCheck
// CSOFF: LineLengthCheck
/**
@@ -162,7 +160,7 @@ public class GlyphMappingTable {
int mi;
if ((i = Arrays.binarySearch(sa, gid)) >= 0) {
mi = getMappedIndex(gid, sa [ i ], ma [ i ]); // matches start of (some) range
- } else if ((i = - (i + 1)) == 0) {
+ } else if ((i = -(i + 1)) == 0) {
mi = -1; // precedes first range
} else if (gid > ea [ --i ]) {
mi = -1; // follows preceding (or last) range
diff --git a/src/java/org/apache/fop/complexscripts/fonts/GlyphPositioningState.java b/src/java/org/apache/fop/complexscripts/fonts/GlyphPositioningState.java
index 6600a8256..08c533860 100644
--- a/src/java/org/apache/fop/complexscripts/fonts/GlyphPositioningState.java
+++ b/src/java/org/apache/fop/complexscripts/fonts/GlyphPositioningState.java
@@ -23,7 +23,6 @@ import org.apache.fop.complexscripts.util.GlyphSequence;
import org.apache.fop.complexscripts.util.ScriptContextTester;
// CSOFF: LineLengthCheck
-// CSOFF: ParameterNumberCheck
/**
* <p>The <code>GlyphPositioningState</code> implements an state object used during glyph positioning
diff --git a/src/java/org/apache/fop/complexscripts/fonts/GlyphPositioningSubtable.java b/src/java/org/apache/fop/complexscripts/fonts/GlyphPositioningSubtable.java
index d269a342a..a94fac20f 100644
--- a/src/java/org/apache/fop/complexscripts/fonts/GlyphPositioningSubtable.java
+++ b/src/java/org/apache/fop/complexscripts/fonts/GlyphPositioningSubtable.java
@@ -23,8 +23,6 @@ import org.apache.fop.complexscripts.util.GlyphSequence;
import org.apache.fop.complexscripts.util.ScriptContextTester;
// CSOFF: LineLengthCheck
-// CSOFF: NoWhitespaceAfterCheck
-// CSOFF: ParameterNumberCheck
/**
* <p>The <code>GlyphPositioningSubtable</code> implements an abstract base of a glyph subtable,
@@ -90,8 +88,8 @@ public abstract class GlyphPositioningSubtable extends GlyphSubtable implements
boolean appliedOneShot = false;
while (ps.hasNext()) {
boolean applied = false;
- if (! appliedOneShot && ps.maybeApplicable()) {
- for (int i = 0, n = sta.length; ! applied && (i < n); i++) {
+ if (!appliedOneShot && ps.maybeApplicable()) {
+ for (int i = 0, n = sta.length; !applied && (i < n); i++) {
if (sequenceIndex < 0) {
applied = ps.apply(sta [ i ]);
} else if (ps.getPosition() == (sequenceStart + sequenceIndex)) {
@@ -102,7 +100,7 @@ public abstract class GlyphPositioningSubtable extends GlyphSubtable implements
}
}
}
- if (! applied || ! ps.didConsume()) {
+ if (!applied || !ps.didConsume()) {
ps.applyDefault();
}
ps.next();
diff --git a/src/java/org/apache/fop/complexscripts/fonts/GlyphPositioningTable.java b/src/java/org/apache/fop/complexscripts/fonts/GlyphPositioningTable.java
index b22766013..ecc933ae2 100644
--- a/src/java/org/apache/fop/complexscripts/fonts/GlyphPositioningTable.java
+++ b/src/java/org/apache/fop/complexscripts/fonts/GlyphPositioningTable.java
@@ -33,9 +33,6 @@ import org.apache.fop.complexscripts.util.GlyphSequence;
import org.apache.fop.complexscripts.util.GlyphTester;
// CSOFF: LineLengthCheck
-// CSOFF: InnerAssignmentCheck
-// CSOFF: NoWhitespaceAfterCheck
-// CSOFF: ParameterNumberCheck
/**
* <p>The <code>GlyphPositioningTable</code> class is a glyph table that implements
@@ -368,7 +365,7 @@ public class GlyphPositioningTable extends GlyphTable {
throw new AdvancedTypographicTableFormatException("illegal entries, " + entries.size() + " entries present, but requires 1 entry");
} else {
Object o;
- if (((o = entries.get(0)) == null) || ! (o instanceof Value[])) {
+ if (((o = entries.get(0)) == null) || !(o instanceof Value[])) {
throw new AdvancedTypographicTableFormatException("illegal entries, single entry must be a Value[], but is: " + ((o != null) ? o.getClass() : null));
} else {
Value[] va = (Value[]) o;
@@ -412,7 +409,7 @@ public class GlyphPositioningTable extends GlyphTable {
int offsetLast = counts[0] + counts[1];
// skip any ignored glyphs prior to first non-ignored glyph
for ( ; offset < offsetLast; ++offset) {
- if (! ps.isIgnoredGlyph(offset)) {
+ if (!ps.isIgnoredGlyph(offset)) {
break;
} else {
ps.consume(1);
@@ -429,7 +426,7 @@ public class GlyphPositioningTable extends GlyphTable {
}
// skip any ignored glyphs prior to second non-ignored glyph
for ( ; offset < offsetLast; ++offset) {
- if (! ps.isIgnoredGlyph(offset)) {
+ if (!ps.isIgnoredGlyph(offset)) {
break;
} else {
ps.consume(1);
@@ -513,7 +510,7 @@ public class GlyphPositioningTable extends GlyphTable {
throw new AdvancedTypographicTableFormatException("illegal entries, " + entries.size() + " entries present, but requires 1 entry");
} else {
Object o;
- if (((o = entries.get(0)) == null) || ! (o instanceof PairValues[][])) {
+ if (((o = entries.get(0)) == null) || !(o instanceof PairValues[][])) {
throw new AdvancedTypographicTableFormatException("illegal entries, first (and only) entry must be a PairValues[][], but is: " + ((o != null) ? o.getClass() : null));
} else {
pvm = (PairValues[][]) o;
@@ -569,27 +566,27 @@ public class GlyphPositioningTable extends GlyphTable {
throw new AdvancedTypographicTableFormatException("illegal entries, " + entries.size() + " entries present, but requires 5 entries");
} else {
Object o;
- if (((o = entries.get(0)) == null) || ! (o instanceof GlyphClassTable)) {
+ if (((o = entries.get(0)) == null) || !(o instanceof GlyphClassTable)) {
throw new AdvancedTypographicTableFormatException("illegal entries, first entry must be an GlyphClassTable, but is: " + ((o != null) ? o.getClass() : null));
} else {
cdt1 = (GlyphClassTable) o;
}
- if (((o = entries.get(1)) == null) || ! (o instanceof GlyphClassTable)) {
+ if (((o = entries.get(1)) == null) || !(o instanceof GlyphClassTable)) {
throw new AdvancedTypographicTableFormatException("illegal entries, second entry must be an GlyphClassTable, but is: " + ((o != null) ? o.getClass() : null));
} else {
cdt2 = (GlyphClassTable) o;
}
- if (((o = entries.get(2)) == null) || ! (o instanceof Integer)) {
+ if (((o = entries.get(2)) == null) || !(o instanceof Integer)) {
throw new AdvancedTypographicTableFormatException("illegal entries, third entry must be an Integer, but is: " + ((o != null) ? o.getClass() : null));
} else {
nc1 = ((Integer)(o)).intValue();
}
- if (((o = entries.get(3)) == null) || ! (o instanceof Integer)) {
+ if (((o = entries.get(3)) == null) || !(o instanceof Integer)) {
throw new AdvancedTypographicTableFormatException("illegal entries, fourth entry must be an Integer, but is: " + ((o != null) ? o.getClass() : null));
} else {
nc2 = ((Integer)(o)).intValue();
}
- if (((o = entries.get(4)) == null) || ! (o instanceof PairValues[][])) {
+ if (((o = entries.get(4)) == null) || !(o instanceof PairValues[][])) {
throw new AdvancedTypographicTableFormatException("illegal entries, fifth entry must be a PairValues[][], but is: " + ((o != null) ? o.getClass() : null));
} else {
pvm = (PairValues[][]) o;
@@ -633,7 +630,7 @@ public class GlyphPositioningTable extends GlyphTable {
int enw = ps.getWidth(gi2);
if ((exa != null) && (ena != null)) {
Value v = ena.getAlignmentAdjustment(exa);
- v.adjust(- enw, 0, 0, 0);
+ v.adjust(-enw, 0, 0, 0);
if (ps.adjust(v)) {
ps.setAdjusted(true);
}
@@ -704,10 +701,10 @@ public class GlyphPositioningTable extends GlyphTable {
throw new AdvancedTypographicTableFormatException("illegal entries, " + entries.size() + " entries present, but requires 1 entry");
} else {
Object o;
- if (((o = entries.get(0)) == null) || ! (o instanceof Anchor[])) {
+ if (((o = entries.get(0)) == null) || !(o instanceof Anchor[])) {
throw new AdvancedTypographicTableFormatException("illegal entries, first (and only) entry must be a Anchor[], but is: " + ((o != null) ? o.getClass() : null));
- } else if ((((Anchor[]) o) . length % 2) != 0) {
- throw new AdvancedTypographicTableFormatException("illegal entries, Anchor[] array must have an even number of entries, but has: " + ((Anchor[]) o) . length);
+ } else if ((((Anchor[]) o) .length % 2) != 0) {
+ throw new AdvancedTypographicTableFormatException("illegal entries, Anchor[] array must have an even number of entries, but has: " + ((Anchor[]) o) .length);
} else {
aa = (Anchor[]) o;
}
@@ -736,7 +733,7 @@ public class GlyphPositioningTable extends GlyphTable {
MarkAnchor ma = getMarkAnchor(ciMark, giMark);
if (ma != null) {
for (int i = 0, n = ps.getPosition(); i < n; i++) {
- int gi = ps.getGlyph(- (i + 1));
+ int gi = ps.getGlyph(-(i + 1));
if (ps.isMark(gi)) {
continue;
} else {
@@ -746,7 +743,7 @@ public class GlyphPositioningTable extends GlyphTable {
// start experimental fix for END OF AYAH in Lateef/Scheherazade
int[] aa = ps.getAdjustment();
if (aa[2] == 0) {
- v.adjust(0, 0, - ps.getWidth(giMark), 0);
+ v.adjust(0, 0, -ps.getWidth(giMark), 0);
}
// end experimental fix for END OF AYAH in Lateef/Scheherazade
if (ps.adjust(v)) {
@@ -835,22 +832,22 @@ public class GlyphPositioningTable extends GlyphTable {
throw new AdvancedTypographicTableFormatException("illegal entries, " + entries.size() + " entries present, but requires 4 entries");
} else {
Object o;
- if (((o = entries.get(0)) == null) || ! (o instanceof GlyphCoverageTable)) {
+ if (((o = entries.get(0)) == null) || !(o instanceof GlyphCoverageTable)) {
throw new AdvancedTypographicTableFormatException("illegal entries, first entry must be an GlyphCoverageTable, but is: " + ((o != null) ? o.getClass() : null));
} else {
bct = (GlyphCoverageTable) o;
}
- if (((o = entries.get(1)) == null) || ! (o instanceof Integer)) {
+ if (((o = entries.get(1)) == null) || !(o instanceof Integer)) {
throw new AdvancedTypographicTableFormatException("illegal entries, second entry must be an Integer, but is: " + ((o != null) ? o.getClass() : null));
} else {
nmc = ((Integer)(o)).intValue();
}
- if (((o = entries.get(2)) == null) || ! (o instanceof MarkAnchor[])) {
+ if (((o = entries.get(2)) == null) || !(o instanceof MarkAnchor[])) {
throw new AdvancedTypographicTableFormatException("illegal entries, third entry must be a MarkAnchor[], but is: " + ((o != null) ? o.getClass() : null));
} else {
maa = (MarkAnchor[]) o;
}
- if (((o = entries.get(3)) == null) || ! (o instanceof Anchor[][])) {
+ if (((o = entries.get(3)) == null) || !(o instanceof Anchor[][])) {
throw new AdvancedTypographicTableFormatException("illegal entries, fourth entry must be a Anchor[][], but is: " + ((o != null) ? o.getClass() : null));
} else {
bam = (Anchor[][]) o;
@@ -881,7 +878,7 @@ public class GlyphPositioningTable extends GlyphTable {
int mxc = getMaxComponentCount();
if (ma != null) {
for (int i = 0, n = ps.getPosition(); i < n; i++) {
- int gi = ps.getGlyph(- (i + 1));
+ int gi = ps.getGlyph(-(i + 1));
if (ps.isMark(gi)) {
continue;
} else {
@@ -989,27 +986,27 @@ public class GlyphPositioningTable extends GlyphTable {
throw new AdvancedTypographicTableFormatException("illegal entries, " + entries.size() + " entries present, but requires 5 entries");
} else {
Object o;
- if (((o = entries.get(0)) == null) || ! (o instanceof GlyphCoverageTable)) {
+ if (((o = entries.get(0)) == null) || !(o instanceof GlyphCoverageTable)) {
throw new AdvancedTypographicTableFormatException("illegal entries, first entry must be an GlyphCoverageTable, but is: " + ((o != null) ? o.getClass() : null));
} else {
lct = (GlyphCoverageTable) o;
}
- if (((o = entries.get(1)) == null) || ! (o instanceof Integer)) {
+ if (((o = entries.get(1)) == null) || !(o instanceof Integer)) {
throw new AdvancedTypographicTableFormatException("illegal entries, second entry must be an Integer, but is: " + ((o != null) ? o.getClass() : null));
} else {
nmc = ((Integer)(o)).intValue();
}
- if (((o = entries.get(2)) == null) || ! (o instanceof Integer)) {
+ if (((o = entries.get(2)) == null) || !(o instanceof Integer)) {
throw new AdvancedTypographicTableFormatException("illegal entries, third entry must be an Integer, but is: " + ((o != null) ? o.getClass() : null));
} else {
mxc = ((Integer)(o)).intValue();
}
- if (((o = entries.get(3)) == null) || ! (o instanceof MarkAnchor[])) {
+ if (((o = entries.get(3)) == null) || !(o instanceof MarkAnchor[])) {
throw new AdvancedTypographicTableFormatException("illegal entries, fourth entry must be a MarkAnchor[], but is: " + ((o != null) ? o.getClass() : null));
} else {
maa = (MarkAnchor[]) o;
}
- if (((o = entries.get(4)) == null) || ! (o instanceof Anchor[][][])) {
+ if (((o = entries.get(4)) == null) || !(o instanceof Anchor[][][])) {
throw new AdvancedTypographicTableFormatException("illegal entries, fifth entry must be a Anchor[][][], but is: " + ((o != null) ? o.getClass() : null));
} else {
lam = (Anchor[][][]) o;
@@ -1125,22 +1122,22 @@ public class GlyphPositioningTable extends GlyphTable {
throw new AdvancedTypographicTableFormatException("illegal entries, " + entries.size() + " entries present, but requires 4 entries");
} else {
Object o;
- if (((o = entries.get(0)) == null) || ! (o instanceof GlyphCoverageTable)) {
+ if (((o = entries.get(0)) == null) || !(o instanceof GlyphCoverageTable)) {
throw new AdvancedTypographicTableFormatException("illegal entries, first entry must be an GlyphCoverageTable, but is: " + ((o != null) ? o.getClass() : null));
} else {
mct2 = (GlyphCoverageTable) o;
}
- if (((o = entries.get(1)) == null) || ! (o instanceof Integer)) {
+ if (((o = entries.get(1)) == null) || !(o instanceof Integer)) {
throw new AdvancedTypographicTableFormatException("illegal entries, second entry must be an Integer, but is: " + ((o != null) ? o.getClass() : null));
} else {
nmc = ((Integer)(o)).intValue();
}
- if (((o = entries.get(2)) == null) || ! (o instanceof MarkAnchor[])) {
+ if (((o = entries.get(2)) == null) || !(o instanceof MarkAnchor[])) {
throw new AdvancedTypographicTableFormatException("illegal entries, third entry must be a MarkAnchor[], but is: " + ((o != null) ? o.getClass() : null));
} else {
maa = (MarkAnchor[]) o;
}
- if (((o = entries.get(3)) == null) || ! (o instanceof Anchor[][])) {
+ if (((o = entries.get(3)) == null) || !(o instanceof Anchor[][])) {
throw new AdvancedTypographicTableFormatException("illegal entries, fourth entry must be a Anchor[][], but is: " + ((o != null) ? o.getClass() : null));
} else {
mam = (Anchor[][]) o;
@@ -1274,7 +1271,7 @@ public class GlyphPositioningTable extends GlyphTable {
throw new AdvancedTypographicTableFormatException("illegal entries, " + entries.size() + " entries present, but requires 1 entry");
} else {
Object o;
- if (((o = entries.get(0)) == null) || ! (o instanceof RuleSet[])) {
+ if (((o = entries.get(0)) == null) || !(o instanceof RuleSet[])) {
throw new AdvancedTypographicTableFormatException("illegal entries, first entry must be an RuleSet[], but is: " + ((o != null) ? o.getClass() : null));
} else {
rsa = (RuleSet[]) o;
@@ -1367,17 +1364,17 @@ public class GlyphPositioningTable extends GlyphTable {
throw new AdvancedTypographicTableFormatException("illegal entries, " + entries.size() + " entries present, but requires 3 entries");
} else {
Object o;
- if (((o = entries.get(0)) == null) || ! (o instanceof GlyphClassTable)) {
+ if (((o = entries.get(0)) == null) || !(o instanceof GlyphClassTable)) {
throw new AdvancedTypographicTableFormatException("illegal entries, first entry must be an GlyphClassTable, but is: " + ((o != null) ? o.getClass() : null));
} else {
cdt = (GlyphClassTable) o;
}
- if (((o = entries.get(1)) == null) || ! (o instanceof Integer)) {
+ if (((o = entries.get(1)) == null) || !(o instanceof Integer)) {
throw new AdvancedTypographicTableFormatException("illegal entries, second entry must be an Integer, but is: " + ((o != null) ? o.getClass() : null));
} else {
ngc = ((Integer)(o)).intValue();
}
- if (((o = entries.get(2)) == null) || ! (o instanceof RuleSet[])) {
+ if (((o = entries.get(2)) == null) || !(o instanceof RuleSet[])) {
throw new AdvancedTypographicTableFormatException("illegal entries, third entry must be an RuleSet[], but is: " + ((o != null) ? o.getClass() : null));
} else {
rsa = (RuleSet[]) o;
@@ -1467,7 +1464,7 @@ public class GlyphPositioningTable extends GlyphTable {
throw new AdvancedTypographicTableFormatException("illegal entries, " + entries.size() + " entries present, but requires 1 entry");
} else {
Object o;
- if (((o = entries.get(0)) == null) || ! (o instanceof RuleSet[])) {
+ if (((o = entries.get(0)) == null) || !(o instanceof RuleSet[])) {
throw new AdvancedTypographicTableFormatException("illegal entries, first entry must be an RuleSet[], but is: " + ((o != null) ? o.getClass() : null));
} else {
rsa = (RuleSet[]) o;
@@ -1585,7 +1582,7 @@ public class GlyphPositioningTable extends GlyphTable {
throw new AdvancedTypographicTableFormatException("illegal entries, " + entries.size() + " entries present, but requires 1 entry");
} else {
Object o;
- if (((o = entries.get(0)) == null) || ! (o instanceof RuleSet[])) {
+ if (((o = entries.get(0)) == null) || !(o instanceof RuleSet[])) {
throw new AdvancedTypographicTableFormatException("illegal entries, first entry must be an RuleSet[], but is: " + ((o != null) ? o.getClass() : null));
} else {
rsa = (RuleSet[]) o;
@@ -1661,27 +1658,27 @@ public class GlyphPositioningTable extends GlyphTable {
throw new AdvancedTypographicTableFormatException("illegal entries, " + entries.size() + " entries present, but requires 5 entries");
} else {
Object o;
- if (((o = entries.get(0)) == null) || ! (o instanceof GlyphClassTable)) {
+ if (((o = entries.get(0)) == null) || !(o instanceof GlyphClassTable)) {
throw new AdvancedTypographicTableFormatException("illegal entries, first entry must be an GlyphClassTable, but is: " + ((o != null) ? o.getClass() : null));
} else {
icdt = (GlyphClassTable) o;
}
- if (((o = entries.get(1)) != null) && ! (o instanceof GlyphClassTable)) {
+ if (((o = entries.get(1)) != null) && !(o instanceof GlyphClassTable)) {
throw new AdvancedTypographicTableFormatException("illegal entries, second entry must be an GlyphClassTable, but is: " + o.getClass());
} else {
bcdt = (GlyphClassTable) o;
}
- if (((o = entries.get(2)) != null) && ! (o instanceof GlyphClassTable)) {
+ if (((o = entries.get(2)) != null) && !(o instanceof GlyphClassTable)) {
throw new AdvancedTypographicTableFormatException("illegal entries, third entry must be an GlyphClassTable, but is: " + o.getClass());
} else {
lcdt = (GlyphClassTable) o;
}
- if (((o = entries.get(3)) == null) || ! (o instanceof Integer)) {
+ if (((o = entries.get(3)) == null) || !(o instanceof Integer)) {
throw new AdvancedTypographicTableFormatException("illegal entries, fourth entry must be an Integer, but is: " + ((o != null) ? o.getClass() : null));
} else {
ngc = ((Integer)(o)).intValue();
}
- if (((o = entries.get(4)) == null) || ! (o instanceof RuleSet[])) {
+ if (((o = entries.get(4)) == null) || !(o instanceof RuleSet[])) {
throw new AdvancedTypographicTableFormatException("illegal entries, fifth entry must be an RuleSet[], but is: " + ((o != null) ? o.getClass() : null));
} else {
rsa = (RuleSet[]) o;
@@ -1752,7 +1749,7 @@ public class GlyphPositioningTable extends GlyphTable {
throw new AdvancedTypographicTableFormatException("illegal entries, " + entries.size() + " entries present, but requires 1 entry");
} else {
Object o;
- if (((o = entries.get(0)) == null) || ! (o instanceof RuleSet[])) {
+ if (((o = entries.get(0)) == null) || !(o instanceof RuleSet[])) {
throw new AdvancedTypographicTableFormatException("illegal entries, first entry must be an RuleSet[], but is: " + ((o != null) ? o.getClass() : null));
} else {
rsa = (RuleSet[]) o;
@@ -2005,7 +2002,7 @@ public class GlyphPositioningTable extends GlyphTable {
boolean first = true;
sb.append("{ ");
if (xPlacement != 0) {
- if (! first) {
+ if (!first) {
sb.append(", ");
} else {
first = false;
@@ -2013,7 +2010,7 @@ public class GlyphPositioningTable extends GlyphTable {
sb.append("xPlacement = " + xPlacement);
}
if (yPlacement != 0) {
- if (! first) {
+ if (!first) {
sb.append(", ");
} else {
first = false;
@@ -2021,7 +2018,7 @@ public class GlyphPositioningTable extends GlyphTable {
sb.append("yPlacement = " + yPlacement);
}
if (xAdvance != 0) {
- if (! first) {
+ if (!first) {
sb.append(", ");
} else {
first = false;
@@ -2029,7 +2026,7 @@ public class GlyphPositioningTable extends GlyphTable {
sb.append("xAdvance = " + xAdvance);
}
if (yAdvance != 0) {
- if (! first) {
+ if (!first) {
sb.append(", ");
} else {
first = false;
@@ -2037,7 +2034,7 @@ public class GlyphPositioningTable extends GlyphTable {
sb.append("yAdvance = " + yAdvance);
}
if (xPlaDevice != null) {
- if (! first) {
+ if (!first) {
sb.append(", ");
} else {
first = false;
@@ -2045,7 +2042,7 @@ public class GlyphPositioningTable extends GlyphTable {
sb.append("xPlaDevice = " + xPlaDevice);
}
if (yPlaDevice != null) {
- if (! first) {
+ if (!first) {
sb.append(", ");
} else {
first = false;
@@ -2053,7 +2050,7 @@ public class GlyphPositioningTable extends GlyphTable {
sb.append("xPlaDevice = " + yPlaDevice);
}
if (xAdvDevice != null) {
- if (! first) {
+ if (!first) {
sb.append(", ");
} else {
first = false;
@@ -2061,7 +2058,7 @@ public class GlyphPositioningTable extends GlyphTable {
sb.append("xAdvDevice = " + xAdvDevice);
}
if (yAdvDevice != null) {
- if (! first) {
+ if (!first) {
sb.append(", ");
} else {
first = false;
@@ -2118,7 +2115,7 @@ public class GlyphPositioningTable extends GlyphTable {
boolean first = true;
sb.append("{ ");
if (glyph != 0) {
- if (! first) {
+ if (!first) {
sb.append(", ");
} else {
first = false;
@@ -2126,7 +2123,7 @@ public class GlyphPositioningTable extends GlyphTable {
sb.append("glyph = " + glyph);
}
if (value1 != null) {
- if (! first) {
+ if (!first) {
sb.append(", ");
} else {
first = false;
@@ -2134,7 +2131,7 @@ public class GlyphPositioningTable extends GlyphTable {
sb.append("value1 = " + value1);
}
if (value2 != null) {
- if (! first) {
+ if (!first) {
sb.append(", ");
} else {
first = false;
diff --git a/src/java/org/apache/fop/complexscripts/fonts/GlyphProcessingState.java b/src/java/org/apache/fop/complexscripts/fonts/GlyphProcessingState.java
index 330a27593..4f6e4181c 100644
--- a/src/java/org/apache/fop/complexscripts/fonts/GlyphProcessingState.java
+++ b/src/java/org/apache/fop/complexscripts/fonts/GlyphProcessingState.java
@@ -29,7 +29,6 @@ import org.apache.fop.complexscripts.util.GlyphTester;
import org.apache.fop.complexscripts.util.ScriptContextTester;
// CSOFF: LineLengthCheck
-// CSOFF: NoWhitespaceAfterCheck
/**
* <p>The <code>GlyphProcessingState</code> implements a common, base state object used during glyph substitution
@@ -459,7 +458,7 @@ public class GlyphProcessingState {
int start = index + offset;
if (start < 0) {
throw new IndexOutOfBoundsException("will attempt index at " + start);
- } else if (! reverseOrder && ((start + count) > indexLast)) {
+ } else if (!reverseOrder && ((start + count) > indexLast)) {
throw new IndexOutOfBoundsException("will attempt index at " + (start + count));
} else if (reverseOrder && ((start + 1) < count)) {
throw new IndexOutOfBoundsException("will attempt index at " + (start - count));
@@ -469,7 +468,7 @@ public class GlyphProcessingState {
} else if (glyphs.length != count) {
throw new IllegalArgumentException("glyphs array is non-null, but its length (" + glyphs.length + "), is not equal to count (" + count + ")");
}
- if (! reverseOrder) {
+ if (!reverseOrder) {
return getGlyphsForward(start, count, ignoreTester, glyphs, counts);
} else {
return getGlyphsReverse(start, count, ignoreTester, glyphs, counts);
@@ -484,7 +483,7 @@ public class GlyphProcessingState {
if (gi == 65535) {
ignored++;
} else {
- if ((ignoreTester == null) || ! ignoreTester.test(gi, getLookupFlags())) {
+ if ((ignoreTester == null) || !ignoreTester.test(gi, getLookupFlags())) {
glyphs [ counted++ ] = gi;
} else {
ignored++;
@@ -506,7 +505,7 @@ public class GlyphProcessingState {
if (gi == 65535) {
ignored++;
} else {
- if ((ignoreTester == null) || ! ignoreTester.test(gi, getLookupFlags())) {
+ if ((ignoreTester == null) || !ignoreTester.test(gi, getLookupFlags())) {
glyphs [ counted++ ] = gi;
} else {
ignored++;
@@ -629,7 +628,7 @@ public class GlyphProcessingState {
int start = index + offset;
if ((start < 0) || (start > indexLast)) {
return new int[] { 0, 0 };
- } else if (! reverseOrder) {
+ } else if (!reverseOrder) {
return getGlyphsAvailableForward(start, ignoreTester);
} else {
return getGlyphsAvailableReverse(start, ignoreTester);
@@ -731,7 +730,7 @@ public class GlyphProcessingState {
int start = index + offset;
if (start < 0) {
throw new IndexOutOfBoundsException("will attempt index at " + start);
- } else if (! reverseOrder && ((start + count) > indexLast)) {
+ } else if (!reverseOrder && ((start + count) > indexLast)) {
throw new IndexOutOfBoundsException("will attempt index at " + (start + count));
} else if (reverseOrder && ((start + 1) < count)) {
throw new IndexOutOfBoundsException("will attempt index at " + (start - count));
@@ -741,7 +740,7 @@ public class GlyphProcessingState {
} else if (associations.length != count) {
throw new IllegalArgumentException("associations array is non-null, but its length (" + associations.length + "), is not equal to count (" + count + ")");
}
- if (! reverseOrder) {
+ if (!reverseOrder) {
return getAssociationsForward(start, count, ignoreTester, associations, counts);
} else {
return getAssociationsReverse(start, count, ignoreTester, associations, counts);
@@ -757,7 +756,7 @@ public class GlyphProcessingState {
if (gi == 65535) {
ignored++;
} else {
- if ((ignoreTester == null) || ! ignoreTester.test(gi, getLookupFlags())) {
+ if ((ignoreTester == null) || !ignoreTester.test(gi, getLookupFlags())) {
if (k < count) {
associations [ k++ ] = getAssociation(i - index);
counted++;
@@ -785,7 +784,7 @@ public class GlyphProcessingState {
if (gi == 65535) {
ignored++;
} else {
- if ((ignoreTester == null) || ! ignoreTester.test(gi, getLookupFlags())) {
+ if ((ignoreTester == null) || !ignoreTester.test(gi, getLookupFlags())) {
if (k < count) {
associations [ k++ ] = getAssociation(i - index);
counted++;
@@ -1169,7 +1168,7 @@ public class GlyphProcessingState {
for (int i = 0, n = ngt; i < n; i++) {
GlyphTester gt = gta [ i ];
if (gt != null) {
- if (! gt.test(gi, flags)) {
+ if (!gt.test(gi, flags)) {
return false;
}
}
diff --git a/src/java/org/apache/fop/complexscripts/fonts/GlyphSubstitutionState.java b/src/java/org/apache/fop/complexscripts/fonts/GlyphSubstitutionState.java
index 0b599d088..108f26c82 100644
--- a/src/java/org/apache/fop/complexscripts/fonts/GlyphSubstitutionState.java
+++ b/src/java/org/apache/fop/complexscripts/fonts/GlyphSubstitutionState.java
@@ -27,7 +27,6 @@ import org.apache.fop.complexscripts.util.GlyphSequence;
import org.apache.fop.complexscripts.util.ScriptContextTester;
// CSOFF: LineLengthCheck
-// CSOFF: NoWhitespaceAfterCheck
/**
* <p>The <code>GlyphSubstitutionState</code> implements an state object used during glyph substitution
@@ -130,7 +129,7 @@ public class GlyphSubstitutionState extends GlyphProcessingState {
* @param predication a predication value to add to association A if predications enabled
*/
public void putGlyph(int glyph, GlyphSequence.CharAssociation a, Object predication) {
- if (! ogb.hasRemaining()) {
+ if (!ogb.hasRemaining()) {
ogb = growBuffer(ogb);
}
ogb.put(glyph);
diff --git a/src/java/org/apache/fop/complexscripts/fonts/GlyphSubstitutionSubtable.java b/src/java/org/apache/fop/complexscripts/fonts/GlyphSubstitutionSubtable.java
index ebcf35b8d..f4e6fb924 100644
--- a/src/java/org/apache/fop/complexscripts/fonts/GlyphSubstitutionSubtable.java
+++ b/src/java/org/apache/fop/complexscripts/fonts/GlyphSubstitutionSubtable.java
@@ -23,7 +23,6 @@ import org.apache.fop.complexscripts.util.GlyphSequence;
import org.apache.fop.complexscripts.util.ScriptContextTester;
// CSOFF: LineLengthCheck
-// CSOFF: NoWhitespaceAfterCheck
/**
* <p>The <code>GlyphSubstitutionSubtable</code> implements an abstract base of a glyph substitution subtable,
@@ -90,8 +89,8 @@ public abstract class GlyphSubstitutionSubtable extends GlyphSubtable implements
boolean appliedOneShot = false;
while (ss.hasNext()) {
boolean applied = false;
- if (! appliedOneShot && ss.maybeApplicable()) {
- for (int i = 0, n = sta.length; ! applied && (i < n); i++) {
+ if (!appliedOneShot && ss.maybeApplicable()) {
+ for (int i = 0, n = sta.length; !applied && (i < n); i++) {
if (sequenceIndex < 0) {
applied = ss.apply(sta [ i ]);
} else if (ss.getPosition() == (sequenceStart + sequenceIndex)) {
@@ -102,7 +101,7 @@ public abstract class GlyphSubstitutionSubtable extends GlyphSubtable implements
}
}
}
- if (! applied || ! ss.didConsume()) {
+ if (!applied || !ss.didConsume()) {
ss.applyDefault();
}
ss.next();
diff --git a/src/java/org/apache/fop/complexscripts/fonts/GlyphSubstitutionTable.java b/src/java/org/apache/fop/complexscripts/fonts/GlyphSubstitutionTable.java
index 18aba212c..da708bf45 100644
--- a/src/java/org/apache/fop/complexscripts/fonts/GlyphSubstitutionTable.java
+++ b/src/java/org/apache/fop/complexscripts/fonts/GlyphSubstitutionTable.java
@@ -31,9 +31,7 @@ import org.apache.fop.complexscripts.scripts.ScriptProcessor;
import org.apache.fop.complexscripts.util.GlyphSequence;
import org.apache.fop.complexscripts.util.GlyphTester;
-// CSOFF: InnerAssignmentCheck
// CSOFF: LineLengthCheck
-// CSOFF: NoWhitespaceAfterCheck
/**
* <p>The <code>GlyphSubstitutionTable</code> class is a glyph table that implements
@@ -306,7 +304,7 @@ public class GlyphSubstitutionTable extends GlyphTable {
Object o = entries.get(0);
int delta = 0;
if (o instanceof Integer) {
- delta = ((Integer) o) . intValue();
+ delta = ((Integer) o) .intValue();
} else {
throw new AdvancedTypographicTableFormatException("illegal entries entry, must be Integer, but is: " + o);
}
@@ -440,7 +438,7 @@ public class GlyphSubstitutionTable extends GlyphTable {
throw new AdvancedTypographicTableFormatException("illegal entries, " + entries.size() + " entries present, but requires 1 entry");
} else {
Object o;
- if (((o = entries.get(0)) == null) || ! (o instanceof int[][])) {
+ if (((o = entries.get(0)) == null) || !(o instanceof int[][])) {
throw new AdvancedTypographicTableFormatException("illegal entries, first entry must be an int[][], but is: " + ((o != null) ? o.getClass() : null));
} else {
gsa = (int[][]) o;
@@ -801,7 +799,7 @@ public class GlyphSubstitutionTable extends GlyphTable {
throw new AdvancedTypographicTableFormatException("illegal entries, " + entries.size() + " entries present, but requires 1 entry");
} else {
Object o;
- if (((o = entries.get(0)) == null) || ! (o instanceof RuleSet[])) {
+ if (((o = entries.get(0)) == null) || !(o instanceof RuleSet[])) {
throw new AdvancedTypographicTableFormatException("illegal entries, first entry must be an RuleSet[], but is: " + ((o != null) ? o.getClass() : null));
} else {
rsa = (RuleSet[]) o;
@@ -894,17 +892,17 @@ public class GlyphSubstitutionTable extends GlyphTable {
throw new AdvancedTypographicTableFormatException("illegal entries, " + entries.size() + " entries present, but requires 3 entries");
} else {
Object o;
- if (((o = entries.get(0)) == null) || ! (o instanceof GlyphClassTable)) {
+ if (((o = entries.get(0)) == null) || !(o instanceof GlyphClassTable)) {
throw new AdvancedTypographicTableFormatException("illegal entries, first entry must be an GlyphClassTable, but is: " + ((o != null) ? o.getClass() : null));
} else {
cdt = (GlyphClassTable) o;
}
- if (((o = entries.get(1)) == null) || ! (o instanceof Integer)) {
+ if (((o = entries.get(1)) == null) || !(o instanceof Integer)) {
throw new AdvancedTypographicTableFormatException("illegal entries, second entry must be an Integer, but is: " + ((o != null) ? o.getClass() : null));
} else {
ngc = ((Integer)(o)).intValue();
}
- if (((o = entries.get(2)) == null) || ! (o instanceof RuleSet[])) {
+ if (((o = entries.get(2)) == null) || !(o instanceof RuleSet[])) {
throw new AdvancedTypographicTableFormatException("illegal entries, third entry must be an RuleSet[], but is: " + ((o != null) ? o.getClass() : null));
} else {
rsa = (RuleSet[]) o;
@@ -994,7 +992,7 @@ public class GlyphSubstitutionTable extends GlyphTable {
throw new AdvancedTypographicTableFormatException("illegal entries, " + entries.size() + " entries present, but requires 1 entry");
} else {
Object o;
- if (((o = entries.get(0)) == null) || ! (o instanceof RuleSet[])) {
+ if (((o = entries.get(0)) == null) || !(o instanceof RuleSet[])) {
throw new AdvancedTypographicTableFormatException("illegal entries, first entry must be an RuleSet[], but is: " + ((o != null) ? o.getClass() : null));
} else {
rsa = (RuleSet[]) o;
@@ -1113,7 +1111,7 @@ public class GlyphSubstitutionTable extends GlyphTable {
throw new AdvancedTypographicTableFormatException("illegal entries, " + entries.size() + " entries present, but requires 1 entry");
} else {
Object o;
- if (((o = entries.get(0)) == null) || ! (o instanceof RuleSet[])) {
+ if (((o = entries.get(0)) == null) || !(o instanceof RuleSet[])) {
throw new AdvancedTypographicTableFormatException("illegal entries, first entry must be an RuleSet[], but is: " + ((o != null) ? o.getClass() : null));
} else {
rsa = (RuleSet[]) o;
@@ -1189,27 +1187,27 @@ public class GlyphSubstitutionTable extends GlyphTable {
throw new AdvancedTypographicTableFormatException("illegal entries, " + entries.size() + " entries present, but requires 5 entries");
} else {
Object o;
- if (((o = entries.get(0)) == null) || ! (o instanceof GlyphClassTable)) {
+ if (((o = entries.get(0)) == null) || !(o instanceof GlyphClassTable)) {
throw new AdvancedTypographicTableFormatException("illegal entries, first entry must be an GlyphClassTable, but is: " + ((o != null) ? o.getClass() : null));
} else {
icdt = (GlyphClassTable) o;
}
- if (((o = entries.get(1)) != null) && ! (o instanceof GlyphClassTable)) {
+ if (((o = entries.get(1)) != null) && !(o instanceof GlyphClassTable)) {
throw new AdvancedTypographicTableFormatException("illegal entries, second entry must be an GlyphClassTable, but is: " + o.getClass());
} else {
bcdt = (GlyphClassTable) o;
}
- if (((o = entries.get(2)) != null) && ! (o instanceof GlyphClassTable)) {
+ if (((o = entries.get(2)) != null) && !(o instanceof GlyphClassTable)) {
throw new AdvancedTypographicTableFormatException("illegal entries, third entry must be an GlyphClassTable, but is: " + o.getClass());
} else {
lcdt = (GlyphClassTable) o;
}
- if (((o = entries.get(3)) == null) || ! (o instanceof Integer)) {
+ if (((o = entries.get(3)) == null) || !(o instanceof Integer)) {
throw new AdvancedTypographicTableFormatException("illegal entries, fourth entry must be an Integer, but is: " + ((o != null) ? o.getClass() : null));
} else {
ngc = ((Integer)(o)).intValue();
}
- if (((o = entries.get(4)) == null) || ! (o instanceof RuleSet[])) {
+ if (((o = entries.get(4)) == null) || !(o instanceof RuleSet[])) {
throw new AdvancedTypographicTableFormatException("illegal entries, fifth entry must be an RuleSet[], but is: " + ((o != null) ? o.getClass() : null));
} else {
rsa = (RuleSet[]) o;
@@ -1280,7 +1278,7 @@ public class GlyphSubstitutionTable extends GlyphTable {
throw new AdvancedTypographicTableFormatException("illegal entries, " + entries.size() + " entries present, but requires 1 entry");
} else {
Object o;
- if (((o = entries.get(0)) == null) || ! (o instanceof RuleSet[])) {
+ if (((o = entries.get(0)) == null) || !(o instanceof RuleSet[])) {
throw new AdvancedTypographicTableFormatException("illegal entries, first entry must be an RuleSet[], but is: " + ((o != null) ? o.getClass() : null));
} else {
rsa = (RuleSet[]) o;
diff --git a/src/java/org/apache/fop/complexscripts/fonts/GlyphSubtable.java b/src/java/org/apache/fop/complexscripts/fonts/GlyphSubtable.java
index a3cad4fd7..e5af471af 100644
--- a/src/java/org/apache/fop/complexscripts/fonts/GlyphSubtable.java
+++ b/src/java/org/apache/fop/complexscripts/fonts/GlyphSubtable.java
@@ -24,7 +24,6 @@ import java.lang.ref.WeakReference;
import java.util.List;
import java.util.Map;
-// CSOFF: InnerAssignmentCheck
// CSOFF: LineLengthCheck
/**
diff --git a/src/java/org/apache/fop/complexscripts/fonts/GlyphTable.java b/src/java/org/apache/fop/complexscripts/fonts/GlyphTable.java
index 33f59194b..d130e654a 100644
--- a/src/java/org/apache/fop/complexscripts/fonts/GlyphTable.java
+++ b/src/java/org/apache/fop/complexscripts/fonts/GlyphTable.java
@@ -37,12 +37,7 @@ import org.apache.commons.logging.LogFactory;
import org.apache.fop.complexscripts.util.GlyphSequence;
import org.apache.fop.complexscripts.util.ScriptContextTester;
-// CSOFF: EmptyForIteratorPadCheck
-// CSOFF: InnerAssignmentCheck
// CSOFF: LineLengthCheck
-// CSOFF: NoWhitespaceAfterCheck
-// CSOFF: ParameterNumberCheck
-// CSOFF: SimplifyBooleanReturnCheck
/**
* <p>Base class for all advanced typographic glyph tables.</p>
@@ -86,7 +81,7 @@ public class GlyphTable {
* @param lookups map from lookup specs to lookup tables
*/
public GlyphTable(GlyphTable gdef, Map/*<LookupSpec,List<String>>*/ lookups) {
- if ((gdef != null) && ! (gdef instanceof GlyphDefinitionTable)) {
+ if ((gdef != null) && !(gdef instanceof GlyphDefinitionTable)) {
throw new AdvancedTypographicTableFormatException("bad glyph definition table");
} else if (lookups == null) {
throw new AdvancedTypographicTableFormatException("lookups must be non-null map");
@@ -166,7 +161,7 @@ public class GlyphTable {
* create resulting cached state.
*/
protected void freezeSubtables() {
- if (! frozen) {
+ if (!frozen) {
for (Iterator it = lookupTables.values().iterator(); it.hasNext(); ) {
LookupTable lt = (LookupTable) it.next();
lt.freezeSubtables(lookupTables);
@@ -188,18 +183,18 @@ public class GlyphTable {
List/*<LookupSpec>*/ matches = new ArrayList/*<LookupSpec>*/();
for (Iterator it = keys.iterator(); it.hasNext();) {
LookupSpec ls = (LookupSpec) it.next();
- if (! "*".equals(script)) {
- if (! ls.getScript().equals(script)) {
+ if (!"*".equals(script)) {
+ if (!ls.getScript().equals(script)) {
continue;
}
}
- if (! "*".equals(language)) {
- if (! ls.getLanguage().equals(language)) {
+ if (!"*".equals(language)) {
+ if (!ls.getLanguage().equals(language)) {
continue;
}
}
- if (! "*".equals(feature)) {
- if (! ls.getFeature().equals(feature)) {
+ if (!"*".equals(feature)) {
+ if (!ls.getFeature().equals(feature)) {
continue;
}
}
@@ -359,17 +354,17 @@ public class GlyphTable {
* @param permitWildcard if true the permit wildcard script, language, or feature
*/
LookupSpec(String script, String language, String feature, boolean permitEmpty, boolean permitWildcard) {
- if ((script == null) || (! permitEmpty && (script.length() == 0))) {
+ if ((script == null) || (!permitEmpty && (script.length() == 0))) {
throw new AdvancedTypographicTableFormatException("script must be non-empty string");
- } else if ((language == null) || (! permitEmpty && (language.length() == 0))) {
+ } else if ((language == null) || (!permitEmpty && (language.length() == 0))) {
throw new AdvancedTypographicTableFormatException("language must be non-empty string");
- } else if ((feature == null) || (! permitEmpty && (feature.length() == 0))) {
+ } else if ((feature == null) || (!permitEmpty && (feature.length() == 0))) {
throw new AdvancedTypographicTableFormatException("feature must be non-empty string");
- } else if (! permitWildcard && script.equals("*")) {
+ } else if (!permitWildcard && script.equals("*")) {
throw new AdvancedTypographicTableFormatException("script must not be wildcard");
- } else if (! permitWildcard && language.equals("*")) {
+ } else if (!permitWildcard && language.equals("*")) {
throw new AdvancedTypographicTableFormatException("language must not be wildcard");
- } else if (! permitWildcard && feature.equals("*")) {
+ } else if (!permitWildcard && feature.equals("*")) {
throw new AdvancedTypographicTableFormatException("feature must not be wildcard");
}
this.script = script.trim();
@@ -405,14 +400,12 @@ public class GlyphTable {
public boolean equals(Object o) {
if (o instanceof LookupSpec) {
LookupSpec l = (LookupSpec) o;
- if (! l.script.equals(script)) {
+ if (!l.script.equals(script)) {
return false;
- } else if (! l.language.equals(language)) {
- return false;
- } else if (! l.feature.equals(feature)) {
+ } else if (!l.language.equals(language)) {
return false;
} else {
- return true;
+ return l.feature.equals(feature);
}
} else {
return false;
@@ -540,7 +533,7 @@ public class GlyphTable {
}
}
// append at end of list
- if (! added && (subtable != null)) {
+ if (!added && (subtable != null)) {
subtables.add(subtable);
added = true;
}
@@ -567,7 +560,7 @@ public class GlyphTable {
}
if (subtables.size() > 0) {
GlyphSubtable st = (GlyphSubtable) subtables.get(0);
- if (! st.isCompatible(subtable)) {
+ if (!st.isCompatible(subtable)) {
throw new AdvancedTypographicTableFormatException("subtable " + subtable + " is not compatible with subtable " + st);
}
}
@@ -580,7 +573,7 @@ public class GlyphTable {
* @param lookupTables map from lookup table identifers, e.g. "lu4", to lookup tables
*/
public void freezeSubtables(Map/*<String,LookupTable>*/ lookupTables) {
- if (! frozen) {
+ if (!frozen) {
GlyphSubtable[] sta = getSubtables();
resolveLookupReferences(sta, lookupTables);
this.subtablesArray = sta;
@@ -1305,7 +1298,7 @@ public class GlyphTable {
Class c = r0.getClass();
for (int i = 1, n = rules.length; i < n; i++) {
Rule r = rules[i];
- if ((r != null) && ! c.isInstance(r)) {
+ if ((r != null) && !c.isInstance(r)) {
throw new AdvancedTypographicTableFormatException("rules[" + i + "] is not an instance of " + c.getName());
}
}
diff --git a/src/java/org/apache/fop/complexscripts/fonts/OTFAdvancedTypographicTableReader.java b/src/java/org/apache/fop/complexscripts/fonts/OTFAdvancedTypographicTableReader.java
index b5457e4e4..de083a995 100644
--- a/src/java/org/apache/fop/complexscripts/fonts/OTFAdvancedTypographicTableReader.java
+++ b/src/java/org/apache/fop/complexscripts/fonts/OTFAdvancedTypographicTableReader.java
@@ -29,14 +29,10 @@ import org.apache.commons.logging.Log;
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
-// CSOFF: InnerAssignmentCheck
-// CSOFF: SimplifyBooleanReturnCheck
+import org.apache.fop.fonts.truetype.OFDirTabEntry;
+import org.apache.fop.fonts.truetype.OFTableName;
+import org.apache.fop.fonts.truetype.OpenFont;
+
// CSOFF: LineLengthCheck
/**
@@ -50,7 +46,7 @@ public final class OTFAdvancedTypographicTableReader {
// logging state
private static Log log = LogFactory.getLog(OTFAdvancedTypographicTableReader.class);
// instance state
- private TTFFile ttf; // parent font file reader
+ private OpenFont otf; // parent font file reader
private FontFileReader in; // input reader
private GlyphDefinitionTable gdef; // glyph definition table
private GlyphSubstitutionTable gsub; // glyph substitution table
@@ -68,10 +64,10 @@ public final class OTFAdvancedTypographicTableReader {
* @param ttf parent font file reader (must be non-null)
* @param in font file reader (must be non-null)
*/
- public OTFAdvancedTypographicTableReader(TTFFile ttf, FontFileReader in) {
- assert ttf != null;
+ public OTFAdvancedTypographicTableReader(OpenFont otf, FontFileReader in) {
+ assert otf != null;
assert in != null;
- this.ttf = ttf;
+ this.otf = otf;
this.in = in;
}
@@ -127,7 +123,8 @@ public final class OTFAdvancedTypographicTableReader {
return gpos;
}
- private void readLangSysTable(TTFTableName tableTag, long langSysTable, String langSysTag) throws IOException {
+ private void readLangSysTable(OFTableName tableTag, long langSysTable, String langSysTag)
+ throws IOException {
in.seekSet(langSysTable);
if (log.isDebugEnabled()) {
log.debug(tableTag + " lang sys table: " + langSysTag);
@@ -169,7 +166,7 @@ public final class OTFAdvancedTypographicTableReader {
private static String defaultTag = "dflt";
- private void readScriptTable(TTFTableName tableTag, long scriptTable, String scriptTag) throws IOException {
+ private void readScriptTable(OFTableName tableTag, long scriptTable, String scriptTag) throws IOException {
in.seekSet(scriptTable);
if (log.isDebugEnabled()) {
log.debug(tableTag + " script table: " + scriptTag);
@@ -222,7 +219,7 @@ public final class OTFAdvancedTypographicTableReader {
seLanguages = null;
}
- private void readScriptList(TTFTableName tableTag, long scriptList) throws IOException {
+ private void readScriptList(OFTableName tableTag, long scriptList) throws IOException {
in.seekSet(scriptList);
// read script record count
int ns = in.readTTFUShort();
@@ -251,7 +248,7 @@ public final class OTFAdvancedTypographicTableReader {
}
}
- private void readFeatureTable(TTFTableName tableTag, long featureTable, String featureTag, int featureIndex) throws IOException {
+ private void readFeatureTable(OFTableName tableTag, long featureTable, String featureTag, int featureIndex) throws IOException {
in.seekSet(featureTable);
if (log.isDebugEnabled()) {
log.debug(tableTag + " feature table: " + featureTag);
@@ -279,7 +276,7 @@ public final class OTFAdvancedTypographicTableReader {
seFeatures.put("f" + featureIndex, new Object[] { featureTag, lul });
}
- private void readFeatureList(TTFTableName tableTag, long featureList) throws IOException {
+ private void readFeatureList(OFTableName tableTag, long featureList) throws IOException {
in.seekSet(featureList);
// read feature record count
int nf = in.readTTFUShort();
@@ -1736,28 +1733,28 @@ public final class OTFAdvancedTypographicTableReader {
// XPlacement
int xp;
if ((valueFormat & GlyphPositioningTable.Value.X_PLACEMENT) != 0) {
- xp = ttf.convertTTFUnit2PDFUnit(in.readTTFShort());
+ xp = otf.convertTTFUnit2PDFUnit(in.readTTFShort());
} else {
xp = 0;
}
// YPlacement
int yp;
if ((valueFormat & GlyphPositioningTable.Value.Y_PLACEMENT) != 0) {
- yp = ttf.convertTTFUnit2PDFUnit(in.readTTFShort());
+ yp = otf.convertTTFUnit2PDFUnit(in.readTTFShort());
} else {
yp = 0;
}
// XAdvance
int xa;
if ((valueFormat & GlyphPositioningTable.Value.X_ADVANCE) != 0) {
- xa = ttf.convertTTFUnit2PDFUnit(in.readTTFShort());
+ xa = otf.convertTTFUnit2PDFUnit(in.readTTFShort());
} else {
xa = 0;
}
// YAdvance
int ya;
if ((valueFormat & GlyphPositioningTable.Value.Y_ADVANCE) != 0) {
- ya = ttf.convertTTFUnit2PDFUnit(in.readTTFShort());
+ ya = otf.convertTTFUnit2PDFUnit(in.readTTFShort());
} else {
ya = 0;
}
@@ -2029,23 +2026,23 @@ public final class OTFAdvancedTypographicTableReader {
int af = in.readTTFUShort();
if (af == 1) {
// read x coordinate
- int x = ttf.convertTTFUnit2PDFUnit(in.readTTFShort());
+ int x = otf.convertTTFUnit2PDFUnit(in.readTTFShort());
// read y coordinate
- int y = ttf.convertTTFUnit2PDFUnit(in.readTTFShort());
+ int y = otf.convertTTFUnit2PDFUnit(in.readTTFShort());
a = new GlyphPositioningTable.Anchor(x, y);
} else if (af == 2) {
// read x coordinate
- int x = ttf.convertTTFUnit2PDFUnit(in.readTTFShort());
+ int x = otf.convertTTFUnit2PDFUnit(in.readTTFShort());
// read y coordinate
- int y = ttf.convertTTFUnit2PDFUnit(in.readTTFShort());
+ int y = otf.convertTTFUnit2PDFUnit(in.readTTFShort());
// read anchor point index
int ap = in.readTTFUShort();
a = new GlyphPositioningTable.Anchor(x, y, ap);
} else if (af == 3) {
// read x coordinate
- int x = ttf.convertTTFUnit2PDFUnit(in.readTTFShort());
+ int x = otf.convertTTFUnit2PDFUnit(in.readTTFShort());
// read y coordinate
- int y = ttf.convertTTFUnit2PDFUnit(in.readTTFShort());
+ int y = otf.convertTTFUnit2PDFUnit(in.readTTFShort());
// read x device table offset
int xdo = in.readTTFUShort();
// read y device table offset
@@ -3145,9 +3142,9 @@ public final class OTFAdvancedTypographicTableReader {
resetATSubState();
}
- private void readLookupTable(TTFTableName tableTag, int lookupSequence, long lookupTable) throws IOException {
- boolean isGSUB = tableTag.equals(TTFTableName.GSUB);
- boolean isGPOS = tableTag.equals(TTFTableName.GPOS);
+ private void readLookupTable(OFTableName tableTag, int lookupSequence, long lookupTable) throws IOException {
+ boolean isGSUB = tableTag.equals(OFTableName.GSUB);
+ boolean isGPOS = tableTag.equals(OFTableName.GPOS);
in.seekSet(lookupTable);
// read lookup type
int lt = in.readTTFUShort();
@@ -3198,7 +3195,7 @@ public final class OTFAdvancedTypographicTableReader {
}
}
- private void readLookupList(TTFTableName tableTag, long lookupList) throws IOException {
+ private void readLookupList(OFTableName tableTag, long lookupList) throws IOException {
in.seekSet(lookupList);
// read lookup record count
int nl = in.readTTFUShort();
@@ -3233,7 +3230,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(TTFTableName tableTag, long scriptList, long featureList, long lookupList) throws IOException {
+ private void readCommonLayoutTables(OFTableName tableTag, long scriptList, long featureList, long lookupList) throws IOException {
if (scriptList > 0) {
readScriptList(tableTag, scriptList);
}
@@ -3245,7 +3242,7 @@ public final class OTFAdvancedTypographicTableReader {
}
}
- private void readGDEFClassDefTable(TTFTableName tableTag, int lookupSequence, long subtableOffset) throws IOException {
+ private void readGDEFClassDefTable(OFTableName tableTag, int lookupSequence, long subtableOffset) throws IOException {
initATSubState();
in.seekSet(subtableOffset);
// subtable is a bare class definition table
@@ -3257,7 +3254,7 @@ public final class OTFAdvancedTypographicTableReader {
resetATSubState();
}
- private void readGDEFAttachmentTable(TTFTableName tableTag, int lookupSequence, long subtableOffset) throws IOException {
+ private void readGDEFAttachmentTable(OFTableName tableTag, int lookupSequence, long subtableOffset) throws IOException {
initATSubState();
in.seekSet(subtableOffset);
// read coverage offset
@@ -3275,7 +3272,7 @@ public final class OTFAdvancedTypographicTableReader {
resetATSubState();
}
- private void readGDEFLigatureCaretTable(TTFTableName tableTag, int lookupSequence, long subtableOffset) throws IOException {
+ private void readGDEFLigatureCaretTable(OFTableName tableTag, int lookupSequence, long subtableOffset) throws IOException {
initATSubState();
in.seekSet(subtableOffset);
// read coverage offset
@@ -3305,7 +3302,7 @@ public final class OTFAdvancedTypographicTableReader {
resetATSubState();
}
- private void readGDEFMarkAttachmentTable(TTFTableName tableTag, int lookupSequence, long subtableOffset) throws IOException {
+ private void readGDEFMarkAttachmentTable(OFTableName tableTag, int lookupSequence, long subtableOffset) throws IOException {
initATSubState();
in.seekSet(subtableOffset);
// subtable is a bare class definition table
@@ -3317,7 +3314,7 @@ public final class OTFAdvancedTypographicTableReader {
resetATSubState();
}
- private void readGDEFMarkGlyphsTableFormat1(TTFTableName tableTag, int lookupSequence, long subtableOffset, int subtableFormat) throws IOException {
+ private void readGDEFMarkGlyphsTableFormat1(OFTableName tableTag, int lookupSequence, long subtableOffset, int subtableFormat) throws IOException {
initATSubState();
in.seekSet(subtableOffset);
// skip over format (already known)
@@ -3351,7 +3348,7 @@ public final class OTFAdvancedTypographicTableReader {
resetATSubState();
}
- private void readGDEFMarkGlyphsTable(TTFTableName tableTag, int lookupSequence, long subtableOffset) throws IOException {
+ private void readGDEFMarkGlyphsTable(OFTableName tableTag, int lookupSequence, long subtableOffset) throws IOException {
in.seekSet(subtableOffset);
// read mark set subtable format
int sf = in.readTTFUShort();
@@ -3367,17 +3364,17 @@ public final class OTFAdvancedTypographicTableReader {
* @throws IOException In case of a I/O problem
*/
private void readGDEF() throws IOException {
- TTFTableName tableTag = TTFTableName.GDEF;
+ OFTableName tableTag = OFTableName.GDEF;
// Initialize temporary state
initATState();
// Read glyph definition (GDEF) table
- TTFDirTabEntry dirTab = ttf.getDirectoryEntry(tableTag);
+ OFDirTabEntry dirTab = otf.getDirectoryEntry(tableTag);
if (gdef != null) {
if (log.isDebugEnabled()) {
log.debug(tableTag + ": ignoring duplicate table");
}
} else if (dirTab != null) {
- ttf.seekTab(in, tableTag, 0);
+ otf.seekTab(in, tableTag, 0);
long version = in.readTTFULong();
if (log.isDebugEnabled()) {
log.debug(tableTag + " version: " + (version / 65536) + "." + (version % 65536));
@@ -3440,17 +3437,17 @@ public final class OTFAdvancedTypographicTableReader {
* @throws IOException In case of a I/O problem
*/
private void readGSUB() throws IOException {
- TTFTableName tableTag = TTFTableName.GSUB;
+ OFTableName tableTag = OFTableName.GSUB;
// Initialize temporary state
initATState();
// Read glyph substitution (GSUB) table
- TTFDirTabEntry dirTab = ttf.getDirectoryEntry(tableTag);
+ OFDirTabEntry dirTab = otf.getDirectoryEntry(tableTag);
if (gpos != null) {
if (log.isDebugEnabled()) {
log.debug(tableTag + ": ignoring duplicate table");
}
} else if (dirTab != null) {
- ttf.seekTab(in, tableTag, 0);
+ otf.seekTab(in, tableTag, 0);
int version = in.readTTFLong();
if (log.isDebugEnabled()) {
log.debug(tableTag + " version: " + (version / 65536) + "." + (version % 65536));
@@ -3477,17 +3474,17 @@ public final class OTFAdvancedTypographicTableReader {
* @throws IOException In case of a I/O problem
*/
private void readGPOS() throws IOException {
- TTFTableName tableTag = TTFTableName.GPOS;
+ OFTableName tableTag = OFTableName.GPOS;
// Initialize temporary state
initATState();
// Read glyph positioning (GPOS) table
- TTFDirTabEntry dirTab = ttf.getDirectoryEntry(tableTag);
+ OFDirTabEntry dirTab = otf.getDirectoryEntry(tableTag);
if (gpos != null) {
if (log.isDebugEnabled()) {
log.debug(tableTag + ": ignoring duplicate table");
}
} else if (dirTab != null) {
- ttf.seekTab(in, tableTag, 0);
+ otf.seekTab(in, tableTag, 0);
int version = in.readTTFLong();
if (log.isDebugEnabled()) {
log.debug(tableTag + " version: " + (version / 65536) + "." + (version % 65536));
@@ -3790,7 +3787,7 @@ public final class OTFAdvancedTypographicTableReader {
} else {
boolean first = true;
for (int i = 0; i < ia.length; i++) {
- if (! first) {
+ if (!first) {
sb.append(' ');
} else {
first = false;
diff --git a/src/java/org/apache/fop/complexscripts/scripts/ArabicScriptProcessor.java b/src/java/org/apache/fop/complexscripts/scripts/ArabicScriptProcessor.java
index 1d310b21d..5e68c8763 100644
--- a/src/java/org/apache/fop/complexscripts/scripts/ArabicScriptProcessor.java
+++ b/src/java/org/apache/fop/complexscripts/scripts/ArabicScriptProcessor.java
@@ -33,10 +33,6 @@ import org.apache.fop.complexscripts.util.GlyphContextTester;
import org.apache.fop.complexscripts.util.GlyphSequence;
import org.apache.fop.complexscripts.util.ScriptContextTester;
-// CSOFF: AvoidNestedBlocksCheck
-// CSOFF: NoWhitespaceAfterCheck
-// CSOFF: InnerAssignmentCheck
-// CSOFF: SimplifyBooleanReturnCheck
// CSOFF: LineLengthCheck
/**
@@ -159,11 +155,11 @@ public class ArabicScriptProcessor extends DefaultScriptProcessor {
} else {
int s = a.getStart();
int e = a.getEnd();
- if (! hasFinalPrecedingContext(ca, nc, s, e)) {
+ if (!hasFinalPrecedingContext(ca, nc, s, e)) {
return false;
} else if (forcesFinalThisContext(ca, nc, s, e)) {
return true;
- } else if (! hasFinalFollowingContext(ca, nc, s, e)) {
+ } else if (!hasFinalFollowingContext(ca, nc, s, e)) {
return false;
} else {
return true;
@@ -180,9 +176,9 @@ public class ArabicScriptProcessor extends DefaultScriptProcessor {
} else {
int s = a.getStart();
int e = a.getEnd();
- if (! hasInitialPrecedingContext(ca, nc, s, e)) {
+ if (!hasInitialPrecedingContext(ca, nc, s, e)) {
return false;
- } else if (! hasInitialFollowingContext(ca, nc, s, e)) {
+ } else if (!hasInitialFollowingContext(ca, nc, s, e)) {
return false;
} else {
return true;
@@ -211,9 +207,9 @@ public class ArabicScriptProcessor extends DefaultScriptProcessor {
} else {
int s = a.getStart();
int e = a.getEnd();
- if (! hasLigaturePrecedingContext(ca, nc, s, e)) {
+ if (!hasLigaturePrecedingContext(ca, nc, s, e)) {
return false;
- } else if (! hasLigatureFollowingContext(ca, nc, s, e)) {
+ } else if (!hasLigatureFollowingContext(ca, nc, s, e)) {
return false;
} else {
return true;
@@ -230,11 +226,11 @@ public class ArabicScriptProcessor extends DefaultScriptProcessor {
} else {
int s = a.getStart();
int e = a.getEnd();
- if (! hasMedialPrecedingContext(ca, nc, s, e)) {
+ if (!hasMedialPrecedingContext(ca, nc, s, e)) {
return false;
- } else if (! hasMedialThisContext(ca, nc, s, e)) {
+ } else if (!hasMedialThisContext(ca, nc, s, e)) {
return false;
- } else if (! hasMedialFollowingContext(ca, nc, s, e)) {
+ } else if (!hasMedialFollowingContext(ca, nc, s, e)) {
return false;
} else {
return true;
diff --git a/src/java/org/apache/fop/complexscripts/scripts/DevanagariScriptProcessor.java b/src/java/org/apache/fop/complexscripts/scripts/DevanagariScriptProcessor.java
index 0459863ee..e3843f444 100644
--- a/src/java/org/apache/fop/complexscripts/scripts/DevanagariScriptProcessor.java
+++ b/src/java/org/apache/fop/complexscripts/scripts/DevanagariScriptProcessor.java
@@ -24,11 +24,6 @@ import org.apache.commons.logging.LogFactory;
import org.apache.fop.complexscripts.util.GlyphSequence;
-// CSOFF: AvoidNestedBlocksCheck
-// CSOFF: NoWhitespaceAfterCheck
-// CSOFF: WhitespaceAfter
-// CSOFF: InnerAssignmentCheck
-// CSOFF: SimplifyBooleanReturnCheck
// CSOFF: LineLengthCheck
/**
@@ -109,7 +104,7 @@ public class DevanagariScriptProcessor extends IndicScriptProcessor {
}
private static boolean containsHalfConsonant(GlyphSequence gs, int k) {
- Boolean half = (Boolean) gs.getAssociation(k) . getPredication("half");
+ Boolean half = (Boolean) gs.getAssociation(k) .getPredication("half");
return (half != null) ? half.booleanValue() : false;
}
@@ -134,7 +129,7 @@ public class DevanagariScriptProcessor extends IndicScriptProcessor {
// first candidate target is after first non-half consonant
for (int i = 0; i < ng; i++) {
if ((i != source) && containsConsonant(gs, i)) {
- if (! containsHalfConsonant(gs, i)) {
+ if (!containsHalfConsonant(gs, i)) {
c1 = i + 1;
break;
}
@@ -142,7 +137,7 @@ public class DevanagariScriptProcessor extends IndicScriptProcessor {
}
// second candidate target is after last non-prebase matra after first candidate or before first syllable or vedic mark
for (int i = (c1 >= 0) ? c1 : 0; i < ng; i++) {
- if (containsMatra(gs, i) && ! containsPreBaseMatra(gs, i)) {
+ if (containsMatra(gs, i) && !containsPreBaseMatra(gs, i)) {
c2 = i + 1;
} else if (containsOtherMark(gs, i)) {
c2 = i;
@@ -159,7 +154,7 @@ public class DevanagariScriptProcessor extends IndicScriptProcessor {
}
private static boolean containsReph(GlyphSequence gs, int k) {
- Boolean rphf = (Boolean) gs.getAssociation(k) . getPredication("rphf");
+ Boolean rphf = (Boolean) gs.getAssociation(k) .getPredication("rphf");
return (rphf != null) ? rphf.booleanValue() : false;
}
@@ -503,13 +498,13 @@ public class DevanagariScriptProcessor extends IndicScriptProcessor {
}
}
static boolean isC(int c) {
- return isType(c,C_C);
+ return isType(c, C_C);
}
static boolean isR(int c) {
- return isType(c,C_C) && hasR(c);
+ return isType(c, C_C) && hasR(c);
}
static boolean isV(int c) {
- return isType(c,C_V);
+ return isType(c, C_V);
}
static boolean isN(int c) {
return c == 0x093C;
@@ -518,10 +513,10 @@ public class DevanagariScriptProcessor extends IndicScriptProcessor {
return c == 0x094D;
}
static boolean isM(int c) {
- return isType(c,C_M);
+ return isType(c, C_M);
}
static boolean isPreM(int c) {
- return isType(c,C_M) && hasFlag(c,C_PRE);
+ return isType(c, C_M) && hasFlag(c, C_PRE);
}
static boolean isX(int c) {
switch (typeOf(c)) {
@@ -535,10 +530,10 @@ public class DevanagariScriptProcessor extends IndicScriptProcessor {
}
}
static boolean hasR(int c) {
- return hasFlag(c,C_R);
+ return hasFlag(c, C_R);
}
static boolean hasN(int c) {
- return hasFlag(c,C_N);
+ return hasFlag(c, C_N);
}
}
diff --git a/src/java/org/apache/fop/complexscripts/scripts/GujaratiScriptProcessor.java b/src/java/org/apache/fop/complexscripts/scripts/GujaratiScriptProcessor.java
index dc97e79a8..e4519623e 100644
--- a/src/java/org/apache/fop/complexscripts/scripts/GujaratiScriptProcessor.java
+++ b/src/java/org/apache/fop/complexscripts/scripts/GujaratiScriptProcessor.java
@@ -24,11 +24,6 @@ import org.apache.commons.logging.LogFactory;
import org.apache.fop.complexscripts.util.GlyphSequence;
-// CSOFF: AvoidNestedBlocksCheck
-// CSOFF: NoWhitespaceAfterCheck
-// CSOFF: WhitespaceAfter
-// CSOFF: InnerAssignmentCheck
-// CSOFF: SimplifyBooleanReturnCheck
// CSOFF: LineLengthCheck
/**
@@ -109,7 +104,7 @@ public class GujaratiScriptProcessor extends IndicScriptProcessor {
}
private static boolean containsHalfConsonant(GlyphSequence gs, int k) {
- Boolean half = (Boolean) gs.getAssociation(k) . getPredication("half");
+ Boolean half = (Boolean) gs.getAssociation(k) .getPredication("half");
return (half != null) ? half.booleanValue() : false;
}
@@ -134,7 +129,7 @@ public class GujaratiScriptProcessor extends IndicScriptProcessor {
// first candidate target is after first non-half consonant
for (int i = 0; i < ng; i++) {
if ((i != source) && containsConsonant(gs, i)) {
- if (! containsHalfConsonant(gs, i)) {
+ if (!containsHalfConsonant(gs, i)) {
c1 = i + 1;
break;
}
@@ -142,7 +137,7 @@ public class GujaratiScriptProcessor extends IndicScriptProcessor {
}
// second candidate target is after last non-prebase matra after first candidate or before first syllable or vedic mark
for (int i = (c1 >= 0) ? c1 : 0; i < ng; i++) {
- if (containsMatra(gs, i) && ! containsPreBaseMatra(gs, i)) {
+ if (containsMatra(gs, i) && !containsPreBaseMatra(gs, i)) {
c2 = i + 1;
} else if (containsOtherMark(gs, i)) {
c2 = i;
@@ -159,7 +154,7 @@ public class GujaratiScriptProcessor extends IndicScriptProcessor {
}
private static boolean containsReph(GlyphSequence gs, int k) {
- Boolean rphf = (Boolean) gs.getAssociation(k) . getPredication("rphf");
+ Boolean rphf = (Boolean) gs.getAssociation(k) .getPredication("rphf");
return (rphf != null) ? rphf.booleanValue() : false;
}
@@ -503,13 +498,13 @@ public class GujaratiScriptProcessor extends IndicScriptProcessor {
}
}
static boolean isC(int c) {
- return isType(c,C_C);
+ return isType(c, C_C);
}
static boolean isR(int c) {
- return isType(c,C_C) && hasR(c);
+ return isType(c, C_C) && hasR(c);
}
static boolean isV(int c) {
- return isType(c,C_V);
+ return isType(c, C_V);
}
static boolean isN(int c) {
return c == 0x0ABC;
@@ -518,10 +513,10 @@ public class GujaratiScriptProcessor extends IndicScriptProcessor {
return c == 0x0ACD;
}
static boolean isM(int c) {
- return isType(c,C_M);
+ return isType(c, C_M);
}
static boolean isPreM(int c) {
- return isType(c,C_M) && hasFlag(c,C_PRE);
+ return isType(c, C_M) && hasFlag(c, C_PRE);
}
static boolean isX(int c) {
switch (typeOf(c)) {
@@ -535,10 +530,10 @@ public class GujaratiScriptProcessor extends IndicScriptProcessor {
}
}
static boolean hasR(int c) {
- return hasFlag(c,C_R);
+ return hasFlag(c, C_R);
}
static boolean hasN(int c) {
- return hasFlag(c,C_N);
+ return hasFlag(c, C_N);
}
}
diff --git a/src/java/org/apache/fop/complexscripts/scripts/GurmukhiScriptProcessor.java b/src/java/org/apache/fop/complexscripts/scripts/GurmukhiScriptProcessor.java
index 262d56864..9c4d49f59 100644
--- a/src/java/org/apache/fop/complexscripts/scripts/GurmukhiScriptProcessor.java
+++ b/src/java/org/apache/fop/complexscripts/scripts/GurmukhiScriptProcessor.java
@@ -25,11 +25,6 @@ import org.apache.commons.logging.LogFactory;
import org.apache.fop.complexscripts.fonts.GlyphDefinitionTable;
import org.apache.fop.complexscripts.util.GlyphSequence;
-// CSOFF: AvoidNestedBlocksCheck
-// CSOFF: NoWhitespaceAfterCheck
-// CSOFF: WhitespaceAfter
-// CSOFF: InnerAssignmentCheck
-// CSOFF: SimplifyBooleanReturnCheck
// CSOFF: LineLengthCheck
/**
@@ -110,7 +105,7 @@ public class GurmukhiScriptProcessor extends IndicScriptProcessor {
}
private static boolean containsHalfConsonant(GlyphSequence gs, int k) {
- Boolean half = (Boolean) gs.getAssociation(k) . getPredication("half");
+ Boolean half = (Boolean) gs.getAssociation(k) .getPredication("half");
return (half != null) ? half.booleanValue() : false;
}
@@ -135,7 +130,7 @@ public class GurmukhiScriptProcessor extends IndicScriptProcessor {
// first candidate target is after first non-half consonant
for (int i = 0; i < ng; i++) {
if ((i != source) && containsConsonant(gs, i)) {
- if (! containsHalfConsonant(gs, i)) {
+ if (!containsHalfConsonant(gs, i)) {
c1 = i + 1;
break;
}
@@ -143,7 +138,7 @@ public class GurmukhiScriptProcessor extends IndicScriptProcessor {
}
// second candidate target is after last non-prebase matra after first candidate or before first syllable or vedic mark
for (int i = (c1 >= 0) ? c1 : 0; i < ng; i++) {
- if (containsMatra(gs, i) && ! containsPreBaseMatra(gs, i)) {
+ if (containsMatra(gs, i) && !containsPreBaseMatra(gs, i)) {
c2 = i + 1;
} else if (containsOtherMark(gs, i)) {
c2 = i;
@@ -160,7 +155,7 @@ public class GurmukhiScriptProcessor extends IndicScriptProcessor {
}
private static boolean containsReph(GlyphSequence gs, int k) {
- Boolean rphf = (Boolean) gs.getAssociation(k) . getPredication("rphf");
+ Boolean rphf = (Boolean) gs.getAssociation(k) .getPredication("rphf");
return (rphf != null) ? rphf.booleanValue() : false;
}
@@ -504,13 +499,13 @@ public class GurmukhiScriptProcessor extends IndicScriptProcessor {
}
}
static boolean isC(int c) {
- return isType(c,C_C);
+ return isType(c, C_C);
}
static boolean isR(int c) {
- return isType(c,C_C) && hasR(c);
+ return isType(c, C_C) && hasR(c);
}
static boolean isV(int c) {
- return isType(c,C_V);
+ return isType(c, C_V);
}
static boolean isN(int c) {
return c == 0x0A3C;
@@ -519,10 +514,10 @@ public class GurmukhiScriptProcessor extends IndicScriptProcessor {
return c == 0x0A4D;
}
static boolean isM(int c) {
- return isType(c,C_M);
+ return isType(c, C_M);
}
static boolean isPreM(int c) {
- return isType(c,C_M) && hasFlag(c,C_PRE);
+ return isType(c, C_M) && hasFlag(c, C_PRE);
}
static boolean isX(int c) {
switch (typeOf(c)) {
@@ -536,10 +531,10 @@ public class GurmukhiScriptProcessor extends IndicScriptProcessor {
}
}
static boolean hasR(int c) {
- return hasFlag(c,C_R);
+ return hasFlag(c, C_R);
}
static boolean hasN(int c) {
- return hasFlag(c,C_N);
+ return hasFlag(c, C_N);
}
@Override
diff --git a/src/java/org/apache/fop/complexscripts/scripts/IndicScriptProcessor.java b/src/java/org/apache/fop/complexscripts/scripts/IndicScriptProcessor.java
index c16c9fe0c..fcabad396 100644
--- a/src/java/org/apache/fop/complexscripts/scripts/IndicScriptProcessor.java
+++ b/src/java/org/apache/fop/complexscripts/scripts/IndicScriptProcessor.java
@@ -36,13 +36,6 @@ import org.apache.fop.complexscripts.util.GlyphContextTester;
import org.apache.fop.complexscripts.util.GlyphSequence;
import org.apache.fop.complexscripts.util.ScriptContextTester;
-// CSOFF: AvoidNestedBlocksCheck
-// CSOFF: NoWhitespaceAfterCheck
-// CSOFF: InnerAssignmentCheck
-// CSOFF: SimplifyBooleanReturnCheck
-// CSOFF: EmptyForIteratorPadCheck
-// CSOFF: WhitespaceAfterCheck
-// CSOFF: ParameterNumberCheck
// CSOFF: LineLengthCheck
/**
@@ -222,7 +215,7 @@ public class IndicScriptProcessor extends DefaultScriptProcessor {
}
private GlyphSequence[] syllabize(GlyphSequence gs, String script, String language) {
- return Syllabizer.getSyllabizer(script, language, getSyllabizerClass()) . syllabize(gs);
+ return Syllabizer.getSyllabizer(script, language, getSyllabizerClass()) .syllabize(gs);
}
private GlyphSequence unsyllabize(GlyphSequence gs, GlyphSequence[] sa) {
@@ -384,12 +377,10 @@ public class IndicScriptProcessor extends DefaultScriptProcessor {
public boolean equals(Object o) {
if (o instanceof Syllabizer) {
Syllabizer s = (Syllabizer) o;
- if (! s.script.equals(script)) {
- return false;
- } else if (! s.language.equals(language)) {
+ if (!s.script.equals(script)) {
return false;
} else {
- return true;
+ return s.language.equals(language);
}
} else {
return false;
@@ -408,7 +399,7 @@ public class IndicScriptProcessor extends DefaultScriptProcessor {
}
return d;
}
- private static Map<String,Syllabizer> syllabizers = new HashMap<String,Syllabizer>();
+ private static Map<String, Syllabizer> syllabizers = new HashMap<String, Syllabizer>();
static Syllabizer getSyllabizer(String script, String language, Class<? extends Syllabizer> syllabizerClass) {
String sid = makeSyllabizerId(script, language);
Syllabizer s = syllabizers.get(sid);
diff --git a/src/java/org/apache/fop/complexscripts/scripts/ScriptProcessor.java b/src/java/org/apache/fop/complexscripts/scripts/ScriptProcessor.java
index 72d092b12..cfcc4ff59 100644
--- a/src/java/org/apache/fop/complexscripts/scripts/ScriptProcessor.java
+++ b/src/java/org/apache/fop/complexscripts/scripts/ScriptProcessor.java
@@ -31,11 +31,7 @@ import org.apache.fop.complexscripts.util.CharScript;
import org.apache.fop.complexscripts.util.GlyphSequence;
import org.apache.fop.complexscripts.util.ScriptContextTester;
-// CSOFF: InnerAssignmentCheck
// CSOFF: LineLengthCheck
-// CSOFF: NoWhitespaceAfterCheck
-// CSOFF: ParameterNumberCheck
-// CSOFF: SimplifyBooleanReturnCheck
/**
* <p>Abstract script processor base class for which an implementation of the substitution and positioning methods
@@ -278,14 +274,12 @@ public abstract class ScriptProcessor {
public boolean equals(Object o) {
if (o instanceof AssembledLookupsKey) {
AssembledLookupsKey k = (AssembledLookupsKey) o;
- if (! table.equals(k.table)) {
+ if (!table.equals(k.table)) {
return false;
- } else if (! Arrays.equals(features, k.features)) {
- return false;
- } else if (! lookups.equals(k.lookups)) {
+ } else if (!Arrays.equals(features, k.features)) {
return false;
} else {
- return true;
+ return lookups.equals(k.lookups);
}
} else {
return false;
diff --git a/src/java/org/apache/fop/complexscripts/util/CharScript.java b/src/java/org/apache/fop/complexscripts/util/CharScript.java
index e81313aef..6809be609 100644
--- a/src/java/org/apache/fop/complexscripts/util/CharScript.java
+++ b/src/java/org/apache/fop/complexscripts/util/CharScript.java
@@ -28,12 +28,6 @@ import java.util.Set;
import org.apache.fop.util.CharUtilities;
-// CSOFF: AvoidNestedBlocksCheck
-// CSOFF: InnerAssignmentCheck
-// CSOFF: LineLengthCheck
-// CSOFF: SimplifyBooleanReturnCheck
-// CSOFF: WhitespaceAfterCheck
-
/**
* <p>Script related utilities.</p>
*
@@ -41,6 +35,8 @@ import org.apache.fop.util.CharUtilities;
*/
public final class CharScript {
+ // CSOFF: LineLength
+
//
// The following script codes are based on ISO 15924. Codes less than 1000 are
// official assignments from 15924; those equal to or greater than 1000 are FOP
@@ -754,16 +750,14 @@ public final class CharScript {
case SCRIPT_UNCODED:
break;
default:
- {
- Integer v = (Integer) e.getValue();
- assert v != null;
- int c = v.intValue();
- if (c > cMax) {
- cMax = c;
- sMax = s;
- }
- break;
+ Integer v = (Integer) e.getValue();
+ assert v != null;
+ int c = v.intValue();
+ if (c > cMax) {
+ cMax = c;
+ sMax = s;
}
+ break;
}
}
if (sMax < 0) {
@@ -823,7 +817,7 @@ public final class CharScript {
* @return a script tag
*/
public static String scriptTagFromCode(int code) {
- Map<Integer,String> m = getScriptTagsMap();
+ Map<Integer, String> m = getScriptTagsMap();
if (m != null) {
String tag;
if ((tag = m.get(Integer.valueOf(code))) != null) {
@@ -842,7 +836,7 @@ public final class CharScript {
* @return a script code
*/
public static int scriptCodeFromTag(String tag) {
- Map<String,Integer> m = getScriptCodeMap();
+ Map<String, Integer> m = getScriptCodeMap();
if (m != null) {
Integer c;
if ((c = m.get(tag)) != null) {
@@ -855,8 +849,8 @@ public final class CharScript {
}
}
- private static Map<Integer,String> scriptTagsMap = null;
- private static Map<String,Integer> scriptCodeMap = null;
+ private static Map<Integer, String> scriptTagsMap = null;
+ private static Map<String, Integer> scriptCodeMap = null;
private static void putScriptTag(Map tm, Map cm, int code, String tag) {
assert tag != null;
@@ -868,8 +862,8 @@ public final class CharScript {
}
private static void makeScriptMaps() {
- HashMap<Integer,String> tm = new HashMap<Integer,String>();
- HashMap<String,Integer> cm = new HashMap<String,Integer>();
+ HashMap<Integer, String> tm = new HashMap<Integer, String>();
+ HashMap<String, Integer> cm = new HashMap<String, Integer>();
putScriptTag(tm, cm, SCRIPT_HEBREW, "hebr");
putScriptTag(tm, cm, SCRIPT_MONGOLIAN, "mong");
putScriptTag(tm, cm, SCRIPT_ARABIC, "arab");
@@ -915,14 +909,14 @@ public final class CharScript {
scriptCodeMap = cm;
}
- private static Map<Integer,String> getScriptTagsMap() {
+ private static Map<Integer, String> getScriptTagsMap() {
if (scriptTagsMap == null) {
makeScriptMaps();
}
return scriptTagsMap;
}
- private static Map<String,Integer> getScriptCodeMap() {
+ private static Map<String, Integer> getScriptCodeMap() {
if (scriptCodeMap == null) {
makeScriptMaps();
}
diff --git a/src/java/org/apache/fop/complexscripts/util/GlyphSequence.java b/src/java/org/apache/fop/complexscripts/util/GlyphSequence.java
index 696d2c8d3..e59dc9b32 100644
--- a/src/java/org/apache/fop/complexscripts/util/GlyphSequence.java
+++ b/src/java/org/apache/fop/complexscripts/util/GlyphSequence.java
@@ -26,10 +26,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
-// CSOFF: InnerAssignmentCheck
// CSOFF: LineLengthCheck
-// CSOFF: WhitespaceAfterCheck
-// CSOFF: NoWhitespaceAfterCheck
/**
* <p>A GlyphSequence encapsulates a sequence of character codes, a sequence of glyph codes,
@@ -639,10 +636,10 @@ public class GlyphSequence implements Cloneable {
private final int offset;
private final int count;
private final int[] subIntervals;
- private Map<String,Object> predications;
+ private Map<String, Object> predications;
// class state
- private static volatile Map<String,PredicationMerger> predicationMergers;
+ private static volatile Map<String, PredicationMerger> predicationMergers;
interface PredicationMerger {
Object merge(String key, Object v1, Object v2);
@@ -724,7 +721,7 @@ public class GlyphSequence implements Cloneable {
public boolean contained(int offset, int count) {
int s = offset;
int e = offset + count;
- if (! isDisjoint()) {
+ if (!isDisjoint()) {
int s0 = getStart();
int e0 = getEnd();
return (s0 >= s) && (e0 <= e);
@@ -748,7 +745,7 @@ public class GlyphSequence implements Cloneable {
*/
public void setPredication(String key, Object value) {
if (predications == null) {
- predications = new HashMap<String,Object>();
+ predications = new HashMap<String, Object>();
}
if (predications != null) {
predications.put(key, value);
@@ -775,7 +772,7 @@ public class GlyphSequence implements Cloneable {
*/
public void mergePredication(String key, Object value) {
if (predications == null) {
- predications = new HashMap<String,Object>();
+ predications = new HashMap<String, Object>();
}
if (predications != null) {
if (predications.containsKey(key)) {
@@ -813,7 +810,7 @@ public class GlyphSequence implements Cloneable {
*/
public void mergePredications(CharAssociation ca) {
if (ca.predications != null) {
- for (Map.Entry<String,Object> e : ca.predications.entrySet()) {
+ for (Map.Entry<String, Object> e : ca.predications.entrySet()) {
mergePredication(e.getKey(), e.getValue());
}
}
@@ -824,7 +821,7 @@ public class GlyphSequence implements Cloneable {
try {
CharAssociation ca = (CharAssociation) super.clone();
if (predications != null) {
- ca.predications = new HashMap<String,Object>(predications);
+ ca.predications = new HashMap<String, Object>(predications);
}
return ca;
} catch (CloneNotSupportedException e) {
@@ -839,7 +836,7 @@ public class GlyphSequence implements Cloneable {
*/
public static void setPredicationMerger(String key, PredicationMerger pm) {
if (predicationMergers == null) {
- predicationMergers = new HashMap<String,PredicationMerger>();
+ predicationMergers = new HashMap<String, PredicationMerger>();
}
if (predicationMergers != null) {
predicationMergers.put(key, pm);
diff --git a/src/java/org/apache/fop/complexscripts/util/NumberConverter.java b/src/java/org/apache/fop/complexscripts/util/NumberConverter.java
index 08b4a4434..bfb8b6571 100644
--- a/src/java/org/apache/fop/complexscripts/util/NumberConverter.java
+++ b/src/java/org/apache/fop/complexscripts/util/NumberConverter.java
@@ -23,9 +23,6 @@ import java.util.ArrayList;
import java.util.List;
// CSOFF: LineLengthCheck
-// CSOFF: InnerAssignmentCheck
-// CSOFF: NoWhitespaceAfterCheck
-// CSOFF: AvoidNestedBlocksCheck
/**
* <p>Implementation of Number to String Conversion algorithm specified by
@@ -186,10 +183,10 @@ public class NumberConverter {
separators.add(token.toArray(new Integer [ token.size() ]));
}
}
- if (! separators.isEmpty()) {
+ if (!separators.isEmpty()) {
this.prefix = separators.remove(0);
}
- if (! separators.isEmpty()) {
+ if (!separators.isEmpty()) {
this.suffix = separators.remove(separators.size() - 1);
}
this.separators = separators.toArray(new Integer [ separators.size() ] []);
@@ -260,33 +257,27 @@ public class NumberConverter {
int s = token[0].intValue();
switch (s) {
case (int) '1':
- {
- fn = formatNumberAsDecimal(number, (int) '1', 1);
- break;
- }
+ fn = formatNumberAsDecimal(number, (int) '1', 1);
+ break;
case (int) 'W':
case (int) 'w':
- {
- fn = formatNumberAsWord(number, (s == (int) 'W') ? Character.UPPERCASE_LETTER : Character.LOWERCASE_LETTER);
- break;
- }
+ fn = formatNumberAsWord(number, (s == (int) 'W') ? Character.UPPERCASE_LETTER : Character.LOWERCASE_LETTER);
+ break;
case (int) 'A': // handled as numeric sequence
case (int) 'a': // handled as numeric sequence
case (int) 'I': // handled as numeric special
case (int) 'i': // handled as numeric special
default:
- {
- if (isStartOfDecimalSequence(s)) {
- fn = formatNumberAsDecimal(number, s, 1);
- } else if (isStartOfAlphabeticSequence(s)) {
- fn = formatNumberAsSequence(number, s, getSequenceBase(s), null);
- } else if (isStartOfNumericSpecial(s)) {
- fn = formatNumberAsSpecial(number, s);
- } else {
- fn = null;
- }
- break;
+ if (isStartOfDecimalSequence(s)) {
+ fn = formatNumberAsDecimal(number, s, 1);
+ } else if (isStartOfAlphabeticSequence(s)) {
+ fn = formatNumberAsSequence(number, s, getSequenceBase(s), null);
+ } else if (isStartOfNumericSpecial(s)) {
+ fn = formatNumberAsSpecial(number, s);
+ } else {
+ fn = null;
}
+ break;
}
} else if ((token.length == 2) && (token[0] == (int) 'W') && (token[1] == (int) 'w')) {
fn = formatNumberAsWord(number, Character.TITLECASE_LETTER);
diff --git a/src/java/org/apache/fop/complexscripts/util/UTF32.java b/src/java/org/apache/fop/complexscripts/util/UTF32.java
index 225966f54..b0f92eb88 100644
--- a/src/java/org/apache/fop/complexscripts/util/UTF32.java
+++ b/src/java/org/apache/fop/complexscripts/util/UTF32.java
@@ -21,7 +21,6 @@ package org.apache.fop.complexscripts.util;
import org.apache.fop.util.CharUtilities;
-// CSOFF: InnerAssignmentCheck
/**
* <p>UTF32 related utilities.</p>
diff --git a/src/java/org/apache/fop/fo/Constants.java b/src/java/org/apache/fop/fo/Constants.java
index 28f02a762..086cbca40 100644
--- a/src/java/org/apache/fop/fo/Constants.java
+++ b/src/java/org/apache/fop/fo/Constants.java
@@ -816,8 +816,11 @@ public interface Constants {
/** Scope for table header */
int PR_X_HEADER_COLUMN = 290;
+ /** For specifying PDF optional content group (layer) binding. */
+ int PR_X_LAYER = 291;
+
/** Number of property constants defined */
- int PROPERTY_COUNT = 290;
+ int PROPERTY_COUNT = 291;
// compound property constants
diff --git a/src/java/org/apache/fop/fo/FOPropertyMapping.java b/src/java/org/apache/fop/fo/FOPropertyMapping.java
index 36e3f21c4..cc4fe9e2c 100644
--- a/src/java/org/apache/fop/fo/FOPropertyMapping.java
+++ b/src/java/org/apache/fop/fo/FOPropertyMapping.java
@@ -2721,6 +2721,13 @@ public final class FOPropertyMapping implements Constants {
m.addEnum("auto", getEnumProperty(EN_AUTO, "AUTO"));
m.setDefault("auto");
addPropertyMaker("z-index", m);
+
+ // fox:layer
+ m = new StringProperty.Maker(PR_X_LAYER);
+ m.setInherited(false);
+ m.setDefault("");
+ addPropertyMaker("fox:layer", m);
+
}
private void createShorthandProperties() {
diff --git a/src/java/org/apache/fop/fo/FObj.java b/src/java/org/apache/fop/fo/FObj.java
index 92dc9fd73..8532b27da 100644
--- a/src/java/org/apache/fop/fo/FObj.java
+++ b/src/java/org/apache/fop/fo/FObj.java
@@ -74,7 +74,8 @@ public abstract class FObj extends FONode implements Constants {
private int bidiLevel = -1;
// The value of properties relevant for all fo objects
- private String id = null;
+ private String id;
+ private String layer;
// End of property values
/**
@@ -148,7 +149,7 @@ public abstract class FObj extends FONode implements Constants {
String attributeName = attList.getQName(i);
String attributeValue = attList.getValue(i);
Property prop = propertyList.getPropertyForAttribute(attList, attributeName, attributeValue);
- if (prop.equals(value)) {
+ if (prop != null && prop.equals(value)) {
return attributeName;
}
}
@@ -173,6 +174,7 @@ public abstract class FObj extends FONode implements Constants {
*/
public void bind(PropertyList pList) throws FOPException {
id = pList.get(PR_ID).getString();
+ layer = pList.get(PR_X_LAYER).getString();
}
/**
@@ -583,6 +585,16 @@ public abstract class FObj extends FONode implements Constants {
return (id != null && id.length() > 0);
}
+ /** @return the "layer" property. */
+ public String getLayer() {
+ return layer;
+ }
+
+ /** @return whether this object has an layer set */
+ public boolean hasLayer() {
+ return (layer != null && layer.length() > 0);
+ }
+
/** {@inheritDoc} */
public String getNamespaceURI() {
return FOElementMapping.URI;
@@ -611,7 +623,7 @@ public abstract class FObj extends FONode implements Constants {
if (bidiLevel >= 0) {
if ((this.bidiLevel < 0) || (bidiLevel < this.bidiLevel)) {
this.bidiLevel = bidiLevel;
- if (parent != null) {
+ if ((parent != null) && !isBidiPropagationBoundary()) {
FObj foParent = (FObj) parent;
int parentBidiLevel = foParent.getBidiLevel();
if ((parentBidiLevel < 0) || (bidiLevel < parentBidiLevel)) {
@@ -646,10 +658,25 @@ public abstract class FObj extends FONode implements Constants {
return level;
}
}
+ if (isBidiInheritanceBoundary()) {
+ break;
+ }
}
return -1;
}
+ protected boolean isBidiBoundary(boolean propagate) {
+ return false;
+ }
+
+ private boolean isBidiInheritanceBoundary() {
+ return isBidiBoundary(false);
+ }
+
+ private boolean isBidiPropagationBoundary() {
+ return isBidiBoundary(true);
+ }
+
/**
* Add a new extension attachment to this FObj.
* (see org.apache.fop.fo.FONode for details)
diff --git a/src/java/org/apache/fop/fo/expr/FunctionBase.java b/src/java/org/apache/fop/fo/expr/FunctionBase.java
index 707424b86..e5e346060 100644
--- a/src/java/org/apache/fop/fo/expr/FunctionBase.java
+++ b/src/java/org/apache/fop/fo/expr/FunctionBase.java
@@ -36,7 +36,8 @@ public abstract class FunctionBase implements Function {
/** {@inheritDoc} */
public Property getOptionalArgDefault(int index, PropertyInfo pi) throws PropertyException {
if (index >= getOptionalArgsCount()) {
- PropertyException e = new PropertyException(new IndexOutOfBoundsException("illegal optional argument index"));
+ PropertyException e = new PropertyException(
+ new IndexOutOfBoundsException("illegal optional argument index"));
e.setPropertyInfo(pi);
throw e;
} else {
diff --git a/src/java/org/apache/fop/fo/expr/PropertyParser.java b/src/java/org/apache/fop/fo/expr/PropertyParser.java
index cac115d6e..26c8f6c1b 100644
--- a/src/java/org/apache/fop/fo/expr/PropertyParser.java
+++ b/src/java/org/apache/fop/fo/expr/PropertyParser.java
@@ -385,7 +385,8 @@ public final class PropertyParser extends PropertyTokenizer {
}
int numArgs = args.size();
if (numArgs < numReq) {
- throw new PropertyException("Expected " + numReq + " required arguments, but only " + numArgs + " specified");
+ throw new PropertyException("Expected " + numReq + " required arguments, but only "
+ + numArgs + " specified");
} else {
for (int i = 0; i < numOpt; i++) {
if (args.size() < (numReq + i + 1)) {
diff --git a/src/java/org/apache/fop/fo/extensions/ExtensionElementMapping.java b/src/java/org/apache/fop/fo/extensions/ExtensionElementMapping.java
index 09b47f02a..82db43e59 100644
--- a/src/java/org/apache/fop/fo/extensions/ExtensionElementMapping.java
+++ b/src/java/org/apache/fop/fo/extensions/ExtensionElementMapping.java
@@ -66,6 +66,8 @@ public class ExtensionElementMapping extends ElementMapping {
PROPERTY_ATTRIBUTES.add("border-before-end-radius");
PROPERTY_ATTRIBUTES.add("border-after-start-radius");
PROPERTY_ATTRIBUTES.add("border-after-end-radius");
+ //Optional content groups (layers)
+ PROPERTY_ATTRIBUTES.add("layer");
}
/**
diff --git a/src/java/org/apache/fop/fo/flow/BlockContainer.java b/src/java/org/apache/fop/fo/flow/BlockContainer.java
index b8616c1ce..b250a117c 100644
--- a/src/java/org/apache/fop/fo/flow/BlockContainer.java
+++ b/src/java/org/apache/fop/fo/flow/BlockContainer.java
@@ -98,7 +98,8 @@ public class BlockContainer extends FObj implements BreakPropertySet, WritingMod
referenceOrientation = pList.get(PR_REFERENCE_ORIENTATION).getNumeric();
span = pList.get(PR_SPAN).getEnum();
writingModeTraits = new WritingModeTraits(
- WritingMode.valueOf(pList.get(PR_WRITING_MODE).getEnum()));
+ WritingMode.valueOf(pList.get(PR_WRITING_MODE).getEnum()),
+ pList.getExplicit(PR_WRITING_MODE) != null);
disableColumnBalancing = pList.get(PR_X_DISABLE_COLUMN_BALANCING).getEnum();
}
@@ -280,6 +281,14 @@ public class BlockContainer extends FObj implements BreakPropertySet, WritingMod
return writingModeTraits.getWritingMode();
}
+ /**
+ * Obtain writing mode explicit indicator.
+ * @return the writing mode explicit indicator
+ */
+ public boolean getExplicitWritingMode() {
+ return writingModeTraits.getExplicitWritingMode();
+ }
+
/** {@inheritDoc} */
public String getLocalName() {
return "block-container";
@@ -292,5 +301,10 @@ public class BlockContainer extends FObj implements BreakPropertySet, WritingMod
public int getNameId() {
return FO_BLOCK_CONTAINER;
}
-}
+ @Override
+ protected boolean isBidiBoundary(boolean propagate) {
+ return getExplicitWritingMode();
+ }
+
+}
diff --git a/src/java/org/apache/fop/fo/flow/InlineContainer.java b/src/java/org/apache/fop/fo/flow/InlineContainer.java
index 748eb593a..5c95fa34e 100644
--- a/src/java/org/apache/fop/fo/flow/InlineContainer.java
+++ b/src/java/org/apache/fop/fo/flow/InlineContainer.java
@@ -37,49 +37,38 @@ import org.apache.fop.traits.Direction;
import org.apache.fop.traits.WritingMode;
import org.apache.fop.traits.WritingModeTraits;
-/**
- * Class modelling the <a href="http://www.w3.org/TR/xsl/#fo_inline-container">
- * <code>fo:inline-container</code></a> object.
- */
public class InlineContainer extends FObj {
- // The value of FO traits (refined properties) that apply to fo:inline-container.
- private Length alignmentAdjust;
- private int alignmentBaseline;
- private Length baselineShift;
+ private LengthRangeProperty inlineProgressionDimension;
private LengthRangeProperty blockProgressionDimension;
+ private int overflow;
private CommonBorderPaddingBackground commonBorderPaddingBackground;
private CommonMarginInline commonMarginInline;
- private int clip;
- private int dominantBaseline;
- private LengthRangeProperty inlineProgressionDimension;
+ private Numeric referenceOrientation;
+ private int displayAlign;
private KeepProperty keepTogether;
+ private KeepProperty keepWithNext;
+ private KeepProperty keepWithPrevious;
private SpaceProperty lineHeight;
- private int overflow;
- private Numeric referenceOrientation;
+ private Length alignmentAdjust;
+ private int alignmentBaseline;
+ private Length baselineShift;
+ private int dominantBaseline;
private WritingModeTraits writingModeTraits;
- // Unused but valid items, commented out for performance:
- // private CommonRelativePosition commonRelativePosition;
- // private int displayAlign;
- // private Length height;
- // private KeepProperty keepWithNext;
- // private KeepProperty keepWithPrevious;
- // private Length width;
- // End of FO trait values
/** used for FO validation */
- private boolean blockItemFound = false;
+ private boolean blockItemFound;
/**
- * Base constructor
+ * Creates a new instance.
*
- * @param parent {@link FONode} that is the parent of this object
+ * @param parent the parent of this inline-container
*/
public InlineContainer(FONode parent) {
super(parent);
}
- /** {@inheritDoc} */
+ @Override
public void bind(PropertyList pList) throws FOPException {
super.bind(pList);
alignmentAdjust = pList.get(PR_ALIGNMENT_ADJUST).getLength();
@@ -88,27 +77,31 @@ public class InlineContainer extends FObj {
blockProgressionDimension = pList.get(PR_BLOCK_PROGRESSION_DIMENSION).getLengthRange();
commonBorderPaddingBackground = pList.getBorderPaddingBackgroundProps();
commonMarginInline = pList.getMarginInlineProps();
- clip = pList.get(PR_CLIP).getEnum();
+ displayAlign = pList.get(PR_DISPLAY_ALIGN).getEnum();
dominantBaseline = pList.get(PR_DOMINANT_BASELINE).getEnum();
inlineProgressionDimension = pList.get(PR_INLINE_PROGRESSION_DIMENSION).getLengthRange();
keepTogether = pList.get(PR_KEEP_TOGETHER).getKeep();
+ keepWithNext = pList.get(PR_KEEP_WITH_NEXT).getKeep();
+ keepWithPrevious = pList.get(PR_KEEP_WITH_PREVIOUS).getKeep();
lineHeight = pList.get(PR_LINE_HEIGHT).getSpace();
overflow = pList.get(PR_OVERFLOW).getEnum();
referenceOrientation = pList.get(PR_REFERENCE_ORIENTATION).getNumeric();
writingModeTraits = new WritingModeTraits(
- WritingMode.valueOf(pList.get(PR_WRITING_MODE).getEnum()));
+ WritingMode.valueOf(pList.get(PR_WRITING_MODE).getEnum()),
+ pList.getExplicit(PR_WRITING_MODE) != null);
}
/**
* {@inheritDoc}
* <br>XSL Content Model: marker* (%block;)+
*/
+ @Override
protected void validateChildNode(Locator loc, String nsURI, String localName)
throws ValidationException {
if (FO_URI.equals(nsURI)) {
if (localName.equals("marker")) {
if (blockItemFound) {
- nodesOutOfOrderError(loc, "fo:marker", "(%block;)");
+ nodesOutOfOrderError(loc, "fo:marker", "(%block;)+");
}
} else if (!isBlockItem(nsURI, localName)) {
invalidChildError(loc, nsURI, localName);
@@ -118,142 +111,131 @@ public class InlineContainer extends FObj {
}
}
- /** {@inheritDoc} */
+ @Override
public void endOfNode() throws FOPException {
if (!blockItemFound) {
missingChildElementError("marker* (%block;)+");
}
}
- /** @return the "alignment-adjust" FO trait */
- public Length getAlignmentAdjust() {
- return alignmentAdjust;
+ /** {@inheritDoc} */
+ public String getLocalName() {
+ return "inline-container";
}
- /** @return the "alignment-baseline" FO trait */
- public int getAlignmentBaseline() {
- return alignmentBaseline;
+ /**
+ * {@inheritDoc}
+ * @return {@link org.apache.fop.fo.Constants#FO_INLINE_CONTAINER}
+ */
+ public int getNameId() {
+ return FO_INLINE_CONTAINER;
}
- /** @return the "baseline-shift" FO trait */
- public Length getBaselineShift() {
- return baselineShift;
+ public LengthRangeProperty getInlineProgressionDimension() {
+ return inlineProgressionDimension;
}
- /** @return the "block-progression-dimension" FO trait */
public LengthRangeProperty getBlockProgressionDimension() {
return blockProgressionDimension;
}
- /** @return the "clip" FO trait */
- public int getClip() {
- return clip;
+ public int getOverflow() {
+ return overflow;
}
- /**@return Returns the {@link CommonBorderPaddingBackground} */
public CommonBorderPaddingBackground getCommonBorderPaddingBackground() {
return this.commonBorderPaddingBackground;
}
- /** @return Returns the {@link CommonMarginInline} */
public CommonMarginInline getCommonMarginInline() {
return this.commonMarginInline;
}
- /** @return the "dominant-baseline" FO trait */
- public int getDominantBaseline() {
- return dominantBaseline;
+ public int getReferenceOrientation() {
+ return referenceOrientation.getValue();
+ }
+
+ public int getDisplayAlign() {
+ return this.displayAlign;
+ }
+
+ public KeepProperty getKeepWithPrevious() {
+ return keepWithPrevious;
}
- /** @return the "keep-together" FO trait */
public KeepProperty getKeepTogether() {
return keepTogether;
}
- /** @return the "inline-progression-dimension" FO trait */
- public LengthRangeProperty getInlineProgressionDimension() {
- return inlineProgressionDimension;
+ public KeepProperty getKeepWithNext() {
+ return keepWithNext;
}
- /** @return the "line-height" FO trait */
public SpaceProperty getLineHeight() {
return lineHeight;
}
- /** @return the "overflow" FO trait */
- public int getOverflow() {
- return overflow;
+ public Length getAlignmentAdjust() {
+ return alignmentAdjust;
}
- /** @return the "reference-orientation" FO trait */
- public int getReferenceOrientation() {
- return referenceOrientation.getValue();
+ public int getAlignmentBaseline() {
+ return alignmentBaseline;
+ }
+
+ public Length getBaselineShift() {
+ return baselineShift;
+ }
+
+ public int getDominantBaseline() {
+ return dominantBaseline;
+ }
+
+ public WritingMode getWritingMode() {
+ return writingModeTraits.getWritingMode();
}
/**
- * Obtain inline progression direction.
- * @return the inline progression direction
+ * Obtain writing mode explicit indicator.
+ * @return the writing mode explicit indicator
*/
+ public boolean getExplicitWritingMode() {
+ return writingModeTraits.getExplicitWritingMode();
+ }
+
public Direction getInlineProgressionDirection() {
return writingModeTraits.getInlineProgressionDirection();
}
- /**
- * Obtain block progression direction.
- * @return the block progression direction
- */
public Direction getBlockProgressionDirection() {
return writingModeTraits.getBlockProgressionDirection();
}
- /**
- * Obtain column progression direction.
- * @return the column progression direction
- */
public Direction getColumnProgressionDirection() {
return writingModeTraits.getColumnProgressionDirection();
}
- /**
- * Obtain row progression direction.
- * @return the row progression direction
- */
public Direction getRowProgressionDirection() {
return writingModeTraits.getRowProgressionDirection();
}
- /**
- * Obtain (baseline) shift direction.
- * @return the (baseline) shift direction
- */
public Direction getShiftDirection() {
return writingModeTraits.getShiftDirection();
}
- /**
- * Obtain writing mode.
- * @return the writing mode
- */
- public WritingMode getWritingMode() {
- return writingModeTraits.getWritingMode();
- }
-
- /** {@inheritDoc} */
- public String getLocalName() {
- return "inline-container";
+ @Override
+ public boolean isDelimitedTextRangeBoundary(int boundary) {
+ return false;
}
- /**
- * {@inheritDoc}
- * @return {@link org.apache.fop.fo.Constants#FO_INLINE_CONTAINER}
- */
- public int getNameId() {
- return FO_INLINE_CONTAINER;
+ @Override
+ public boolean generatesReferenceAreas() {
+ return true;
}
@Override
- public boolean isDelimitedTextRangeBoundary(int boundary) {
- return false;
+ protected boolean isBidiBoundary(boolean propagate) {
+ return getExplicitWritingMode();
}
}
diff --git a/src/java/org/apache/fop/fo/flow/table/Table.java b/src/java/org/apache/fop/fo/flow/table/Table.java
index 983954e79..e1252e45e 100644
--- a/src/java/org/apache/fop/fo/flow/table/Table.java
+++ b/src/java/org/apache/fop/fo/flow/table/Table.java
@@ -139,7 +139,8 @@ public class Table extends TableFObj implements ColumnNumberManagerHolder, Break
tableOmitFooterAtBreak = pList.get(PR_TABLE_OMIT_FOOTER_AT_BREAK).getEnum();
tableOmitHeaderAtBreak = pList.get(PR_TABLE_OMIT_HEADER_AT_BREAK).getEnum();
writingModeTraits = new WritingModeTraits(
- WritingMode.valueOf(pList.get(PR_WRITING_MODE).getEnum()));
+ WritingMode.valueOf(pList.get(PR_WRITING_MODE).getEnum()),
+ pList.getExplicit(PR_WRITING_MODE) != null);
//Bind extension properties
widowContentLimit = pList.get(PR_X_WIDOW_CONTENT_LIMIT).getLength();
@@ -554,6 +555,11 @@ public class Table extends TableFObj implements ColumnNumberManagerHolder, Break
return writingModeTraits.getWritingMode();
}
+ /** {@inheritDoc} */
+ public boolean getExplicitWritingMode() {
+ return writingModeTraits.getExplicitWritingMode();
+ }
+
/** @return the "fox:widow-content-limit" extension FO trait */
public Length getWidowContentLimit() {
return widowContentLimit;
@@ -620,4 +626,9 @@ public class Table extends TableFObj implements ColumnNumberManagerHolder, Break
return ranges;
}
+ @Override
+ protected boolean isBidiBoundary(boolean propagate) {
+ return getExplicitWritingMode();
+ }
+
}
diff --git a/src/java/org/apache/fop/fo/pagination/PageSequence.java b/src/java/org/apache/fop/fo/pagination/PageSequence.java
index 368b69f90..ba874d31f 100644
--- a/src/java/org/apache/fop/fo/pagination/PageSequence.java
+++ b/src/java/org/apache/fop/fo/pagination/PageSequence.java
@@ -96,7 +96,8 @@ public class PageSequence extends AbstractPageSequence implements WritingModeTra
masterReference = pList.get(PR_MASTER_REFERENCE).getString();
referenceOrientation = pList.get(PR_REFERENCE_ORIENTATION).getNumeric();
writingModeTraits = new WritingModeTraits(
- WritingMode.valueOf(pList.get(PR_WRITING_MODE).getEnum()));
+ WritingMode.valueOf(pList.get(PR_WRITING_MODE).getEnum()),
+ pList.getExplicit(PR_WRITING_MODE) != null);
if (masterReference == null || masterReference.equals("")) {
missingPropertyError("master-reference");
}
@@ -403,6 +404,16 @@ public class PageSequence extends AbstractPageSequence implements WritingModeTra
}
}
+ /**
+ * {@inheritDoc}
+ */
+ public boolean getExplicitWritingMode() {
+ if (writingModeTraits != null) {
+ return writingModeTraits.getExplicitWritingMode();
+ } else {
+ return false;
+ }
+ }
@Override
protected Stack collectDelimitedTextRanges(Stack ranges, DelimitedTextRange currentRange) {
@@ -423,6 +434,11 @@ public class PageSequence extends AbstractPageSequence implements WritingModeTra
return ranges;
}
+ @Override
+ protected boolean isBidiBoundary(boolean propagate) {
+ return true;
+ }
+
/**
* Releases a page-sequence's children after the page-sequence has been fully processed.
*/
diff --git a/src/java/org/apache/fop/fo/properties/GenericShorthandParser.java b/src/java/org/apache/fop/fo/properties/GenericShorthandParser.java
index ac3d4d13a..140b96aac 100644
--- a/src/java/org/apache/fop/fo/properties/GenericShorthandParser.java
+++ b/src/java/org/apache/fop/fo/properties/GenericShorthandParser.java
@@ -83,13 +83,17 @@ public class GenericShorthandParser implements ShorthandParser {
PropertyList propertyList)
throws PropertyException {
Property prop = null;
+ String vProperty = "";
// Try each of the stored values in turn
Iterator iprop = property.getList().iterator();
while (iprop.hasNext() && prop == null) {
Property p = (Property)iprop.next();
+ if (p.getNCname() != null) {
+ vProperty += p.getNCname() + " ";
+ }
prop = maker.convertShorthandProperty(propertyList, p, null);
- propertyList.validatePropertyValue(p.getNCname(), prop, property);
}
+ propertyList.validatePropertyValue(vProperty.trim(), prop, property);
return prop;
}
diff --git a/src/java/org/apache/fop/fonts/FontLoader.java b/src/java/org/apache/fop/fonts/FontLoader.java
index f7ee24cbc..09e38260e 100644
--- a/src/java/org/apache/fop/fonts/FontLoader.java
+++ b/src/java/org/apache/fop/fonts/FontLoader.java
@@ -26,7 +26,7 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.fop.apps.io.InternalResourceResolver;
-import org.apache.fop.fonts.truetype.TTFFontLoader;
+import org.apache.fop.fonts.truetype.OFFontLoader;
import org.apache.fop.fonts.type1.Type1FontLoader;
/**
@@ -105,7 +105,7 @@ public abstract class FontLoader {
}
loader = new Type1FontLoader(fontFileURI, embedded, useKerning, resourceResolver);
} else {
- loader = new TTFFontLoader(fontFileURI, subFontName, embedded, embeddingMode,
+ loader = new OFFontLoader(fontFileURI, subFontName, embedded, embeddingMode,
encodingMode, useKerning, useAdvanced, resourceResolver);
}
return loader.getFont();
diff --git a/src/java/org/apache/fop/fonts/FontManagerConfigurator.java b/src/java/org/apache/fop/fonts/FontManagerConfigurator.java
index fc2ce06a6..72c1684b6 100644
--- a/src/java/org/apache/fop/fonts/FontManagerConfigurator.java
+++ b/src/java/org/apache/fop/fonts/FontManagerConfigurator.java
@@ -48,20 +48,24 @@ public class FontManagerConfigurator {
private final Configuration cfg;
- private final URI defaultBaseUri;
+ private final URI baseURI;
+
+ private final URI fallbackURI;
private final ResourceResolver resourceResolver;
/**
* Main constructor
* @param cfg the font manager configuration object
- * @param defaultBaseUri the default URI base to use for URI resolution
+ * @param baseURI the URI against which to resolve relative URIs
+ * @param fallbackURI the URI to use as a fallback if font-base is unspecified
* @param resourceResolver the resource resolver
*/
- public FontManagerConfigurator(Configuration cfg, URI defaultBaseUri,
+ public FontManagerConfigurator(Configuration cfg, URI baseURI, URI fallbackURI,
ResourceResolver resourceResolver) {
this.cfg = cfg;
- this.defaultBaseUri = defaultBaseUri;
+ this.baseURI = baseURI;
+ this.fallbackURI = fallbackURI;
this.resourceResolver = resourceResolver;
}
@@ -77,13 +81,13 @@ public class FontManagerConfigurator {
URI fontBase = InternalResourceResolver.getBaseURI(cfg.getChild("font-base")
.getValue(null));
fontManager.setResourceResolver(ResourceResolverFactory.createInternalResourceResolver(
- defaultBaseUri.resolve(fontBase), resourceResolver));
+ baseURI.resolve(fontBase), resourceResolver));
} catch (URISyntaxException use) {
LogUtil.handleException(log, use, true);
}
} else {
fontManager.setResourceResolver(ResourceResolverFactory.createInternalResourceResolver(
- defaultBaseUri, resourceResolver));
+ fallbackURI, resourceResolver));
}
// caching (fonts)
if (cfg.getChild("use-cache", false) != null) {
diff --git a/src/java/org/apache/fop/fonts/FontTriplet.java b/src/java/org/apache/fop/fonts/FontTriplet.java
index 2c1d89088..9392cc8b9 100644
--- a/src/java/org/apache/fop/fonts/FontTriplet.java
+++ b/src/java/org/apache/fop/fonts/FontTriplet.java
@@ -27,7 +27,8 @@ import java.io.Serializable;
*/
public class FontTriplet implements Comparable<FontTriplet>, Serializable {
- public static final FontTriplet DEFAULT_FONT_TRIPLET = new FontTriplet("any", Font.STYLE_NORMAL, Font.WEIGHT_NORMAL);
+ public static final FontTriplet DEFAULT_FONT_TRIPLET
+ = new FontTriplet("any", Font.STYLE_NORMAL, Font.WEIGHT_NORMAL);
/** serial version UID */
private static final long serialVersionUID = 1168991106658033508L;
diff --git a/src/java/org/apache/fop/fonts/MultiByteFont.java b/src/java/org/apache/fop/fonts/MultiByteFont.java
index 1d1186dcb..cc3d06650 100644
--- a/src/java/org/apache/fop/fonts/MultiByteFont.java
+++ b/src/java/org/apache/fop/fonts/MultiByteFont.java
@@ -23,6 +23,7 @@ import java.awt.Rectangle;
import java.nio.CharBuffer;
import java.nio.IntBuffer;
import java.util.BitSet;
+import java.util.LinkedHashMap;
import java.util.Map;
import org.apache.commons.logging.Log;
@@ -71,6 +72,15 @@ public class MultiByteFont extends CIDFont implements Substitutable, Positionabl
/** Contains the character bounding boxes for all characters in the font */
protected Rectangle[] boundingBoxes;
+ private boolean isOTFFile = false;
+
+ // since for most users the most likely glyphs are in the first cmap segments we store their mapping.
+ private static final int NUM_MOST_LIKELY_GLYPHS = 256;
+ private int[] mostLikelyGlyphs = new int[NUM_MOST_LIKELY_GLYPHS];
+
+ //A map to store each used glyph from the CID set against the glyph name.
+ private LinkedHashMap<Integer, String> usedGlyphNames = new LinkedHashMap<Integer, String>();
+
/**
* Default constructor
*/
@@ -115,6 +125,14 @@ public class MultiByteFont extends CIDFont implements Substitutable, Positionabl
return cidType;
}
+ public void setIsOTFFile(boolean isOTFFile) {
+ this.isOTFFile = isOTFFile;
+ }
+
+ public boolean isOTFFile() {
+ return this.isOTFFile;
+ }
+
/**
* Sets the CIDType.
* @param cidType The cidType to set
@@ -151,6 +169,14 @@ public class MultiByteFont extends CIDFont implements Substitutable, Positionabl
return this.cidSet;
}
+ public void mapUsedGlyphName(int gid, String value) {
+ usedGlyphNames.put(gid, value);
+ }
+
+ public LinkedHashMap<Integer, String> getUsedGlyphNames() {
+ return usedGlyphNames;
+ }
+
/** {@inheritDoc} */
@Override
public String getEncodingName() {
@@ -187,10 +213,15 @@ public class MultiByteFont extends CIDFont implements Substitutable, Positionabl
* @return the glyph index (or 0 if the glyph is not available)
*/
// [TBD] - needs optimization, i.e., change from linear search to binary search
- private int findGlyphIndex(int c) {
+ public int findGlyphIndex(int c) {
int idx = c;
int retIdx = SingleByteEncoding.NOT_FOUND_CODE_POINT;
+ // for most users the most likely glyphs are in the first cmap segments (meaning the one with
+ // the lowest unicode start values)
+ if (idx < NUM_MOST_LIKELY_GLYPHS && mostLikelyGlyphs[idx] != 0) {
+ return mostLikelyGlyphs[idx];
+ }
for (int i = 0; (i < cmap.length) && retIdx == 0; i++) {
if (cmap[i].getUnicodeStart() <= idx
&& cmap[i].getUnicodeEnd() >= idx) {
@@ -198,6 +229,9 @@ public class MultiByteFont extends CIDFont implements Substitutable, Positionabl
retIdx = cmap[i].getGlyphStartIndex()
+ idx
- cmap[i].getUnicodeStart();
+ if (idx < NUM_MOST_LIKELY_GLYPHS) {
+ mostLikelyGlyphs[idx] = retIdx;
+ }
}
}
return retIdx;
@@ -291,22 +325,6 @@ public class MultiByteFont extends CIDFont implements Substitutable, Positionabl
return findCharacterFromGlyphIndex(gi, true);
}
-
- /** {@inheritDoc} */
- @Override
- public char mapChar(char c) {
- notifyMapOperation();
- int glyphIndex = findGlyphIndex(c);
- if (glyphIndex == SingleByteEncoding.NOT_FOUND_CODE_POINT) {
- warnMissingGlyph(c);
- glyphIndex = findGlyphIndex(Typeface.NOT_FOUND);
- }
- if (isEmbeddable()) {
- glyphIndex = cidSet.mapChar(glyphIndex, c);
- }
- return (char) glyphIndex;
- }
-
protected BitSet getGlyphIndices() {
BitSet bitset = new BitSet();
bitset.set(0);
@@ -339,6 +357,23 @@ public class MultiByteFont extends CIDFont implements Substitutable, Positionabl
/** {@inheritDoc} */
@Override
+ public char mapChar(char c) {
+ notifyMapOperation();
+ int glyphIndex = findGlyphIndex(c);
+ if (glyphIndex == SingleByteEncoding.NOT_FOUND_CODE_POINT) {
+ warnMissingGlyph(c);
+ if (!isOTFFile) {
+ glyphIndex = findGlyphIndex(Typeface.NOT_FOUND);
+ }
+ }
+ if (isEmbeddable()) {
+ glyphIndex = cidSet.mapChar(glyphIndex, c);
+ }
+ return (char) glyphIndex;
+ }
+
+ /** {@inheritDoc} */
+ @Override
public boolean hasChar(char c) {
return (findGlyphIndex(c) != SingleByteEncoding.NOT_FOUND_CODE_POINT);
}
diff --git a/src/java/org/apache/fop/fonts/SingleByteFont.java b/src/java/org/apache/fop/fonts/SingleByteFont.java
index d412609d5..2a6b04761 100644
--- a/src/java/org/apache/fop/fonts/SingleByteFont.java
+++ b/src/java/org/apache/fop/fonts/SingleByteFont.java
@@ -33,7 +33,7 @@ import org.apache.commons.logging.LogFactory;
import org.apache.xmlgraphics.fonts.Glyphs;
import org.apache.fop.apps.io.InternalResourceResolver;
-import org.apache.fop.fonts.truetype.TTFFile.PostScriptVersion;
+import org.apache.fop.fonts.truetype.OpenFont.PostScriptVersion;
/**
* Generic SingleByte font
diff --git a/src/java/org/apache/fop/fonts/apps/AbstractFontReader.java b/src/java/org/apache/fop/fonts/apps/AbstractFontReader.java
index 39f7e14a4..3da33ec3f 100644
--- a/src/java/org/apache/fop/fonts/apps/AbstractFontReader.java
+++ b/src/java/org/apache/fop/fonts/apps/AbstractFontReader.java
@@ -32,8 +32,6 @@ import javax.xml.transform.TransformerFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
-import org.apache.fop.util.CommandLineLogger;
-
/**
* Abstract base class for the PFM and TTF Reader command-line applications.
*/
@@ -90,10 +88,6 @@ public abstract class AbstractFontReader {
protected static void setLogLevel(String level) {
// Set the evel for future loggers.
LogFactory.getFactory().setAttribute("level", level);
- if (log instanceof CommandLineLogger) {
- // Set the level for the logger creates already.
- ((CommandLineLogger) log).setLogLevel(level);
- }
}
/**
diff --git a/src/java/org/apache/fop/fonts/apps/PFMReader.java b/src/java/org/apache/fop/fonts/apps/PFMReader.java
index e5e8ca524..dd7f7343c 100644
--- a/src/java/org/apache/fop/fonts/apps/PFMReader.java
+++ b/src/java/org/apache/fop/fonts/apps/PFMReader.java
@@ -29,11 +29,8 @@ import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
-import org.apache.commons.logging.LogFactory;
-
import org.apache.fop.Version;
import org.apache.fop.fonts.type1.PFMFile;
-import org.apache.fop.util.CommandLineLogger;
/**
* A tool which reads PFM files from Adobe Type 1 fonts and creates
@@ -92,14 +89,6 @@ public class PFMReader extends AbstractFontReader {
Map options = new java.util.HashMap();
String[] arguments = parseArguments(options, args);
- // Enable the simple command line logging when no other logger is
- // defined.
- LogFactory logFactory = LogFactory.getFactory();
- if (System.getProperty("org.apache.commons.logging.Log") == null) {
- logFactory.setAttribute("org.apache.commons.logging.Log",
- CommandLineLogger.class.getName());
- }
-
determineLogLevel(options);
PFMReader app = new PFMReader();
diff --git a/src/java/org/apache/fop/fonts/apps/TTFReader.java b/src/java/org/apache/fop/fonts/apps/TTFReader.java
index 900417350..f63e2bb07 100644
--- a/src/java/org/apache/fop/fonts/apps/TTFReader.java
+++ b/src/java/org/apache/fop/fonts/apps/TTFReader.java
@@ -32,16 +32,13 @@ import org.w3c.dom.Element;
import org.xml.sax.Attributes;
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.OFFontLoader;
import org.apache.fop.fonts.truetype.TTFFile;
-import org.apache.fop.util.CommandLineLogger;
-// CSOFF: InnerAssignmentCheck
// CSOFF: LineLengthCheck
/**
@@ -123,14 +120,6 @@ public class TTFReader extends AbstractFontReader {
Map options = new java.util.HashMap();
String[] arguments = parseArguments(options, args);
- // Enable the simple command line logging when no other logger is
- // defined.
- LogFactory logFactory = LogFactory.getFactory();
- if (System.getProperty("org.apache.commons.logging.Log") == null) {
- logFactory.setAttribute("org.apache.commons.logging.Log",
- CommandLineLogger.class.getName());
- }
-
determineLogLevel(options);
TTFReader app = new TTFReader();
@@ -224,7 +213,8 @@ public class TTFReader extends AbstractFontReader {
InputStream stream = new FileInputStream(fileName);
try {
FontFileReader reader = new FontFileReader(stream);
- boolean supported = ttfFile.readFont(reader, fontName);
+ String header = OFFontLoader.readHeader(reader);
+ boolean supported = ttfFile.readFont(reader, header, fontName);
if (!supported) {
return null;
}
diff --git a/src/java/org/apache/fop/fonts/autodetect/FontInfoFinder.java b/src/java/org/apache/fop/fonts/autodetect/FontInfoFinder.java
index e115264bb..21ebd4937 100644
--- a/src/java/org/apache/fop/fonts/autodetect/FontInfoFinder.java
+++ b/src/java/org/apache/fop/fonts/autodetect/FontInfoFinder.java
@@ -43,8 +43,8 @@ import org.apache.fop.fonts.FontTriplet;
import org.apache.fop.fonts.FontUtil;
import org.apache.fop.fonts.MultiByteFont;
import org.apache.fop.fonts.truetype.FontFileReader;
+import org.apache.fop.fonts.truetype.OFFontLoader;
import org.apache.fop.fonts.truetype.TTFFile;
-import org.apache.fop.fonts.truetype.TTFFontLoader;
/**
* Attempts to determine correct FontInfo
@@ -220,7 +220,7 @@ public class FontInfoFinder {
log.debug("Loading " + fontName);
}
try {
- TTFFontLoader ttfLoader = new TTFFontLoader(fontURI, fontName, true,
+ OFFontLoader ttfLoader = new OFFontLoader(fontURI, fontName, true,
EmbeddingMode.AUTO, EncodingMode.AUTO, useKerning, useAdvanced,
resourceResolver);
customFont = ttfLoader.getFont();
diff --git a/src/java/org/apache/fop/fonts/cff/CFFDataReader.java b/src/java/org/apache/fop/fonts/cff/CFFDataReader.java
new file mode 100644
index 000000000..66126fb84
--- /dev/null
+++ b/src/java/org/apache/fop/fonts/cff/CFFDataReader.java
@@ -0,0 +1,927 @@
+/*
+ * 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.cff;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.fontbox.cff.CFFDataInput;
+import org.apache.fontbox.cff.CFFOperator;
+
+import org.apache.fop.fonts.truetype.FontFileReader;
+import org.apache.fop.fonts.truetype.OTFFile;
+
+/**
+ * A class to read the CFF data from an OTF CFF font file.
+ */
+public class CFFDataReader {
+ private CFFDataInput cffData;
+
+ private byte[] header;
+ private CFFIndexData nameIndex;
+ private CFFIndexData topDICTIndex;
+ private CFFIndexData stringIndex;
+ private CFFIndexData charStringIndex;
+ private CFFIndexData globalIndexSubr;
+ private CFFIndexData localIndexSubr;
+ private CustomEncoding encoding;
+ private FDSelect fdSelect;
+ private List<FontDict> fdFonts;
+
+ private static final int DOUBLE_BYTE_OPERATOR = 12;
+ private static final int NUM_STANDARD_STRINGS = 391;
+
+ /** Commonly used parsed dictionaries */
+ private LinkedHashMap<String, DICTEntry> topDict;
+
+ public CFFDataReader() {
+
+ }
+
+ /**
+ * Constructor for the CFF data reader which accepts the CFF byte data
+ * as an argument.
+ * @param cffDataArray A byte array which holds the CFF data
+ */
+ public CFFDataReader(byte[] cffDataArray) throws IOException {
+ cffData = new CFFDataInput(cffDataArray);
+ readCFFData();
+ }
+
+ /**
+ * Constructor for the CFF data reader which accepts a FontFileReader object
+ * which points to the original font file as an argument.
+ * @param fontFile The font file as represented by a FontFileReader object
+ */
+ public CFFDataReader(FontFileReader fontFile) throws IOException {
+ cffData = new CFFDataInput(OTFFile.getCFFData(fontFile));
+ readCFFData();
+ }
+
+ private void readCFFData() throws IOException {
+ header = readHeader();
+ nameIndex = readIndex();
+ topDICTIndex = readIndex();
+ topDict = parseDictData(topDICTIndex.getData());
+ stringIndex = readIndex();
+ globalIndexSubr = readIndex();
+ charStringIndex = readCharStringIndex();
+ encoding = readEncoding();
+ fdSelect = readFDSelect();
+ localIndexSubr = readLocalIndexSubrs();
+ fdFonts = parseCIDData();
+ }
+
+ public Map<String, DICTEntry> getPrivateDict(DICTEntry privateEntry) throws IOException {
+ return parseDictData(getPrivateDictBytes(privateEntry));
+ }
+
+ public byte[] getPrivateDictBytes(DICTEntry privateEntry) throws IOException {
+ int privateLength = privateEntry.getOperands().get(0).intValue();
+ int privateOffset = privateEntry.getOperands().get(1).intValue();
+ return getCFFOffsetBytes(privateOffset, privateLength);
+ }
+
+ /**
+ * Retrieves a number of bytes from the CFF data stream
+ * @param offset The offset of the bytes to retrieve
+ * @param length The number of bytes to retrieve
+ * @return Returns a byte array of requested bytes
+ * @throws IOException Throws an IO Exception if an error occurs
+ */
+ private byte[] getCFFOffsetBytes(int offset, int length) throws IOException {
+ cffData.setPosition(offset);
+ return cffData.readBytes(length);
+ }
+
+ /**
+ * Parses the dictionary data and returns a map of objects for each entry
+ * @param dictData The data for the dictionary data
+ * @return Returns a map of type DICTEntry identified by the operand name
+ * @throws IOException Throws an IO Exception if an error occurs
+ */
+ public LinkedHashMap<String, DICTEntry> parseDictData(byte[] dictData) throws IOException {
+ LinkedHashMap<String, DICTEntry> dictEntries = new LinkedHashMap<String, DICTEntry>();
+ List<Number> operands = new ArrayList<Number>();
+ List<Integer> operandLengths = new ArrayList<Integer>();
+ int lastOperandLength = 0;
+ for (int i = 0; i < dictData.length; i++) {
+ int readByte = dictData[i] & 0xFF;
+ if (readByte < 28) {
+ int[] operator = new int[(readByte == DOUBLE_BYTE_OPERATOR) ? 2 : 1];
+ if (readByte == DOUBLE_BYTE_OPERATOR) {
+ operator[0] = dictData[i];
+ operator[1] = dictData[i + 1];
+ i++;
+ } else {
+ operator[0] = dictData[i];
+ }
+ String operatorName = "";
+ CFFOperator tempOp = null;
+ if (operator.length > 1) {
+ tempOp = CFFOperator.getOperator(new CFFOperator.Key(operator[0], operator[1]));
+ } else {
+ tempOp = CFFOperator.getOperator(new CFFOperator.Key(operator[0]));
+ }
+ if (tempOp != null) {
+ operatorName = tempOp.getName();
+ }
+ DICTEntry newEntry = new DICTEntry();
+ newEntry.setOperator(operator);
+ newEntry.setOperands(new ArrayList<Number>(operands));
+ newEntry.setOperatorName(operatorName);
+ newEntry.setOffset(i - lastOperandLength);
+ newEntry.setOperandLength(lastOperandLength);
+ newEntry.setOperandLengths(new ArrayList<Integer>(operandLengths));
+ byte[] byteData = new byte[lastOperandLength + operator.length];
+ System.arraycopy(dictData, i - operator.length - (lastOperandLength - 1),
+ byteData, 0, operator.length + lastOperandLength);
+ newEntry.setByteData(byteData);
+ dictEntries.put(operatorName, newEntry);
+ operands.clear();
+ operandLengths.clear();
+ lastOperandLength = 0;
+ } else {
+ if (readByte >= 32 && readByte <= 246) {
+ operands.add(readByte - 139);
+ lastOperandLength += 1;
+ operandLengths.add(1);
+ } else if (readByte >= 247 && readByte <= 250) {
+ operands.add((readByte - 247) * 256 + (dictData[i + 1] & 0xFF) + 108);
+ lastOperandLength += 2;
+ operandLengths.add(2);
+ i++;
+ } else if (readByte >= 251 && readByte <= 254) {
+ operands.add(-(readByte - 251) * 256 - (dictData[i + 1] & 0xFF) - 108);
+ lastOperandLength += 2;
+ operandLengths.add(2);
+ i++;
+ } else if (readByte == 28) {
+ operands.add((dictData[i + 1] & 0xFF) << 8 | (dictData[i + 2] & 0xFF));
+ lastOperandLength += 3;
+ operandLengths.add(3);
+ i += 2;
+ } else if (readByte == 29) {
+ operands.add((dictData[i + 1] & 0xFF) << 24 | (dictData[i + 2] & 0xFF) << 16
+ | (dictData[i + 3] & 0xFF) << 8 | (dictData[i + 4] & 0xFF));
+ lastOperandLength += 5;
+ operandLengths.add(5);
+ i += 4;
+ } else if (readByte == 30) {
+ boolean terminatorFound = false;
+ StringBuilder realNumber = new StringBuilder();
+ int byteCount = 1;
+ do {
+ byte nibblesByte = dictData[++i];
+ byteCount++;
+ terminatorFound = readNibble(realNumber, (nibblesByte >> 4) & 0x0F);
+ if (!terminatorFound) {
+ terminatorFound = readNibble(realNumber, nibblesByte & 0x0F);
+ }
+ } while (!terminatorFound);
+ operands.add(Double.valueOf(realNumber.toString()));
+ lastOperandLength += byteCount;
+ operandLengths.add(byteCount);
+ }
+ }
+ }
+ return dictEntries;
+ }
+
+ private boolean readNibble(StringBuilder realNumber, int nibble) {
+ if (nibble <= 0x9) {
+ realNumber.append(nibble);
+ } else {
+ switch (nibble) {
+ case 0xa: realNumber.append("."); break;
+ case 0xb: realNumber.append("E"); break;
+ case 0xc: realNumber.append("E-"); break;
+ case 0xd: break;
+ case 0xe: realNumber.append("-"); break;
+ case 0xf: return true;
+ default: throw new AssertionError("Unexpected nibble value");
+ }
+ }
+ return false;
+ }
+
+ /**
+ * A class containing data for a dictionary entry
+ */
+ public static class DICTEntry {
+ private int[] operator;
+ private List<Number> operands;
+ private List<Integer> operandLengths;
+ private String operatorName;
+ private int offset;
+ private int operandLength;
+ private byte[] data = new byte[0];
+
+ public void setOperator(int[] operator) {
+ this.operator = operator;
+ }
+
+ public int[] getOperator() {
+ return this.operator;
+ }
+
+ public void setOperands(List<Number> operands) {
+ this.operands = operands;
+ }
+
+ public List<Number> getOperands() {
+ return this.operands;
+ }
+
+ public void setOperatorName(String operatorName) {
+ this.operatorName = operatorName;
+ }
+
+ public String getOperatorName() {
+ return this.operatorName;
+ }
+
+ public void setOffset(int offset) {
+ this.offset = offset;
+ }
+
+ public int getOffset() {
+ return this.offset;
+ }
+
+ public void setOperandLength(int operandLength) {
+ this.operandLength = operandLength;
+ }
+
+ public int getOperandLength() {
+ return this.operandLength;
+ }
+
+ public void setByteData(byte[] data) {
+ this.data = data.clone();
+ }
+
+ public byte[] getByteData() {
+ return data.clone();
+ }
+
+ public void setOperandLengths(List<Integer> operandLengths) {
+ this.operandLengths = operandLengths;
+ }
+
+ public List<Integer> getOperandLengths() {
+ return operandLengths;
+ }
+ }
+
+ private byte[] readHeader() throws IOException {
+ //Read known header
+ byte[] fixedHeader = cffData.readBytes(4);
+ int hdrSize = (fixedHeader[2] & 0xFF);
+ byte[] extra = cffData.readBytes(hdrSize - 4);
+ byte[] header = new byte[hdrSize];
+ for (int i = 0; i < fixedHeader.length; i++) {
+ header[i] = fixedHeader[i];
+ }
+ for (int i = 4; i < extra.length; i++) {
+ header[i] = extra[i - 4];
+ }
+ return header;
+ }
+
+ /**
+ * Reads a CFF index object are the specified offset position
+ * @param offset The position of the index object to read
+ * @return Returns an object representing the index
+ * @throws IOException Throws an IO Exception if an error occurs
+ */
+ public CFFIndexData readIndex(int offset) throws IOException {
+ cffData.setPosition(offset);
+ return readIndex();
+ }
+
+ private CFFIndexData readIndex() throws IOException {
+ return readIndex(cffData);
+ }
+
+ /**
+ * Reads an index from the current position of the CFFDataInput object
+ * @param input The object holding the CFF byte data
+ * @return Returns an object representing the index
+ * @throws IOException Throws an IO Exception if an error occurs
+ */
+ public CFFIndexData readIndex(CFFDataInput input) throws IOException {
+ CFFIndexData nameIndex = new CFFIndexData();
+ if (input != null) {
+ int origPos = input.getPosition();
+ nameIndex.parseIndexHeader(input);
+ int tableSize = input.getPosition() - origPos;
+ nameIndex.setByteData(input.getPosition() - tableSize, tableSize);
+ }
+ return nameIndex;
+ }
+
+ /**
+ * Retrieves the SID for the given GID object
+ * @param charsetOffset The offset of the charset data
+ * @param GID The GID for which to retrieve the SID
+ * @return Returns the SID as an integer
+ */
+ public int getSIDFromGID(int charsetOffset, int gid) throws IOException {
+ if (gid == 0) {
+ return 0;
+ }
+ cffData.setPosition(charsetOffset);
+ int charsetFormat = cffData.readCard8();
+ switch (charsetFormat) {
+ case 0: //Adjust for .notdef character
+ cffData.setPosition(cffData.getPosition() + (--gid * 2));
+ return cffData.readSID();
+ case 1: return getSIDFromGIDFormat(gid, 1);
+ case 2: return getSIDFromGIDFormat(gid, 2);
+ default: return 0;
+ }
+ }
+
+ private int getSIDFromGIDFormat(int gid, int format) throws IOException {
+ int glyphCount = 0;
+ while (true) {
+ int oldGlyphCount = glyphCount;
+ int start = cffData.readSID();
+ glyphCount += ((format == 1) ? cffData.readCard8() : cffData.readCard16()) + 1;
+ if (gid <= glyphCount) {
+ return start + (gid - oldGlyphCount) - 1;
+ }
+ }
+ }
+
+ public byte[] getHeader() {
+ return header.clone();
+ }
+
+ public CFFIndexData getNameIndex() {
+ return nameIndex;
+ }
+
+ public CFFIndexData getTopDictIndex() {
+ return topDICTIndex;
+ }
+
+ public LinkedHashMap<String, DICTEntry> getTopDictEntries() {
+ return topDict;
+ }
+
+ public CFFIndexData getStringIndex() {
+ return stringIndex;
+ }
+
+ public CFFIndexData getGlobalIndexSubr() {
+ return globalIndexSubr;
+ }
+
+ public CFFIndexData getLocalIndexSubr() {
+ return localIndexSubr;
+ }
+
+ public CFFIndexData getCharStringIndex() {
+ return charStringIndex;
+ }
+
+ public CFFDataInput getCFFData() {
+ return cffData;
+ }
+
+ public CustomEncoding getEncoding() {
+ return encoding;
+ }
+
+ public FDSelect getFDSelect() {
+ return fdSelect;
+ }
+
+ public List<FontDict> getFDFonts() {
+ return fdFonts;
+ }
+
+ public CFFDataInput getLocalSubrsForGlyph(int glyph) throws IOException {
+ //Subsets are currently written using a Format0 FDSelect
+ FDSelect fontDictionary = getFDSelect();
+ if (fontDictionary instanceof Format0FDSelect) {
+ Format0FDSelect fdSelect = (Format0FDSelect)fontDictionary;
+ int found = fdSelect.getFDIndexes()[glyph];
+ FontDict font = getFDFonts().get(found);
+ byte[] localSubrData = font.getLocalSubrData().getByteData();
+ if (localSubrData != null) {
+ return new CFFDataInput(localSubrData);
+ } else {
+ return null;
+ }
+ } else if (fontDictionary instanceof Format3FDSelect) {
+ Format3FDSelect fdSelect = (Format3FDSelect)fontDictionary;
+ int index = 0;
+ for (int first : fdSelect.getRanges().keySet()) {
+ if (first > glyph) {
+ break;
+ }
+ index++;
+ }
+ FontDict font = getFDFonts().get(index);
+ byte[] localSubrsData = font.getLocalSubrData().getByteData();
+ if (localSubrsData != null) {
+ return new CFFDataInput(localSubrsData);
+ } else {
+ return null;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Parses the char string index from the CFF byte data
+ * @param offset The offset to the char string index
+ * @return Returns the char string index object
+ * @throws IOException Throws an IO Exception if an error occurs
+ */
+ public CFFIndexData readCharStringIndex() throws IOException {
+ int offset = topDict.get("CharStrings").getOperands().get(0).intValue();
+ cffData.setPosition(offset);
+ return readIndex();
+ }
+
+ private CustomEncoding readEncoding() throws IOException {
+ CustomEncoding foundEncoding = null;
+ if (topDict.get("Encoding") != null) {
+ int offset = topDict.get("Encoding").getOperands().get(0).intValue();
+ if (offset != 0 && offset != 1) {
+ //No need to set the offset as we are reading the data sequentially.
+ int format = cffData.readCard8();
+ int numEntries = cffData.readCard8();
+ switch (format) {
+ case 0:
+ foundEncoding = readFormat0Encoding(format, numEntries);
+ break;
+ case 1:
+ foundEncoding = readFormat1Encoding(format, numEntries);
+ break;
+ default: break;
+ }
+ }
+ }
+ return foundEncoding;
+ }
+
+ private Format0Encoding readFormat0Encoding(int format, int numEntries)
+ throws IOException {
+ Format0Encoding newEncoding = new Format0Encoding();
+ newEncoding.setFormat(format);
+ newEncoding.setNumEntries(numEntries);
+ int[] codes = new int[numEntries];
+ for (int i = 0; i < numEntries; i++) {
+ codes[i] = cffData.readCard8();
+ }
+ newEncoding.setCodes(codes);
+ return newEncoding;
+ }
+
+ private Format1Encoding readFormat1Encoding(int format, int numEntries)
+ throws IOException {
+ Format1Encoding newEncoding = new Format1Encoding();
+ newEncoding.setFormat(format);
+ newEncoding.setNumEntries(numEntries);
+ LinkedHashMap<Integer, Integer> ranges = new LinkedHashMap<Integer, Integer>();
+ for (int i = 0; i < numEntries; i++) {
+ int first = cffData.readCard8();
+ int left = cffData.readCard8();
+ ranges.put(first, left);
+ }
+ newEncoding.setRanges(ranges);
+ return newEncoding;
+ }
+
+ private FDSelect readFDSelect() throws IOException {
+ FDSelect fdSelect = null;
+ DICTEntry fdSelectEntry = topDict.get("FDSelect");
+ if (fdSelectEntry != null) {
+ int fdOffset = fdSelectEntry.getOperands().get(0).intValue();
+ cffData.setPosition(fdOffset);
+ int format = cffData.readCard8();
+ switch (format) {
+ case 0:
+ fdSelect = readFormat0FDSelect();
+ break;
+ case 3:
+ fdSelect = readFormat3FDSelect();
+ break;
+ default:
+ }
+ }
+ return fdSelect;
+ }
+
+ private Format0FDSelect readFormat0FDSelect() throws IOException {
+ Format0FDSelect newFDs = new Format0FDSelect();
+ newFDs.setFormat(0);
+ int glyphCount = charStringIndex.getNumObjects();
+ int[] fds = new int[glyphCount];
+ for (int i = 0; i < glyphCount; i++) {
+ fds[i] = cffData.readCard8();
+ }
+ newFDs.setFDIndexes(fds);
+ return newFDs;
+ }
+
+ private Format3FDSelect readFormat3FDSelect() throws IOException {
+ Format3FDSelect newFDs = new Format3FDSelect();
+ newFDs.setFormat(3);
+ int rangeCount = cffData.readCard16();
+ newFDs.setRangeCount(rangeCount);
+ LinkedHashMap<Integer, Integer> ranges = new LinkedHashMap<Integer, Integer>();
+ for (int i = 0; i < rangeCount; i++) {
+ int first = cffData.readCard16();
+ int fd = cffData.readCard8();
+ ranges.put(first, fd);
+ }
+ newFDs.setRanges(ranges);
+ newFDs.setSentinelGID(cffData.readCard16());
+ return newFDs;
+ }
+
+ private List<FontDict> parseCIDData() throws IOException {
+ ArrayList<FontDict> fdFonts = new ArrayList<FontDict>();
+ if (topDict.get("ROS") != null) {
+ DICTEntry fdArray = topDict.get("FDArray");
+ if (fdArray != null) {
+ int fdIndex = fdArray.getOperands().get(0).intValue();
+ CFFIndexData fontDicts = readIndex(fdIndex);
+ for (int i = 0; i < fontDicts.getNumObjects(); i++) {
+ FontDict newFontDict = new FontDict();
+
+ byte[] fdData = fontDicts.getValue(i);
+ LinkedHashMap<String, DICTEntry> fdEntries = parseDictData(fdData);
+ newFontDict.setByteData(fontDicts.getValuePosition(i), fontDicts.getValueLength(i));
+ DICTEntry fontFDEntry = fdEntries.get("FontName");
+ newFontDict.setFontName(getString(fontFDEntry.getOperands().get(0).intValue()));
+ DICTEntry privateFDEntry = fdEntries.get("Private");
+ if (privateFDEntry != null) {
+ newFontDict = setFDData(privateFDEntry, newFontDict);
+ }
+
+ fdFonts.add(newFontDict);
+ }
+ }
+ }
+ return fdFonts;
+ }
+
+ private FontDict setFDData(DICTEntry privateFDEntry, FontDict newFontDict) throws IOException {
+ int privateFDLength = privateFDEntry.getOperands().get(0).intValue();
+ int privateFDOffset = privateFDEntry.getOperands().get(1).intValue();
+ cffData.setPosition(privateFDOffset);
+ byte[] privateDict = cffData.readBytes(privateFDLength);
+ newFontDict.setPrivateDictData(privateFDOffset, privateFDLength);
+ LinkedHashMap<String, DICTEntry> privateEntries = parseDictData(privateDict);
+ DICTEntry subroutines = privateEntries.get("Subrs");
+ if (subroutines != null) {
+ CFFIndexData localSubrs = readIndex(privateFDOffset
+ + subroutines.getOperands().get(0).intValue());
+ newFontDict.setLocalSubrData(localSubrs);
+ } else {
+ newFontDict.setLocalSubrData(new CFFIndexData());
+ }
+ return newFontDict;
+ }
+
+ private String getString(int sid) throws IOException {
+ return new String(stringIndex.getValue(sid - NUM_STANDARD_STRINGS));
+ }
+
+ private CFFIndexData readLocalIndexSubrs() throws IOException {
+ CFFIndexData localSubrs = null;
+ DICTEntry privateEntry = topDict.get("Private");
+ if (privateEntry != null) {
+ int length = privateEntry.getOperands().get(0).intValue();
+ int offset = privateEntry.getOperands().get(1).intValue();
+ cffData.setPosition(offset);
+ byte[] privateData = cffData.readBytes(length);
+ LinkedHashMap<String, DICTEntry> privateDict = parseDictData(privateData);
+ DICTEntry localSubrsEntry = privateDict.get("Subrs");
+ if (localSubrsEntry != null) {
+ int localOffset = offset + localSubrsEntry.getOperands().get(0).intValue();
+ cffData.setPosition(localOffset);
+ localSubrs = readIndex();
+ }
+ }
+ return localSubrs;
+ }
+
+ /**
+ * Parent class which provides the ability to retrieve byte data from
+ * a sub-table.
+ */
+ public class CFFSubTable {
+ private DataLocation dataLocation = new DataLocation();
+
+ public void setByteData(int position, int length) {
+ dataLocation = new DataLocation(position, length);
+ }
+
+ public byte[] getByteData() throws IOException {
+ int oldPos = cffData.getPosition();
+ try {
+ cffData.setPosition(dataLocation.getDataPosition());
+ return cffData.readBytes(dataLocation.getDataLength());
+ } finally {
+ cffData.setPosition(oldPos);
+ }
+ }
+ }
+
+ /**
+ * An object used to hold index data from the CFF data
+ */
+ public class CFFIndexData extends CFFSubTable {
+ private int numObjects;
+ private int offSize;
+ private int[] offsets = new int[0];
+ private DataLocation dataLocation = new DataLocation();
+
+ public void setNumObjects(int numObjects) {
+ this.numObjects = numObjects;
+ }
+
+ public int getNumObjects() {
+ return this.numObjects;
+ }
+
+ public void setOffSize(int offSize) {
+ this.offSize = offSize;
+ }
+
+ public int getOffSize() {
+ return this.offSize;
+ }
+
+ public void setOffsets(int[] offsets) {
+ this.offsets = offsets.clone();
+ }
+
+ public int[] getOffsets() {
+ return offsets.clone();
+ }
+
+ public void setData(int position, int length) {
+ dataLocation = new DataLocation(position, length);
+ }
+
+ public byte[] getData() throws IOException {
+ int origPos = cffData.getPosition();
+ try {
+ cffData.setPosition(dataLocation.getDataPosition());
+ return cffData.readBytes(dataLocation.getDataLength());
+ } finally {
+ cffData.setPosition(origPos);
+ }
+ }
+
+ /**
+ * Parses index data from an index object found within the CFF byte data
+ * @param cffData A byte array containing the CFF data
+ * @throws IOException Throws an IO Exception if an error occurs
+ */
+ public void parseIndexHeader(CFFDataInput cffData) throws IOException {
+ setNumObjects(cffData.readCard16());
+ setOffSize(cffData.readOffSize());
+ int[] offsets = new int[getNumObjects() + 1];
+ byte[] bytes;
+ //Fills the offsets array
+ for (int i = 0; i <= getNumObjects(); i++) {
+ switch (getOffSize()) {
+ case 1:
+ offsets[i] = cffData.readCard8();
+ break;
+ case 2:
+ offsets[i] = cffData.readCard16();
+ break;
+ case 3:
+ bytes = cffData.readBytes(3);
+ offsets[i] = ((bytes[0] & 0xFF) << 16) + ((bytes[1] & 0xFF) << 8) + (bytes[2] & 0xFF);
+ break;
+ case 4:
+ bytes = cffData.readBytes(4);
+ offsets[i] = ((bytes[0] & 0xFF) << 24) + ((bytes[1] & 0xFF) << 16)
+ + ((bytes[2] & 0xFF) << 8) + (bytes[3] & 0xFF);
+ break;
+ default: continue;
+ }
+ }
+ setOffsets(offsets);
+ int position = cffData.getPosition();
+ int dataSize = offsets[offsets.length - 1] - offsets[0];
+
+ cffData.setPosition(cffData.getPosition() + dataSize);
+ setData(position, dataSize);
+ }
+
+ /**
+ * Retrieves data from the index data
+ * @param index The index position of the data to retrieve
+ * @return Returns the byte data for the given index
+ * @throws IOException Throws an IO Exception if an error occurs
+ */
+ public byte[] getValue(int index) throws IOException {
+ int oldPos = cffData.getPosition();
+ try {
+ cffData.setPosition(dataLocation.getDataPosition() + (offsets[index] - 1));
+ return cffData.readBytes(offsets[index + 1] - offsets[index]);
+ } finally {
+ cffData.setPosition(oldPos);
+ }
+ }
+
+ public int getValuePosition(int index) {
+ return dataLocation.getDataPosition() + (offsets[index] - 1);
+ }
+
+ public int getValueLength(int index) {
+ return offsets[index + 1] - offsets[index];
+ }
+ }
+
+ public abstract class CustomEncoding {
+ private int format;
+ private int numEntries;
+
+ public void setFormat(int format) {
+ this.format = format;
+ }
+
+ public int getFormat() {
+ return format;
+ }
+
+ public void setNumEntries(int numEntries) {
+ this.numEntries = numEntries;
+ }
+
+ public int getNumEntries() {
+ return numEntries;
+ }
+ }
+
+ public class Format0Encoding extends CustomEncoding {
+ private int[] codes = new int[0];
+
+ public void setCodes(int[] codes) {
+ this.codes = codes.clone();
+ }
+
+ public int[] getCodes() {
+ return codes.clone();
+ }
+ }
+
+ public class Format1Encoding extends CustomEncoding {
+ private LinkedHashMap<Integer, Integer> ranges;
+
+ public void setRanges(LinkedHashMap<Integer, Integer> ranges) {
+ this.ranges = ranges;
+ }
+
+ public LinkedHashMap<Integer, Integer> getRanges() {
+ return ranges;
+ }
+ }
+
+ public class FDSelect {
+ private int format;
+
+ public void setFormat(int format) {
+ this.format = format;
+ }
+
+ public int getFormat() {
+ return format;
+ }
+ }
+
+ public class Format0FDSelect extends FDSelect {
+ private int[] fds = new int[0];
+
+ public void setFDIndexes(int[] fds) {
+ this.fds = fds.clone();
+ }
+
+ public int[] getFDIndexes() {
+ return fds.clone();
+ }
+ }
+
+ public class Format3FDSelect extends FDSelect {
+ private int rangeCount;
+ private LinkedHashMap<Integer, Integer> ranges;
+ private int sentinelGID;
+
+ public void setRangeCount(int rangeCount) {
+ this.rangeCount = rangeCount;
+ }
+
+ public int getRangeCount() {
+ return rangeCount;
+ }
+
+ public void setRanges(LinkedHashMap<Integer, Integer> ranges) {
+ this.ranges = ranges;
+ }
+
+ public LinkedHashMap<Integer, Integer> getRanges() {
+ return ranges;
+ }
+
+ public void setSentinelGID(int sentinelGID) {
+ this.sentinelGID = sentinelGID;
+ }
+
+ public int getSentinelGID() {
+ return sentinelGID;
+ }
+ }
+
+ public class FontDict extends CFFSubTable {
+ private String fontName;
+ private DataLocation dataLocation = new DataLocation();
+ private CFFIndexData localSubrData;
+
+ public void setFontName(String groupName) {
+ this.fontName = groupName;
+ }
+
+ public String getFontName() {
+ return fontName;
+ }
+
+ public void setPrivateDictData(int position, int length) {
+ dataLocation = new DataLocation(position, length);
+ }
+
+ public byte[] getPrivateDictData() throws IOException {
+ int origPos = cffData.getPosition();
+ try {
+ cffData.setPosition(dataLocation.getDataPosition());
+ return cffData.readBytes(dataLocation.getDataLength());
+ } finally {
+ cffData.setPosition(origPos);
+ }
+ }
+
+ public void setLocalSubrData(CFFIndexData localSubrData) {
+ this.localSubrData = localSubrData;
+ }
+
+ public CFFIndexData getLocalSubrData() {
+ return localSubrData;
+ }
+ }
+
+ private static class DataLocation {
+ private int dataPosition;
+ private int dataLength;
+
+ public DataLocation() {
+ dataPosition = 0;
+ dataLength = 0;
+ }
+
+ public DataLocation(int position, int length) {
+ this.dataPosition = position;
+ this.dataLength = length;
+ }
+
+ public int getDataPosition() {
+ return dataPosition;
+ }
+
+ public int getDataLength() {
+ return dataLength;
+ }
+ }
+}
diff --git a/src/java/org/apache/fop/fonts/truetype/GlyfTable.java b/src/java/org/apache/fop/fonts/truetype/GlyfTable.java
index f26ac2ffd..90abccaf2 100644
--- a/src/java/org/apache/fop/fonts/truetype/GlyfTable.java
+++ b/src/java/org/apache/fop/fonts/truetype/GlyfTable.java
@@ -31,7 +31,7 @@ import java.util.TreeSet;
*/
public class GlyfTable {
- private final TTFMtxEntry[] mtxTab;
+ private final OFMtxEntry[] mtxTab;
private final long tableOffset;
@@ -47,7 +47,7 @@ public class GlyfTable {
/** All the glyphs that are composed, but do not appear in the subset. */
private Set<Integer> composedGlyphs = new TreeSet<Integer>();
- GlyfTable(FontFileReader in, TTFMtxEntry[] metrics, TTFDirTabEntry dirTableEntry,
+ GlyfTable(FontFileReader in, OFMtxEntry[] metrics, OFDirTabEntry dirTableEntry,
Map<Integer, Integer> glyphs) throws IOException {
mtxTab = metrics;
tableOffset = dirTableEntry.getOffset();
diff --git a/src/java/org/apache/fop/fonts/truetype/TTFDirTabEntry.java b/src/java/org/apache/fop/fonts/truetype/OFDirTabEntry.java
index c273d4471..a9c471d5e 100644
--- a/src/java/org/apache/fop/fonts/truetype/TTFDirTabEntry.java
+++ b/src/java/org/apache/fop/fonts/truetype/OFDirTabEntry.java
@@ -26,17 +26,17 @@ import java.io.UnsupportedEncodingException;
/**
* This class represents an entry to a TrueType font's Dir Tab.
*/
-public class TTFDirTabEntry {
+public class OFDirTabEntry {
private byte[] tag = new byte[4];
private int checksum;
private long offset;
private long length;
- public TTFDirTabEntry() {
+ public OFDirTabEntry() {
}
- public TTFDirTabEntry(long offset, long length) {
+ public OFDirTabEntry(long offset, long length) {
this.offset = offset;
this.length = length;
}
diff --git a/src/java/org/apache/fop/fonts/truetype/TTFFontLoader.java b/src/java/org/apache/fop/fonts/truetype/OFFontLoader.java
index 98f81ab3e..7168389ff 100644
--- a/src/java/org/apache/fop/fonts/truetype/TTFFontLoader.java
+++ b/src/java/org/apache/fop/fonts/truetype/OFFontLoader.java
@@ -38,13 +38,13 @@ 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.fonts.truetype.OpenFont.PostScriptVersion;
import org.apache.fop.util.HexEncoder;
/**
* Loads a TrueType font into memory directly from the original font file.
*/
-public class TTFFontLoader extends FontLoader {
+public class OFFontLoader extends FontLoader {
private MultiByteFont multiFont;
private SingleByteFont singleFont;
@@ -57,7 +57,7 @@ public class TTFFontLoader extends FontLoader {
* @param fontFileURI the URI representing the font file
* @param resourceResolver the resource resolver for font URI resolution
*/
- public TTFFontLoader(URI fontFileURI, InternalResourceResolver resourceResolver) {
+ public OFFontLoader(URI fontFileURI, InternalResourceResolver resourceResolver) {
this(fontFileURI, null, true, EmbeddingMode.AUTO, EncodingMode.AUTO, true, true, resourceResolver);
}
@@ -73,7 +73,7 @@ public class TTFFontLoader extends FontLoader {
* @param useAdvanced true to enable loading advanced info if available, false to disable
* @param resolver the FontResolver for font URI resolution
*/
- public TTFFontLoader(URI fontFileURI, String subFontName, boolean embedded,
+ public OFFontLoader(URI fontFileURI, String subFontName, boolean embedded,
EmbeddingMode embeddingMode, EncodingMode encodingMode, boolean useKerning,
boolean useAdvanced, InternalResourceResolver resolver) {
super(fontFileURI, embedded, useKerning, useAdvanced, resolver);
@@ -102,26 +102,30 @@ public class TTFFontLoader extends FontLoader {
private void read(String ttcFontName) throws IOException {
InputStream in = resourceResolver.getResource(this.fontFileURI);
try {
- TTFFile ttf = new TTFFile(useKerning, useAdvanced);
FontFileReader reader = new FontFileReader(in);
- boolean supported = ttf.readFont(reader, ttcFontName);
+ String header = readHeader(reader);
+ boolean isCFF = header.equals("OTTO");
+ OpenFont otf = (isCFF) ? new OTFFile() : new TTFFile(useKerning, useAdvanced);
+ boolean supported = otf.readFont(reader, header, ttcFontName);
if (!supported) {
- throw new IOException("TrueType font is not supported: " + fontFileURI);
+ throw new IOException("The font does not have a Unicode cmap table: " + fontFileURI);
}
- buildFont(ttf, ttcFontName);
+ buildFont(otf, ttcFontName);
loaded = true;
} finally {
IOUtils.closeQuietly(in);
}
}
-
- private void buildFont(TTFFile ttf, String ttcFontName) {
- if (ttf.isCFF()) {
- throw new UnsupportedOperationException(
- "OpenType fonts with CFF data are not supported, yet");
+ public static String readHeader(FontFileReader fontFile) throws IOException {
+ if (fontFile != null) {
+ fontFile.seekSet(0);
+ return fontFile.readTTFString(4); // TTF_FIXED_SIZE (4 bytes)
}
+ return null;
+ }
+ private void buildFont(OpenFont otf, String ttcFontName) {
boolean isCid = this.embedded;
if (this.encodingMode == EncodingMode.SINGLE_BYTE) {
isCid = false;
@@ -129,6 +133,7 @@ public class TTFFontLoader extends FontLoader {
if (isCid) {
multiFont = new MultiByteFont(resourceResolver, embeddingMode);
+ multiFont.setIsOTFFile(otf instanceof OTFFile);
returnFont = multiFont;
multiFont.setTTCName(ttcFontName);
} else {
@@ -136,47 +141,51 @@ public class TTFFontLoader extends FontLoader {
returnFont = singleFont;
}
- returnFont.setFontName(ttf.getPostScriptName());
- returnFont.setFullName(ttf.getFullName());
- returnFont.setFamilyNames(ttf.getFamilyNames());
- returnFont.setFontSubFamilyName(ttf.getSubFamilyName());
- returnFont.setCapHeight(ttf.getCapHeight());
- returnFont.setXHeight(ttf.getXHeight());
- returnFont.setAscender(ttf.getLowerCaseAscent());
- returnFont.setDescender(ttf.getLowerCaseDescent());
- returnFont.setFontBBox(ttf.getFontBBox());
- returnFont.setUnderlinePosition(ttf.getUnderlinePosition() - ttf.getUnderlineThickness() / 2);
- returnFont.setUnderlineThickness(ttf.getUnderlineThickness());
- returnFont.setStrikeoutPosition(ttf.getStrikeoutPosition() - ttf.getStrikeoutThickness() / 2);
- returnFont.setStrikeoutThickness(ttf.getStrikeoutThickness());
- returnFont.setFlags(ttf.getFlags());
- returnFont.setStemV(Integer.parseInt(ttf.getStemV())); //not used for TTF
- returnFont.setItalicAngle(Integer.parseInt(ttf.getItalicAngle()));
+ returnFont.setFontName(otf.getPostScriptName());
+ returnFont.setFullName(otf.getFullName());
+ returnFont.setFamilyNames(otf.getFamilyNames());
+ returnFont.setFontSubFamilyName(otf.getSubFamilyName());
+ returnFont.setCapHeight(otf.getCapHeight());
+ returnFont.setXHeight(otf.getXHeight());
+ returnFont.setAscender(otf.getLowerCaseAscent());
+ returnFont.setDescender(otf.getLowerCaseDescent());
+ returnFont.setFontBBox(otf.getFontBBox());
+ returnFont.setUnderlinePosition(otf.getUnderlinePosition() - otf.getUnderlineThickness() / 2);
+ returnFont.setUnderlineThickness(otf.getUnderlineThickness());
+ returnFont.setStrikeoutPosition(otf.getStrikeoutPosition() - otf.getStrikeoutThickness() / 2);
+ returnFont.setStrikeoutThickness(otf.getStrikeoutThickness());
+ returnFont.setFlags(otf.getFlags());
+ returnFont.setStemV(Integer.parseInt(otf.getStemV())); //not used for TTF
+ returnFont.setItalicAngle(Integer.parseInt(otf.getItalicAngle()));
returnFont.setMissingWidth(0);
- returnFont.setWeight(ttf.getWeightClass());
+ returnFont.setWeight(otf.getWeightClass());
returnFont.setEmbeddingMode(this.embeddingMode);
if (isCid) {
- multiFont.setCIDType(CIDFontType.CIDTYPE2);
- multiFont.setWidthArray(ttf.getWidths());
- multiFont.setBBoxArray(ttf.getBoundingBoxes());
+ if (otf instanceof OTFFile) {
+ multiFont.setCIDType(CIDFontType.CIDTYPE0);
+ } else {
+ multiFont.setCIDType(CIDFontType.CIDTYPE2);
+ }
+ multiFont.setWidthArray(otf.getWidths());
+ multiFont.setBBoxArray(otf.getBoundingBoxes());
} else {
singleFont.setFontType(FontType.TRUETYPE);
- singleFont.setEncoding(ttf.getCharSetName());
- returnFont.setFirstChar(ttf.getFirstChar());
- returnFont.setLastChar(ttf.getLastChar());
- singleFont.setTrueTypePostScriptVersion(ttf.getPostScriptVersion());
- copyGlyphMetricsSingleByte(ttf);
+ singleFont.setEncoding(otf.getCharSetName());
+ returnFont.setFirstChar(otf.getFirstChar());
+ returnFont.setLastChar(otf.getLastChar());
+ singleFont.setTrueTypePostScriptVersion(otf.getPostScriptVersion());
+ copyGlyphMetricsSingleByte(otf);
}
- returnFont.setCMap(getCMap(ttf));
+ returnFont.setCMap(getCMap(otf));
- if (useKerning) {
- copyKerning(ttf, isCid);
+ if (otf.getKerning() != null && useKerning) {
+ copyKerning(otf, isCid);
}
if (useAdvanced) {
- copyAdvanced(ttf);
+ copyAdvanced(otf);
}
if (this.embedded) {
- if (ttf.isEmbeddable()) {
+ if (otf.isEmbeddable()) {
returnFont.setEmbedURI(this.fontFileURI);
} else {
String msg = "The font " + this.fontFileURI + " is not embeddable due to a"
@@ -186,28 +195,29 @@ public class TTFFontLoader extends FontLoader {
}
}
- private CMapSegment[] getCMap(TTFFile ttf) {
- CMapSegment[] array = new CMapSegment[ttf.getCMaps().size()];
- return ttf.getCMaps().toArray(array);
+ private CMapSegment[] getCMap(OpenFont otf) {
+ CMapSegment[] array = new CMapSegment[otf.getCMaps().size()];
+ return otf.getCMaps().toArray(array);
}
- private void copyGlyphMetricsSingleByte(TTFFile ttf) {
- int[] wx = ttf.getWidths();
- Rectangle[] bboxes = ttf.getBoundingBoxes();
+ private void copyGlyphMetricsSingleByte(OpenFont otf) {
+ int[] wx = otf.getWidths();
+ Rectangle[] bboxes = otf.getBoundingBoxes();
for (int i = singleFont.getFirstChar(); i <= singleFont.getLastChar(); i++) {
- singleFont.setWidth(i, ttf.getCharWidth(i));
- int[] bbox = ttf.getBBox(i);
+ singleFont.setWidth(i, otf.getCharWidth(i));
+ int[] bbox = otf.getBBox(i);
singleFont.setBoundingBox(i,
new Rectangle(bbox[0], bbox[1], bbox[2] - bbox[0], bbox[3] - bbox[1]));
}
- for (CMapSegment segment : ttf.getCMaps()) {
+
+ for (CMapSegment segment : otf.getCMaps()) {
if (segment.getUnicodeStart() < 0xFFFE) {
for (char u = (char)segment.getUnicodeStart(); u <= segment.getUnicodeEnd(); u++) {
int codePoint = singleFont.getEncoding().mapChar(u);
if (codePoint <= 0) {
int glyphIndex = segment.getGlyphStartIndex() + u - segment.getUnicodeStart();
- String glyphName = ttf.getGlyphName(glyphIndex);
- if (glyphName.length() == 0 && ttf.getPostScriptVersion() != PostScriptVersion.V2) {
+ String glyphName = otf.getGlyphName(glyphIndex);
+ if (glyphName.length() == 0 && otf.getPostScriptVersion() != PostScriptVersion.V2) {
glyphName = "u" + HexEncoder.encode(u);
}
if (glyphName.length() > 0) {
@@ -224,22 +234,22 @@ public class TTFFontLoader extends FontLoader {
/**
* Copy kerning information.
*/
- private void copyKerning(TTFFile ttf, boolean isCid) {
+ private void copyKerning(OpenFont otf, boolean isCid) {
// Get kerning
Set<Integer> kerningSet;
if (isCid) {
- kerningSet = ttf.getKerning().keySet();
+ kerningSet = otf.getKerning().keySet();
} else {
- kerningSet = ttf.getAnsiKerning().keySet();
+ kerningSet = otf.getAnsiKerning().keySet();
}
for (Integer kpx1 : kerningSet) {
Map<Integer, Integer> h2;
if (isCid) {
- h2 = ttf.getKerning().get(kpx1);
+ h2 = otf.getKerning().get(kpx1);
} else {
- h2 = ttf.getAnsiKerning().get(kpx1);
+ h2 = otf.getAnsiKerning().get(kpx1);
}
returnFont.putKerningEntry(kpx1, h2);
}
@@ -248,12 +258,12 @@ public class TTFFontLoader extends FontLoader {
/**
* Copy advanced typographic information.
*/
- private void copyAdvanced(TTFFile ttf) {
+ private void copyAdvanced(OpenFont otf) {
if (returnFont instanceof MultiByteFont) {
MultiByteFont mbf = (MultiByteFont) returnFont;
- mbf.setGDEF(ttf.getGDEF());
- mbf.setGSUB(ttf.getGSUB());
- mbf.setGPOS(ttf.getGPOS());
+ mbf.setGDEF(otf.getGDEF());
+ mbf.setGSUB(otf.getGSUB());
+ mbf.setGPOS(otf.getGPOS());
}
}
diff --git a/src/java/org/apache/fop/fonts/truetype/TTFMtxEntry.java b/src/java/org/apache/fop/fonts/truetype/OFMtxEntry.java
index 6884a633d..89af2296f 100644
--- a/src/java/org/apache/fop/fonts/truetype/TTFMtxEntry.java
+++ b/src/java/org/apache/fop/fonts/truetype/OFMtxEntry.java
@@ -24,7 +24,7 @@ import java.util.List;
/**
* This class represents a TrueType Mtx Entry.
*/
-class TTFMtxEntry {
+class OFMtxEntry {
private int wx;
private int lsb;
diff --git a/src/java/org/apache/fop/fonts/truetype/TTFTableName.java b/src/java/org/apache/fop/fonts/truetype/OFTableName.java
index e5ad63128..f6264129a 100644
--- a/src/java/org/apache/fop/fonts/truetype/TTFTableName.java
+++ b/src/java/org/apache/fop/fonts/truetype/OFTableName.java
@@ -24,98 +24,104 @@ 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 {
+public final class OFTableName {
/** The first table in a TrueType font file containing metadata about other tables. */
- public static final TTFTableName TABLE_DIRECTORY = new TTFTableName("tableDirectory");
+ public static final OFTableName TABLE_DIRECTORY = new OFTableName("tableDirectory");
+
+ /** Baseline data */
+ public static final OFTableName BASE = new OFTableName("BASE");
+
+ /** CFF data/ */
+ public static final OFTableName CFF = new OFTableName("CFF ");
/** Embedded bitmap data. */
- public static final TTFTableName EBDT = new TTFTableName("EBDT");
+ public static final OFTableName EBDT = new OFTableName("EBDT");
/** Embedded bitmap location data. */
- public static final TTFTableName EBLC = new TTFTableName("EBLC");
+ public static final OFTableName EBLC = new OFTableName("EBLC");
/** Embedded bitmap scaling data. */
- public static final TTFTableName EBSC = new TTFTableName("EBSC");
+ public static final OFTableName EBSC = new OFTableName("EBSC");
/** A FontForge specific table. */
- public static final TTFTableName FFTM = new TTFTableName("FFTM");
+ public static final OFTableName FFTM = new OFTableName("FFTM");
/** Divides glyphs into various classes that make using the GPOS/GSUB tables easier. */
- public static final TTFTableName GDEF = new TTFTableName("GDEF");
+ public static final OFTableName GDEF = new OFTableName("GDEF");
/** Provides kerning information, mark-to-base, etc. for opentype fonts. */
- public static final TTFTableName GPOS = new TTFTableName("GPOS");
+ public static final OFTableName GPOS = new OFTableName("GPOS");
/** Provides ligature information, swash, etc. for opentype fonts. */
- public static final TTFTableName GSUB = new TTFTableName("GSUB");
+ public static final OFTableName GSUB = new OFTableName("GSUB");
/** Linear threshold table. */
- public static final TTFTableName LTSH = new TTFTableName("LTSH");
+ public static final OFTableName LTSH = new OFTableName("LTSH");
/** OS/2 and Windows specific metrics. */
- public static final TTFTableName OS2 = new TTFTableName("OS/2");
+ public static final OFTableName OS2 = new OFTableName("OS/2");
/** PCL 5 data. */
- public static final TTFTableName PCLT = new TTFTableName("PCLT");
+ public static final OFTableName PCLT = new OFTableName("PCLT");
/** Vertical Device Metrics table. */
- public static final TTFTableName VDMX = new TTFTableName("VDMX");
+ public static final OFTableName VDMX = new OFTableName("VDMX");
/** Character to glyph mapping. */
- public static final TTFTableName CMAP = new TTFTableName("cmap");
+ public static final OFTableName CMAP = new OFTableName("cmap");
/** Control Value Table. */
- public static final TTFTableName CVT = new TTFTableName("cvt ");
+ public static final OFTableName CVT = new OFTableName("cvt ");
/** Font program. */
- public static final TTFTableName FPGM = new TTFTableName("fpgm");
+ public static final OFTableName FPGM = new OFTableName("fpgm");
/** Grid-fitting and scan conversion procedure (grayscale). */
- public static final TTFTableName GASP = new TTFTableName("gasp");
+ public static final OFTableName GASP = new OFTableName("gasp");
/** Glyph data. */
- public static final TTFTableName GLYF = new TTFTableName("glyf");
+ public static final OFTableName GLYF = new OFTableName("glyf");
/** Horizontal device metrics. */
- public static final TTFTableName HDMX = new TTFTableName("hdmx");
+ public static final OFTableName HDMX = new OFTableName("hdmx");
/** Font header. */
- public static final TTFTableName HEAD = new TTFTableName("head");
+ public static final OFTableName HEAD = new OFTableName("head");
/** Horizontal header. */
- public static final TTFTableName HHEA = new TTFTableName("hhea");
+ public static final OFTableName HHEA = new OFTableName("hhea");
/** Horizontal metrics. */
- public static final TTFTableName HMTX = new TTFTableName("hmtx");
+ public static final OFTableName HMTX = new OFTableName("hmtx");
/** Kerning. */
- public static final TTFTableName KERN = new TTFTableName("kern");
+ public static final OFTableName KERN = new OFTableName("kern");
/** Index to location. */
- public static final TTFTableName LOCA = new TTFTableName("loca");
+ public static final OFTableName LOCA = new OFTableName("loca");
/** Maximum profile. */
- public static final TTFTableName MAXP = new TTFTableName("maxp");
+ public static final OFTableName MAXP = new OFTableName("maxp");
/** Naming table. */
- public static final TTFTableName NAME = new TTFTableName("name");
+ public static final OFTableName NAME = new OFTableName("name");
/** PostScript information. */
- public static final TTFTableName POST = new TTFTableName("post");
+ public static final OFTableName POST = new OFTableName("post");
/** CVT Program. */
- public static final TTFTableName PREP = new TTFTableName("prep");
+ public static final OFTableName PREP = new OFTableName("prep");
/** Vertical Metrics header. */
- public static final TTFTableName VHEA = new TTFTableName("vhea");
+ public static final OFTableName VHEA = new OFTableName("vhea");
/** Vertical Metrics. */
- public static final TTFTableName VMTX = new TTFTableName("vmtx");
+ public static final OFTableName VMTX = new OFTableName("vmtx");
private final String name;
- private TTFTableName(String name) {
+ private OFTableName(String name) {
this.name = name;
}
@@ -131,9 +137,9 @@ public final class TTFTableName {
* @param tableName table name as in the Table Directory
* @return TTFTableName
*/
- public static TTFTableName getValue(String tableName) {
+ public static OFTableName getValue(String tableName) {
if (tableName != null) {
- return new TTFTableName(tableName);
+ return new OFTableName(tableName);
}
throw new IllegalArgumentException("A TrueType font table name must not be null");
}
@@ -148,10 +154,10 @@ public final class TTFTableName {
if (o == this) {
return true;
}
- if (!(o instanceof TTFTableName)) {
+ if (!(o instanceof OFTableName)) {
return false;
}
- TTFTableName to = (TTFTableName) o;
+ OFTableName to = (OFTableName) o;
return this.name.equals(to.getName());
}
diff --git a/src/java/org/apache/fop/fonts/truetype/OTFFile.java b/src/java/org/apache/fop/fonts/truetype/OTFFile.java
new file mode 100644
index 000000000..3976b5994
--- /dev/null
+++ b/src/java/org/apache/fop/fonts/truetype/OTFFile.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.fonts.truetype;
+
+import java.io.IOException;
+
+import org.apache.fontbox.cff.CFFDataInput;
+import org.apache.fontbox.cff.CFFFont;
+import org.apache.fontbox.cff.CFFFont.Mapping;
+import org.apache.fontbox.cff.CFFParser;
+
+public class OTFFile extends OpenFont {
+
+ protected CFFFont fileFont;
+
+ public OTFFile() throws IOException {
+ checkForFontbox();
+ }
+
+ private void checkForFontbox() throws IOException {
+ try {
+ Class.forName("org.apache.fontbox.cff.CFFFont");
+ } catch (ClassNotFoundException ex) {
+ throw new IOException("The Fontbox jar was not found in the classpath. This is "
+ + "required for OTF CFF ssupport.");
+ }
+ }
+
+ @Override
+ protected void updateBBoxAndOffset() throws IOException {
+ UnicodeMapping[] mappings = unicodeMappings.toArray(new UnicodeMapping[0]);
+ for (int i = 0; i < mappings.length; i++) {
+ int glyphIdx = mappings[i].getGlyphIndex();
+ Mapping m = fileFont.getGIDMappings().get(glyphIdx);
+ int[] bbox = fileFont.getBoundingBox(m.getSID());
+ String name = fileFont.getNameOfCharFromCode(m.getSID());
+ mtxTab[glyphIdx].setBoundingBox(bbox);
+ mtxTab[glyphIdx].setName(name);
+ }
+ }
+
+ @Override
+ protected void initializeFont(FontFileReader in) throws IOException {
+ fontFile = in;
+ fontFile.seekSet(0);
+ CFFParser parser = new CFFParser();
+ fileFont = parser.parse(in.getAllBytes()).get(0);
+ }
+
+ protected void readName() throws IOException {
+ Object familyName = fileFont.getProperty("FamilyName");
+ if (familyName != null && !familyName.equals("")) {
+ familyNames.add(familyName.toString());
+ fullName = familyName.toString();
+ } else {
+ fullName = fileFont.getName();
+ familyNames.add(fullName);
+ }
+ }
+
+ /**
+ * Reads the CFFData from a given font file
+ * @param fontFile The font file being read
+ * @return The byte data found in the CFF table
+ */
+ public static byte[] getCFFData(FontFileReader fontFile) throws IOException {
+ byte[] cff = new byte[0];
+ CFFDataInput input = new CFFDataInput(fontFile.getAllBytes());
+ input.readBytes(4); //OTTO
+ short numTables = input.readShort();
+ input.readShort(); //searchRange
+ input.readShort(); //entrySelector
+ input.readShort(); //rangeShift
+
+ for (int q = 0; q < numTables; q++) {
+ String tagName = new String(input.readBytes(4));
+ readLong(input); //Checksum
+ long offset = readLong(input);
+ long length = readLong(input);
+ if (tagName.equals("CFF ")) {
+ cff = new byte[(int)length];
+ System.arraycopy(fontFile.getAllBytes(), (int)offset, cff, 0, cff.length);
+ break;
+ }
+ }
+ return cff;
+ }
+
+ private static long readLong(CFFDataInput input) throws IOException {
+ return (input.readCard16() << 16) | input.readCard16();
+ }
+}
diff --git a/src/java/org/apache/fop/fonts/truetype/OTFSubSetFile.java b/src/java/org/apache/fop/fonts/truetype/OTFSubSetFile.java
new file mode 100644
index 000000000..4d0cce67a
--- /dev/null
+++ b/src/java/org/apache/fop/fonts/truetype/OTFSubSetFile.java
@@ -0,0 +1,1097 @@
+/*
+ * 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.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.apache.fontbox.cff.CFFStandardString;
+import org.apache.fontbox.cff.encoding.CFFEncoding;
+
+import org.apache.fop.fonts.MultiByteFont;
+import org.apache.fop.fonts.cff.CFFDataReader;
+import org.apache.fop.fonts.cff.CFFDataReader.CFFIndexData;
+import org.apache.fop.fonts.cff.CFFDataReader.DICTEntry;
+import org.apache.fop.fonts.cff.CFFDataReader.FDSelect;
+import org.apache.fop.fonts.cff.CFFDataReader.FontDict;
+import org.apache.fop.fonts.cff.CFFDataReader.Format0FDSelect;
+import org.apache.fop.fonts.cff.CFFDataReader.Format3FDSelect;
+
+/**
+ * Reads an OpenType CFF file and generates a subset
+ * The OpenType specification can be found at the Microsoft
+ * Typography site: http://www.microsoft.com/typography/otspec/
+ */
+public class OTFSubSetFile extends OTFFile {
+
+ private byte[] output;
+ private int currentPos = 0;
+ private int realSize = 0;
+
+ /** A map containing each glyph to be included in the subset
+ * with their existing and new GID's **/
+ private LinkedHashMap<Integer, Integer> subsetGlyphs;
+
+ /** A map of the new GID to SID used to construct the charset table **/
+ private LinkedHashMap<Integer, Integer> gidToSID;
+
+ private CFFIndexData localIndexSubr;
+ private CFFIndexData globalIndexSubr;
+
+ /** List of subroutines to write to the local / global indexes in the subset font **/
+ private List<byte[]> subsetLocalIndexSubr;
+ private List<byte[]> subsetGlobalIndexSubr;
+
+ /** For fonts which have an FDSelect or ROS flag in Top Dict, this is used to store the
+ * local subroutine indexes for each group as opposed to the above subsetLocalIndexSubr */
+ private ArrayList<List<byte[]>> fdSubrs;
+
+ /** The subset FD Select table used to store the mappings between glyphs and their
+ * associated FDFont object which point to a private dict and local subroutines. */
+ private LinkedHashMap<Integer, FDIndexReference> subsetFDSelect;
+
+ /** A list of unique subroutines from the global / local subroutine indexes */
+ private List<Integer> localUniques;
+ private List<Integer> globalUniques;
+
+ /** A store of the number of subroutines each global / local subroutine will store **/
+ private int subsetLocalSubrCount;
+ private int subsetGlobalSubrCount;
+
+ /** A list of char string data for each glyph to be stored in the subset font **/
+ private List<byte[]> subsetCharStringsIndex;
+
+ /** The embedded name to change in the name table **/
+ private String embeddedName;
+
+ /** An array used to hold the string index data for the subset font **/
+ private List<byte[]> stringIndexData = new ArrayList<byte[]>();
+
+ /** The CFF reader object used to read data and offsets from the original font file */
+ private CFFDataReader cffReader = null;
+
+ /** The class used to represent this font **/
+ private MultiByteFont mbFont;
+
+ /** The number of standard strings in CFF **/
+ private static final int NUM_STANDARD_STRINGS = 391;
+ /** The operator used to identify a local subroutine reference */
+ private static final int LOCAL_SUBROUTINE = 10;
+ /** The operator used to identify a global subroutine reference */
+ private static final int GLOBAL_SUBROUTINE = 29;
+
+ public OTFSubSetFile() throws IOException {
+ super();
+ }
+
+ public void readFont(FontFileReader in, String embeddedName, String header,
+ MultiByteFont mbFont) throws IOException {
+ this.mbFont = mbFont;
+ readFont(in, embeddedName, header, mbFont.getUsedGlyphs());
+ }
+
+ /**
+ * Reads 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 header The header of 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
+ */
+ void readFont(FontFileReader in, String embeddedName, String header,
+ Map<Integer, Integer> usedGlyphs) throws IOException {
+ fontFile = in;
+
+ currentPos = 0;
+ realSize = 0;
+
+ this.embeddedName = embeddedName;
+
+ //Sort by the new GID and store in a LinkedHashMap
+ subsetGlyphs = sortByValue(usedGlyphs);
+
+ output = new byte[in.getFileSize()];
+
+ initializeFont(in);
+
+ cffReader = new CFFDataReader(fontFile);
+
+ //Create the CIDFontType0C data
+ createCFF();
+ }
+
+ private LinkedHashMap<Integer, Integer> sortByValue(Map<Integer, Integer> map) {
+ List<Entry<Integer, Integer>> list = new ArrayList<Entry<Integer, Integer>>(map.entrySet());
+ Collections.sort(list, new Comparator<Entry<Integer, Integer>>() {
+ public int compare(Entry<Integer, Integer> o1, Entry<Integer, Integer> o2) {
+ return ((Comparable<Integer>) o1.getValue()).compareTo(o2.getValue());
+ }
+ });
+
+ LinkedHashMap<Integer, Integer> result = new LinkedHashMap<Integer, Integer>();
+ for (Entry<Integer, Integer> entry : list) {
+ result.put(entry.getKey(), entry.getValue());
+ }
+ return result;
+ }
+
+ private void createCFF() throws IOException {
+ //Header
+ writeBytes(cffReader.getHeader());
+
+ //Name Index
+ writeIndex(Arrays.asList(embeddedName.getBytes()));
+
+ //Keep offset of the topDICT so it can be updated once all data has been written
+ int topDictOffset = currentPos;
+ //Top DICT Index and Data
+ byte[] topDictIndex = cffReader.getTopDictIndex().getByteData();
+ int offSize = topDictIndex[2];
+ writeBytes(topDictIndex, 0, 3 + (offSize * 2));
+ int topDictDataOffset = currentPos;
+ writeTopDICT();
+
+ //Create the char string index data and related local / global subroutines
+ if (cffReader.getFDSelect() == null) {
+ createCharStringData();
+ } else {
+ createCharStringDataCID();
+ }
+
+ //If it is a CID-Keyed font, store each FD font and add each SID
+ List<Integer> fontNameSIDs = null;
+ List<Integer> subsetFDFonts = null;
+ if (cffReader.getFDSelect() != null) {
+ subsetFDFonts = getUsedFDFonts();
+ fontNameSIDs = storeFDStrings(subsetFDFonts);
+ }
+
+ //String index
+ writeStringIndex();
+
+ //Global subroutine index
+ writeIndex(subsetGlobalIndexSubr);
+
+ //Encoding
+ int encodingOffset = currentPos;
+ writeEncoding(fileFont.getEncoding());
+
+ //Charset table
+ int charsetOffset = currentPos;
+ writeCharsetTable(cffReader.getFDSelect() != null);
+
+ //FDSelect table
+ int fdSelectOffset = currentPos;
+ if (cffReader.getFDSelect() != null) {
+ writeFDSelect();
+ }
+
+ //Char Strings Index
+ int charStringOffset = currentPos;
+ writeIndex(subsetCharStringsIndex);
+
+ if (cffReader.getFDSelect() == null) {
+ //Keep offset to modify later with the local subroutine index offset
+ int privateDictOffset = currentPos;
+ writePrivateDict();
+
+ //Local subroutine index
+ int localIndexOffset = currentPos;
+ writeIndex(subsetLocalIndexSubr);
+
+ //Update the offsets
+ updateOffsets(topDictOffset, charsetOffset, charStringOffset, privateDictOffset,
+ localIndexOffset, encodingOffset);
+ } else {
+ List<Integer> privateDictOffsets = writeCIDDictsAndSubrs(subsetFDFonts);
+ int fdArrayOffset = writeFDArray(subsetFDFonts, privateDictOffsets, fontNameSIDs);
+
+ updateCIDOffsets(topDictDataOffset, fdArrayOffset, fdSelectOffset, charsetOffset,
+ charStringOffset, encodingOffset);
+ }
+ }
+
+ private List<Integer> storeFDStrings(List<Integer> uniqueNewRefs) throws IOException {
+ ArrayList<Integer> fontNameSIDs = new ArrayList<Integer>();
+ List<FontDict> fdFonts = cffReader.getFDFonts();
+ for (int i = 0; i < uniqueNewRefs.size(); i++) {
+ FontDict fdFont = fdFonts.get(uniqueNewRefs.get(i));
+ byte[] fdFontByteData = fdFont.getByteData();
+ Map<String, DICTEntry> fdFontDict = cffReader.parseDictData(fdFontByteData);
+ fontNameSIDs.add(stringIndexData.size() + NUM_STANDARD_STRINGS);
+ stringIndexData.add(cffReader.getStringIndex().getValue(fdFontDict.get("FontName")
+ .getOperands().get(0).intValue() - NUM_STANDARD_STRINGS));
+ }
+ return fontNameSIDs;
+ }
+
+ private void writeBytes(byte[] out) {
+ for (int i = 0; i < out.length; i++) {
+ output[currentPos++] = out[i];
+ realSize++;
+ }
+ }
+
+ private void writeBytes(byte[] out, int offset, int length) {
+ for (int i = offset; i < offset + length; i++) {
+ output[currentPos++] = out[i];
+ realSize++;
+ }
+ }
+
+ private void writeEncoding(CFFEncoding encoding) throws IOException {
+ LinkedHashMap<String, DICTEntry> topDICT = cffReader.getTopDictEntries();
+ DICTEntry encodingEntry = topDICT.get("Encoding");
+ if (encodingEntry != null && encodingEntry.getOperands().get(0).intValue() != 0
+ && encodingEntry.getOperands().get(0).intValue() != 1) {
+ writeByte(0);
+ writeByte(gidToSID.size());
+ for (int gid : gidToSID.keySet()) {
+ int code = encoding.getCode(gidToSID.get(gid));
+ writeByte(code);
+ }
+ }
+ }
+
+ private void writeTopDICT() throws IOException {
+ LinkedHashMap<String, DICTEntry> topDICT = cffReader.getTopDictEntries();
+ List<String> topDictStringEntries = Arrays.asList("version", "Notice", "Copyright",
+ "FullName", "FamilyName", "Weight", "PostScript");
+ for (Map.Entry<String, DICTEntry> dictEntry : topDICT.entrySet()) {
+ String dictKey = dictEntry.getKey();
+ DICTEntry entry = dictEntry.getValue();
+ //If the value is an SID, update the reference but keep the size the same
+ if (dictKey.equals("ROS")) {
+ writeROSEntry(entry);
+ } else if (dictKey.equals("CIDCount")) {
+ writeCIDCount(entry);
+ } else if (topDictStringEntries.contains(dictKey)) {
+ writeTopDictStringEntry(entry);
+ } else {
+ writeBytes(entry.getByteData());
+ }
+ }
+ }
+
+ private void writeROSEntry(DICTEntry dictEntry) throws IOException {
+ int sidA = dictEntry.getOperands().get(0).intValue();
+ if (sidA > 390) {
+ stringIndexData.add(cffReader.getStringIndex().getValue(sidA - NUM_STANDARD_STRINGS));
+ }
+ int sidAStringIndex = stringIndexData.size() + 390;
+ int sidB = dictEntry.getOperands().get(1).intValue();
+ if (sidB > 390) {
+ stringIndexData.add("Identity".getBytes());
+ }
+ int sidBStringIndex = stringIndexData.size() + 390;
+ byte[] cidEntryByteData = dictEntry.getByteData();
+ cidEntryByteData = updateOffset(cidEntryByteData, 0, dictEntry.getOperandLengths().get(0),
+ sidAStringIndex);
+ cidEntryByteData = updateOffset(cidEntryByteData, dictEntry.getOperandLengths().get(0),
+ dictEntry.getOperandLengths().get(1), sidBStringIndex);
+ cidEntryByteData = updateOffset(cidEntryByteData, dictEntry.getOperandLengths().get(0)
+ + dictEntry.getOperandLengths().get(1), dictEntry.getOperandLengths().get(2), 139);
+ writeBytes(cidEntryByteData);
+ }
+
+ private void writeCIDCount(DICTEntry dictEntry) throws IOException {
+ byte[] cidCountByteData = dictEntry.getByteData();
+ cidCountByteData = updateOffset(cidCountByteData, 0, dictEntry.getOperandLengths().get(0),
+ subsetGlyphs.size());
+ writeBytes(cidCountByteData);
+ }
+
+ private void writeTopDictStringEntry(DICTEntry dictEntry) throws IOException {
+ int sid = dictEntry.getOperands().get(0).intValue();
+ if (sid > 391) {
+ stringIndexData.add(cffReader.getStringIndex().getValue(sid - 391));
+ }
+
+ byte[] newDictEntry = createNewRef(stringIndexData.size() + 390, dictEntry.getOperator(),
+ dictEntry.getOperandLength());
+ writeBytes(newDictEntry);
+ }
+
+ private void writeStringIndex() throws IOException {
+ Map<String, DICTEntry> topDICT = cffReader.getTopDictEntries();
+ int charsetOffset = topDICT.get("charset").getOperands().get(0).intValue();
+
+ gidToSID = new LinkedHashMap<Integer, Integer>();
+
+ for (int gid : subsetGlyphs.keySet()) {
+ int sid = cffReader.getSIDFromGID(charsetOffset, gid);
+ //Check whether the SID falls into the standard string set
+ if (sid < NUM_STANDARD_STRINGS) {
+ gidToSID.put(subsetGlyphs.get(gid), sid);
+ if (mbFont != null) {
+ mbFont.mapUsedGlyphName(subsetGlyphs.get(gid),
+ CFFStandardString.getName(sid));
+ }
+ } else {
+ int index = sid - NUM_STANDARD_STRINGS;
+ if (index <= cffReader.getStringIndex().getNumObjects()) {
+ if (mbFont != null) {
+ mbFont.mapUsedGlyphName(subsetGlyphs.get(gid),
+ new String(cffReader.getStringIndex().getValue(index)));
+ }
+ gidToSID.put(subsetGlyphs.get(gid), stringIndexData.size() + 391);
+ stringIndexData.add(cffReader.getStringIndex().getValue(index));
+ } else {
+ if (mbFont != null) {
+ mbFont.mapUsedGlyphName(subsetGlyphs.get(gid), ".notdef");
+ }
+ gidToSID.put(subsetGlyphs.get(gid), index);
+ }
+ }
+ }
+ //Write the String Index
+ writeIndex(stringIndexData);
+ }
+
+ private void createCharStringDataCID() throws IOException {
+ CFFIndexData charStringsIndex = cffReader.getCharStringIndex();
+
+ FDSelect fontDictionary = cffReader.getFDSelect();
+ if (fontDictionary instanceof Format0FDSelect) {
+ throw new UnsupportedOperationException("OTF CFF CID Format0 currently not implemented");
+ } else if (fontDictionary instanceof Format3FDSelect) {
+ Format3FDSelect fdSelect = (Format3FDSelect)fontDictionary;
+ Map<Integer, Integer> subsetGroups = new HashMap<Integer, Integer>();
+
+ List<Integer> uniqueGroups = new ArrayList<Integer>();
+ for (int gid : subsetGlyphs.keySet()) {
+ Integer[] ranges = fdSelect.getRanges().keySet().toArray(new Integer[0]);
+ for (int i = 0; i < ranges.length; i++) {
+ int nextRange = -1;
+ if (i < ranges.length - 1) {
+ nextRange = ranges[i + 1];
+ } else {
+ nextRange = fdSelect.getSentinelGID();
+ }
+ if (gid >= ranges[i] && gid < nextRange) {
+ subsetGroups.put(gid, fdSelect.getRanges().get(ranges[i]));
+ if (!uniqueGroups.contains(fdSelect.getRanges().get(ranges[i]))) {
+ uniqueGroups.add(fdSelect.getRanges().get(ranges[i]));
+ }
+ }
+ }
+ }
+
+ //Prepare resources
+ globalIndexSubr = cffReader.getGlobalIndexSubr();
+
+ //Create the new char string index
+ subsetCharStringsIndex = new ArrayList<byte[]>();
+
+ globalUniques = new ArrayList<Integer>();
+
+ subsetFDSelect = new LinkedHashMap<Integer, FDIndexReference>();
+
+ List<List<Integer>> foundLocalUniques = new ArrayList<List<Integer>>();
+ for (int i = 0; i < uniqueGroups.size(); i++) {
+ foundLocalUniques.add(new ArrayList<Integer>());
+ }
+ for (int gid : subsetGlyphs.keySet()) {
+ int group = subsetGroups.get(gid);
+ localIndexSubr = cffReader.getFDFonts().get(group).getLocalSubrData();
+ localUniques = foundLocalUniques.get(uniqueGroups.indexOf(subsetGroups.get(gid)));
+
+ FDIndexReference newFDReference = new FDIndexReference(
+ uniqueGroups.indexOf(subsetGroups.get(gid)), subsetGroups.get(gid));
+ subsetFDSelect.put(subsetGlyphs.get(gid), newFDReference);
+ byte[] data = charStringsIndex.getValue(gid);
+ preScanForSubsetIndexSize(data);
+ }
+
+ //Create the two lists which are to store the local and global subroutines
+ subsetGlobalIndexSubr = new ArrayList<byte[]>();
+
+ fdSubrs = new ArrayList<List<byte[]>>();
+ subsetGlobalSubrCount = globalUniques.size();
+ globalUniques.clear();
+ localUniques = null;
+
+ for (int l = 0; l < foundLocalUniques.size(); l++) {
+ fdSubrs.add(new ArrayList<byte[]>());
+ }
+ List<List<Integer>> foundLocalUniquesB = new ArrayList<List<Integer>>();
+ for (int k = 0; k < uniqueGroups.size(); k++) {
+ foundLocalUniquesB.add(new ArrayList<Integer>());
+ }
+ for (Integer gid : subsetGlyphs.keySet()) {
+ int group = subsetGroups.get(gid);
+ localIndexSubr = cffReader.getFDFonts().get(group).getLocalSubrData();
+ localUniques = foundLocalUniquesB.get(subsetFDSelect.get(subsetGlyphs.get(gid)).getNewFDIndex());
+ byte[] data = charStringsIndex.getValue(gid);
+ subsetLocalIndexSubr = fdSubrs.get(subsetFDSelect.get(subsetGlyphs.get(gid)).getNewFDIndex());
+ subsetLocalSubrCount = foundLocalUniques.get(
+ subsetFDSelect.get(subsetGlyphs.get(gid)).getNewFDIndex()).size();
+ data = readCharStringData(data, subsetLocalSubrCount);
+ subsetCharStringsIndex.add(data);
+ }
+ }
+ }
+
+ private void writeFDSelect() {
+ writeByte(0); //Format
+ for (Integer gid : subsetFDSelect.keySet()) {
+ writeByte(subsetFDSelect.get(gid).getNewFDIndex());
+ }
+ }
+
+ private List<Integer> getUsedFDFonts() {
+ List<Integer> uniqueNewRefs = new ArrayList<Integer>();
+ for (int gid : subsetFDSelect.keySet()) {
+ int fdIndex = subsetFDSelect.get(gid).getOldFDIndex();
+ if (!uniqueNewRefs.contains(fdIndex)) {
+ uniqueNewRefs.add(fdIndex);
+ }
+ }
+ return uniqueNewRefs;
+ }
+
+ private List<Integer> writeCIDDictsAndSubrs(List<Integer> uniqueNewRefs)
+ throws IOException {
+ List<Integer> privateDictOffsets = new ArrayList<Integer>();
+ List<FontDict> fdFonts = cffReader.getFDFonts();
+ for (int i = 0; i < uniqueNewRefs.size(); i++) {
+ FontDict curFDFont = fdFonts.get(uniqueNewRefs.get(i));
+ HashMap<String, DICTEntry> fdPrivateDict = cffReader.parseDictData(
+ curFDFont.getPrivateDictData());
+ int privateDictOffset = currentPos;
+ privateDictOffsets.add(privateDictOffset);
+ byte[] fdPrivateDictByteData = curFDFont.getPrivateDictData();
+ if (fdPrivateDict.get("Subrs") != null) {
+ fdPrivateDictByteData = updateOffset(fdPrivateDictByteData, fdPrivateDict.get("Subrs").getOffset(),
+ fdPrivateDict.get("Subrs").getOperandLength(),
+ fdPrivateDictByteData.length);
+ }
+ writeBytes(fdPrivateDictByteData);
+ writeIndex(fdSubrs.get(i));
+ }
+ return privateDictOffsets;
+ }
+
+ private int writeFDArray(List<Integer> uniqueNewRefs, List<Integer> privateDictOffsets,
+ List<Integer> fontNameSIDs)
+ throws IOException {
+ int offset = currentPos;
+ List<FontDict> fdFonts = cffReader.getFDFonts();
+
+ writeCard16(uniqueNewRefs.size());
+ writeByte(1); //Offset size
+ writeByte(1); //First offset
+
+ int count = 1;
+ for (int i = 0; i < uniqueNewRefs.size(); i++) {
+ FontDict fdFont = fdFonts.get(uniqueNewRefs.get(i));
+ count += fdFont.getByteData().length;
+ writeByte(count);
+ }
+
+ for (int i = 0; i < uniqueNewRefs.size(); i++) {
+ FontDict fdFont = fdFonts.get(uniqueNewRefs.get(i));
+ byte[] fdFontByteData = fdFont.getByteData();
+ Map<String, DICTEntry> fdFontDict = cffReader.parseDictData(fdFontByteData);
+ //Update the SID to the FontName
+ fdFontByteData = updateOffset(fdFontByteData, fdFontDict.get("FontName").getOffset() - 1,
+ fdFontDict.get("FontName").getOperandLengths().get(0),
+ fontNameSIDs.get(i));
+ //Update the Private dict reference
+ fdFontByteData = updateOffset(fdFontByteData, fdFontDict.get("Private").getOffset()
+ + fdFontDict.get("Private").getOperandLengths().get(0),
+ fdFontDict.get("Private").getOperandLengths().get(1),
+ privateDictOffsets.get(i));
+ writeBytes(fdFontByteData);
+ }
+ return offset;
+ }
+
+ private class FDIndexReference {
+ private int newFDIndex;
+ private int oldFDIndex;
+
+ public FDIndexReference(int newFDIndex, int oldFDIndex) {
+ this.newFDIndex = newFDIndex;
+ this.oldFDIndex = oldFDIndex;
+ }
+
+ public int getNewFDIndex() {
+ return newFDIndex;
+ }
+
+ public int getOldFDIndex() {
+ return oldFDIndex;
+ }
+ }
+
+ private void createCharStringData() throws IOException {
+ Map<String, DICTEntry> topDICT = cffReader.getTopDictEntries();
+
+ CFFIndexData charStringsIndex = cffReader.getCharStringIndex();
+
+ DICTEntry privateEntry = topDICT.get("Private");
+ if (privateEntry != null) {
+ int privateOffset = privateEntry.getOperands().get(1).intValue();
+ Map<String, DICTEntry> privateDICT = cffReader.getPrivateDict(privateEntry);
+
+ if (privateDICT.get("Subrs") != null) {
+ int localSubrOffset = privateOffset + privateDICT.get("Subrs").getOperands().get(0).intValue();
+ localIndexSubr = cffReader.readIndex(localSubrOffset);
+ } else {
+ localIndexSubr = cffReader.readIndex(null);
+ }
+ }
+
+ globalIndexSubr = cffReader.getGlobalIndexSubr();
+
+ //Create the two lists which are to store the local and global subroutines
+ subsetLocalIndexSubr = new ArrayList<byte[]>();
+ subsetGlobalIndexSubr = new ArrayList<byte[]>();
+
+ //Create the new char string index
+ subsetCharStringsIndex = new ArrayList<byte[]>();
+
+ localUniques = new ArrayList<Integer>();
+ globalUniques = new ArrayList<Integer>();
+
+ for (int gid : subsetGlyphs.keySet()) {
+ byte[] data = charStringsIndex.getValue(gid);
+ preScanForSubsetIndexSize(data);
+ }
+
+ //Store the size of each subset index and clear the unique arrays
+ subsetLocalSubrCount = localUniques.size();
+ subsetGlobalSubrCount = globalUniques.size();
+ localUniques.clear();
+ globalUniques.clear();
+
+ for (int gid : subsetGlyphs.keySet()) {
+ byte[] data = charStringsIndex.getValue(gid);
+ //Retrieve modified char string data and fill local / global subroutine arrays
+ data = readCharStringData(data, subsetLocalSubrCount);
+ subsetCharStringsIndex.add(data);
+ }
+ }
+
+ private void preScanForSubsetIndexSize(byte[] data) throws IOException {
+ boolean hasLocalSubroutines = localIndexSubr != null && localIndexSubr.getNumObjects() > 0;
+ boolean hasGlobalSubroutines = globalIndexSubr != null && globalIndexSubr.getNumObjects() > 0;
+ BytesNumber operand = new BytesNumber(-1, -1);
+ for (int dataPos = 0; dataPos < data.length; dataPos++) {
+ int b0 = data[dataPos] & 0xff;
+ if (b0 == LOCAL_SUBROUTINE && hasLocalSubroutines) {
+ int subrNumber = getSubrNumber(localIndexSubr.getNumObjects(), operand.getNumber());
+
+ if (!localUniques.contains(subrNumber) && subrNumber < localIndexSubr.getNumObjects()) {
+ localUniques.add(subrNumber);
+ byte[] subr = localIndexSubr.getValue(subrNumber);
+ preScanForSubsetIndexSize(subr);
+ }
+ operand.clearNumber();
+ } else if (b0 == GLOBAL_SUBROUTINE && hasGlobalSubroutines) {
+ int subrNumber = getSubrNumber(globalIndexSubr.getNumObjects(), operand.getNumber());
+
+ if (!globalUniques.contains(subrNumber) && subrNumber < globalIndexSubr.getNumObjects()) {
+ globalUniques.add(subrNumber);
+ byte[] subr = globalIndexSubr.getValue(subrNumber);
+ preScanForSubsetIndexSize(subr);
+ }
+ operand.clearNumber();
+ } else if ((b0 >= 0 && b0 <= 27) || (b0 >= 29 && b0 <= 31)) {
+ operand.clearNumber();
+ if (b0 == 19 || b0 == 20) {
+ dataPos += 1;
+ }
+ } else if (b0 == 28 || (b0 >= 32 && b0 <= 255)) {
+ operand = readNumber(b0, data, dataPos);
+ dataPos += operand.getNumBytes() - 1;
+ }
+ }
+ }
+
+ private int getSubrNumber(int numSubroutines, int operand) {
+ int bias = getBias(numSubroutines);
+ return bias + operand;
+ }
+
+ private byte[] readCharStringData(byte[] data, int subsetLocalSubrCount) throws IOException {
+ boolean hasLocalSubroutines = localIndexSubr != null && localIndexSubr.getNumObjects() > 0;
+ boolean hasGlobalSubroutines = globalIndexSubr != null && globalIndexSubr.getNumObjects() > 0;
+ BytesNumber operand = new BytesNumber(-1, -1);
+ for (int dataPos = 0; dataPos < data.length; dataPos++) {
+ int b0 = data[dataPos] & 0xff;
+ if (b0 == 10 && hasLocalSubroutines) {
+ int subrNumber = getSubrNumber(localIndexSubr.getNumObjects(), operand.getNumber());
+
+ int newRef = getNewRefForReference(subrNumber, localUniques, localIndexSubr, subsetLocalIndexSubr,
+ subsetLocalSubrCount);
+
+ if (newRef != -1) {
+ byte[] newData = constructNewRefData(dataPos, data, operand, subsetLocalSubrCount,
+ newRef, new int[] {10});
+ dataPos -= data.length - newData.length;
+ data = newData;
+ }
+
+ operand.clearNumber();
+ } else if (b0 == 29 && hasGlobalSubroutines) {
+ int subrNumber = getSubrNumber(globalIndexSubr.getNumObjects(), operand.getNumber());
+
+ int newRef = getNewRefForReference(subrNumber, globalUniques, globalIndexSubr, subsetGlobalIndexSubr,
+ subsetGlobalSubrCount);
+
+ if (newRef != -1) {
+ byte[] newData = constructNewRefData(dataPos, data, operand, subsetGlobalSubrCount,
+ newRef, new int[] {29});
+ dataPos -= (data.length - newData.length);
+ data = newData;
+ }
+
+ operand.clearNumber();
+ } else if ((b0 >= 0 && b0 <= 27) || (b0 >= 29 && b0 <= 31)) {
+ operand.clearNumber();
+ if (b0 == 19 || b0 == 20) {
+ dataPos += 1;
+ }
+ } else if (b0 == 28 || (b0 >= 32 && b0 <= 255)) {
+ operand = readNumber(b0, data, dataPos);
+ dataPos += operand.getNumBytes() - 1;
+ }
+ }
+
+ //Return the data with the modified references to our arrays
+ return data;
+ }
+
+ private int getNewRefForReference(int subrNumber, List<Integer> uniquesArray,
+ CFFIndexData indexSubr, List<byte[]> subsetIndexSubr, int subrCount) throws IOException {
+ int newRef = -1;
+ if (!uniquesArray.contains(subrNumber)) {
+ if (subrNumber < indexSubr.getNumObjects()) {
+ byte[] subr = indexSubr.getValue(subrNumber);
+ subr = readCharStringData(subr, subrCount);
+ if (!uniquesArray.contains(subrNumber)) {
+ uniquesArray.add(subrNumber);
+ subsetIndexSubr.add(subr);
+ newRef = subsetIndexSubr.size() - 1;
+ } else {
+ newRef = uniquesArray.indexOf(subrNumber);
+ }
+ }
+ } else {
+ newRef = uniquesArray.indexOf(subrNumber);
+ }
+ return newRef;
+ }
+
+ private int getBias(int subrCount) {
+ if (subrCount < 1240) {
+ return 107;
+ } else if (subrCount < 33900) {
+ return 1131;
+ } else {
+ return 32768;
+ }
+ }
+
+ private byte[] constructNewRefData(int curDataPos, byte[] currentData, BytesNumber operand,
+ int fullSubsetIndexSize, int curSubsetIndexSize, int[] operatorCode) {
+ //Create the new array with the modified reference
+ byte[] newData;
+ int startRef = curDataPos - operand.getNumBytes();
+ int length = operand.getNumBytes() + 1;
+ byte[] preBytes = new byte[startRef];
+ System.arraycopy(currentData, 0, preBytes, 0, startRef);
+ int newBias = getBias(fullSubsetIndexSize);
+ int newRef = curSubsetIndexSize - newBias;
+ byte[] newRefBytes = createNewRef(newRef, operatorCode, -1);
+ newData = concatArray(preBytes, newRefBytes);
+ byte[] postBytes = new byte[currentData.length - (startRef + length)];
+ System.arraycopy(currentData, startRef + length, postBytes, 0,
+ currentData.length - (startRef + length));
+ return concatArray(newData, postBytes);
+ }
+
+ public static byte[] createNewRef(int newRef, int[] operatorCode, int forceLength) {
+ byte[] newRefBytes;
+ int sizeOfOperator = operatorCode.length;
+ if ((forceLength == -1 && newRef <= 107) || forceLength == 1) {
+ newRefBytes = new byte[1 + sizeOfOperator];
+ //The index values are 0 indexed
+ newRefBytes[0] = (byte)(newRef + 139);
+ for (int i = 0; i < operatorCode.length; i++) {
+ newRefBytes[1 + i] = (byte)operatorCode[i];
+ }
+ } else if ((forceLength == -1 && newRef <= 1131) || forceLength == 2) {
+ newRefBytes = new byte[2 + sizeOfOperator];
+ if (newRef <= 363) {
+ newRefBytes[0] = (byte)247;
+ } else if (newRef <= 619) {
+ newRefBytes[0] = (byte)248;
+ } else if (newRef <= 875) {
+ newRefBytes[0] = (byte)249;
+ } else {
+ newRefBytes[0] = (byte)250;
+ }
+ newRefBytes[1] = (byte)(newRef - 108);
+ for (int i = 0; i < operatorCode.length; i++) {
+ newRefBytes[2 + i] = (byte)operatorCode[i];
+ }
+ } else if ((forceLength == -1 && newRef <= 32767) || forceLength == 3) {
+ newRefBytes = new byte[3 + sizeOfOperator];
+ newRefBytes[0] = 28;
+ newRefBytes[1] = (byte)(newRef >> 8);
+ newRefBytes[2] = (byte)newRef;
+ for (int i = 0; i < operatorCode.length; i++) {
+ newRefBytes[3 + i] = (byte)operatorCode[i];
+ }
+ } else {
+ newRefBytes = new byte[5 + sizeOfOperator];
+ newRefBytes[0] = 29;
+ newRefBytes[1] = (byte)(newRef >> 24);
+ newRefBytes[2] = (byte)(newRef >> 16);
+ newRefBytes[3] = (byte)(newRef >> 8);
+ newRefBytes[4] = (byte)newRef;
+ for (int i = 0; i < operatorCode.length; i++) {
+ newRefBytes[5 + i] = (byte)operatorCode[i];
+ }
+ }
+ return newRefBytes;
+ }
+
+ public static byte[] concatArray(byte[] a, byte[] b) {
+ int aLen = a.length;
+ int bLen = b.length;
+ byte[] c = new byte[aLen + bLen];
+ System.arraycopy(a, 0, c, 0, aLen);
+ System.arraycopy(b, 0, c, aLen, bLen);
+ return c;
+ }
+
+ private int writeIndex(List<byte[]> dataArray) {
+ int hdrTotal = 3;
+ //2 byte number of items
+ this.writeCard16(dataArray.size());
+ //Offset Size: 1 byte = 256, 2 bytes = 65536 etc.
+ int totLength = 0;
+ for (int i = 0; i < dataArray.size(); i++) {
+ totLength += dataArray.get(i).length;
+ }
+ int offSize = 1;
+ if (totLength <= (1 << 8)) {
+ offSize = 1;
+ } else if (totLength <= (1 << 16)) {
+ offSize = 2;
+ } else if (totLength <= (1 << 24)) {
+ offSize = 3;
+ } else {
+ offSize = 4;
+ }
+ this.writeByte(offSize);
+ //Count the first offset 1
+ hdrTotal += offSize;
+ int total = 0;
+ for (int i = 0; i < dataArray.size(); i++) {
+ hdrTotal += offSize;
+ int length = dataArray.get(i).length;
+ switch (offSize) {
+ case 1:
+ if (i == 0) {
+ writeByte(1);
+ }
+ total += length;
+ writeByte(total + 1);
+ break;
+ case 2:
+ if (i == 0) {
+ writeCard16(1);
+ }
+ total += length;
+ writeCard16(total + 1);
+ break;
+ case 3:
+ if (i == 0) {
+ writeThreeByteNumber(1);
+ }
+ total += length;
+ writeThreeByteNumber(total + 1);
+ break;
+ case 4:
+ if (i == 0) {
+ writeULong(1);
+ }
+ total += length;
+ writeULong(total + 1);
+ break;
+ default:
+ throw new AssertionError("Offset Size was not an expected value.");
+ }
+ }
+ for (int i = 0; i < dataArray.size(); i++) {
+ writeBytes(dataArray.get(i));
+ }
+ return hdrTotal + total;
+ }
+
+
+ private BytesNumber readNumber(int b0, byte[] input, int curPos) throws IOException {
+ if (b0 == 28) {
+ int b1 = input[curPos + 1] & 0xff;
+ int b2 = input[curPos + 2] & 0xff;
+ return new BytesNumber(Integer.valueOf((short) (b1 << 8 | b2)), 3);
+ } else if (b0 >= 32 && b0 <= 246) {
+ return new BytesNumber(Integer.valueOf(b0 - 139), 1);
+ } else if (b0 >= 247 && b0 <= 250) {
+ int b1 = input[curPos + 1] & 0xff;
+ return new BytesNumber(Integer.valueOf((b0 - 247) * 256 + b1 + 108), 2);
+ } else if (b0 >= 251 && b0 <= 254) {
+ int b1 = input[curPos + 1] & 0xff;
+ return new BytesNumber(Integer.valueOf(-(b0 - 251) * 256 - b1 - 108), 2);
+ } else if (b0 == 255) {
+ int b1 = input[curPos + 1] & 0xff;
+ int b2 = input[curPos + 2] & 0xff;
+ return new BytesNumber(Integer.valueOf((short)(b1 << 8 | b2)), 5);
+ } else {
+ throw new IllegalArgumentException();
+ }
+ }
+
+ /**
+ * A class used to store the last number operand and also it's size in bytes
+ */
+ private static final class BytesNumber {
+ private int number;
+ private int numBytes;
+
+ public BytesNumber(int number, int numBytes) {
+ this.number = number;
+ this.numBytes = numBytes;
+ }
+
+ public int getNumber() {
+ return this.number;
+ }
+
+ public int getNumBytes() {
+ return this.numBytes;
+ }
+
+ public void clearNumber() {
+ this.number = -1;
+ this.numBytes = -1;
+ }
+ }
+
+ private void writeCharsetTable(boolean cidFont) throws IOException {
+ writeByte(0);
+ for (int gid : gidToSID.keySet()) {
+ if (cidFont && gid == 0) {
+ continue;
+ }
+ writeCard16((cidFont) ? gid : gidToSID.get(gid));
+ }
+ }
+
+ private void writePrivateDict() throws IOException {
+ Map<String, DICTEntry> topDICT = cffReader.getTopDictEntries();
+
+ DICTEntry privateEntry = topDICT.get("Private");
+ if (privateEntry != null) {
+ writeBytes(cffReader.getPrivateDictBytes(privateEntry));
+ }
+ }
+
+ private void updateOffsets(int topDictOffset, int charsetOffset, int charStringOffset,
+ int privateDictOffset, int localIndexOffset, int encodingOffset)
+ throws IOException {
+ Map<String, DICTEntry> topDICT = cffReader.getTopDictEntries();
+ Map<String, DICTEntry> privateDICT = null;
+
+ DICTEntry privateEntry = topDICT.get("Private");
+ if (privateEntry != null) {
+ privateDICT = cffReader.getPrivateDict(privateEntry);
+ }
+
+ int dataPos = 3 + (cffReader.getTopDictIndex().getOffSize()
+ * cffReader.getTopDictIndex().getOffsets().length);
+ int dataTopDictOffset = topDictOffset + dataPos;
+
+ updateFixedOffsets(topDICT, dataTopDictOffset, charsetOffset, charStringOffset, encodingOffset);
+
+ if (privateDICT != null) {
+ //Private index offset in the top dict
+ int oldPrivateOffset = dataTopDictOffset + privateEntry.getOffset();
+ output = updateOffset(output, oldPrivateOffset + privateEntry.getOperandLengths().get(0),
+ privateEntry.getOperandLengths().get(1), privateDictOffset);
+
+ //Update the local subroutine index offset in the private dict
+ DICTEntry subroutines = privateDICT.get("Subrs");
+ int oldLocalSubrOffset = privateDictOffset + subroutines.getOffset();
+ //Value needs to be converted to -139 etc.
+ int encodeValue = 0;
+ if (subroutines.getOperandLength() == 1) {
+ encodeValue = 139;
+ }
+ output = updateOffset(output, oldLocalSubrOffset, subroutines.getOperandLength(),
+ (localIndexOffset - privateDictOffset) + encodeValue);
+ }
+ }
+
+ private void updateFixedOffsets(Map<String, DICTEntry> topDICT, int dataTopDictOffset,
+ int charsetOffset, int charStringOffset, int encodingOffset) {
+ //Charset offset in the top dict
+ DICTEntry charset = topDICT.get("charset");
+ int oldCharsetOffset = dataTopDictOffset + charset.getOffset();
+ output = updateOffset(output, oldCharsetOffset, charset.getOperandLength(), charsetOffset);
+
+ //Char string index offset in the private dict
+ DICTEntry charString = topDICT.get("CharStrings");
+ int oldCharStringOffset = dataTopDictOffset + charString.getOffset();
+ output = updateOffset(output, oldCharStringOffset, charString.getOperandLength(), charStringOffset);
+
+ DICTEntry encodingEntry = topDICT.get("Encoding");
+ if (encodingEntry != null && encodingEntry.getOperands().get(0).intValue() != 0
+ && encodingEntry.getOperands().get(0).intValue() != 1) {
+ int oldEncodingOffset = dataTopDictOffset + encodingEntry.getOffset();
+ output = updateOffset(output, oldEncodingOffset, encodingEntry.getOperandLength(), encodingOffset);
+ }
+ }
+
+ private void updateCIDOffsets(int topDictDataOffset, int fdArrayOffset, int fdSelectOffset,
+ int charsetOffset, int charStringOffset, int encodingOffset) {
+ LinkedHashMap<String, DICTEntry> topDict = cffReader.getTopDictEntries();
+
+ DICTEntry fdArrayEntry = topDict.get("FDArray");
+ if (fdArrayEntry != null) {
+ output = updateOffset(output, topDictDataOffset + fdArrayEntry.getOffset() - 1,
+ fdArrayEntry.getOperandLength(), fdArrayOffset);
+ }
+
+ DICTEntry fdSelect = topDict.get("FDSelect");
+ if (fdSelect != null) {
+ output = updateOffset(output, topDictDataOffset + fdSelect.getOffset() - 1,
+ fdSelect.getOperandLength(), fdSelectOffset);
+ }
+
+ updateFixedOffsets(topDict, topDictDataOffset, charsetOffset, charStringOffset, encodingOffset);
+ }
+
+ private byte[] updateOffset(byte[] out, int position, int length, int replacement) {
+ switch (length) {
+ case 1:
+ out[position] = (byte)(replacement & 0xFF);
+ break;
+ case 2:
+ if (replacement <= 363) {
+ out[position] = (byte)247;
+ } else if (replacement <= 619) {
+ out[position] = (byte)248;
+ } else if (replacement <= 875) {
+ out[position] = (byte)249;
+ } else {
+ out[position] = (byte)250;
+ }
+ out[position + 1] = (byte)(replacement - 108);
+ break;
+ case 3:
+ out[position] = (byte)28;
+ out[position + 1] = (byte)((replacement >> 8) & 0xFF);
+ out[position + 2] = (byte)(replacement & 0xFF);
+ break;
+ case 5:
+ out[position] = (byte)29;
+ out[position + 1] = (byte)((replacement >> 24) & 0xFF);
+ out[position + 2] = (byte)((replacement >> 16) & 0xFF);
+ out[position + 3] = (byte)((replacement >> 8) & 0xFF);
+ out[position + 4] = (byte)(replacement & 0xFF);
+ break;
+ default:
+ }
+ return out;
+ }
+
+ /**
+ * Appends a byte to the output array,
+ * updates currentPost but not realSize
+ */
+ private void writeByte(int b) {
+ output[currentPos++] = (byte)b;
+ realSize++;
+ }
+
+ /**
+ * Appends a USHORT to the output array,
+ * updates currentPost but not realSize
+ */
+ private void writeCard16(int s) {
+ byte b1 = (byte)((s >> 8) & 0xff);
+ byte b2 = (byte)(s & 0xff);
+ writeByte(b1);
+ writeByte(b2);
+ }
+
+ private void writeThreeByteNumber(int s) {
+ byte b1 = (byte)((s >> 16) & 0xFF);
+ byte b2 = (byte)((s >> 8) & 0xFF);
+ byte b3 = (byte)(s & 0xFF);
+ output[currentPos++] = b1;
+ output[currentPos++] = b2;
+ output[currentPos++] = b3;
+ realSize += 3;
+ }
+
+ /**
+ * Appends a ULONG to the output array,
+ * at the given position
+ */
+ 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);
+ output[currentPos++] = b1;
+ output[currentPos++] = b2;
+ output[currentPos++] = b3;
+ output[currentPos++] = b4;
+ realSize += 4;
+ }
+
+ /**
+ * 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;
+ }
+}
diff --git a/src/java/org/apache/fop/fonts/truetype/OpenFont.java b/src/java/org/apache/fop/fonts/truetype/OpenFont.java
new file mode 100644
index 000000000..3f4765cdc
--- /dev/null
+++ b/src/java/org/apache/fop/fonts/truetype/OpenFont.java
@@ -0,0 +1,1971 @@
+/*
+ * 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.awt.Rectangle;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.BitSet;
+import java.util.Collections;
+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.io.IOUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.xmlgraphics.fonts.Glyphs;
+
+import org.apache.fop.complexscripts.fonts.AdvancedTypographicTableFormatException;
+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;
+import org.apache.fop.fonts.MultiByteFont;
+
+public abstract class OpenFont {
+
+ static final byte NTABS = 24;
+ 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 static final String ENCODING = "WinAnsiEncoding"; // Default encoding
+
+ private static final short FIRST_CHAR = 0;
+
+ protected boolean useKerning = false;
+ private boolean isEmbeddable = true;
+ private boolean hasSerifs = true;
+ /**
+ * Table directory
+ */
+ protected Map<OFTableName, OFDirTabEntry> dirTabs;
+
+ private Map<Integer, Map<Integer, Integer>> kerningTab; // for CIDs
+ private Map<Integer, Map<Integer, Integer>> ansiKerningTab; // For winAnsiEncoding
+ private List<CMapSegment> cmaps;
+ protected List<UnicodeMapping> unicodeMappings;
+
+ private int upem; // unitsPerEm from "head" table
+ private int nhmtx; // Number of horizontal metrics
+ private PostScriptVersion postScriptVersion;
+ protected int locaFormat;
+ /**
+ * Offset to last loca
+ */
+ protected long lastLoca = 0;
+ protected int numberOfGlyphs; // Number of glyphs in font (read from "maxp" table)
+
+ /**
+ * Contains glyph data
+ */
+ protected OFMtxEntry[] mtxTab; // Contains glyph data
+
+ protected String postScriptName = "";
+ protected String fullName = "";
+ protected String notice = "";
+ protected final Set<String> familyNames = new HashSet<String>();
+ protected String subFamilyName = "";
+
+ private long italicAngle = 0;
+ private long isFixedPitch = 0;
+ private int fontBBox1 = 0;
+ private int fontBBox2 = 0;
+ private int fontBBox3 = 0;
+ private int fontBBox4 = 0;
+ private int capHeight = 0;
+ private int os2CapHeight = 0;
+ private int underlinePosition;
+ private int underlineThickness;
+ private int strikeoutPosition;
+ private int strikeoutThickness;
+ private int xHeight = 0;
+ private int os2xHeight = 0;
+ //Effective ascender/descender
+ private int ascender = 0;
+ private int descender = 0;
+ //Ascender/descender from hhea table
+ private int hheaAscender = 0;
+ private int hheaDescender = 0;
+ //Ascender/descender from OS/2 table
+ private int os2Ascender = 0;
+ private int os2Descender = 0;
+ private int usWeightClass = 0;
+
+ private short lastChar = 0;
+
+ private int[] ansiWidth;
+ private Map<Integer, List<Integer>> ansiIndex;
+
+ // internal mapping of glyph indexes to unicode indexes
+ // used for quick mappings in this class
+ private final Map<Integer, Integer> glyphToUnicodeMap = new HashMap<Integer, Integer>();
+ private final Map<Integer, Integer> unicodeToGlyphMap = new HashMap<Integer, Integer>();
+
+ private boolean isCFF;
+
+ // advanced typographic table support
+ protected boolean useAdvanced = false;
+ protected OTFAdvancedTypographicTableReader advancedTableReader;
+
+ /**
+ * 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;
+ }
+
+ /**
+ * logging instance
+ */
+ protected Log log = LogFactory.getLog(TTFFile.class);
+
+ public OpenFont() {
+ this(true, false);
+ }
+
+ /**
+ * Constructor
+ * @param useKerning true if kerning data should be loaded
+ * @param useAdvanced true if advanced typographic tables should be loaded
+ */
+ public OpenFont(boolean useKerning, boolean useAdvanced) {
+ this.useKerning = useKerning;
+ this.useAdvanced = useAdvanced;
+ }
+
+ /**
+ * Key-value helper class.
+ */
+ final class UnicodeMapping implements Comparable {
+
+ private final int unicodeIndex;
+ private final int glyphIndex;
+
+ UnicodeMapping(int glyphIndex, int unicodeIndex) {
+ this.unicodeIndex = unicodeIndex;
+ this.glyphIndex = glyphIndex;
+ glyphToUnicodeMap.put(new Integer(glyphIndex), new Integer(unicodeIndex));
+ unicodeToGlyphMap.put(new Integer(unicodeIndex), new Integer(glyphIndex));
+ }
+
+ /**
+ * Returns the glyphIndex.
+ * @return the glyph index
+ */
+ public int getGlyphIndex() {
+ return glyphIndex;
+ }
+
+ /**
+ * Returns the unicodeIndex.
+ * @return the Unicode index
+ */
+ public int getUnicodeIndex() {
+ return unicodeIndex;
+ }
+
+
+ /** {@inheritDoc} */
+ public int hashCode() {
+ int hc = unicodeIndex;
+ hc = 19 * hc + (hc ^ glyphIndex);
+ return hc;
+ }
+
+ /** {@inheritDoc} */
+ public boolean equals(Object o) {
+ if (o instanceof UnicodeMapping) {
+ UnicodeMapping m = (UnicodeMapping) o;
+ if (unicodeIndex != m.unicodeIndex) {
+ return false;
+ } else {
+ return (glyphIndex == m.glyphIndex);
+ }
+ } else {
+ return false;
+ }
+ }
+
+ /** {@inheritDoc} */
+ public int compareTo(Object o) {
+ if (o instanceof UnicodeMapping) {
+ UnicodeMapping m = (UnicodeMapping) o;
+ if (unicodeIndex > m.unicodeIndex) {
+ return 1;
+ } else if (unicodeIndex < m.unicodeIndex) {
+ return -1;
+ } else {
+ return 0;
+ }
+ } else {
+ return -1;
+ }
+ }
+ }
+
+ /**
+ * Obtain directory table entry.
+ * @param name (tag) of entry
+ * @return a directory table entry or null if none found
+ */
+ public OFDirTabEntry getDirectoryEntry(OFTableName name) {
+ return dirTabs.get(name);
+ }
+
+ /**
+ * Position inputstream to position indicated
+ * in the dirtab offset + offset
+ * @param in font file reader
+ * @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, OFTableName tableName,
+ long offset) throws IOException {
+ OFDirTabEntry dt = dirTabs.get(tableName);
+ if (dt == null) {
+ log.error("Dirtab " + tableName.getName() + " not found.");
+ return false;
+ } else {
+ in.seekSet(dt.getOffset() + offset);
+ }
+ return true;
+ }
+
+ /**
+ * Convert from truetype unit to pdf unit based on the
+ * unitsPerEm field in the "head" table
+ * @param n truetype unit
+ * @return pdf unit
+ */
+ public int convertTTFUnit2PDFUnit(int n) {
+ int ret;
+ if (n < 0) {
+ long rest1 = n % upem;
+ long storrest = 1000 * rest1;
+ long ledd2 = (storrest != 0 ? rest1 / storrest : 0);
+ ret = -((-1000 * n) / upem - (int)ledd2);
+ } else {
+ ret = (n / upem) * 1000 + ((n % upem) * 1000) / upem;
+ }
+
+ return ret;
+ }
+
+ /**
+ * Read the cmap table,
+ * return false if the table is not present or only unsupported
+ * tables are present. Currently only unicode cmaps are supported.
+ * Set the unicodeIndex in the TTFMtxEntries and fills in the
+ * cmaps vector.
+ */
+ protected boolean readCMAP() throws IOException {
+
+ unicodeMappings = new ArrayList<OpenFont.UnicodeMapping>();
+
+ seekTab(fontFile, OFTableName.CMAP, 2);
+ int numCMap = fontFile.readTTFUShort(); // Number of cmap subtables
+ long cmapUniOffset = 0;
+ long symbolMapOffset = 0;
+
+ if (log.isDebugEnabled()) {
+ log.debug(numCMap + " cmap tables");
+ }
+
+ //Read offset for all tables. We are only interested in the unicode table
+ for (int i = 0; i < numCMap; i++) {
+ int cmapPID = fontFile.readTTFUShort();
+ int cmapEID = fontFile.readTTFUShort();
+ long cmapOffset = fontFile.readTTFLong();
+
+ if (log.isDebugEnabled()) {
+ log.debug("Platform ID: " + cmapPID + " Encoding: " + cmapEID);
+ }
+
+ if (cmapPID == 3 && cmapEID == 1) {
+ cmapUniOffset = cmapOffset;
+ }
+ if (cmapPID == 3 && cmapEID == 0) {
+ symbolMapOffset = cmapOffset;
+ }
+ }
+
+ if (cmapUniOffset > 0) {
+ return readUnicodeCmap(cmapUniOffset, 1);
+ } else if (symbolMapOffset > 0) {
+ return readUnicodeCmap(symbolMapOffset, 0);
+ } else {
+ log.fatal("Unsupported TrueType font: No Unicode or Symbol cmap table"
+ + " not present. Aborting");
+ return false;
+ }
+ }
+
+ private boolean readUnicodeCmap(long cmapUniOffset, int encodingID)
+ throws IOException {
+ //Read CMAP table and correct mtxTab.index
+ int mtxPtr = 0;
+
+ // Read unicode cmap
+ seekTab(fontFile, OFTableName.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) {
+ 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);
+ log.debug("searchRange : " + cmapSearchRange);
+ log.debug("entrySelector: " + cmapEntrySelector);
+ log.debug("rangeShift : " + cmapRangeShift);
+ }
+
+
+ int[] cmapEndCounts = new int[cmapSegCountX2 / 2];
+ int[] cmapStartCounts = new int[cmapSegCountX2 / 2];
+ int[] cmapDeltas = new int[cmapSegCountX2 / 2];
+ int[] cmapRangeOffsets = new int[cmapSegCountX2 / 2];
+
+ for (int i = 0; i < (cmapSegCountX2 / 2); i++) {
+ cmapEndCounts[i] = fontFile.readTTFUShort();
+ }
+
+ fontFile.skip(2); // Skip reservedPad
+
+ for (int i = 0; i < (cmapSegCountX2 / 2); i++) {
+ cmapStartCounts[i] = fontFile.readTTFUShort();
+ }
+
+ for (int i = 0; i < (cmapSegCountX2 / 2); i++) {
+ cmapDeltas[i] = fontFile.readTTFShort();
+ }
+
+ //int startRangeOffset = in.getCurrentPos();
+
+ for (int i = 0; i < (cmapSegCountX2 / 2); i++) {
+ cmapRangeOffsets[i] = fontFile.readTTFUShort();
+ }
+
+ int glyphIdArrayOffset = fontFile.getCurrentPos();
+
+ BitSet eightBitGlyphs = new BitSet(256);
+
+ // Insert the unicode id for the glyphs in mtxTab
+ // and fill in the cmaps ArrayList
+ for (int i = 0; i < cmapStartCounts.length; i++) {
+
+ if (log.isTraceEnabled()) {
+ log.trace(i + ": " + cmapStartCounts[i]
+ + " - " + cmapEndCounts[i]);
+ }
+ if (log.isDebugEnabled()) {
+ if (isInPrivateUseArea(cmapStartCounts[i], cmapEndCounts[i])) {
+ log.debug("Font contains glyphs in the Unicode private use area: "
+ + Integer.toHexString(cmapStartCounts[i]) + " - "
+ + Integer.toHexString(cmapEndCounts[i]));
+ }
+ }
+
+ for (int j = cmapStartCounts[i]; j <= cmapEndCounts[i]; j++) {
+
+ // Update lastChar
+ if (j < 256 && j > lastChar) {
+ lastChar = (short)j;
+ }
+
+ if (j < 256) {
+ eightBitGlyphs.set(j);
+ }
+
+ if (mtxPtr < mtxTab.length) {
+ int glyphIdx;
+ // the last character 65535 = .notdef
+ // may have a range offset
+ if (cmapRangeOffsets[i] != 0 && j != 65535) {
+ int glyphOffset = glyphIdArrayOffset
+ + ((cmapRangeOffsets[i] / 2)
+ + (j - cmapStartCounts[i])
+ + (i)
+ - cmapSegCountX2 / 2) * 2;
+ fontFile.seekSet(glyphOffset);
+ glyphIdx = (fontFile.readTTFUShort() + cmapDeltas[i])
+ & 0xffff;
+ //mtxTab[glyphIdx].setName(mtxTab[glyphIdx].getName() + " - "+(char)j);
+ unicodeMappings.add(new UnicodeMapping(glyphIdx, j));
+ mtxTab[glyphIdx].getUnicodeIndex().add(new Integer(j));
+
+ if (encodingID == 0 && j >= 0xF020 && j <= 0xF0FF) {
+ //Experimental: Mapping 0xF020-0xF0FF to 0x0020-0x00FF
+ //Tested with Wingdings and Symbol TTF fonts which map their
+ //glyphs in the region 0xF020-0xF0FF.
+ int mapped = j - 0xF000;
+ if (!eightBitGlyphs.get(mapped)) {
+ //Only map if Unicode code point hasn't been mapped before
+ unicodeMappings.add(new UnicodeMapping(glyphIdx, mapped));
+ mtxTab[glyphIdx].getUnicodeIndex().add(new Integer(mapped));
+ }
+ }
+
+ // Also add winAnsiWidth
+ List<Integer> v = ansiIndex.get(new Integer(j));
+ if (v != null) {
+ for (Integer aIdx : v) {
+ ansiWidth[aIdx.intValue()]
+ = mtxTab[glyphIdx].getWx();
+
+ if (log.isTraceEnabled()) {
+ log.trace("Added width "
+ + mtxTab[glyphIdx].getWx()
+ + " uni: " + j
+ + " ansi: " + aIdx.intValue());
+ }
+ }
+ }
+
+ if (log.isTraceEnabled()) {
+ log.trace("Idx: "
+ + glyphIdx
+ + " Delta: " + cmapDeltas[i]
+ + " Unicode: " + j
+ + " name: " + mtxTab[glyphIdx].getName());
+ }
+ } else {
+ glyphIdx = (j + cmapDeltas[i]) & 0xffff;
+
+ if (glyphIdx < mtxTab.length) {
+ mtxTab[glyphIdx].getUnicodeIndex().add(new Integer(j));
+ } else {
+ log.debug("Glyph " + glyphIdx
+ + " out of range: "
+ + mtxTab.length);
+ }
+
+ unicodeMappings.add(new UnicodeMapping(glyphIdx, j));
+ if (glyphIdx < mtxTab.length) {
+ mtxTab[glyphIdx].getUnicodeIndex().add(new Integer(j));
+ } else {
+ log.debug("Glyph " + glyphIdx
+ + " out of range: "
+ + mtxTab.length);
+ }
+
+ // Also add winAnsiWidth
+ List<Integer> v = ansiIndex.get(new Integer(j));
+ if (v != null) {
+ for (Integer aIdx : v) {
+ ansiWidth[aIdx.intValue()] = mtxTab[glyphIdx].getWx();
+ }
+ }
+
+ //getLogger().debug("IIdx: " +
+ // mtxPtr +
+ // " Delta: " + cmap_deltas[i] +
+ // " Unicode: " + j +
+ // " name: " +
+ // mtxTab[(j+cmap_deltas[i]) & 0xffff].name);
+
+ }
+ if (glyphIdx < mtxTab.length) {
+ if (mtxTab[glyphIdx].getUnicodeIndex().size() < 2) {
+ mtxPtr++;
+ }
+ }
+ }
+ }
+ }
+ } else {
+ log.error("Cmap format not supported: " + cmapFormat);
+ return false;
+ }
+ return true;
+ }
+
+ private boolean isInPrivateUseArea(int start, int end) {
+ return (isInPrivateUseArea(start) || isInPrivateUseArea(end));
+ }
+
+ private boolean isInPrivateUseArea(int unicode) {
+ return (unicode >= 0xE000 && unicode <= 0xF8FF);
+ }
+
+ /**
+ *
+ * @return mmtx data
+ */
+ public List<OFMtxEntry> getMtx() {
+ return Collections.unmodifiableList(Arrays.asList(mtxTab));
+ }
+
+ /**
+ * Print first char/last char
+ */
+ private void printMaxMin() {
+ int min = 255;
+ int max = 0;
+ for (int i = 0; i < mtxTab.length; i++) {
+ if (mtxTab[i].getIndex() < min) {
+ min = mtxTab[i].getIndex();
+ }
+ if (mtxTab[i].getIndex() > max) {
+ max = mtxTab[i].getIndex();
+ }
+ }
+ log.info("Min: " + min);
+ log.info("Max: " + max);
+ }
+
+
+ /**
+ * Reads the font using a FontFileReader.
+ *
+ * @param in The FontFileReader to use
+ * @throws IOException In case of an I/O problem
+ */
+ public void readFont(FontFileReader in, String header) throws IOException {
+ readFont(in, header, (String)null);
+ }
+
+ /**
+ * initialize the ansiWidths array (for winAnsiEncoding)
+ * and fill with the missingwidth
+ */
+ protected void initAnsiWidths() {
+ ansiWidth = new int[256];
+ for (int i = 0; i < 256; i++) {
+ ansiWidth[i] = mtxTab[0].getWx();
+ }
+
+ // 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 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<Integer> v = ansiIndex.get(uni);
+ if (v == null) {
+ v = new ArrayList<Integer>();
+ ansiIndex.put(uni, v);
+ }
+ v.add(ansi);
+ }
+ }
+
+ /**
+ * Read the font data.
+ * If the fontfile is a TrueType Collection (.ttc file)
+ * the name of the font to read data for must be supplied,
+ * else the name is ignored.
+ *
+ * @param in The FontFileReader to use
+ * @param name The name of the font
+ * @return boolean Returns true if the font is valid
+ * @throws IOException In case of an I/O problem
+ */
+ public boolean readFont(FontFileReader in, String header, String name) throws IOException {
+ initializeFont(in);
+ /*
+ * Check if TrueType collection, and that the name
+ * exists in the collection
+ */
+ if (!checkTTC(header, name)) {
+ if (name == null) {
+ throw new IllegalArgumentException(
+ "For TrueType collection you must specify which font "
+ + "to select (-ttcname)");
+ } else {
+ throw new IOException(
+ "Name does not exist in the TrueType collection: " + name);
+ }
+ }
+
+ readDirTabs();
+ readFontHeader();
+ getNumGlyphs();
+ if (log.isDebugEnabled()) {
+ log.debug("Number of glyphs in font: " + numberOfGlyphs);
+ }
+ readHorizontalHeader();
+ readHorizontalMetrics();
+ initAnsiWidths();
+ readPostScript();
+ readOS2();
+ determineAscDesc();
+
+ readName();
+ boolean pcltFound = readPCLT();
+ // Read cmap table and fill in ansiwidths
+ boolean valid = readCMAP();
+ if (!valid) {
+ return false;
+ }
+
+ // Create cmaps for bfentries
+ createCMaps();
+ updateBBoxAndOffset();
+
+ if (useKerning) {
+ readKerning();
+ }
+ handleCharacterSpacing(in);
+
+ guessVerticalMetricsFromGlyphBBox();
+ 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 header, MultiByteFont mbfont) throws IOException {
+ readFont(in, header, mbfont.getTTCName());
+ }
+
+ protected abstract void updateBBoxAndOffset() throws IOException;
+
+ protected abstract void readName() throws IOException;
+
+ protected abstract void initializeFont(FontFileReader in) throws IOException;
+
+ protected void handleCharacterSpacing(FontFileReader in) throws IOException {
+ // Read advanced typographic tables.
+ if (useAdvanced) {
+ try {
+ OTFAdvancedTypographicTableReader atr
+ = new OTFAdvancedTypographicTableReader(this, in);
+ atr.readAll();
+ this.advancedTableReader = atr;
+ } catch (AdvancedTypographicTableFormatException e) {
+ log.warn(
+ "Encountered format constraint violation in advanced (typographic) table (AT) "
+ + "in font '" + getFullName() + "', ignoring AT data: "
+ + e.getMessage()
+ );
+ }
+ }
+
+ }
+
+ protected void createCMaps() {
+ cmaps = new ArrayList<CMapSegment>();
+ int unicodeStart;
+ int glyphStart;
+ int unicodeEnd;
+
+ Iterator<UnicodeMapping> e = unicodeMappings.iterator();
+ UnicodeMapping um = e.next();
+ UnicodeMapping lastMapping = um;
+
+ unicodeStart = um.getUnicodeIndex();
+ glyphStart = um.getGlyphIndex();
+
+ while (e.hasNext()) {
+ um = e.next();
+ if (((lastMapping.getUnicodeIndex() + 1) != um.getUnicodeIndex())
+ || ((lastMapping.getGlyphIndex() + 1) != um.getGlyphIndex())) {
+ unicodeEnd = lastMapping.getUnicodeIndex();
+ cmaps.add(new CMapSegment(unicodeStart, unicodeEnd, glyphStart));
+ unicodeStart = um.getUnicodeIndex();
+ glyphStart = um.getGlyphIndex();
+ }
+ lastMapping = um;
+ }
+
+ unicodeEnd = lastMapping.getUnicodeIndex();
+ cmaps.add(new CMapSegment(unicodeStart, unicodeEnd, glyphStart));
+ }
+
+ /**
+ * Returns the PostScript name of the font.
+ * @return String The PostScript name
+ */
+ public String getPostScriptName() {
+ if (postScriptName.length() == 0) {
+ return FontUtil.stripWhiteSpace(getFullName());
+ } else {
+ return postScriptName;
+ }
+ }
+
+ PostScriptVersion getPostScriptVersion() {
+ return postScriptVersion;
+ }
+
+ /**
+ * Returns the font family names of the font.
+ * @return Set The family names (a Set of Strings)
+ */
+ public Set<String> getFamilyNames() {
+ return familyNames;
+ }
+
+ /**
+ * Returns the font sub family name of the font.
+ * @return String The sub family name
+ */
+ public String getSubFamilyName() {
+ return subFamilyName;
+ }
+
+ /**
+ * Returns the full name of the font.
+ * @return String The full name
+ */
+ public String getFullName() {
+ return fullName;
+ }
+
+ /**
+ * Returns the name of the character set used.
+ * @return String The caracter set
+ */
+ public String getCharSetName() {
+ return ENCODING;
+ }
+
+ /**
+ * Returns the CapHeight attribute of the font.
+ * @return int The CapHeight
+ */
+ public int getCapHeight() {
+ return convertTTFUnit2PDFUnit(capHeight);
+ }
+
+ /**
+ * Returns the XHeight attribute of the font.
+ * @return int The XHeight
+ */
+ public int getXHeight() {
+ return convertTTFUnit2PDFUnit(xHeight);
+ }
+
+ /**
+ * 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 |= 64;
+ }
+ if (isFixedPitch != 0) {
+ flags |= 2;
+ }
+ if (hasSerifs) {
+ flags |= 1;
+ }
+ return flags;
+ }
+
+ /**
+ * Returns the weight class of this font. Valid values are 100, 200....,800, 900.
+ * @return the weight class value (or 0 if there was no OS/2 table in the font)
+ */
+ public int getWeightClass() {
+ return this.usWeightClass;
+ }
+
+ /**
+ * Returns the StemV attribute of the font.
+ * @return String The StemV
+ */
+ public String getStemV() {
+ return "0";
+ }
+
+ /**
+ * Returns the ItalicAngle attribute of the font.
+ * @return String The ItalicAngle
+ */
+ public String getItalicAngle() {
+ String ia = Short.toString((short)(italicAngle / 0x10000));
+
+ // This is the correct italic angle, however only int italic
+ // angles are supported at the moment so this is commented out.
+ /*
+ * if ((italicAngle % 0x10000) > 0 )
+ * ia=ia+(comma+Short.toString((short)((short)((italicAngle % 0x10000)*1000)/0x10000)));
+ */
+ return ia;
+ }
+
+ /**
+ * @return int[] The font bbox
+ */
+ public int[] getFontBBox() {
+ final int[] fbb = new int[4];
+ fbb[0] = convertTTFUnit2PDFUnit(fontBBox1);
+ fbb[1] = convertTTFUnit2PDFUnit(fontBBox2);
+ fbb[2] = convertTTFUnit2PDFUnit(fontBBox3);
+ fbb[3] = convertTTFUnit2PDFUnit(fontBBox4);
+
+ return fbb;
+ }
+
+ /**
+ * Returns the LowerCaseAscent attribute of the font.
+ * @return int The LowerCaseAscent
+ */
+ public int getLowerCaseAscent() {
+ return convertTTFUnit2PDFUnit(ascender);
+ }
+
+ /**
+ * Returns the LowerCaseDescent attribute of the font.
+ * @return int The LowerCaseDescent
+ */
+ public int getLowerCaseDescent() {
+ return convertTTFUnit2PDFUnit(descender);
+ }
+
+ /**
+ * Returns the index of the last character, but this is for WinAnsiEncoding
+ * only, so the last char is < 256.
+ * @return short Index of the last character (<256)
+ */
+ public short getLastChar() {
+ return lastChar;
+ }
+
+ /**
+ * Returns the index of the first character.
+ * @return short Index of the first character
+ */
+ public short getFirstChar() {
+ return FIRST_CHAR;
+ }
+
+ /**
+ * Returns an array of character widths.
+ * @return int[] The character widths
+ */
+ public int[] getWidths() {
+ int[] wx = new int[mtxTab.length];
+ for (int i = 0; i < wx.length; i++) {
+ wx[i] = convertTTFUnit2PDFUnit(mtxTab[i].getWx());
+ }
+ return wx;
+ }
+
+ public Rectangle[] getBoundingBoxes() {
+ Rectangle[] boundingBoxes = new Rectangle[mtxTab.length];
+ for (int i = 0; i < boundingBoxes.length; i++) {
+ int[] boundingBox = mtxTab[i].getBoundingBox();
+ boundingBoxes[i] = new Rectangle(
+ convertTTFUnit2PDFUnit(boundingBox[0]),
+ convertTTFUnit2PDFUnit(boundingBox[1]),
+ convertTTFUnit2PDFUnit(boundingBox[2] - boundingBox[0]),
+ convertTTFUnit2PDFUnit(boundingBox[3] - boundingBox[1]));
+ }
+ return boundingBoxes;
+ }
+
+ /**
+ * Returns an array (xMin, yMin, xMax, yMax) for a glyph.
+ *
+ * @param glyphIndex the index of the glyph
+ * @return int[] Array defining bounding box.
+ */
+ public int[] getBBox(int glyphIndex) {
+ int[] bboxInTTFUnits = mtxTab[glyphIndex].getBoundingBox();
+ int[] bbox = new int[4];
+ for (int i = 0; i < 4; i++) {
+ bbox[i] = convertTTFUnit2PDFUnit(bboxInTTFUnits[i]);
+ }
+ return bbox;
+ }
+
+ /**
+ * Returns the width of a given character.
+ * @param idx Index of the character
+ * @return int Standard width
+ */
+ public int getCharWidth(int idx) {
+ return convertTTFUnit2PDFUnit(ansiWidth[idx]);
+ }
+
+ /**
+ * Returns the kerning table.
+ * @return Map The kerning table
+ */
+ public Map<Integer, Map<Integer, Integer>> getKerning() {
+ return kerningTab;
+ }
+
+ /**
+ * Returns the ANSI kerning table.
+ * @return Map The ANSI kerning table
+ */
+ public Map<Integer, Map<Integer, Integer>> getAnsiKerning() {
+ return ansiKerningTab;
+ }
+
+ public int getUnderlinePosition() {
+ return convertTTFUnit2PDFUnit(underlinePosition);
+ }
+
+ public int getUnderlineThickness() {
+ return convertTTFUnit2PDFUnit(underlineThickness);
+ }
+
+ public int getStrikeoutPosition() {
+ return convertTTFUnit2PDFUnit(strikeoutPosition);
+ }
+
+ public int getStrikeoutThickness() {
+ return convertTTFUnit2PDFUnit(strikeoutThickness);
+ }
+
+ /**
+ * Indicates if the font may be embedded.
+ * @return boolean True if it may be embedded
+ */
+ public boolean isEmbeddable() {
+ return isEmbeddable;
+ }
+
+ /**
+ * Indicates whether or not the font is an OpenType
+ * CFF font (rather than a TrueType font).
+ * @return true if the font is in OpenType CFF format.
+ */
+ public boolean isCFF() {
+ return this.isCFF;
+ }
+
+ /**
+ * Read Table Directory from the current position in the
+ * FontFileReader and fill the global HashMap dirTabs
+ * with the table name (String) as key and a TTFDirTabEntry
+ * as value.
+ * @throws IOException in case of an I/O problem
+ */
+ 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");
+ break;
+ case 0x4F54544F: //"OTTO"
+ this.isCFF = true;
+ log.debug("sfnt version: OpenType with CFF data");
+ break;
+ case 0x74727565: //"true"
+ log.debug("sfnt version: Apple TrueType");
+ break;
+ case 0x74797031: //"typ1"
+ log.debug("sfnt version: Apple Type 1 housed in sfnt wrapper");
+ break;
+ default:
+ log.debug("Unknown sfnt version: " + Integer.toHexString(sfntVersion));
+ break;
+ }
+ int ntabs = fontFile.readTTFUShort();
+ fontFile.skip(6); // 3xTTF_USHORT_SIZE
+
+ dirTabs = new HashMap<OFTableName, OFDirTabEntry>();
+ OFDirTabEntry[] pd = new OFDirTabEntry[ntabs];
+ log.debug("Reading " + ntabs + " dir tables");
+
+ for (int i = 0; i < ntabs; i++) {
+ pd[i] = new OFDirTabEntry();
+ String tableName = pd[i].read(fontFile);
+ dirTabs.put(OFTableName.getValue(tableName), pd[i]);
+ }
+ dirTabs.put(OFTableName.TABLE_DIRECTORY,
+ new OFDirTabEntry(0L, fontFile.getCurrentPos()));
+ log.debug("dir tables: " + dirTabs.keySet());
+ }
+
+ /**
+ * Read the "head" table, this reads the bounding box and
+ * sets the upem (unitsPerEM) variable
+ * @throws IOException in case of an I/O problem
+ */
+ protected void readFontHeader() throws IOException {
+ seekTab(fontFile, OFTableName.HEAD, 2 * 4 + 2 * 4);
+ int flags = fontFile.readTTFUShort();
+ if (log.isDebugEnabled()) {
+ log.debug("flags: " + flags + " - " + Integer.toString(flags, 2));
+ }
+ upem = fontFile.readTTFUShort();
+ if (log.isDebugEnabled()) {
+ log.debug("unit per em: " + upem);
+ }
+
+ fontFile.skip(16);
+
+ fontBBox1 = fontFile.readTTFShort();
+ fontBBox2 = fontFile.readTTFShort();
+ fontBBox3 = fontFile.readTTFShort();
+ fontBBox4 = fontFile.readTTFShort();
+ if (log.isDebugEnabled()) {
+ log.debug("font bbox: xMin=" + fontBBox1
+ + " yMin=" + fontBBox2
+ + " xMax=" + fontBBox3
+ + " yMax=" + fontBBox4);
+ }
+
+ fontFile.skip(2 + 2 + 2);
+
+ locaFormat = fontFile.readTTFShort();
+ }
+
+ /**
+ * Read the number of glyphs from the "maxp" table
+ * @throws IOException in case of an I/O problem
+ */
+ protected void getNumGlyphs() throws IOException {
+ seekTab(fontFile, OFTableName.MAXP, 4);
+ numberOfGlyphs = fontFile.readTTFUShort();
+ }
+
+
+ /**
+ * 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.
+ * @throws IOException in case of an I/O problem
+ */
+ protected void readHorizontalHeader()
+ throws IOException {
+ seekTab(fontFile, OFTableName.HHEA, 4);
+ hheaAscender = fontFile.readTTFShort();
+ hheaDescender = fontFile.readTTFShort();
+
+ fontFile.skip(2 + 2 + 3 * 2 + 8 * 2);
+ nhmtx = fontFile.readTTFUShort();
+
+ if (log.isDebugEnabled()) {
+ log.debug("hhea.Ascender: " + formatUnitsForDebug(hheaAscender));
+ log.debug("hhea.Descender: " + formatUnitsForDebug(hheaDescender));
+ log.debug("Number of horizontal metrics: " + nhmtx);
+ }
+ }
+
+ /**
+ * Read "hmtx" table and put the horizontal metrics
+ * 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
+ * @throws IOException in case of an I/O problem
+ */
+ protected void readHorizontalMetrics()
+ throws IOException {
+ seekTab(fontFile, OFTableName.HMTX, 0);
+
+ int mtxSize = Math.max(numberOfGlyphs, nhmtx);
+ mtxTab = new OFMtxEntry[mtxSize];
+
+ if (log.isTraceEnabled()) {
+ log.trace("*** Widths array: \n");
+ }
+ for (int i = 0; i < mtxSize; i++) {
+ mtxTab[i] = new OFMtxEntry();
+ }
+ for (int i = 0; i < nhmtx; i++) {
+ mtxTab[i].setWx(fontFile.readTTFUShort());
+ mtxTab[i].setLsb(fontFile.readTTFUShort());
+
+ if (log.isTraceEnabled()) {
+ log.trace(" width[" + i + "] = "
+ + convertTTFUnit2PDFUnit(mtxTab[i].getWx()) + ";");
+ }
+ }
+
+ if (nhmtx < mtxSize) {
+ // Fill in the missing widths
+ int lastWidth = mtxTab[nhmtx - 1].getWx();
+ for (int i = nhmtx; i < mtxSize; i++) {
+ mtxTab[i].setWx(lastWidth);
+ mtxTab[i].setLsb(fontFile.readTTFUShort());
+ }
+ }
+ }
+
+
+ /**
+ * Read the "post" table
+ * containing the PostScript names of the glyphs.
+ */
+ protected void readPostScript() throws IOException {
+ seekTab(fontFile, OFTableName.POST, 0);
+ int postFormat = fontFile.readTTFLong();
+ italicAngle = fontFile.readTTFULong();
+ underlinePosition = fontFile.readTTFShort();
+ underlineThickness = fontFile.readTTFShort();
+ isFixedPitch = fontFile.readTTFULong();
+
+ //Skip memory usage values
+ fontFile.skip(4 * 4);
+
+ log.debug("PostScript format: 0x" + Integer.toHexString(postFormat));
+ switch (postFormat) {
+ case 0x00010000:
+ log.debug("PostScript format 1");
+ 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 = fontFile.readTTFUShort();
+
+ // Read indexes
+ for (int i = 0; i < l; i++) {
+ mtxTab[i].setIndex(fontFile.readTTFUShort());
+
+ if (mtxTab[i].getIndex() > 257) {
+ //Index is not in the Macintosh standard set
+ numGlyphStrings++;
+ }
+
+ if (log.isTraceEnabled()) {
+ log.trace("PostScript index: " + mtxTab[i].getIndexAsString());
+ }
+ }
+
+ // firstChar=minIndex;
+ String[] psGlyphsBuffer = new String[numGlyphStrings];
+ if (log.isDebugEnabled()) {
+ log.debug("Reading " + numGlyphStrings
+ + " glyphnames, that are not in the standard Macintosh"
+ + " set. Total number of glyphs=" + l);
+ }
+ for (int i = 0; i < psGlyphsBuffer.length; i++) {
+ psGlyphsBuffer[i] = fontFile.readTTFString(fontFile.readTTFUByte());
+ }
+
+ //Set glyph names
+ for (int i = 0; i < l; i++) {
+ 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() - MAC_GLYPH_ORDERING.length;
+
+ if (log.isTraceEnabled()) {
+ log.trace(k + " i=" + i + " mtx=" + mtxTab.length
+ + " ps=" + psGlyphsBuffer.length);
+ }
+
+ mtxTab[i].setName(psGlyphsBuffer[k]);
+ }
+ }
+ }
+
+ break;
+ 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;
+ }
+ }
+
+
+ /**
+ * Read the "OS/2" table
+ */
+ protected void readOS2() throws IOException {
+ // Check if font is embeddable
+ OFDirTabEntry os2Entry = dirTabs.get(OFTableName.OS2);
+ if (os2Entry != null) {
+ seekTab(fontFile, OFTableName.OS2, 0);
+ int version = fontFile.readTTFUShort();
+ if (log.isDebugEnabled()) {
+ log.debug("OS/2 table: version=" + version
+ + ", offset=" + os2Entry.getOffset() + ", len=" + os2Entry.getLength());
+ }
+ fontFile.skip(2); //xAvgCharWidth
+ this.usWeightClass = fontFile.readTTFUShort();
+
+ // usWidthClass
+ fontFile.skip(2);
+
+ int fsType = fontFile.readTTFUShort();
+ if (fsType == 2) {
+ isEmbeddable = false;
+ } else {
+ isEmbeddable = true;
+ }
+ fontFile.skip(8 * 2);
+ strikeoutThickness = fontFile.readTTFShort();
+ strikeoutPosition = fontFile.readTTFShort();
+ fontFile.skip(2);
+ fontFile.skip(10); //panose array
+ fontFile.skip(4 * 4); //unicode ranges
+ fontFile.skip(4);
+ fontFile.skip(3 * 2);
+ int v;
+ 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 = fontFile.readTTFShort(); //sTypoLineGap
+ if (log.isDebugEnabled()) {
+ log.debug("sTypoLineGap: " + v);
+ }
+ v = fontFile.readTTFUShort(); //usWinAscent
+ if (log.isDebugEnabled()) {
+ log.debug("usWinAscent: " + formatUnitsForDebug(v));
+ }
+ 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)) {
+ 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);
+ }
+ }
+
+ } else {
+ isEmbeddable = true;
+ }
+ }
+
+ /**
+ * Read the "PCLT" table to find xHeight and capHeight.
+ * @throws IOException In case of a I/O problem
+ */
+ protected boolean readPCLT() throws IOException {
+ OFDirTabEntry dirTab = dirTabs.get(OFTableName.PCLT);
+ if (dirTab != null) {
+ fontFile.seekSet(dirTab.getOffset() + 4 + 4 + 2);
+ xHeight = fontFile.readTTFUShort();
+ log.debug("xHeight from PCLT: " + formatUnitsForDebug(xHeight));
+ fontFile.skip(2 * 2);
+ capHeight = fontFile.readTTFUShort();
+ log.debug("capHeight from PCLT: " + formatUnitsForDebug(capHeight));
+ fontFile.skip(2 + 16 + 8 + 6 + 1 + 1);
+
+ int serifStyle = fontFile.readTTFUByte();
+ serifStyle = serifStyle >> 6;
+ serifStyle = serifStyle & 3;
+ if (serifStyle == 1) {
+ hasSerifs = false;
+ } else {
+ hasSerifs = true;
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Determines the right source for the ascender and descender values. The problem here is
+ * that the interpretation of these values is not the same for every font. There doesn't seem
+ * to be a uniform definition of an ascender and a descender. In some fonts
+ * the hhea values are defined after the Apple interpretation, but not in every font. The
+ * same problem is in the OS/2 table. FOP needs the ascender and descender to determine the
+ * baseline so we need values which add up more or less to the "em box". However, due to
+ * accent modifiers a character can grow beyond the em box.
+ */
+ protected void determineAscDesc() {
+ int hheaBoxHeight = hheaAscender - hheaDescender;
+ int os2BoxHeight = os2Ascender - os2Descender;
+ if (os2Ascender > 0 && os2BoxHeight <= upem) {
+ ascender = os2Ascender;
+ descender = os2Descender;
+ } else if (hheaAscender > 0 && hheaBoxHeight <= upem) {
+ ascender = hheaAscender;
+ descender = hheaDescender;
+ } else {
+ if (os2Ascender > 0) {
+ //Fall back to info from OS/2 if possible
+ ascender = os2Ascender;
+ descender = os2Descender;
+ } else {
+ ascender = hheaAscender;
+ descender = hheaDescender;
+ }
+ }
+
+ if (log.isDebugEnabled()) {
+ log.debug("Font box height: " + (ascender - descender));
+ if (ascender - descender > upem) {
+ log.debug("Ascender and descender together are larger than the em box.");
+ }
+ }
+ }
+
+ protected void guessVerticalMetricsFromGlyphBBox() {
+ // Approximate capHeight from height of "H"
+ // It's most unlikely that a font misses the PCLT table
+ // This also assumes that postscriptnames exists ("H")
+ // Should look it up in the cmap (that wouldn't help
+ // for charsets without H anyway...)
+ // Same for xHeight with the letter "x"
+ int localCapHeight = 0;
+ int localXHeight = 0;
+ int localAscender = 0;
+ int localDescender = 0;
+ for (int i = 0; i < mtxTab.length; i++) {
+ if ("H".equals(mtxTab[i].getName())) {
+ localCapHeight = mtxTab[i].getBoundingBox()[3];
+ } else if ("x".equals(mtxTab[i].getName())) {
+ localXHeight = mtxTab[i].getBoundingBox()[3];
+ } else if ("d".equals(mtxTab[i].getName())) {
+ localAscender = mtxTab[i].getBoundingBox()[3];
+ } else if ("p".equals(mtxTab[i].getName())) {
+ localDescender = mtxTab[i].getBoundingBox()[1];
+ } else {
+ // OpenType Fonts with a version 3.0 "post" table don't have glyph names.
+ // Use Unicode indices instead.
+ List unicodeIndex = mtxTab[i].getUnicodeIndex();
+ if (unicodeIndex.size() > 0) {
+ //Only the first index is used
+ char ch = (char)((Integer)unicodeIndex.get(0)).intValue();
+ if (ch == 'H') {
+ localCapHeight = mtxTab[i].getBoundingBox()[3];
+ } else if (ch == 'x') {
+ localXHeight = mtxTab[i].getBoundingBox()[3];
+ } else if (ch == 'd') {
+ localAscender = mtxTab[i].getBoundingBox()[3];
+ } else if (ch == 'p') {
+ localDescender = mtxTab[i].getBoundingBox()[1];
+ }
+ }
+ }
+ }
+ if (log.isDebugEnabled()) {
+ log.debug("Ascender from glyph 'd': " + formatUnitsForDebug(localAscender));
+ log.debug("Descender from glyph 'p': " + formatUnitsForDebug(localDescender));
+ }
+ if (ascender - descender > upem) {
+ log.debug("Replacing specified ascender/descender with derived values to get values"
+ + " which fit in the em box.");
+ ascender = localAscender;
+ descender = localDescender;
+ }
+
+ if (log.isDebugEnabled()) {
+ log.debug("xHeight from glyph 'x': " + formatUnitsForDebug(localXHeight));
+ log.debug("CapHeight from glyph 'H': " + formatUnitsForDebug(localCapHeight));
+ }
+ if (capHeight == 0) {
+ capHeight = localCapHeight;
+ if (capHeight == 0) {
+ capHeight = os2CapHeight;
+ }
+ if (capHeight == 0) {
+ log.debug("capHeight value could not be determined."
+ + " The font may not work as expected.");
+ }
+ }
+ if (xHeight == 0) {
+ xHeight = localXHeight;
+ if (xHeight == 0) {
+ xHeight = os2xHeight;
+ }
+ if (xHeight == 0) {
+ log.debug("xHeight value could not be determined."
+ + " The font may not work as expected.");
+ }
+ }
+ }
+
+ /**
+ * Read the kerning table, create a table for both CIDs and
+ * winAnsiEncoding.
+ * @throws IOException In case of a I/O problem
+ */
+ protected void readKerning() throws IOException {
+ // Read kerning
+ kerningTab = new HashMap<Integer, Map<Integer, Integer>>();
+ ansiKerningTab = new HashMap<Integer, Map<Integer, Integer>>();
+ OFDirTabEntry dirTab = dirTabs.get(OFTableName.KERN);
+ if (dirTab != null) {
+ seekTab(fontFile, OFTableName.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;
+ }
+ if ((k >> 8) != 0) {
+ continue;
+ }
+
+ k = fontFile.readTTFUShort();
+ fontFile.skip(3 * 2);
+ while (k-- > 0) {
+ 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);
+ final Integer u2 = glyphToUnicode(j);
+ if (iObj == null) {
+ // happens for many fonts (Ubuntu font set),
+ // stray entries in the kerning table??
+ log.debug("Ignoring kerning pair because no Unicode index was"
+ + " found for the first glyph " + i);
+ } else if (u2 == null) {
+ log.debug("Ignoring kerning pair because Unicode index was"
+ + " found for the second glyph " + i);
+ } else {
+ Map<Integer, Integer> adjTab = kerningTab.get(iObj);
+ if (adjTab == null) {
+ adjTab = new HashMap<Integer, Integer>();
+ }
+ adjTab.put(u2, new Integer(convertTTFUnit2PDFUnit(kpx)));
+ kerningTab.put(iObj, adjTab);
+ }
+ }
+ }
+ }
+
+ // Create winAnsiEncoded kerning table from kerningTab
+ // (could probably be simplified, for now we remap back to CID indexes and
+ // then to winAnsi)
+ for (Integer unicodeKey1 : kerningTab.keySet()) {
+ Integer cidKey1 = unicodeToGlyph(unicodeKey1.intValue());
+ Map<Integer, Integer> akpx = new HashMap<Integer, Integer>();
+ Map<Integer, Integer> ckpx = kerningTab.get(unicodeKey1);
+
+ for (Integer unicodeKey2 : ckpx.keySet()) {
+ Integer cidKey2 = unicodeToGlyph(unicodeKey2.intValue());
+ Integer kern = ckpx.get(unicodeKey2);
+
+ Iterator uniMap = mtxTab[cidKey2.intValue()].getUnicodeIndex().listIterator();
+ while (uniMap.hasNext()) {
+ Integer unicodeKey = (Integer)uniMap.next();
+ Integer[] ansiKeys = unicodeToWinAnsi(unicodeKey.intValue());
+ for (int u = 0; u < ansiKeys.length; u++) {
+ akpx.put(ansiKeys[u], kern);
+ }
+ }
+ }
+
+ if (akpx.size() > 0) {
+ Iterator uniMap = mtxTab[cidKey1.intValue()].getUnicodeIndex().listIterator();
+ while (uniMap.hasNext()) {
+ Integer unicodeKey = (Integer)uniMap.next();
+ Integer[] ansiKeys = unicodeToWinAnsi(unicodeKey.intValue());
+ for (int u = 0; u < ansiKeys.length; u++) {
+ ansiKerningTab.put(ansiKeys[u], akpx);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * 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<OFTableName, OFDirTabEntry>> sortedDirTabs = sortDirTabMap(dirTabs);
+ byte[] file = fontFile.getAllBytes();
+ TTFTableOutputStream tableOut = ttfOut.getTableOutputStream();
+ TTFGlyphOutputStream glyphOut = ttfOut.getGlyphOutputStream();
+ ttfOut.startFontStream();
+ for (Map.Entry<OFTableName, OFDirTabEntry> entry : sortedDirTabs) {
+ int offset = (int) entry.getValue().getOffset();
+ int paddedLength = (int) entry.getValue().getLength();
+ paddedLength += getPadSize(offset + paddedLength);
+ if (entry.getKey().equals(OFTableName.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.
+ */
+ SortedSet<Map.Entry<OFTableName, OFDirTabEntry>>
+ sortDirTabMap(Map<OFTableName, OFDirTabEntry> directoryTabs) {
+ SortedSet<Map.Entry<OFTableName, OFDirTabEntry>> sortedSet
+ = new TreeSet<Map.Entry<OFTableName, OFDirTabEntry>>(
+ new Comparator<Map.Entry<OFTableName, OFDirTabEntry>>() {
+
+ public int compare(Entry<OFTableName, OFDirTabEntry> o1,
+ Entry<OFTableName, OFDirTabEntry> 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;
+ }
+
+ /**
+ * Check if this is a TrueType collection and that the given
+ * name exists in the collection.
+ * If it does, set offset in fontfile to the beginning of
+ * the Table Directory for that font.
+ * @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(String tag, String name) throws IOException {
+ if ("ttcf".equals(tag)) {
+ // This is a TrueType Collection
+ fontFile.skip(4);
+
+ // Read directory offsets
+ int numDirectories = (int)fontFile.readTTFULong();
+ // int numDirectories=in.readTTFUShort();
+ long[] dirOffsets = new long[numDirectories];
+ for (int i = 0; i < numDirectories; i++) {
+ dirOffsets[i] = fontFile.readTTFULong();
+ }
+
+ log.info("This is a TrueType collection file with "
+ + numDirectories + " fonts");
+ log.info("Containing the following fonts: ");
+ // Read all the directories and name tables to check
+ // If the font exists - this is a bit ugly, but...
+ boolean found = false;
+
+ // Iterate through all name tables even if font
+ // Is found, just to show all the names
+ long dirTabOffset = 0;
+ for (int i = 0; (i < numDirectories); i++) {
+ fontFile.seekSet(dirOffsets[i]);
+ readDirTabs();
+
+ readName();
+
+ if (fullName.equals(name)) {
+ found = true;
+ dirTabOffset = dirOffsets[i];
+ log.info(fullName + " <-- selected");
+ } else {
+ log.info(fullName);
+ }
+
+ // Reset names
+ notice = "";
+ fullName = "";
+ familyNames.clear();
+ postScriptName = "";
+ subFamilyName = "";
+ }
+
+ fontFile.seekSet(dirTabOffset);
+ return found;
+ } else {
+ fontFile.seekSet(0);
+ return true;
+ }
+ }
+
+ /**
+ * Return TTC font names
+ * @param in FontFileReader to read from
+ * @return True if not collection or font name present, false otherwise
+ * @throws IOException In case of an I/O problem
+ */
+ public final List<String> getTTCnames(FontFileReader in) throws IOException {
+ this.fontFile = in;
+
+ List<String> fontNames = new ArrayList<String>();
+ String tag = in.readTTFString(4);
+
+ if ("ttcf".equals(tag)) {
+ // This is a TrueType Collection
+ in.skip(4);
+
+ // Read directory offsets
+ int numDirectories = (int)in.readTTFULong();
+ long[] dirOffsets = new long[numDirectories];
+ for (int i = 0; i < numDirectories; i++) {
+ dirOffsets[i] = in.readTTFULong();
+ }
+
+ log.info("This is a TrueType collection file with "
+ + numDirectories + " fonts");
+ log.info("Containing the following fonts: ");
+
+ for (int i = 0; (i < numDirectories); i++) {
+ in.seekSet(dirOffsets[i]);
+ readDirTabs();
+
+ readName();
+
+ log.info(fullName);
+ fontNames.add(fullName);
+
+ // Reset names
+ notice = "";
+ fullName = "";
+ familyNames.clear();
+ postScriptName = "";
+ subFamilyName = "";
+ }
+
+ in.seekSet(0);
+ return fontNames;
+ } else {
+ log.error("Not a TTC!");
+ return null;
+ }
+ }
+
+ /*
+ * Helper classes, they are not very efficient, but that really
+ * doesn't matter...
+ */
+ private Integer[] unicodeToWinAnsi(int unicode) {
+ 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 ret.toArray(new Integer[0]);
+ }
+
+ /**
+ * Dumps a few informational values to System.out.
+ */
+ public void printStuff() {
+ System.out.println("Font name: " + postScriptName);
+ System.out.println("Full name: " + fullName);
+ System.out.println("Family name: " + familyNames);
+ System.out.println("Subfamily name: " + subFamilyName);
+ System.out.println("Notice: " + notice);
+ System.out.println("xHeight: " + convertTTFUnit2PDFUnit(xHeight));
+ System.out.println("capheight: " + convertTTFUnit2PDFUnit(capHeight));
+
+ int italic = (int)(italicAngle >> 16);
+ System.out.println("Italic: " + italic);
+ System.out.print("ItalicAngle: " + (short)(italicAngle / 0x10000));
+ if ((italicAngle % 0x10000) > 0) {
+ System.out.print("."
+ + (short)((italicAngle % 0x10000) * 1000)
+ / 0x10000);
+ }
+ System.out.println();
+ System.out.println("Ascender: " + convertTTFUnit2PDFUnit(ascender));
+ System.out.println("Descender: " + convertTTFUnit2PDFUnit(descender));
+ System.out.println("FontBBox: [" + convertTTFUnit2PDFUnit(fontBBox1)
+ + " " + convertTTFUnit2PDFUnit(fontBBox2) + " "
+ + convertTTFUnit2PDFUnit(fontBBox3) + " "
+ + convertTTFUnit2PDFUnit(fontBBox4) + "]");
+ }
+
+ private String formatUnitsForDebug(int units) {
+ return units + " -> " + convertTTFUnit2PDFUnit(units) + " internal units";
+ }
+
+ /**
+ * Map a glyph index to the corresponding unicode code point
+ *
+ * @param glyphIndex
+ * @return unicode code point
+ */
+ private Integer glyphToUnicode(int glyphIndex) {
+ return glyphToUnicodeMap.get(new Integer(glyphIndex));
+ }
+
+ /**
+ * Map a unicode code point to the corresponding glyph index
+ *
+ * @param unicodeIndex unicode code point
+ * @return glyph index
+ */
+ private Integer unicodeToGlyph(int unicodeIndex) throws IOException {
+ final Integer result
+ = unicodeToGlyphMap.get(new Integer(unicodeIndex));
+ if (result == null) {
+ throw new IOException(
+ "Glyph index not found for unicode value " + unicodeIndex);
+ }
+ 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
+ */
+ public boolean hasAdvancedTable() {
+ if (advancedTableReader != null) {
+ return advancedTableReader.hasAdvancedTable();
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Returns the GDEF table or null if none present.
+ * @return the GDEF table
+ */
+ public GlyphDefinitionTable getGDEF() {
+ if (advancedTableReader != null) {
+ return advancedTableReader.getGDEF();
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Returns the GSUB table or null if none present.
+ * @return the GSUB table
+ */
+ public GlyphSubstitutionTable getGSUB() {
+ if (advancedTableReader != null) {
+ return advancedTableReader.getGSUB();
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Returns the GPOS table or null if none present.
+ * @return the GPOS table
+ */
+ public GlyphPositioningTable getGPOS() {
+ if (advancedTableReader != null) {
+ return advancedTableReader.getGPOS();
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Static main method to get info about a TrueType font.
+ * @param args The command line arguments
+ */
+ public static void main(String[] args) {
+ InputStream stream = null;
+ try {
+ boolean useKerning = true;
+ boolean useAdvanced = true;
+
+ stream = new FileInputStream(args[0]);
+ FontFileReader reader = new FontFileReader(stream);
+
+ String name = null;
+ if (args.length >= 2) {
+ name = args[1];
+ }
+
+ String header = OFFontLoader.readHeader(reader);
+ boolean isCFF = header.equals("OTTO");
+ OpenFont otfFile = (isCFF) ? new OTFFile() : new TTFFile(useKerning, useAdvanced);
+ otfFile.readFont(reader, header, name);
+ otfFile.printStuff();
+
+ } catch (IOException ioe) {
+ System.err.println("Problem reading font: " + ioe.toString());
+ ioe.printStackTrace(System.err);
+ } finally {
+ IOUtils.closeQuietly(stream);
+ }
+ }
+}
diff --git a/src/java/org/apache/fop/fonts/truetype/TTFFile.java b/src/java/org/apache/fop/fonts/truetype/TTFFile.java
index 62686bbd4..52df45ffb 100644
--- a/src/java/org/apache/fop/fonts/truetype/TTFFile.java
+++ b/src/java/org/apache/fop/fonts/truetype/TTFFile.java
@@ -19,224 +19,14 @@
package org.apache.fop.fonts.truetype;
-import java.awt.Rectangle;
-import java.io.FileInputStream;
import java.io.IOException;
-import java.io.InputStream;
-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.io.IOUtils;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
-import org.apache.xmlgraphics.fonts.Glyphs;
-
-import org.apache.fop.complexscripts.fonts.AdvancedTypographicTableFormatException;
-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;
/**
* Reads a TrueType file or a TrueType Collection.
* The TrueType spec can be found at the Microsoft.
* Typography site: http://www.microsoft.com/truetype/
*/
-public class TTFFile {
-
- static final byte NTABS = 24;
- 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;
-
- private boolean useKerning = false;
-
- private boolean isEmbeddable = true;
- private boolean hasSerifs = true;
- /**
- * Table directory
- */
- 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<CMapSegment> cmaps;
- private Set<UnicodeMapping> unicodeMappings;
-
- private int upem; // unitsPerEm from "head" table
- private int nhmtx; // Number of horizontal metrics
- 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)
-
- /**
- * Contains glyph data
- */
- protected TTFMtxEntry[] mtxTab; // Contains glyph data
-
- private String postScriptName = "";
- private String fullName = "";
- private String notice = "";
- private Set<String> familyNames = new HashSet<String>();
- private String subFamilyName = "";
-
- private long italicAngle = 0;
- private long isFixedPitch = 0;
- private int fontBBox1 = 0;
- private int fontBBox2 = 0;
- private int fontBBox3 = 0;
- private int fontBBox4 = 0;
- private int capHeight = 0;
- private int os2CapHeight = 0;
- private int underlinePosition = 0;
- private int underlineThickness = 0;
- private int strikeoutPosition;
- private int strikeoutThickness;
- private int xHeight = 0;
- private int os2xHeight = 0;
- //Effective ascender/descender
- private int ascender = 0;
- private int descender = 0;
- //Ascender/descender from hhea table
- private int hheaAscender = 0;
- private int hheaDescender = 0;
- //Ascender/descender from OS/2 table
- private int os2Ascender = 0;
- private int os2Descender = 0;
- private int usWeightClass = 0;
-
- private short lastChar = 0;
-
- private int[] ansiWidth;
- private Map<Integer, List<Integer>> ansiIndex;
-
- // internal mapping of glyph indexes to unicode indexes
- // used for quick mappings in this class
- private final Map<Integer, Integer> glyphToUnicodeMap = new HashMap<Integer, Integer>();
- private final Map<Integer, Integer> unicodeToGlyphMap = new HashMap<Integer, Integer>();
-
- private TTFDirTabEntry currentDirTab;
-
- private boolean isCFF;
-
- // advanced typographic table support
- private boolean useAdvanced = false;
- private OTFAdvancedTypographicTableReader advancedTableReader;
-
- /**
- * logging instance
- */
- protected Log log = LogFactory.getLog(TTFFile.class);
+public class TTFFile extends OpenFont {
public TTFFile() {
this(true, false);
@@ -248,1214 +38,15 @@ public class TTFFile {
* @param useAdvanced true if advanced typographic tables should be loaded
*/
public TTFFile(boolean useKerning, boolean useAdvanced) {
- this.useKerning = useKerning;
- this.useAdvanced = useAdvanced;
- }
-
- /**
- * Key-value helper class.
- */
- final class UnicodeMapping implements Comparable {
-
- private final int unicodeIndex;
- private final int glyphIndex;
-
- UnicodeMapping(int glyphIndex, int unicodeIndex) {
- this.unicodeIndex = unicodeIndex;
- this.glyphIndex = glyphIndex;
- glyphToUnicodeMap.put(new Integer(glyphIndex), new Integer(unicodeIndex));
- unicodeToGlyphMap.put(new Integer(unicodeIndex), new Integer(glyphIndex));
- }
-
- /**
- * Returns the glyphIndex.
- * @return the glyph index
- */
- public int getGlyphIndex() {
- return glyphIndex;
- }
-
- /**
- * Returns the unicodeIndex.
- * @return the Unicode index
- */
- public int getUnicodeIndex() {
- return unicodeIndex;
- }
-
-
- /** {@inheritDoc} */
- public int hashCode() {
- int hc = unicodeIndex;
- hc = 19 * hc + (hc ^ glyphIndex);
- return hc;
- }
-
- /** {@inheritDoc} */
- public boolean equals(Object o) {
- if (o instanceof UnicodeMapping) {
- UnicodeMapping m = (UnicodeMapping) o;
- if (unicodeIndex != m.unicodeIndex) {
- return false;
- } else {
- return (glyphIndex == m.glyphIndex);
- }
- } else {
- return false;
- }
- }
-
- /** {@inheritDoc} */
- public int compareTo(Object o) {
- if (o instanceof UnicodeMapping) {
- UnicodeMapping m = (UnicodeMapping) o;
- if (unicodeIndex > m.unicodeIndex) {
- return 1;
- } else if (unicodeIndex < m.unicodeIndex) {
- return -1;
- } else {
- return 0;
- }
- } else {
- return -1;
- }
- }
- }
-
- /**
- * 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(TTFTableName name) {
- return dirTabs.get(name);
- }
-
- /**
- * Position inputstream to position indicated
- * in the dirtab offset + offset
- * @param in font file reader
- * @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, TTFTableName tableName,
- long offset) throws IOException {
- TTFDirTabEntry dt = dirTabs.get(tableName);
- if (dt == null) {
- log.error("Dirtab " + tableName.getName() + " not found.");
- return false;
- } else {
- in.seekSet(dt.getOffset() + offset);
- this.currentDirTab = dt;
- }
- return true;
- }
-
- /**
- * Convert from truetype unit to pdf unit based on the
- * unitsPerEm field in the "head" table
- * @param n truetype unit
- * @return pdf unit
- */
- public int convertTTFUnit2PDFUnit(int n) {
- int ret;
- if (n < 0) {
- long rest1 = n % upem;
- long storrest = 1000 * rest1;
- long ledd2 = (storrest != 0 ? rest1 / storrest : 0);
- ret = -((-1000 * n) / upem - (int)ledd2);
- } else {
- ret = (n / upem) * 1000 + ((n % upem) * 1000) / upem;
- }
-
- return ret;
- }
-
- /**
- * Read the cmap table,
- * return false if the table is not present or only unsupported
- * tables are present. Currently only unicode cmaps are supported.
- * Set the unicodeIndex in the TTFMtxEntries and fills in the
- * cmaps vector.
- */
- private boolean readCMAP() throws IOException {
-
- unicodeMappings = new java.util.TreeSet();
-
- seekTab(fontFile, TTFTableName.CMAP, 2);
- int numCMap = fontFile.readTTFUShort(); // Number of cmap subtables
- long cmapUniOffset = 0;
- long symbolMapOffset = 0;
-
- if (log.isDebugEnabled()) {
- log.debug(numCMap + " cmap tables");
- }
-
- //Read offset for all tables. We are only interested in the unicode table
- for (int i = 0; i < numCMap; i++) {
- int cmapPID = fontFile.readTTFUShort();
- int cmapEID = fontFile.readTTFUShort();
- long cmapOffset = fontFile.readTTFLong();
-
- if (log.isDebugEnabled()) {
- log.debug("Platform ID: " + cmapPID + " Encoding: " + cmapEID);
- }
-
- if (cmapPID == 3 && cmapEID == 1) {
- cmapUniOffset = cmapOffset;
- }
- if (cmapPID == 3 && cmapEID == 0) {
- symbolMapOffset = cmapOffset;
- }
- }
-
- if (cmapUniOffset > 0) {
- return readUnicodeCmap(cmapUniOffset, 1);
- } else if (symbolMapOffset > 0) {
- return readUnicodeCmap(symbolMapOffset, 0);
- } else {
- log.fatal("Unsupported TrueType font: No Unicode or Symbol cmap table"
- + " not present. Aborting");
- return false;
- }
- }
-
- private boolean readUnicodeCmap(
- long cmapUniOffset, int encodingID)
- throws IOException {
- //Read CMAP table and correct mtxTab.index
- int mtxPtr = 0;
-
- // Read unicode cmap
- 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) {
- 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);
- log.debug("searchRange : " + cmapSearchRange);
- log.debug("entrySelector: " + cmapEntrySelector);
- log.debug("rangeShift : " + cmapRangeShift);
- }
-
-
- int[] cmapEndCounts = new int[cmapSegCountX2 / 2];
- int[] cmapStartCounts = new int[cmapSegCountX2 / 2];
- int[] cmapDeltas = new int[cmapSegCountX2 / 2];
- int[] cmapRangeOffsets = new int[cmapSegCountX2 / 2];
-
- for (int i = 0; i < (cmapSegCountX2 / 2); i++) {
- cmapEndCounts[i] = fontFile.readTTFUShort();
- }
-
- fontFile.skip(2); // Skip reservedPad
-
- for (int i = 0; i < (cmapSegCountX2 / 2); i++) {
- cmapStartCounts[i] = fontFile.readTTFUShort();
- }
-
- for (int i = 0; i < (cmapSegCountX2 / 2); i++) {
- cmapDeltas[i] = fontFile.readTTFShort();
- }
-
- //int startRangeOffset = in.getCurrentPos();
-
- for (int i = 0; i < (cmapSegCountX2 / 2); i++) {
- cmapRangeOffsets[i] = fontFile.readTTFUShort();
- }
-
- int glyphIdArrayOffset = fontFile.getCurrentPos();
-
- BitSet eightBitGlyphs = new BitSet(256);
-
- // Insert the unicode id for the glyphs in mtxTab
- // and fill in the cmaps ArrayList
-
- for (int i = 0; i < cmapStartCounts.length; i++) {
-
- if (log.isTraceEnabled()) {
- log.trace(i + ": " + cmapStartCounts[i]
- + " - " + cmapEndCounts[i]);
- }
- if (log.isDebugEnabled()) {
- if (isInPrivateUseArea(cmapStartCounts[i], cmapEndCounts[i])) {
- log.debug("Font contains glyphs in the Unicode private use area: "
- + Integer.toHexString(cmapStartCounts[i]) + " - "
- + Integer.toHexString(cmapEndCounts[i]));
- }
- }
-
- for (int j = cmapStartCounts[i]; j <= cmapEndCounts[i]; j++) {
-
- // Update lastChar
- if (j < 256 && j > lastChar) {
- lastChar = (short)j;
- }
-
- if (j < 256) {
- eightBitGlyphs.set(j);
- }
-
- if (mtxPtr < mtxTab.length) {
- int glyphIdx;
- // the last character 65535 = .notdef
- // may have a range offset
- if (cmapRangeOffsets[i] != 0 && j != 65535) {
- int glyphOffset = glyphIdArrayOffset
- + ((cmapRangeOffsets[i] / 2)
- + (j - cmapStartCounts[i])
- + (i)
- - cmapSegCountX2 / 2) * 2;
- 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<Integer> v = ansiIndex.get(new Integer(j));
- if (v != null) {
- for (Integer aIdx : v) {
- ansiWidth[aIdx.intValue()]
- = mtxTab[glyphIdx].getWx();
-
- if (log.isTraceEnabled()) {
- log.trace("Added width "
- + mtxTab[glyphIdx].getWx()
- + " uni: " + j
- + " ansi: " + aIdx.intValue());
- }
- }
- }
-
- if (log.isTraceEnabled()) {
- log.trace("Idx: "
- + glyphIdx
- + " Delta: " + cmapDeltas[i]
- + " Unicode: " + j
- + " name: " + mtxTab[glyphIdx].getName());
- }
- } else {
- glyphIdx = (j + cmapDeltas[i]) & 0xffff;
-
- if (glyphIdx < mtxTab.length) {
- mtxTab[glyphIdx].getUnicodeIndex().add(new Integer(j));
- } else {
- log.debug("Glyph " + glyphIdx
- + " out of range: "
- + mtxTab.length);
- }
-
- unicodeMappings.add(new UnicodeMapping(glyphIdx, j));
- if (glyphIdx < mtxTab.length) {
- mtxTab[glyphIdx].getUnicodeIndex().add(new Integer(j));
- } else {
- log.debug("Glyph " + glyphIdx
- + " out of range: "
- + mtxTab.length);
- }
-
- // Also add winAnsiWidth
- List<Integer> v = ansiIndex.get(new Integer(j));
- if (v != null) {
- for (Integer aIdx : v) {
- ansiWidth[aIdx.intValue()] = mtxTab[glyphIdx].getWx();
- }
- }
-
- //getLogger().debug("IIdx: " +
- // mtxPtr +
- // " Delta: " + cmap_deltas[i] +
- // " Unicode: " + j +
- // " name: " +
- // mtxTab[(j+cmap_deltas[i]) & 0xffff].name);
-
- }
- if (glyphIdx < mtxTab.length) {
- if (mtxTab[glyphIdx].getUnicodeIndex().size() < 2) {
- mtxPtr++;
- }
- }
- }
- }
- }
- } else {
- log.error("Cmap format not supported: " + cmapFormat);
- return false;
- }
- return true;
- }
-
- private boolean isInPrivateUseArea(int start, int end) {
- return (isInPrivateUseArea(start) || isInPrivateUseArea(end));
- }
-
- private boolean isInPrivateUseArea(int unicode) {
- return (unicode >= 0xE000 && unicode <= 0xF8FF);
- }
-
- /**
- * Print first char/last char
- */
- private void printMaxMin() {
- int min = 255;
- int max = 0;
- for (int i = 0; i < mtxTab.length; i++) {
- if (mtxTab[i].getIndex() < min) {
- min = mtxTab[i].getIndex();
- }
- if (mtxTab[i].getIndex() > max) {
- max = mtxTab[i].getIndex();
- }
- }
- log.info("Min: " + min);
- log.info("Max: " + max);
- }
-
-
- /**
- * Reads the font using a FontFileReader.
- *
- * @param in The FontFileReader to use
- * @throws IOException In case of an I/O problem
- */
- public void readFont(FontFileReader in) throws IOException {
- readFont(in, (String)null);
- }
-
- /**
- * initialize the ansiWidths array (for winAnsiEncoding)
- * and fill with the missingwidth
- */
- private void initAnsiWidths() {
- ansiWidth = new int[256];
- for (int i = 0; i < 256; i++) {
- ansiWidth[i] = mtxTab[0].getWx();
- }
-
- // 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 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<Integer> v = ansiIndex.get(uni);
- if (v == null) {
- v = new ArrayList<Integer>();
- ansiIndex.put(uni, v);
- }
- v.add(ansi);
- }
- }
-
- /**
- * Read the font data.
- * If the fontfile is a TrueType Collection (.ttc file)
- * the name of the font to read data for must be supplied,
- * else the name is ignored.
- *
- * @param in The FontFileReader to use
- * @param name The name of the font
- * @return boolean Returns true if the font is valid
- * @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(name)) {
- if (name == null) {
- throw new IllegalArgumentException(
- "For TrueType collection you must specify which font "
- + "to select (-ttcname)");
- } else {
- throw new IOException(
- "Name does not exist in the TrueType collection: " + name);
- }
- }
-
- readDirTabs();
- readFontHeader();
- getNumGlyphs();
- if (log.isDebugEnabled()) {
- log.debug("Number of glyphs in font: " + numberOfGlyphs);
- }
- readHorizontalHeader();
- readHorizontalMetrics();
- initAnsiWidths();
- readPostScript();
- readOS2();
- determineAscDesc();
- if (!isCFF) {
- readIndexToLocation();
- readGlyf();
- }
- readName();
- boolean pcltFound = readPCLT();
- // Read cmap table and fill in ansiwidths
- boolean valid = readCMAP();
- if (!valid) {
- return false;
- }
- // Create cmaps for bfentries
- createCMaps();
-
- if (useKerning) {
- readKerning();
- }
-
- // Read advanced typographic tables.
- if (useAdvanced) {
- try {
- OTFAdvancedTypographicTableReader atr
- = new OTFAdvancedTypographicTableReader(this, in);
- atr.readAll();
- this.advancedTableReader = atr;
- } catch (AdvancedTypographicTableFormatException e) {
- log.warn(
- "Encountered format constraint violation in advanced (typographic) table (AT) "
- + "in font '" + getFullName() + "', ignoring AT data: "
- + e.getMessage()
- );
- }
- }
-
- guessVerticalMetricsFromGlyphBBox();
- 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 ArrayList<CMapSegment>();
- int unicodeStart;
- int glyphStart;
- int unicodeEnd;
-
- Iterator<UnicodeMapping> e = unicodeMappings.iterator();
- UnicodeMapping um = e.next();
- UnicodeMapping lastMapping = um;
-
- unicodeStart = um.getUnicodeIndex();
- glyphStart = um.getGlyphIndex();
-
- while (e.hasNext()) {
- um = e.next();
- if (((lastMapping.getUnicodeIndex() + 1) != um.getUnicodeIndex())
- || ((lastMapping.getGlyphIndex() + 1) != um.getGlyphIndex())) {
- unicodeEnd = lastMapping.getUnicodeIndex();
- cmaps.add(new CMapSegment(unicodeStart, unicodeEnd, glyphStart));
- unicodeStart = um.getUnicodeIndex();
- glyphStart = um.getGlyphIndex();
- }
- lastMapping = um;
- }
-
- unicodeEnd = lastMapping.getUnicodeIndex();
- cmaps.add(new CMapSegment(unicodeStart, unicodeEnd, glyphStart));
- }
-
- /**
- * Returns the PostScript name of the font.
- * @return String The PostScript name
- */
- public String getPostScriptName() {
- if (postScriptName.length() == 0) {
- return FontUtil.stripWhiteSpace(getFullName());
- } else {
- return postScriptName;
- }
- }
-
- PostScriptVersion getPostScriptVersion() {
- return postScriptVersion;
- }
-
- /**
- * Returns the font family names of the font.
- * @return Set The family names (a Set of Strings)
- */
- public Set<String> getFamilyNames() {
- return familyNames;
- }
-
- /**
- * Returns the font sub family name of the font.
- * @return String The sub family name
- */
- public String getSubFamilyName() {
- return subFamilyName;
- }
-
- /**
- * Returns the full name of the font.
- * @return String The full name
- */
- public String getFullName() {
- return fullName;
- }
-
- /**
- * Returns the name of the character set used.
- * @return String The caracter set
- */
- public String getCharSetName() {
- return encoding;
- }
-
- /**
- * Returns the CapHeight attribute of the font.
- * @return int The CapHeight
- */
- public int getCapHeight() {
- return convertTTFUnit2PDFUnit(capHeight);
- }
-
- /**
- * Returns the XHeight attribute of the font.
- * @return int The XHeight
- */
- public int getXHeight() {
- return convertTTFUnit2PDFUnit(xHeight);
- }
-
- /**
- * 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 |= 64;
- }
- if (isFixedPitch != 0) {
- flags |= 2;
- }
- if (hasSerifs) {
- flags |= 1;
- }
- return flags;
- }
-
- /**
- * Returns the weight class of this font. Valid values are 100, 200....,800, 900.
- * @return the weight class value (or 0 if there was no OS/2 table in the font)
- */
- public int getWeightClass() {
- return this.usWeightClass;
- }
-
- /**
- * Returns the StemV attribute of the font.
- * @return String The StemV
- */
- public String getStemV() {
- return "0";
- }
-
- /**
- * Returns the ItalicAngle attribute of the font.
- * @return String The ItalicAngle
- */
- public String getItalicAngle() {
- String ia = Short.toString((short)(italicAngle / 0x10000));
-
- // This is the correct italic angle, however only int italic
- // angles are supported at the moment so this is commented out.
- /*
- * if ((italicAngle % 0x10000) > 0 )
- * ia=ia+(comma+Short.toString((short)((short)((italicAngle % 0x10000)*1000)/0x10000)));
- */
- return ia;
- }
-
- /**
- * @return int[] The font bbox
- */
- public int[] getFontBBox() {
- final int[] fbb = new int[4];
- fbb[0] = convertTTFUnit2PDFUnit(fontBBox1);
- fbb[1] = convertTTFUnit2PDFUnit(fontBBox2);
- fbb[2] = convertTTFUnit2PDFUnit(fontBBox3);
- fbb[3] = convertTTFUnit2PDFUnit(fontBBox4);
-
- return fbb;
- }
-
- /**
- * Returns the LowerCaseAscent attribute of the font.
- * @return int The LowerCaseAscent
- */
- public int getLowerCaseAscent() {
- return convertTTFUnit2PDFUnit(ascender);
- }
-
- /**
- * Returns the LowerCaseDescent attribute of the font.
- * @return int The LowerCaseDescent
- */
- public int getLowerCaseDescent() {
- return convertTTFUnit2PDFUnit(descender);
- }
-
- /**
- * Returns the index of the last character, but this is for WinAnsiEncoding
- * only, so the last char is < 256.
- * @return short Index of the last character (<256)
- */
- public short getLastChar() {
- return lastChar;
- }
-
- /**
- * Returns the index of the first character.
- * @return short Index of the first character
- */
- public short getFirstChar() {
- return firstChar;
- }
-
- /**
- * Returns an array of character widths.
- * @return int[] The character widths
- */
- public int[] getWidths() {
- int[] wx = new int[mtxTab.length];
- for (int i = 0; i < wx.length; i++) {
- wx[i] = convertTTFUnit2PDFUnit(mtxTab[i].getWx());
- }
- return wx;
- }
-
- public Rectangle[] getBoundingBoxes() {
- Rectangle[] boundingBoxes = new Rectangle[mtxTab.length];
- for (int i = 0; i < boundingBoxes.length; i++) {
- int[] boundingBox = mtxTab[i].getBoundingBox();
- boundingBoxes[i] = new Rectangle(
- convertTTFUnit2PDFUnit(boundingBox[0]),
- convertTTFUnit2PDFUnit(boundingBox[1]),
- convertTTFUnit2PDFUnit(boundingBox[2] - boundingBox[0]),
- convertTTFUnit2PDFUnit(boundingBox[3] - boundingBox[1]));
- }
- return boundingBoxes;
- }
-
- /**
- * Returns an array (xMin, yMin, xMax, yMax) for a glyph.
- *
- * @param glyphIndex the index of the glyph
- * @return int[] Array defining bounding box.
- */
- public int[] getBBox(int glyphIndex) {
- int[] bboxInTTFUnits = mtxTab[glyphIndex].getBoundingBox();
- int[] bbox = new int[4];
- for (int i = 0; i < 4; i++) {
- bbox[i] = convertTTFUnit2PDFUnit(bboxInTTFUnits[i]);
- }
- return bbox;
- }
-
- /**
- * Returns the width of a given character.
- * @param idx Index of the character
- * @return int Standard width
- */
- public int getCharWidth(int idx) {
- return convertTTFUnit2PDFUnit(ansiWidth[idx]);
- }
-
- /**
- * Returns the kerning table.
- * @return Map The kerning table
- */
- public Map<Integer, Map<Integer, Integer>> getKerning() {
- return kerningTab;
- }
-
- /**
- * Returns the ANSI kerning table.
- * @return Map The ANSI kerning table
- */
- public Map<Integer, Map<Integer, Integer>> getAnsiKerning() {
- return ansiKerningTab;
- }
-
- public int getUnderlinePosition() {
- return convertTTFUnit2PDFUnit(underlinePosition);
- }
-
- public int getUnderlineThickness() {
- return convertTTFUnit2PDFUnit(underlineThickness);
- }
-
- public int getStrikeoutPosition() {
- return convertTTFUnit2PDFUnit(strikeoutPosition);
- }
-
- public int getStrikeoutThickness() {
- return convertTTFUnit2PDFUnit(strikeoutThickness);
- }
-
- /**
- * Indicates if the font may be embedded.
- * @return boolean True if it may be embedded
- */
- public boolean isEmbeddable() {
- return isEmbeddable;
- }
-
- /**
- * Indicates whether or not the font is an OpenType
- * CFF font (rather than a TrueType font).
- * @return true if the font is in OpenType CFF format.
- */
- public boolean isCFF() {
- return this.isCFF;
- }
-
- /**
- * Read Table Directory from the current position in the
- * FontFileReader and fill the global HashMap dirTabs
- * with the table name (String) as key and a TTFDirTabEntry
- * as value.
- * @throws IOException in case of an I/O problem
- */
- 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");
- break;
- case 0x4F54544F: //"OTTO"
- this.isCFF = true;
- log.debug("sfnt version: OpenType with CFF data");
- break;
- case 0x74727565: //"true"
- log.debug("sfnt version: Apple TrueType");
- break;
- case 0x74797031: //"typ1"
- log.debug("sfnt version: Apple Type 1 housed in sfnt wrapper");
- break;
- default:
- log.debug("Unknown sfnt version: " + Integer.toHexString(sfntVersion));
- break;
- }
- int ntabs = fontFile.readTTFUShort();
- fontFile.skip(6); // 3xTTF_USHORT_SIZE
-
- 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();
- 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
- * @throws IOException in case of an I/O problem
- */
- 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 = fontFile.readTTFUShort();
- if (log.isDebugEnabled()) {
- log.debug("unit per em: " + upem);
- }
-
- fontFile.skip(16);
-
- fontBBox1 = fontFile.readTTFShort();
- fontBBox2 = fontFile.readTTFShort();
- fontBBox3 = fontFile.readTTFShort();
- fontBBox4 = fontFile.readTTFShort();
- if (log.isDebugEnabled()) {
- log.debug("font bbox: xMin=" + fontBBox1
- + " yMin=" + fontBBox2
- + " xMax=" + fontBBox3
- + " yMax=" + fontBBox4);
- }
-
- fontFile.skip(2 + 2 + 2);
-
- locaFormat = fontFile.readTTFShort();
- }
-
- /**
- * Read the number of glyphs from the "maxp" table
- * @throws IOException in case of an I/O problem
- */
- protected void getNumGlyphs() throws IOException {
- seekTab(fontFile, TTFTableName.MAXP, 4);
- numberOfGlyphs = fontFile.readTTFUShort();
- }
-
-
- /**
- * 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.
- * @throws IOException in case of an I/O problem
- */
- protected void readHorizontalHeader()
- throws IOException {
- seekTab(fontFile, TTFTableName.HHEA, 4);
- hheaAscender = fontFile.readTTFShort();
- hheaDescender = fontFile.readTTFShort();
-
- fontFile.skip(2 + 2 + 3 * 2 + 8 * 2);
- nhmtx = fontFile.readTTFUShort();
-
- if (log.isDebugEnabled()) {
- log.debug("hhea.Ascender: " + formatUnitsForDebug(hheaAscender));
- log.debug("hhea.Descender: " + formatUnitsForDebug(hheaDescender));
- log.debug("Number of horizontal metrics: " + nhmtx);
- }
- }
-
- /**
- * Read "hmtx" table and put the horizontal metrics
- * 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
- * @throws IOException in case of an I/O problem
- */
- protected void readHorizontalMetrics()
- throws IOException {
- seekTab(fontFile, TTFTableName.HMTX, 0);
-
- int mtxSize = Math.max(numberOfGlyphs, nhmtx);
- mtxTab = new TTFMtxEntry[mtxSize];
-
- if (log.isTraceEnabled()) {
- log.trace("*** Widths array: \n");
- }
- for (int i = 0; i < mtxSize; i++) {
- mtxTab[i] = new TTFMtxEntry();
- }
- for (int i = 0; i < nhmtx; i++) {
- mtxTab[i].setWx(fontFile.readTTFUShort());
- mtxTab[i].setLsb(fontFile.readTTFUShort());
-
- if (log.isTraceEnabled()) {
- log.trace(" width[" + i + "] = "
- + convertTTFUnit2PDFUnit(mtxTab[i].getWx()) + ";");
- }
- }
-
- if (nhmtx < mtxSize) {
- // Fill in the missing widths
- int lastWidth = mtxTab[nhmtx - 1].getWx();
- for (int i = nhmtx; i < mtxSize; i++) {
- mtxTab[i].setWx(lastWidth);
- mtxTab[i].setLsb(fontFile.readTTFUShort());
- }
- }
- }
-
-
- /**
- * Read the "post" table
- * containing the PostScript names of the glyphs.
- */
- 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
- fontFile.skip(4 * 4);
-
- log.debug("PostScript format: 0x" + Integer.toHexString(postFormat));
- switch (postFormat) {
- case 0x00010000:
- log.debug("PostScript format 1");
- 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 = fontFile.readTTFUShort();
-
- // Read indexes
- for (int i = 0; i < l; i++) {
- mtxTab[i].setIndex(fontFile.readTTFUShort());
-
- if (mtxTab[i].getIndex() > 257) {
- //Index is not in the Macintosh standard set
- numGlyphStrings++;
- }
-
- if (log.isTraceEnabled()) {
- log.trace("PostScript index: " + mtxTab[i].getIndexAsString());
- }
- }
-
- // firstChar=minIndex;
- String[] psGlyphsBuffer = new String[numGlyphStrings];
- if (log.isDebugEnabled()) {
- log.debug("Reading " + numGlyphStrings
- + " glyphnames, that are not in the standard Macintosh"
- + " set. Total number of glyphs=" + l);
- }
- for (int i = 0; i < psGlyphsBuffer.length; i++) {
- psGlyphsBuffer[i] = fontFile.readTTFString(fontFile.readTTFUByte());
- }
-
- //Set glyph names
- for (int i = 0; i < l; i++) {
- 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() - MAC_GLYPH_ORDERING.length;
-
- if (log.isTraceEnabled()) {
- log.trace(k + " i=" + i + " mtx=" + mtxTab.length
- + " ps=" + psGlyphsBuffer.length);
- }
-
- mtxTab[i].setName(psGlyphsBuffer[k]);
- }
- }
- }
-
- break;
- 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;
- }
- }
-
-
- /**
- * Read the "OS/2" table
- */
- private void readOS2() throws IOException {
- // Check if font is embeddable
- TTFDirTabEntry os2Entry = dirTabs.get(TTFTableName.OS2);
- if (os2Entry != null) {
- 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());
- }
- fontFile.skip(2); //xAvgCharWidth
- this.usWeightClass = fontFile.readTTFUShort();
-
- // usWidthClass
- fontFile.skip(2);
-
- int fsType = fontFile.readTTFUShort();
- if (fsType == 2) {
- isEmbeddable = false;
- } else {
- isEmbeddable = true;
- }
- fontFile.skip(8 * 2);
- strikeoutThickness = fontFile.readTTFShort();
- strikeoutPosition = fontFile.readTTFShort();
- fontFile.skip(2);
- fontFile.skip(10); //panose array
- fontFile.skip(4 * 4); //unicode ranges
- fontFile.skip(4);
- fontFile.skip(3 * 2);
- int v;
- 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 = fontFile.readTTFShort(); //sTypoLineGap
- if (log.isDebugEnabled()) {
- log.debug("sTypoLineGap: " + v);
- }
- v = fontFile.readTTFUShort(); //usWinAscent
- if (log.isDebugEnabled()) {
- log.debug("usWinAscent: " + formatUnitsForDebug(v));
- }
- 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)) {
- 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);
- }
- }
-
- } else {
- isEmbeddable = true;
- }
- }
-
- /**
- * Read the "loca" table.
- * @throws IOException In case of a I/O problem
- */
- protected final void readIndexToLocation()
- throws IOException {
- 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 ? fontFile.readTTFULong()
- : (fontFile.readTTFUShort() << 1));
- }
- lastLoca = (locaFormat == 1 ? fontFile.readTTFULong()
- : (fontFile.readTTFUShort() << 1));
- }
-
- /**
- * Read the "glyf" table to find the bounding boxes.
- * @throws IOException In case of a I/O problem
- */
- 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()) {
- fontFile.seekSet(dirTab.getOffset() + mtxTab[i].getOffset());
- fontFile.skip(2);
- final int[] bbox = {
- fontFile.readTTFShort(),
- fontFile.readTTFShort(),
- fontFile.readTTFShort(),
- fontFile.readTTFShort()};
- mtxTab[i].setBoundingBox(bbox);
- } else {
- mtxTab[i].setBoundingBox(mtxTab[0].getBoundingBox());
- }
- }
-
-
- 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()) {
- fontFile.seekSet(n + mtxTab[i].getOffset());
- fontFile.skip(2);
- final int[] bbox = {
- 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)*/
- final int bbox0 = mtxTab[0].getBoundingBox()[0];
- final int[] bbox = {bbox0, bbox0, bbox0, bbox0};
- mtxTab[i].setBoundingBox(bbox);
- /* Original code
- mtxTab[i].bbox[0] = mtxTab[0].bbox[0];
- mtxTab[i].bbox[1] = mtxTab[0].bbox[0];
- mtxTab[i].bbox[2] = mtxTab[0].bbox[0];
- mtxTab[i].bbox[3] = mtxTab[0].bbox[0]; */
- }
- if (log.isTraceEnabled()) {
- log.trace(mtxTab[i].toString(this));
- }
- }
+ super(useKerning, useAdvanced);
}
/**
* Read the "name" table.
* @throws IOException In case of a I/O problem
*/
- private void readName() throws IOException {
- seekTab(fontFile, TTFTableName.NAME, 2);
+ protected void readName() throws IOException {
+ seekTab(fontFile, OFTableName.NAME, 2);
int i = fontFile.getCurrentPos();
int n = fontFile.readTTFUShort();
int j = fontFile.readTTFUShort() + i - 2;
@@ -1521,572 +112,84 @@ public class TTFFile {
}
/**
- * Read the "PCLT" table to find xHeight and capHeight.
+ * Read the "glyf" table to find the bounding boxes.
* @throws IOException In case of a I/O problem
*/
- private boolean readPCLT() throws IOException {
- TTFDirTabEntry dirTab = dirTabs.get(TTFTableName.PCLT);
- if (dirTab != null) {
- fontFile.seekSet(dirTab.getOffset() + 4 + 4 + 2);
- xHeight = fontFile.readTTFUShort();
- log.debug("xHeight from PCLT: " + formatUnitsForDebug(xHeight));
- fontFile.skip(2 * 2);
- capHeight = fontFile.readTTFUShort();
- log.debug("capHeight from PCLT: " + formatUnitsForDebug(capHeight));
- fontFile.skip(2 + 16 + 8 + 6 + 1 + 1);
-
- int serifStyle = fontFile.readTTFUByte();
- serifStyle = serifStyle >> 6;
- serifStyle = serifStyle & 3;
- if (serifStyle == 1) {
- hasSerifs = false;
- } else {
- hasSerifs = true;
- }
- return true;
- } else {
- return false;
- }
- }
-
- /**
- * Determines the right source for the ascender and descender values. The problem here is
- * that the interpretation of these values is not the same for every font. There doesn't seem
- * to be a uniform definition of an ascender and a descender. In some fonts
- * the hhea values are defined after the Apple interpretation, but not in every font. The
- * same problem is in the OS/2 table. FOP needs the ascender and descender to determine the
- * baseline so we need values which add up more or less to the "em box". However, due to
- * accent modifiers a character can grow beyond the em box.
- */
- private void determineAscDesc() {
- int hheaBoxHeight = hheaAscender - hheaDescender;
- int os2BoxHeight = os2Ascender - os2Descender;
- if (os2Ascender > 0 && os2BoxHeight <= upem) {
- ascender = os2Ascender;
- descender = os2Descender;
- } else if (hheaAscender > 0 && hheaBoxHeight <= upem) {
- ascender = hheaAscender;
- descender = hheaDescender;
- } else {
- if (os2Ascender > 0) {
- //Fall back to info from OS/2 if possible
- ascender = os2Ascender;
- descender = os2Descender;
- } else {
- ascender = hheaAscender;
- descender = hheaDescender;
- }
- }
-
- if (log.isDebugEnabled()) {
- log.debug("Font box height: " + (ascender - descender));
- if (ascender - descender > upem) {
- log.debug("Ascender and descender together are larger than the em box.");
- }
+ private void readGlyf() throws IOException {
+ OFDirTabEntry dirTab = dirTabs.get(OFTableName.GLYF);
+ if (dirTab == null) {
+ throw new IOException("glyf table not found, cannot continue");
}
- }
-
- private void guessVerticalMetricsFromGlyphBBox() {
- // Approximate capHeight from height of "H"
- // It's most unlikely that a font misses the PCLT table
- // This also assumes that postscriptnames exists ("H")
- // Should look it up in the cmap (that wouldn't help
- // for charsets without H anyway...)
- // Same for xHeight with the letter "x"
- int localCapHeight = 0;
- int localXHeight = 0;
- int localAscender = 0;
- int localDescender = 0;
- for (int i = 0; i < mtxTab.length; i++) {
- if ("H".equals(mtxTab[i].getName())) {
- localCapHeight = mtxTab[i].getBoundingBox()[3];
- } else if ("x".equals(mtxTab[i].getName())) {
- localXHeight = mtxTab[i].getBoundingBox()[3];
- } else if ("d".equals(mtxTab[i].getName())) {
- localAscender = mtxTab[i].getBoundingBox()[3];
- } else if ("p".equals(mtxTab[i].getName())) {
- localDescender = mtxTab[i].getBoundingBox()[1];
+ for (int i = 0; i < (numberOfGlyphs - 1); i++) {
+ if (mtxTab[i].getOffset() != mtxTab[i + 1].getOffset()) {
+ fontFile.seekSet(dirTab.getOffset() + mtxTab[i].getOffset());
+ fontFile.skip(2);
+ final int[] bbox = {
+ fontFile.readTTFShort(),
+ fontFile.readTTFShort(),
+ fontFile.readTTFShort(),
+ fontFile.readTTFShort()};
+ mtxTab[i].setBoundingBox(bbox);
} else {
- // OpenType Fonts with a version 3.0 "post" table don't have glyph names.
- // Use Unicode indices instead.
- List unicodeIndex = mtxTab[i].getUnicodeIndex();
- if (unicodeIndex.size() > 0) {
- //Only the first index is used
- char ch = (char)((Integer)unicodeIndex.get(0)).intValue();
- if (ch == 'H') {
- localCapHeight = mtxTab[i].getBoundingBox()[3];
- } else if (ch == 'x') {
- localXHeight = mtxTab[i].getBoundingBox()[3];
- } else if (ch == 'd') {
- localAscender = mtxTab[i].getBoundingBox()[3];
- } else if (ch == 'p') {
- localDescender = mtxTab[i].getBoundingBox()[1];
- }
- }
- }
- }
- if (log.isDebugEnabled()) {
- log.debug("Ascender from glyph 'd': " + formatUnitsForDebug(localAscender));
- log.debug("Descender from glyph 'p': " + formatUnitsForDebug(localDescender));
- }
- if (ascender - descender > upem) {
- log.debug("Replacing specified ascender/descender with derived values to get values"
- + " which fit in the em box.");
- ascender = localAscender;
- descender = localDescender;
- }
-
- if (log.isDebugEnabled()) {
- log.debug("xHeight from glyph 'x': " + formatUnitsForDebug(localXHeight));
- log.debug("CapHeight from glyph 'H': " + formatUnitsForDebug(localCapHeight));
- }
- if (capHeight == 0) {
- capHeight = localCapHeight;
- if (capHeight == 0) {
- capHeight = os2CapHeight;
- }
- if (capHeight == 0) {
- log.debug("capHeight value could not be determined."
- + " The font may not work as expected.");
- }
- }
- if (xHeight == 0) {
- xHeight = localXHeight;
- if (xHeight == 0) {
- xHeight = os2xHeight;
- }
- if (xHeight == 0) {
- log.debug("xHeight value could not be determined."
- + " The font may not work as expected.");
- }
- }
- }
-
- /**
- * Read the kerning table, create a table for both CIDs and
- * winAnsiEncoding.
- * @throws IOException In case of a I/O problem
- */
- private void readKerning() throws IOException {
- // Read kerning
- kerningTab = new HashMap<Integer, Map<Integer, Integer>>();
- ansiKerningTab = new HashMap<Integer, Map<Integer, Integer>>();
- TTFDirTabEntry dirTab = dirTabs.get(TTFTableName.KERN);
- if (dirTab != null) {
- 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;
- }
- if ((k >> 8) != 0) {
- continue;
- }
-
- k = fontFile.readTTFUShort();
- fontFile.skip(3 * 2);
- while (k-- > 0) {
- 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);
- final Integer u2 = glyphToUnicode(j);
- if (iObj == null) {
- // happens for many fonts (Ubuntu font set),
- // stray entries in the kerning table??
- log.debug("Ignoring kerning pair because no Unicode index was"
- + " found for the first glyph " + i);
- } else if (u2 == null) {
- log.debug("Ignoring kerning pair because Unicode index was"
- + " found for the second glyph " + i);
- } else {
- Map<Integer, Integer> adjTab = kerningTab.get(iObj);
- if (adjTab == null) {
- adjTab = new HashMap<Integer, Integer>();
- }
- adjTab.put(u2, new Integer(convertTTFUnit2PDFUnit(kpx)));
- kerningTab.put(iObj, adjTab);
- }
- }
- }
- }
-
- // Create winAnsiEncoded kerning table from kerningTab
- // (could probably be simplified, for now we remap back to CID indexes and
- // then to winAnsi)
- for (Integer unicodeKey1 : kerningTab.keySet()) {
- Integer cidKey1 = unicodeToGlyph(unicodeKey1.intValue());
- Map<Integer, Integer> akpx = new HashMap<Integer, Integer>();
- Map<Integer, Integer> ckpx = kerningTab.get(unicodeKey1);
-
- for (Integer unicodeKey2 : ckpx.keySet()) {
- Integer cidKey2 = unicodeToGlyph(unicodeKey2.intValue());
- Integer kern = ckpx.get(unicodeKey2);
-
- Iterator uniMap = mtxTab[cidKey2.intValue()].getUnicodeIndex().listIterator();
- while (uniMap.hasNext()) {
- Integer unicodeKey = (Integer)uniMap.next();
- Integer[] ansiKeys = unicodeToWinAnsi(unicodeKey.intValue());
- for (int u = 0; u < ansiKeys.length; u++) {
- akpx.put(ansiKeys[u], kern);
- }
- }
- }
-
- if (akpx.size() > 0) {
- Iterator uniMap = mtxTab[cidKey1.intValue()].getUnicodeIndex().listIterator();
- while (uniMap.hasNext()) {
- Integer unicodeKey = (Integer)uniMap.next();
- Integer[] ansiKeys = unicodeToWinAnsi(unicodeKey.intValue());
- for (int u = 0; u < ansiKeys.length; u++) {
- ansiKerningTab.put(ansiKeys[u], akpx);
- }
- }
- }
+ mtxTab[i].setBoundingBox(mtxTab[0].getBoundingBox());
}
}
- }
- /**
- * 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);
+ long n = (dirTabs.get(OFTableName.GLYF)).getOffset();
+ for (int i = 0; i < numberOfGlyphs; i++) {
+ if ((i + 1) >= mtxTab.length
+ || mtxTab[i].getOffset() != mtxTab[i + 1].getOffset()) {
+ fontFile.seekSet(n + mtxTab[i].getOffset());
+ fontFile.skip(2);
+ final int[] bbox = {
+ fontFile.readTTFShort(),
+ fontFile.readTTFShort(),
+ fontFile.readTTFShort(),
+ fontFile.readTTFShort()};
+ mtxTab[i].setBoundingBox(bbox);
} 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.
- */
- 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;
- }
-
- /**
- * Check if this is a TrueType collection and that the given
- * name exists in the collection.
- * If it does, set offset in fontfile to the beginning of
- * the Table Directory for that font.
- * @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(String name) throws IOException {
- String tag = fontFile.readTTFString(4);
-
- if ("ttcf".equals(tag)) {
- // This is a TrueType Collection
- fontFile.skip(4);
-
- // Read directory offsets
- int numDirectories = (int)fontFile.readTTFULong();
- // int numDirectories=in.readTTFUShort();
- long[] dirOffsets = new long[numDirectories];
- for (int i = 0; i < numDirectories; i++) {
- dirOffsets[i] = fontFile.readTTFULong();
- }
-
- log.info("This is a TrueType collection file with "
- + numDirectories + " fonts");
- log.info("Containing the following fonts: ");
- // Read all the directories and name tables to check
- // If the font exists - this is a bit ugly, but...
- boolean found = false;
-
- // Iterate through all name tables even if font
- // Is found, just to show all the names
- long dirTabOffset = 0;
- for (int i = 0; (i < numDirectories); i++) {
- fontFile.seekSet(dirOffsets[i]);
- readDirTabs();
-
- readName();
-
- if (fullName.equals(name)) {
- found = true;
- dirTabOffset = dirOffsets[i];
- log.info(fullName + " <-- selected");
- } else {
- log.info(fullName);
- }
-
- // Reset names
- notice = "";
- fullName = "";
- familyNames.clear();
- postScriptName = "";
- subFamilyName = "";
- }
-
- fontFile.seekSet(dirTabOffset);
- return found;
- } else {
- fontFile.seekSet(0);
- return true;
- }
- }
-
- /**
- * Return TTC font names
- * @param in FontFileReader to read from
- * @return True if not collection or font name present, false otherwise
- * @throws IOException In case of an I/O problem
- */
- public final List<String> getTTCnames(FontFileReader in) throws IOException {
- this.fontFile = in;
-
- List<String> fontNames = new ArrayList<String>();
- String tag = in.readTTFString(4);
-
- if ("ttcf".equals(tag)) {
- // This is a TrueType Collection
- in.skip(4);
-
- // Read directory offsets
- int numDirectories = (int)in.readTTFULong();
- long[] dirOffsets = new long[numDirectories];
- for (int i = 0; i < numDirectories; i++) {
- dirOffsets[i] = in.readTTFULong();
- }
-
- log.info("This is a TrueType collection file with "
- + numDirectories + " fonts");
- log.info("Containing the following fonts: ");
-
- for (int i = 0; (i < numDirectories); i++) {
- in.seekSet(dirOffsets[i]);
- readDirTabs();
-
- readName();
-
- log.info(fullName);
- fontNames.add(fullName);
-
- // Reset names
- notice = "";
- fullName = "";
- familyNames.clear();
- postScriptName = "";
- subFamilyName = "";
+ /**@todo Verify that this is correct, looks like a copy/paste bug (jm)*/
+ final int bbox0 = mtxTab[0].getBoundingBox()[0];
+ final int[] bbox = {bbox0, bbox0, bbox0, bbox0};
+ mtxTab[i].setBoundingBox(bbox);
+ /* Original code
+ mtxTab[i].bbox[0] = mtxTab[0].bbox[0];
+ mtxTab[i].bbox[1] = mtxTab[0].bbox[0];
+ mtxTab[i].bbox[2] = mtxTab[0].bbox[0];
+ mtxTab[i].bbox[3] = mtxTab[0].bbox[0]; */
}
-
- in.seekSet(0);
- return fontNames;
- } else {
- log.error("Not a TTC!");
- return null;
- }
- }
-
- /*
- * Helper classes, they are not very efficient, but that really
- * doesn't matter...
- */
- private Integer[] unicodeToWinAnsi(int unicode) {
- 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));
+ if (log.isTraceEnabled()) {
+ log.trace(mtxTab[i].toString(this));
}
}
- return ret.toArray(new Integer[0]);
}
- /**
- * Dumps a few informational values to System.out.
- */
- public void printStuff() {
- System.out.println("Font name: " + postScriptName);
- System.out.println("Full name: " + fullName);
- System.out.println("Family name: " + familyNames);
- System.out.println("Subfamily name: " + subFamilyName);
- System.out.println("Notice: " + notice);
- System.out.println("xHeight: " + convertTTFUnit2PDFUnit(xHeight));
- System.out.println("capheight: " + convertTTFUnit2PDFUnit(capHeight));
-
- int italic = (int)(italicAngle >> 16);
- System.out.println("Italic: " + italic);
- System.out.print("ItalicAngle: " + (short)(italicAngle / 0x10000));
- if ((italicAngle % 0x10000) > 0) {
- System.out.print("."
- + (short)((italicAngle % 0x10000) * 1000)
- / 0x10000);
- }
- System.out.println();
- System.out.println("Ascender: " + convertTTFUnit2PDFUnit(ascender));
- System.out.println("Descender: " + convertTTFUnit2PDFUnit(descender));
- System.out.println("FontBBox: [" + convertTTFUnit2PDFUnit(fontBBox1)
- + " " + convertTTFUnit2PDFUnit(fontBBox2) + " "
- + convertTTFUnit2PDFUnit(fontBBox3) + " "
- + convertTTFUnit2PDFUnit(fontBBox4) + "]");
- }
-
- private String formatUnitsForDebug(int units) {
- return units + " -> " + convertTTFUnit2PDFUnit(units) + " internal units";
- }
-
- /**
- * Map a glyph index to the corresponding unicode code point
- *
- * @param glyphIndex
- * @return unicode code point
- */
- private Integer glyphToUnicode(int glyphIndex) {
- return glyphToUnicodeMap.get(new Integer(glyphIndex));
+ @Override
+ protected void updateBBoxAndOffset() throws IOException {
+ readIndexToLocation();
+ readGlyf();
}
/**
- * Map a unicode code point to the corresponding glyph index
- *
- * @param unicodeIndex unicode code point
- * @return glyph index
- */
- private Integer unicodeToGlyph(int unicodeIndex) throws IOException {
- final Integer result
- = unicodeToGlyphMap.get(new Integer(unicodeIndex));
- if (result == null) {
- throw new IOException(
- "Glyph index not found for unicode value " + unicodeIndex);
- }
- 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
- */
- public boolean hasAdvancedTable() {
- if (advancedTableReader != null) {
- return advancedTableReader.hasAdvancedTable();
- } else {
- return false;
- }
- }
-
- /**
- * Returns the GDEF table or null if none present.
- * @return the GDEF table
- */
- public GlyphDefinitionTable getGDEF() {
- if (advancedTableReader != null) {
- return advancedTableReader.getGDEF();
- } else {
- return null;
- }
- }
-
- /**
- * Returns the GSUB table or null if none present.
- * @return the GSUB table
+ * Read the "loca" table.
+ * @throws IOException In case of a I/O problem
*/
- public GlyphSubstitutionTable getGSUB() {
- if (advancedTableReader != null) {
- return advancedTableReader.getGSUB();
- } else {
- return null;
+ protected final void readIndexToLocation()
+ throws IOException {
+ if (!seekTab(fontFile, OFTableName.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?)");
}
- }
-
- /**
- * Returns the GPOS table or null if none present.
- * @return the GPOS table
- */
- public GlyphPositioningTable getGPOS() {
- if (advancedTableReader != null) {
- return advancedTableReader.getGPOS();
- } else {
- return null;
+ for (int i = 0; i < numberOfGlyphs; i++) {
+ mtxTab[i].setOffset(locaFormat == 1 ? fontFile.readTTFULong()
+ : (fontFile.readTTFUShort() << 1));
}
+ lastLoca = (locaFormat == 1 ? fontFile.readTTFULong()
+ : (fontFile.readTTFUShort() << 1));
}
- /**
- * Static main method to get info about a TrueType font.
- * @param args The command line arguments
- */
- public static void main(String[] args) {
- InputStream stream = null;
- try {
- boolean useKerning = true;
- boolean useAdvanced = true;
- TTFFile ttfFile = new TTFFile(useKerning, useAdvanced);
-
- stream = new FileInputStream(args[0]);
- FontFileReader reader = new FontFileReader(stream);
-
- String name = null;
- if (args.length >= 2) {
- name = args[1];
- }
-
- ttfFile.readFont(reader, name);
- ttfFile.printStuff();
-
- } catch (IOException ioe) {
- System.err.println("Problem reading font: " + ioe.toString());
- ioe.printStackTrace(System.err);
- } finally {
- IOUtils.closeQuietly(stream);
- }
+ @Override
+ protected void initializeFont(FontFileReader in) throws IOException {
+ fontFile = in;
}
}
diff --git a/src/java/org/apache/fop/fonts/truetype/TTFSubSetFile.java b/src/java/org/apache/fop/fonts/truetype/TTFSubSetFile.java
index c9a0e6c8e..ff46af1c7 100644
--- a/src/java/org/apache/fop/fonts/truetype/TTFSubSetFile.java
+++ b/src/java/org/apache/fop/fonts/truetype/TTFSubSetFile.java
@@ -43,7 +43,7 @@ public class TTFSubSetFile extends TTFFile {
* Offsets in name table to be filled out by table.
* The offsets are to the checkSum field
*/
- private Map<TTFTableName, Integer> offsets = new HashMap<TTFTableName, Integer>();
+ private Map<OFTableName, Integer> offsets = new HashMap<OFTableName, Integer>();
private int checkSumAdjustmentOffset = 0;
private int locaOffset = 0;
@@ -67,8 +67,8 @@ public class TTFSubSetFile extends TTFFile {
}
/** The dir tab entries in the new subset font. */
- private Map<TTFTableName, TTFDirTabEntry> newDirTabs
- = new HashMap<TTFTableName, TTFDirTabEntry>();
+ private Map<OFTableName, OFDirTabEntry> newDirTabs
+ = new HashMap<OFTableName, OFDirTabEntry>();
private int determineTableCount() {
int numTables = 4; //4 req'd tables: head,hhea,hmtx,maxp
@@ -117,29 +117,29 @@ 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);
+ writeTableName(OFTableName.OS2);
if (hasCvt()) {
- writeTableName(TTFTableName.CVT);
+ writeTableName(OFTableName.CVT);
}
if (hasFpgm()) {
- writeTableName(TTFTableName.FPGM);
+ writeTableName(OFTableName.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);
+ writeTableName(OFTableName.GLYF);
+ writeTableName(OFTableName.HEAD);
+ writeTableName(OFTableName.HHEA);
+ writeTableName(OFTableName.HMTX);
+ writeTableName(OFTableName.LOCA);
+ writeTableName(OFTableName.MAXP);
+ writeTableName(OFTableName.NAME);
+ writeTableName(OFTableName.POST);
if (hasPrep()) {
- writeTableName(TTFTableName.PREP);
+ writeTableName(OFTableName.PREP);
}
- newDirTabs.put(TTFTableName.TABLE_DIRECTORY, new TTFDirTabEntry(0, currentPos));
+ newDirTabs.put(OFTableName.TABLE_DIRECTORY, new OFDirTabEntry(0, currentPos));
}
- private void writeTableName(TTFTableName tableName) {
+ private void writeTableName(OFTableName tableName) {
writeString(tableName.getName());
offsets.put(tableName, currentPos);
currentPos += 12;
@@ -148,15 +148,15 @@ public class TTFSubSetFile extends TTFFile {
private boolean hasCvt() {
- return dirTabs.containsKey(TTFTableName.CVT);
+ return dirTabs.containsKey(OFTableName.CVT);
}
private boolean hasFpgm() {
- return dirTabs.containsKey(TTFTableName.FPGM);
+ return dirTabs.containsKey(OFTableName.FPGM);
}
private boolean hasPrep() {
- return dirTabs.containsKey(TTFTableName.PREP);
+ return dirTabs.containsKey(OFTableName.PREP);
}
/**
@@ -165,15 +165,15 @@ public class TTFSubSetFile extends TTFFile {
private void createLoca(int size) throws IOException {
pad4();
locaOffset = currentPos;
- int dirTableOffset = offsets.get(TTFTableName.LOCA);
+ int dirTableOffset = offsets.get(OFTableName.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);
+ private boolean copyTable(FontFileReader in, OFTableName tableName) throws IOException {
+ OFDirTabEntry entry = dirTabs.get(tableName);
if (entry != null) {
pad4();
seekTab(in, tableName, 0);
@@ -193,28 +193,28 @@ public class TTFSubSetFile extends TTFFile {
* Copy the cvt table as is from original font to subset font
*/
private boolean createCvt(FontFileReader in) throws IOException {
- return copyTable(in, TTFTableName.CVT);
+ return copyTable(in, OFTableName.CVT);
}
/**
* Copy the fpgm table as is from original font to subset font
*/
private boolean createFpgm(FontFileReader in) throws IOException {
- return copyTable(in, TTFTableName.FPGM);
+ return copyTable(in, OFTableName.FPGM);
}
/**
* Copy the name table as is from the original.
*/
private boolean createName(FontFileReader in) throws IOException {
- return copyTable(in, TTFTableName.NAME);
+ return copyTable(in, OFTableName.NAME);
}
/**
* Copy the OS/2 table as is from the original.
*/
private boolean createOS2(FontFileReader in) throws IOException {
- return copyTable(in, TTFTableName.OS2);
+ return copyTable(in, OFTableName.OS2);
}
/**
@@ -222,8 +222,8 @@ public class TTFSubSetFile extends TTFFile {
* and set num glyphs to size
*/
private void createMaxp(FontFileReader in, int size) throws IOException {
- TTFTableName maxp = TTFTableName.MAXP;
- TTFDirTabEntry entry = dirTabs.get(maxp);
+ OFTableName maxp = OFTableName.MAXP;
+ OFDirTabEntry entry = dirTabs.get(maxp);
if (entry != null) {
pad4();
seekTab(in, maxp, 0);
@@ -240,8 +240,8 @@ public class TTFSubSetFile extends TTFFile {
}
private void createPost(FontFileReader in) throws IOException {
- TTFTableName post = TTFTableName.POST;
- TTFDirTabEntry entry = dirTabs.get(post);
+ OFTableName post = OFTableName.POST;
+ OFDirTabEntry entry = dirTabs.get(post);
if (entry != null) {
pad4();
seekTab(in, post, 0);
@@ -266,7 +266,7 @@ public class TTFSubSetFile extends TTFFile {
* Copy the prep table as is from original font to subset font
*/
private boolean createPrep(FontFileReader in) throws IOException {
- return copyTable(in, TTFTableName.PREP);
+ return copyTable(in, OFTableName.PREP);
}
@@ -275,15 +275,15 @@ public class TTFSubSetFile extends TTFFile {
* and fill in size of hmtx table
*/
private void createHhea(FontFileReader in, int size) throws IOException {
- TTFDirTabEntry entry = dirTabs.get(TTFTableName.HHEA);
+ OFDirTabEntry entry = dirTabs.get(OFTableName.HHEA);
if (entry != null) {
pad4();
- seekTab(in, TTFTableName.HHEA, 0);
+ seekTab(in, OFTableName.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);
+ updateCheckSum(currentPos, (int) entry.getLength(), OFTableName.HHEA);
currentPos += (int) entry.getLength();
realSize += (int) entry.getLength();
} else {
@@ -299,8 +299,8 @@ public class TTFSubSetFile extends TTFFile {
* in checkSumAdjustmentOffset
*/
private void createHead(FontFileReader in) throws IOException {
- TTFTableName head = TTFTableName.HEAD;
- TTFDirTabEntry entry = dirTabs.get(head);
+ OFTableName head = OFTableName.HEAD;
+ OFDirTabEntry entry = dirTabs.get(head);
if (entry != null) {
pad4();
seekTab(in, head, 0);
@@ -329,8 +329,8 @@ public class TTFSubSetFile extends TTFFile {
*/
private void createGlyf(FontFileReader in,
Map<Integer, Integer> glyphs) throws IOException {
- TTFTableName glyf = TTFTableName.GLYF;
- TTFDirTabEntry entry = dirTabs.get(glyf);
+ OFTableName glyf = OFTableName.GLYF;
+ OFDirTabEntry entry = dirTabs.get(glyf);
int size = 0;
int startPos = 0;
int endOffset = 0; // Store this as the last loca
@@ -393,10 +393,10 @@ public class TTFSubSetFile extends TTFFile {
writeULong(locaOffset + glyphs.size() * 4, endOffset);
int locaSize = glyphs.size() * 4 + 4;
int checksum = getCheckSum(output, locaOffset, locaSize);
- writeULong(offsets.get(TTFTableName.LOCA), checksum);
+ writeULong(offsets.get(OFTableName.LOCA), checksum);
int padSize = (locaOffset + locaSize) % 4;
- newDirTabs.put(TTFTableName.LOCA,
- new TTFDirTabEntry(locaOffset, locaSize + padSize));
+ newDirTabs.put(OFTableName.LOCA,
+ new OFDirTabEntry(locaOffset, locaSize + padSize));
} else {
throw new IOException("Can't find glyf table");
}
@@ -420,8 +420,8 @@ public class TTFSubSetFile extends TTFFile {
*/
private void createHmtx(FontFileReader in,
Map<Integer, Integer> glyphs) throws IOException {
- TTFTableName hmtx = TTFTableName.HMTX;
- TTFDirTabEntry entry = dirTabs.get(hmtx);
+ OFTableName hmtx = OFTableName.HMTX;
+ OFDirTabEntry entry = dirTabs.get(hmtx);
int longHorMetricSize = glyphs.size() * 2;
int leftSideBearingSize = glyphs.size() * 2;
@@ -457,11 +457,11 @@ public class TTFSubSetFile extends TTFFile {
* new index as (Integer) value)
* @throws IOException in case of an I/O problem
*/
- public void readFont(FontFileReader in, String name,
+ public void readFont(FontFileReader in, String name, String header,
Map<Integer, Integer> glyphs) throws IOException {
fontFile = in;
//Check if TrueType collection, and that the name exists in the collection
- if (!checkTTC(name)) {
+ if (!checkTTC(header, name)) {
throw new IOException("Failed to read font");
}
@@ -533,7 +533,7 @@ public class TTFSubSetFile extends TTFFile {
glyphOffsets[i + 1] - glyphOffsets[i]);
}
// Stream the last glyph
- TTFDirTabEntry glyf = newDirTabs.get(TTFTableName.GLYF);
+ OFDirTabEntry glyf = newDirTabs.get(OFTableName.GLYF);
long lastGlyphLength = glyf.getLength()
- (glyphOffsets[glyphOffsets.length - 1] - glyf.getOffset());
glyphOut.streamGlyph(output, glyphOffsets[glyphOffsets.length - 1],
@@ -543,14 +543,14 @@ public class TTFSubSetFile extends TTFFile {
@Override
public void stream(TTFOutputStream ttfOut) throws IOException {
- SortedSet<Map.Entry<TTFTableName, TTFDirTabEntry>> sortedDirTabs
+ SortedSet<Map.Entry<OFTableName, OFDirTabEntry>> 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)) {
+ for (Map.Entry<OFTableName, OFDirTabEntry> entry : sortedDirTabs) {
+ if (entry.getKey().equals(OFTableName.GLYF)) {
handleGlyphSubset(glyphOut);
} else {
tableOut.streamTable(output, (int) entry.getValue().getOffset(),
@@ -562,7 +562,7 @@ public class TTFSubSetFile extends TTFFile {
private void scanGlyphs(FontFileReader in, Map<Integer, Integer> subsetGlyphs)
throws IOException {
- TTFDirTabEntry glyfTableInfo = dirTabs.get(TTFTableName.GLYF);
+ OFDirTabEntry glyfTableInfo = dirTabs.get(OFTableName.GLYF);
if (glyfTableInfo == null) {
throw new IOException("Glyf table could not be found");
}
@@ -663,11 +663,11 @@ public class TTFSubSetFile extends TTFFile {
}
- private void updateCheckSum(int tableStart, int tableSize, TTFTableName tableName) {
+ private void updateCheckSum(int tableStart, int tableSize, OFTableName 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));
+ newDirTabs.put(tableName, new OFDirTabEntry(tableStart, tableSize + padSize));
writeULong(offset, checksum);
writeULong(offset + 4, tableStart);
writeULong(offset + 8, tableSize);
diff --git a/src/java/org/apache/fop/fonts/type1/AFMFile.java b/src/java/org/apache/fop/fonts/type1/AFMFile.java
index f6bc3b163..2aa718ea0 100644
--- a/src/java/org/apache/fop/fonts/type1/AFMFile.java
+++ b/src/java/org/apache/fop/fonts/type1/AFMFile.java
@@ -319,7 +319,7 @@ public class AFMFile {
*/
public void addCharMetrics(AFMCharMetrics metrics) {
String name = metrics.getCharName();
- if (metrics.getUnicodeSequence() == null) {
+ if (metrics.getUnicodeSequence() == null && name.equals(".notdef")) {
//Ignore as no Unicode assignment is possible
return;
}
diff --git a/src/java/org/apache/fop/fonts/type1/CharMetricsHandler.java b/src/java/org/apache/fop/fonts/type1/CharMetricsHandler.java
index 8ef172875..79753f3f8 100644
--- a/src/java/org/apache/fop/fonts/type1/CharMetricsHandler.java
+++ b/src/java/org/apache/fop/fonts/type1/CharMetricsHandler.java
@@ -28,6 +28,8 @@ import java.util.regex.Pattern;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.apache.xmlgraphics.fonts.Glyphs;
+
import org.apache.fop.fonts.NamedCharacter;
import org.apache.fop.fonts.type1.AFMParser.ValueHandler;
@@ -102,9 +104,10 @@ abstract class CharMetricsHandler {
AFMCharMetrics chm = defaultHandler.parse(line, stack, afmFileName);
NamedCharacter namedChar = chm.getCharacter();
if (namedChar != null) {
- int codePoint = AdobeStandardEncoding.getAdobeCodePoint(namedChar.getName());
- if (chm.getCharCode() != codePoint) {
- LOG.info(afmFileName + ": named character '" + namedChar.getName() + "'"
+ String charName = namedChar.getName();
+ int codePoint = AdobeStandardEncoding.getAdobeCodePoint(charName);
+ if (chm.getCharCode() != codePoint && !Glyphs.NOTDEF.equals(charName)) {
+ LOG.info(afmFileName + ": named character '" + charName + "'"
+ " has an incorrect code point: " + chm.getCharCode()
+ ". Changed to " + codePoint);
chm.setCharCode(codePoint);
diff --git a/src/java/org/apache/fop/fonts/type1/Type1FontLoader.java b/src/java/org/apache/fop/fonts/type1/Type1FontLoader.java
index 7922ff6fd..260ef209f 100644
--- a/src/java/org/apache/fop/fonts/type1/Type1FontLoader.java
+++ b/src/java/org/apache/fop/fonts/type1/Type1FontLoader.java
@@ -173,7 +173,21 @@ public class Type1FontLoader extends FontLoader {
addUnencodedBasedOnAFM(afm);
}
} else {
- if (pfm.getCharSet() >= 0 && pfm.getCharSet() <= 2) {
+ if (pfm.getCharSet() == 2 && !pfm.getCharSetName().equals("Symbol")) {
+ int[] table = new int[256];
+ String[] charNameMap = new String[256];
+ int j = 0;
+ for (int i = pfm.getFirstChar(); i < pfm.getLastChar(); i++) {
+ if (j < table.length) {
+ table[j] = i;
+ table[j + 1] = i;
+ j += 2;
+ }
+ charNameMap[i] = String.format("x%03o", i);
+ }
+ CodePointMapping mapping = new CodePointMapping("custom", table, charNameMap);
+ singleFont.setEncoding(mapping);
+ } else if (pfm.getCharSet() >= 0 && pfm.getCharSet() <= 2) {
singleFont.setEncoding(pfm.getCharSetName() + "Encoding");
} else {
log.warn("The PFM reports an unsupported encoding ("
@@ -400,10 +414,7 @@ public class Type1FontLoader extends FontLoader {
List<AFMCharMetrics> chars = afm.getCharMetrics();
for (AFMCharMetrics charMetrics : chars) {
if (charMetrics.getCharCode() >= 0) {
- String u = charMetrics.getUnicodeSequence();
- if (u != null && u.length() == 1) {
- mappingCount++;
- }
+ mappingCount++;
}
}
// ...and now build the table.
@@ -416,6 +427,10 @@ public class Type1FontLoader extends FontLoader {
String unicodes = charMetrics.getUnicodeSequence();
if (unicodes == null) {
log.info("No Unicode mapping for glyph: " + charMetrics);
+ table[idx] = charMetrics.getCharCode();
+ idx++;
+ table[idx] = charMetrics.getCharCode();
+ idx++;
} else if (unicodes.length() == 1) {
table[idx] = charMetrics.getCharCode();
idx++;
diff --git a/src/java/org/apache/fop/layoutmgr/AbstractBaseLayoutManager.java b/src/java/org/apache/fop/layoutmgr/AbstractBaseLayoutManager.java
index 5d7cc0b64..635e267c1 100644
--- a/src/java/org/apache/fop/layoutmgr/AbstractBaseLayoutManager.java
+++ b/src/java/org/apache/fop/layoutmgr/AbstractBaseLayoutManager.java
@@ -65,9 +65,6 @@ public abstract class AbstractBaseLayoutManager
public AbstractBaseLayoutManager(FObj fo) {
this.fobj = fo;
setGeneratesReferenceArea(fo.generatesReferenceAreas());
- if (getGeneratesReferenceArea()) {
- setGeneratesBlockArea(true);
- }
}
// --------- Property Resolution related functions --------- //
diff --git a/src/java/org/apache/fop/layoutmgr/AbstractLayoutManager.java b/src/java/org/apache/fop/layoutmgr/AbstractLayoutManager.java
index 0285a41e7..2dd18e4ee 100644
--- a/src/java/org/apache/fop/layoutmgr/AbstractLayoutManager.java
+++ b/src/java/org/apache/fop/layoutmgr/AbstractLayoutManager.java
@@ -342,6 +342,34 @@ public abstract class AbstractLayoutManager extends AbstractBaseLayoutManager im
&& isFinished());
}
+ public boolean hasLineAreaDescendant() {
+ if (childLMs == null || childLMs.isEmpty()) {
+ return false;
+ } else {
+ for (LayoutManager childLM : childLMs) {
+ if (childLM.hasLineAreaDescendant()) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public int getBaselineOffset() {
+ if (childLMs != null) {
+ for (LayoutManager childLM : childLMs) {
+ if (childLM.hasLineAreaDescendant()) {
+ return childLM.getBaselineOffset();
+ }
+ }
+ }
+ throw newNoLineAreaDescendantException();
+ }
+
+ protected IllegalStateException newNoLineAreaDescendantException() {
+ return new IllegalStateException("getBaselineOffset called on an object that has no line-area descendant");
+ }
+
/**
* Transfers foreign attributes from the formatting object to the area.
* @param targetArea the area to set the attributes on
diff --git a/src/java/org/apache/fop/layoutmgr/AreaAdditionUtil.java b/src/java/org/apache/fop/layoutmgr/AreaAdditionUtil.java
index 0ed6cb69b..d731ab62a 100644
--- a/src/java/org/apache/fop/layoutmgr/AreaAdditionUtil.java
+++ b/src/java/org/apache/fop/layoutmgr/AreaAdditionUtil.java
@@ -33,11 +33,11 @@ public final class AreaAdditionUtil {
/**
* Creates the child areas for the given layout manager.
- * @param bslm the BlockStackingLayoutManager instance for which "addAreas" is performed.
+ * @param parentLM the parent layout manager
* @param parentIter the position iterator
* @param layoutContext the layout context
*/
- public static void addAreas(BlockStackingLayoutManager bslm,
+ public static void addAreas(AbstractLayoutManager parentLM,
PositionIterator parentIter, LayoutContext layoutContext) {
LayoutManager childLM;
LayoutContext lc = LayoutContext.offspringOf(layoutContext);
@@ -46,8 +46,8 @@ public final class AreaAdditionUtil {
Position firstPos = null;
Position lastPos = null;
- if (bslm != null) {
- bslm.addId();
+ if (parentLM != null) {
+ parentLM.addId();
}
// "unwrap" the NonLeafPositions stored in parentIter
@@ -86,11 +86,11 @@ public final class AreaAdditionUtil {
//doesn't give us that info.
}
- if (bslm != null) {
- bslm.registerMarkers(
+ if (parentLM != null) {
+ parentLM.registerMarkers(
true,
- bslm.isFirst(firstPos),
- bslm.isLast(lastPos));
+ parentLM.isFirst(firstPos),
+ parentLM.isLast(lastPos));
}
PositionIterator childPosIter = new PositionIterator(positionList.listIterator());
@@ -113,11 +113,11 @@ public final class AreaAdditionUtil {
childLM.addAreas(childPosIter, lc);
}
- if (bslm != null) {
- bslm.registerMarkers(
+ if (parentLM != null) {
+ parentLM.registerMarkers(
false,
- bslm.isFirst(firstPos),
- bslm.isLast(lastPos));
+ parentLM.isFirst(firstPos),
+ parentLM.isLast(lastPos));
}
diff --git a/src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java b/src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java
index a23cd28f1..3da6974a6 100644
--- a/src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java
+++ b/src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java
@@ -37,6 +37,7 @@ import org.apache.fop.datatypes.FODimension;
import org.apache.fop.datatypes.Length;
import org.apache.fop.fo.flow.BlockContainer;
import org.apache.fop.fo.properties.CommonAbsolutePosition;
+import org.apache.fop.fo.properties.CommonBorderPaddingBackground;
import org.apache.fop.fo.properties.KeepProperty;
import org.apache.fop.traits.MinOptMax;
import org.apache.fop.traits.SpaceVal;
@@ -44,8 +45,8 @@ import org.apache.fop.traits.SpaceVal;
/**
* LayoutManager for a block-container FO.
*/
-public class BlockContainerLayoutManager extends BlockStackingLayoutManager implements
- ConditionalElementListener, BreakOpportunity {
+public class BlockContainerLayoutManager extends SpacedBorderedPaddedBlockLayoutManager
+ implements BreakOpportunity {
/**
* logging instance
@@ -79,13 +80,6 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager impl
private MinOptMax foBlockSpaceBefore;
private MinOptMax foBlockSpaceAfter;
- private boolean discardBorderBefore;
- private boolean discardBorderAfter;
- private boolean discardPaddingBefore;
- private boolean discardPaddingAfter;
- private MinOptMax effSpaceBefore;
- private MinOptMax effSpaceAfter;
-
private int horizontalOverflow;
private double contentRectOffsetX = 0;
private double contentRectOffsetY = 0;
@@ -96,6 +90,7 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager impl
*/
public BlockContainerLayoutManager(BlockContainer node) {
super(node);
+ setGeneratesBlockArea(true);
}
/** {@inheritDoc} */
@@ -128,6 +123,11 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager impl
.spaceAfter.getSpace().getOptimum(this).getLength().getValue(this);
}
+ @Override
+ protected CommonBorderPaddingBackground getCommonBorderPaddingBackground() {
+ return getBlockContainerFO().getCommonBorderPaddingBackground();
+ }
+
private void resetSpaces() {
this.discardBorderBefore = false;
this.discardBorderAfter = false;
@@ -867,6 +867,7 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager impl
transferForeignAttributes(viewportBlockArea);
TraitSetter.setProducerID(viewportBlockArea, getBlockContainerFO().getId());
+ TraitSetter.setLayer(viewportBlockArea, getBlockContainerFO().getLayer());
TraitSetter.addBorders(viewportBlockArea,
getBlockContainerFO().getCommonBorderPaddingBackground(),
discardBorderBefore, discardBorderAfter, false, false, this);
@@ -994,51 +995,6 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager impl
}
/** {@inheritDoc} */
- public void notifySpace(RelSide side, MinOptMax effectiveLength) {
- if (RelSide.BEFORE == side) {
- if (log.isDebugEnabled()) {
- log.debug(this + ": Space " + side + ", "
- + this.effSpaceBefore + "-> " + effectiveLength);
- }
- this.effSpaceBefore = effectiveLength;
- } else {
- if (log.isDebugEnabled()) {
- log.debug(this + ": Space " + side + ", "
- + this.effSpaceAfter + "-> " + effectiveLength);
- }
- this.effSpaceAfter = effectiveLength;
- }
- }
-
- /** {@inheritDoc} */
- public void notifyBorder(RelSide side, MinOptMax effectiveLength) {
- if (effectiveLength == null) {
- if (RelSide.BEFORE == side) {
- this.discardBorderBefore = true;
- } else {
- this.discardBorderAfter = true;
- }
- }
- if (log.isDebugEnabled()) {
- log.debug(this + ": Border " + side + " -> " + effectiveLength);
- }
- }
-
- /** {@inheritDoc} */
- public void notifyPadding(RelSide side, MinOptMax effectiveLength) {
- if (effectiveLength == null) {
- if (RelSide.BEFORE == side) {
- this.discardPaddingBefore = true;
- } else {
- this.discardPaddingAfter = true;
- }
- }
- if (log.isDebugEnabled()) {
- log.debug(this + ": Padding " + side + " -> " + effectiveLength);
- }
- }
-
- /** {@inheritDoc} */
public boolean handleOverflow(int milliPoints) {
if (milliPoints > this.horizontalOverflow) {
this.horizontalOverflow = milliPoints;
diff --git a/src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java b/src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java
index 0fb738aea..d3bdc7b85 100644
--- a/src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java
+++ b/src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java
@@ -32,6 +32,7 @@ import org.apache.fop.area.Block;
import org.apache.fop.area.LineArea;
import org.apache.fop.datatypes.Length;
import org.apache.fop.fo.FONode;
+import org.apache.fop.fo.properties.CommonBorderPaddingBackground;
import org.apache.fop.fo.properties.KeepProperty;
import org.apache.fop.fonts.Font;
import org.apache.fop.fonts.FontInfo;
@@ -44,8 +45,8 @@ import org.apache.fop.traits.SpaceVal;
/**
* LayoutManager for a block FO.
*/
-public class BlockLayoutManager extends BlockStackingLayoutManager implements ConditionalElementListener,
- BreakOpportunity {
+public class BlockLayoutManager extends SpacedBorderedPaddedBlockLayoutManager
+ implements BreakOpportunity {
/** logging instance */
private static Log log = LogFactory.getLog(BlockLayoutManager.class);
@@ -60,13 +61,6 @@ public class BlockLayoutManager extends BlockStackingLayoutManager implements Co
private int follow = 2000;
//private int middleShift = 0;
- private boolean discardBorderBefore;
- private boolean discardBorderAfter;
- private boolean discardPaddingBefore;
- private boolean discardPaddingAfter;
- private MinOptMax effSpaceBefore;
- private MinOptMax effSpaceAfter;
-
/**
* Creates a new BlockLayoutManager.
* @param inBlock the block FO object to create the layout manager for.
@@ -100,6 +94,11 @@ public class BlockLayoutManager extends BlockStackingLayoutManager implements Co
.getOptimum(this).getLength().getValue(this);
}
+ @Override
+ protected CommonBorderPaddingBackground getCommonBorderPaddingBackground() {
+ return getBlockFO().getCommonBorderPaddingBackground();
+ }
+
/** {@inheritDoc} */
@Override
public List getNextKnuthElements(LayoutContext context, int alignment) {
@@ -360,7 +359,7 @@ public class BlockLayoutManager extends BlockStackingLayoutManager implements Co
curBlockArea.setIPD(super.getContentAreaIPD());
- curBlockArea.setBidiLevel(getBlockFO().getBidiLevel());
+ curBlockArea.setBidiLevel(getBlockFO().getBidiLevelRecursive());
TraitSetter.addBreaks(curBlockArea,
getBlockFO().getBreakBefore(), getBlockFO().getBreakAfter());
@@ -381,6 +380,7 @@ public class BlockLayoutManager extends BlockStackingLayoutManager implements Co
getBlockFO().getCommonBorderPaddingBackground(),
startIndent, endIndent,
this);
+ TraitSetter.setLayer(curBlockArea, getBlockFO().getLayer());
curBlockArea.setLocale(getBlockFO().getCommonHyphenation().getLocale());
curBlockArea.setLocation(FONode.getLocatorString(getBlockFO().getLocator()));
@@ -457,51 +457,6 @@ public class BlockLayoutManager extends BlockStackingLayoutManager implements Co
}
/** {@inheritDoc} */
- public void notifySpace(RelSide side, MinOptMax effectiveLength) {
- if (RelSide.BEFORE == side) {
- if (log.isDebugEnabled()) {
- log.debug(this + ": Space " + side + ", "
- + this.effSpaceBefore + "-> " + effectiveLength);
- }
- this.effSpaceBefore = effectiveLength;
- } else {
- if (log.isDebugEnabled()) {
- log.debug(this + ": Space " + side + ", "
- + this.effSpaceAfter + "-> " + effectiveLength);
- }
- this.effSpaceAfter = effectiveLength;
- }
- }
-
- /** {@inheritDoc} */
- public void notifyBorder(RelSide side, MinOptMax effectiveLength) {
- if (effectiveLength == null) {
- if (RelSide.BEFORE == side) {
- this.discardBorderBefore = true;
- } else {
- this.discardBorderAfter = true;
- }
- }
- if (log.isDebugEnabled()) {
- log.debug(this + ": Border " + side + " -> " + effectiveLength);
- }
- }
-
- /** {@inheritDoc} */
- public void notifyPadding(RelSide side, MinOptMax effectiveLength) {
- if (effectiveLength == null) {
- if (RelSide.BEFORE == side) {
- this.discardPaddingBefore = true;
- } else {
- this.discardPaddingAfter = true;
- }
- }
- if (log.isDebugEnabled()) {
- log.debug(this + ": Padding " + side + " -> " + effectiveLength);
- }
- }
-
- /** {@inheritDoc} */
@Override
public boolean isRestartable() {
return true;
diff --git a/src/java/org/apache/fop/layoutmgr/BlockStackingLayoutManager.java b/src/java/org/apache/fop/layoutmgr/BlockStackingLayoutManager.java
index d11f062cb..250e07727 100644
--- a/src/java/org/apache/fop/layoutmgr/BlockStackingLayoutManager.java
+++ b/src/java/org/apache/fop/layoutmgr/BlockStackingLayoutManager.java
@@ -36,6 +36,7 @@ import org.apache.fop.fo.properties.BreakPropertySet;
import org.apache.fop.fo.properties.CommonBorderPaddingBackground;
import org.apache.fop.fo.properties.KeepProperty;
import org.apache.fop.fo.properties.SpaceProperty;
+import org.apache.fop.layoutmgr.inline.InlineContainerLayoutManager;
import org.apache.fop.layoutmgr.inline.InlineLayoutManager;
import org.apache.fop.traits.MinOptMax;
import org.apache.fop.util.ListUtil;
@@ -1246,6 +1247,8 @@ public abstract class BlockStackingLayoutManager extends AbstractLayoutManager
public boolean handleOverflow(int milliPoints) {
if (getParent() instanceof BlockStackingLayoutManager) {
return ((BlockStackingLayoutManager) getParent()).handleOverflow(milliPoints);
+ } else if (getParent() instanceof InlineContainerLayoutManager) {
+ return ((InlineContainerLayoutManager) getParent()).handleOverflow(milliPoints);
}
return false;
}
diff --git a/src/java/org/apache/fop/layoutmgr/ExternalDocumentLayoutManager.java b/src/java/org/apache/fop/layoutmgr/ExternalDocumentLayoutManager.java
index 7cbe33f26..aae5a6b30 100644
--- a/src/java/org/apache/fop/layoutmgr/ExternalDocumentLayoutManager.java
+++ b/src/java/org/apache/fop/layoutmgr/ExternalDocumentLayoutManager.java
@@ -242,8 +242,14 @@ public class ExternalDocumentLayoutManager extends AbstractPageSequenceLayoutMan
pv.setPage(pageArea);
RegionViewport rv = new RegionViewport(referenceRect);
- rv.setIPD(referenceRect.width);
- rv.setBPD(referenceRect.height);
+
+ if (pageSeq.getReferenceOrientation() % 180 == 0) {
+ rv.setIPD(referenceRect.width);
+ rv.setBPD(referenceRect.height);
+ } else {
+ rv.setIPD(referenceRect.height);
+ rv.setBPD(referenceRect.width);
+ }
rv.setClip(true);
BodyRegion body = new BodyRegion(Constants.FO_REGION_BODY,
diff --git a/src/java/org/apache/fop/layoutmgr/FlowLayoutManager.java b/src/java/org/apache/fop/layoutmgr/FlowLayoutManager.java
index 0260046e1..4c7afa8d6 100644
--- a/src/java/org/apache/fop/layoutmgr/FlowLayoutManager.java
+++ b/src/java/org/apache/fop/layoutmgr/FlowLayoutManager.java
@@ -58,6 +58,7 @@ public class FlowLayoutManager extends BlockStackingLayoutManager
*/
public FlowLayoutManager(PageSequenceLayoutManager pslm, Flow node) {
super(node);
+ setGeneratesBlockArea(true);
setParent(pslm);
}
diff --git a/src/java/org/apache/fop/layoutmgr/LayoutManager.java b/src/java/org/apache/fop/layoutmgr/LayoutManager.java
index 985131bf1..8d1e4001c 100644
--- a/src/java/org/apache/fop/layoutmgr/LayoutManager.java
+++ b/src/java/org/apache/fop/layoutmgr/LayoutManager.java
@@ -178,6 +178,25 @@ public interface LayoutManager extends PercentBaseContext {
List getChangedKnuthElements(List oldList, int alignment);
/**
+ * Whether the FO handled by this layout manager has a descendant (including itself)
+ * that will generate a line-area.
+ *
+ * @return {@code true} if a descendant line-area will be generated, {@code false} otherwise
+ */
+ boolean hasLineAreaDescendant();
+
+ /**
+ * Returns the position of the dominant-baseline of this FO's first descendant
+ * line-area. <p>The behavior of this method is undefined if this FO has no descendant
+ * line-area, and an exception may be thrown. See {@link #hasLineAreaDescendant()}</p>
+ *
+ * @return this FO's space-before plus the distance from the before-edge of its
+ * allocation-rectangle to the dominant-baseline of the first line-area descendant
+ * @see #hasLineAreaDescendant()
+ */
+ int getBaselineOffset();
+
+ /**
* Returns the IPD of the content area
* @return the IPD of the content area
*/
diff --git a/src/java/org/apache/fop/layoutmgr/LayoutManagerMapping.java b/src/java/org/apache/fop/layoutmgr/LayoutManagerMapping.java
index 292251a84..0e333d219 100644
--- a/src/java/org/apache/fop/layoutmgr/LayoutManagerMapping.java
+++ b/src/java/org/apache/fop/layoutmgr/LayoutManagerMapping.java
@@ -73,7 +73,7 @@ import org.apache.fop.layoutmgr.inline.CharacterLayoutManager;
import org.apache.fop.layoutmgr.inline.ContentLayoutManager;
import org.apache.fop.layoutmgr.inline.ExternalGraphicLayoutManager;
import org.apache.fop.layoutmgr.inline.FootnoteLayoutManager;
-import org.apache.fop.layoutmgr.inline.ICLayoutManager;
+import org.apache.fop.layoutmgr.inline.InlineContainerLayoutManager;
import org.apache.fop.layoutmgr.inline.InlineLayoutManager;
import org.apache.fop.layoutmgr.inline.InstreamForeignObjectLM;
import org.apache.fop.layoutmgr.inline.LeaderLayoutManager;
@@ -257,9 +257,9 @@ public class LayoutManagerMapping implements LayoutManagerMaker {
/** a layout manager maker */
public static class InlineLayoutManagerMaker extends Maker {
/** {@inheritDoc} */
- public void make(FONode node, List lms) {
- lms.add(new InlineLayoutManager((InlineLevel) node));
- }
+ public void make(FONode node, List lms) {
+ lms.add(new InlineLayoutManager((InlineLevel) node));
+ }
}
/** a layout manager maker */
@@ -274,9 +274,7 @@ public class LayoutManagerMapping implements LayoutManagerMaker {
public static class InlineContainerLayoutManagerMaker extends Maker {
/** {@inheritDoc} */
public void make(FONode node, List lms) {
- ArrayList childList = new ArrayList();
- super.make(node, childList);
- lms.add(new ICLayoutManager((InlineContainer) node, childList));
+ lms.add(new InlineContainerLayoutManager((InlineContainer) node));
}
}
diff --git a/src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java b/src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java
index 59145dd72..a5084bac5 100644
--- a/src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java
+++ b/src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java
@@ -140,7 +140,10 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
*/
protected class KnuthPageNode extends KnuthNode {
- /** Additional length due to footnotes. */
+ /** Additional length due to already inserted footnotes. */
+ public int insertedFootnotes;
+
+ /** Total length of the footnotes. */
public int totalFootnotes;
/** Index of the last inserted footnote. */
@@ -152,7 +155,8 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
public KnuthPageNode(int position,
int line, int fitness,
int totalWidth, int totalStretch, int totalShrink,
- int totalFootnotes, int footnoteListIndex, int footnoteElementIndex,
+ int insertedFootnotes, int totalFootnotes,
+ int footnoteListIndex, int footnoteElementIndex,
double adjustRatio, int availableShrink, int availableStretch,
int difference, double totalDemerits, KnuthNode previous) {
super(position, line, fitness,
@@ -160,6 +164,7 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
adjustRatio, availableShrink, availableStretch,
difference, totalDemerits, previous);
this.totalFootnotes = totalFootnotes;
+ this.insertedFootnotes = insertedFootnotes;
this.footnoteListIndex = footnoteListIndex;
this.footnoteElementIndex = footnoteElementIndex;
}
@@ -172,7 +177,8 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
*/
protected class BestPageRecords extends BestRecords {
- private int[] bestFootnotesLength = new int[4];
+ private int[] bestInsertedFootnotesLength = new int[4];
+ private int[] bestTotalFootnotesLength = new int[4];
private int[] bestFootnoteListIndex = new int[4];
private int[] bestFootnoteElementIndex = new int[4];
@@ -182,13 +188,18 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
super.addRecord(demerits, node, adjust,
availableShrink, availableStretch,
difference, fitness);
- bestFootnotesLength[fitness] = insertedFootnotesLength;
+ bestInsertedFootnotesLength[fitness] = insertedFootnotesLength;
+ bestTotalFootnotesLength[fitness] = totalFootnotesLength;
bestFootnoteListIndex[fitness] = footnoteListIndex;
bestFootnoteElementIndex[fitness] = footnoteElementIndex;
}
- public int getFootnotesLength(int fitness) {
- return bestFootnotesLength[fitness];
+ public int getInsertedFootnotesLength(int fitness) {
+ return bestInsertedFootnotesLength[fitness];
+ }
+
+ public int getTotalFootnotesLength(int fitness) {
+ return bestTotalFootnotesLength[fitness];
}
public int getFootnoteListIndex(int fitness) {
@@ -287,7 +298,8 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
int difference, double totalDemerits, KnuthNode previous) {
return new KnuthPageNode(position, line, fitness,
totalWidth, totalStretch, totalShrink,
- insertedFootnotesLength, footnoteListIndex, footnoteElementIndex,
+ insertedFootnotesLength, totalFootnotesLength,
+ footnoteListIndex, footnoteElementIndex,
adjustRatio, availableShrink, availableStretch,
difference, totalDemerits, previous);
}
@@ -298,7 +310,8 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
int totalWidth, int totalStretch, int totalShrink) {
return new KnuthPageNode(position, line, fitness,
totalWidth, totalStretch, totalShrink,
- ((BestPageRecords) best).getFootnotesLength(fitness),
+ ((BestPageRecords) best).getInsertedFootnotesLength(fitness),
+ ((BestPageRecords) best).getTotalFootnotesLength(fitness),
((BestPageRecords) best).getFootnoteListIndex(fitness),
((BestPageRecords) best).getFootnoteElementIndex(fitness),
best.getAdjust(fitness), best.getAvailableShrink(fitness),
@@ -405,6 +418,12 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
resetFootnotes(((KnuthBlockBox) resetElement).getElementLists());
}
}
+ assert restartingNode instanceof KnuthPageNode;
+ KnuthPageNode restartingPageNode = (KnuthPageNode) restartingNode;
+ footnoteElementIndex = restartingPageNode.footnoteElementIndex;
+ footnoteListIndex = restartingPageNode.footnoteListIndex;
+ totalFootnotesLength = restartingPageNode.totalFootnotes;
+ insertedFootnotesLength = restartingPageNode.insertedFootnotes;
}
return returnValue;
}
@@ -413,13 +432,6 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
for (int i = 0; i < elementLists.size(); i++) {
ListUtil.removeLast(footnotesList);
ListUtil.removeLast(lengthList);
-
- // update totalFootnotesLength
- if (!lengthList.isEmpty()) {
- totalFootnotesLength = ListUtil.getLast(lengthList);
- } else {
- totalFootnotesLength = 0;
- }
}
// update footnotesPending;
if (footnotesList.size() == 0) {
@@ -502,7 +514,7 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
}
if (footnotesPending) {
// compute the total length of the footnotes not yet inserted
- int allFootnotes = totalFootnotesLength - pageNode.totalFootnotes;
+ int allFootnotes = totalFootnotesLength - pageNode.insertedFootnotes;
if (allFootnotes > 0) {
// this page contains some footnote citations
// add the footnote separator width
@@ -511,7 +523,7 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
// there is enough space to insert all footnotes:
// add the whole allFootnotes length
actualWidth += allFootnotes;
- insertedFootnotesLength = pageNode.totalFootnotes + allFootnotes;
+ insertedFootnotesLength = pageNode.insertedFootnotes + allFootnotes;
footnoteListIndex = footnotesList.size() - 1;
footnoteElementIndex
= getFootnoteList(footnoteListIndex).size() - 1;
@@ -528,7 +540,7 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
// this is the first feasible break; in this case it is allowed
// to break and defer, if necessary, old and new footnotes
actualWidth += footnoteSplit;
- insertedFootnotesLength = pageNode.totalFootnotes + footnoteSplit;
+ insertedFootnotesLength = pageNode.insertedFootnotes + footnoteSplit;
// footnoteListIndex has been set in getFootnoteSplit()
// footnoteElementIndex has been set in getFootnoteSplit()
} else {
@@ -538,7 +550,7 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
// that cannot be broken:
// add the whole allFootnotes length, so this breakpoint will be discarded
actualWidth += allFootnotes;
- insertedFootnotesLength = pageNode.totalFootnotes + allFootnotes;
+ insertedFootnotesLength = pageNode.insertedFootnotes + allFootnotes;
footnoteListIndex = footnotesList.size() - 1;
footnoteElementIndex
= getFootnoteList(footnoteListIndex).size() - 1;
@@ -569,7 +581,7 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
private boolean canDeferOldFootnotes(KnuthPageNode node, int contentElementIndex) {
return (noBreakBetween(node.position, contentElementIndex)
&& deferredFootnotes(node.footnoteListIndex,
- node.footnoteElementIndex, node.totalFootnotes));
+ node.footnoteElementIndex, node.insertedFootnotes));
}
/**
@@ -649,7 +661,7 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
boolean canDeferOldFootnotes) {
return getFootnoteSplit(activeNode.footnoteListIndex,
activeNode.footnoteElementIndex,
- activeNode.totalFootnotes,
+ activeNode.insertedFootnotes,
availableLength, canDeferOldFootnotes);
}
@@ -714,10 +726,8 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
int prevIndex = -1;
int index = -1;
- while (!(somethingAdded && splitLength > availableLength)) {
- if (!somethingAdded) {
- somethingAdded = true;
- } else {
+ while (splitLength <= availableLength) {
+ if (somethingAdded) {
prevSplitLength = splitLength;
prevIndex = index;
}
@@ -733,6 +743,10 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
// element is a box
splitLength += element.getWidth();
boxPreceding = true;
+ if (splitLength > prevSplitLength) {
+ // and it is non-empty
+ somethingAdded = true;
+ }
} else if (element.isGlue()) {
// element is a glue
if (boxPreceding) {
@@ -749,6 +763,7 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
index = noteListIterator.previousIndex();
break;
}
+ boxPreceding = false;
}
}
}
@@ -758,7 +773,6 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
// page here
// if prevSplitLength is > 0 we can insert some footnote content in this page
// and insert the remaining in the following one
- //TODO: check this conditional, as the first one is always false...?
if (!somethingAdded) {
// there was not enough space to add a piece of the first new footnote
// this is not a good break
@@ -781,7 +795,7 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
if (difference > 0) {
int maxAdjustment = totalStretch - activeNode.totalStretch;
// add the footnote separator stretch if some footnote content will be added
- if (((KnuthPageNode) activeNode).totalFootnotes < totalFootnotesLength) {
+ if (((KnuthPageNode) activeNode).insertedFootnotes < totalFootnotesLength) {
maxAdjustment += footnoteSeparatorLength.getStretch();
}
if (maxAdjustment > 0) {
@@ -792,7 +806,7 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
} else if (difference < 0) {
int maxAdjustment = totalShrink - activeNode.totalShrink;
// add the footnote separator shrink if some footnote content will be added
- if (((KnuthPageNode) activeNode).totalFootnotes < totalFootnotesLength) {
+ if (((KnuthPageNode) activeNode).insertedFootnotes < totalFootnotesLength) {
maxAdjustment += footnoteSeparatorLength.getShrink();
}
if (maxAdjustment > 0) {
@@ -866,7 +880,7 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
for (KnuthPageNode node = (KnuthPageNode) getNode(i);
node != null;
node = (KnuthPageNode) node.next) {
- if (node.totalFootnotes < totalFootnotesLength) {
+ if (node.insertedFootnotes < totalFootnotesLength) {
// layout remaining footnote bodies
createFootnotePages(node);
}
@@ -876,7 +890,7 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
private void createFootnotePages(KnuthPageNode lastNode) {
- insertedFootnotesLength = lastNode.totalFootnotes;
+ insertedFootnotesLength = lastNode.insertedFootnotes;
footnoteListIndex = lastNode.footnoteListIndex;
footnoteElementIndex = lastNode.footnoteElementIndex;
int availableBPD = getLineWidth(lastNode.line);
@@ -902,7 +916,7 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
// cannot add any content: create a new node and start again
KnuthPageNode node = (KnuthPageNode)
createNode(lastNode.position, prevNode.line + 1, 1,
- insertedFootnotesLength - prevNode.totalFootnotes,
+ insertedFootnotesLength - prevNode.insertedFootnotes,
0, 0,
0, 0, 0,
0, 0, prevNode);
@@ -916,7 +930,7 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
// create the last node
KnuthPageNode node = (KnuthPageNode)
createNode(lastNode.position, prevNode.line + 1, 1,
- totalFootnotesLength - prevNode.totalFootnotes, 0, 0,
+ totalFootnotesLength - prevNode.insertedFootnotes, 0, 0,
0, 0, 0,
0, 0, prevNode);
addNode(node.line, node);
diff --git a/src/java/org/apache/fop/layoutmgr/SpacedBorderedPaddedBlockLayoutManager.java b/src/java/org/apache/fop/layoutmgr/SpacedBorderedPaddedBlockLayoutManager.java
new file mode 100644
index 000000000..2ac41e96a
--- /dev/null
+++ b/src/java/org/apache/fop/layoutmgr/SpacedBorderedPaddedBlockLayoutManager.java
@@ -0,0 +1,112 @@
+/*
+ * 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.layoutmgr;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.fop.fo.FObj;
+import org.apache.fop.fo.properties.CommonBorderPaddingBackground;
+import org.apache.fop.traits.MinOptMax;
+
+/**
+ * A block-stacking layout manager for an FO that supports spaces, border and padding.
+ */
+public abstract class SpacedBorderedPaddedBlockLayoutManager extends BlockStackingLayoutManager
+ implements ConditionalElementListener {
+
+ private static final Log LOG = LogFactory.getLog(BlockLayoutManager.class);
+
+ protected MinOptMax effSpaceBefore;
+
+ protected MinOptMax effSpaceAfter;
+
+ protected boolean discardBorderBefore;
+ protected boolean discardBorderAfter;
+ protected boolean discardPaddingBefore;
+ protected boolean discardPaddingAfter;
+
+ public SpacedBorderedPaddedBlockLayoutManager(FObj node) {
+ super(node);
+ }
+
+ public void notifySpace(RelSide side, MinOptMax effectiveLength) {
+ if (RelSide.BEFORE == side) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(this + ": Space " + side + ", "
+ + this.effSpaceBefore + "-> " + effectiveLength);
+ }
+ this.effSpaceBefore = effectiveLength;
+ } else {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(this + ": Space " + side + ", "
+ + this.effSpaceAfter + "-> " + effectiveLength);
+ }
+ this.effSpaceAfter = effectiveLength;
+ }
+ }
+
+ public void notifyBorder(RelSide side, MinOptMax effectiveLength) {
+ if (effectiveLength == null) {
+ if (RelSide.BEFORE == side) {
+ this.discardBorderBefore = true;
+ } else {
+ this.discardBorderAfter = true;
+ }
+ }
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(this + ": Border " + side + " -> " + effectiveLength);
+ }
+ }
+
+ public void notifyPadding(RelSide side, MinOptMax effectiveLength) {
+ if (effectiveLength == null) {
+ if (RelSide.BEFORE == side) {
+ this.discardPaddingBefore = true;
+ } else {
+ this.discardPaddingAfter = true;
+ }
+ }
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(this + ": Padding " + side + " -> " + effectiveLength);
+ }
+ }
+
+ @Override
+ public int getBaselineOffset() {
+ int baselineOffset = super.getBaselineOffset();
+ if (effSpaceBefore != null) {
+ baselineOffset += effSpaceBefore.getOpt();
+ }
+ if (!discardBorderBefore) {
+ baselineOffset += getCommonBorderPaddingBackground().getBorderBeforeWidth(false);
+ }
+ if (!discardPaddingBefore) {
+ baselineOffset += getCommonBorderPaddingBackground().getPaddingBefore(false, this);
+ }
+ return baselineOffset;
+ }
+
+ /**
+ * Returns the {@link CommonBorderPaddingBackground} instance from the FO handled by this layout manager.
+ */
+ protected abstract CommonBorderPaddingBackground getCommonBorderPaddingBackground();
+
+}
diff --git a/src/java/org/apache/fop/layoutmgr/TraitSetter.java b/src/java/org/apache/fop/layoutmgr/TraitSetter.java
index 739d535ca..af40f0681 100644
--- a/src/java/org/apache/fop/layoutmgr/TraitSetter.java
+++ b/src/java/org/apache/fop/layoutmgr/TraitSetter.java
@@ -617,4 +617,15 @@ public final class TraitSetter {
area.addTrait(Trait.PROD_ID, id);
}
}
+
+ /**
+ * Sets the optional content group layer as a trait on the area.
+ * @param area the area to set the traits on
+ * @param layer the layer ID to set
+ */
+ public static void setLayer(Area area, String layer) {
+ if (layer != null && layer.length() > 0) {
+ area.addTrait(Trait.LAYER, layer);
+ }
+ }
}
diff --git a/src/java/org/apache/fop/layoutmgr/inline/AlignmentContext.java b/src/java/org/apache/fop/layoutmgr/inline/AlignmentContext.java
index c1992965c..192956abc 100644
--- a/src/java/org/apache/fop/layoutmgr/inline/AlignmentContext.java
+++ b/src/java/org/apache/fop/layoutmgr/inline/AlignmentContext.java
@@ -295,7 +295,7 @@ public class AlignmentContext implements Constants {
* Return the dominant baseline identifier.
* @return the dominant baseline identifier
*/
- private int getDominantBaselineIdentifier() {
+ public int getDominantBaselineIdentifier() {
return actualBaselineTable.getDominantBaselineIdentifier();
}
diff --git a/src/java/org/apache/fop/layoutmgr/inline/ContentLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/ContentLayoutManager.java
index b3c768987..c067b040f 100644
--- a/src/java/org/apache/fop/layoutmgr/inline/ContentLayoutManager.java
+++ b/src/java/org/apache/fop/layoutmgr/inline/ContentLayoutManager.java
@@ -332,6 +332,15 @@ public class ContentLayoutManager extends AbstractBaseLayoutManager
return parentLM.getPSLM();
}
+
+ public boolean hasLineAreaDescendant() {
+ return true;
+ }
+
+ public int getBaselineOffset() {
+ return childLM.getBaselineOffset();
+ }
+
// --------- Property Resolution related functions --------- //
/**
diff --git a/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java
new file mode 100644
index 000000000..54237a914
--- /dev/null
+++ b/src/java/org/apache/fop/layoutmgr/inline/InlineContainerLayoutManager.java
@@ -0,0 +1,326 @@
+/*
+ * 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.layoutmgr.inline;
+
+import java.awt.geom.Rectangle2D;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.fop.area.Area;
+import org.apache.fop.area.Trait;
+import org.apache.fop.area.inline.Container;
+import org.apache.fop.area.inline.InlineViewport;
+import org.apache.fop.datatypes.Length;
+import org.apache.fop.datatypes.LengthBase;
+import org.apache.fop.datatypes.SimplePercentBaseContext;
+import org.apache.fop.fo.Constants;
+import org.apache.fop.fo.flow.InlineContainer;
+import org.apache.fop.fo.properties.CommonBorderPaddingBackground;
+import org.apache.fop.fo.properties.LengthRangeProperty;
+import org.apache.fop.fo.properties.Property;
+import org.apache.fop.layoutmgr.AbstractLayoutManager;
+import org.apache.fop.layoutmgr.AreaAdditionUtil;
+import org.apache.fop.layoutmgr.BlockLevelEventProducer;
+import org.apache.fop.layoutmgr.ElementListUtils;
+import org.apache.fop.layoutmgr.InlineKnuthSequence;
+import org.apache.fop.layoutmgr.KnuthPossPosIter;
+import org.apache.fop.layoutmgr.KnuthSequence;
+import org.apache.fop.layoutmgr.LayoutContext;
+import org.apache.fop.layoutmgr.LayoutManager;
+import org.apache.fop.layoutmgr.ListElement;
+import org.apache.fop.layoutmgr.NonLeafPosition;
+import org.apache.fop.layoutmgr.Position;
+import org.apache.fop.layoutmgr.PositionIterator;
+import org.apache.fop.layoutmgr.SpaceResolver;
+import org.apache.fop.layoutmgr.TraitSetter;
+
+/**
+ * This creates a single inline container area after
+ * laying out the child block areas. All footnotes, floats
+ * and id areas are maintained for later retrieval.
+ */
+public class InlineContainerLayoutManager extends AbstractLayoutManager implements InlineLevelLayoutManager {
+
+ private CommonBorderPaddingBackground borderProps;
+ private int contentAreaIPD;
+ private int contentAreaBPD;
+
+ private List<ListElement> childElements;
+ private int ipdOverflow;
+ private AlignmentContext alignmentContext;
+ private InlineViewport currentViewport;
+ private Container referenceArea;
+
+ public InlineContainerLayoutManager(InlineContainer node) {
+ super(node);
+ setGeneratesReferenceArea(true);
+ }
+
+ @Override
+ public void initialize() {
+ InlineContainer node = (InlineContainer) fobj;
+ borderProps = node.getCommonBorderPaddingBackground();
+ }
+
+ private InlineContainer getInlineContainer() {
+ assert fobj instanceof InlineContainer;
+ return (InlineContainer) fobj;
+ }
+
+ @Override
+ public List<KnuthSequence> getNextKnuthElements(LayoutContext context, int alignment) {
+ determineIPD(context);
+ childElements = getChildKnuthElements(context, alignment);
+ determineBPD();
+ alignmentContext = makeAlignmentContext(context);
+ Position position = new Position(this, 0);
+ KnuthSequence knuthSequence = new InlineKnuthSequence();
+ knuthSequence.add(new KnuthInlineBox(contentAreaIPD, alignmentContext, position, false));
+ List<KnuthSequence> knuthElements = new ArrayList<KnuthSequence>(1);
+ knuthElements.add(knuthSequence);
+ setFinished(true);
+ return knuthElements;
+ }
+
+ private void determineIPD(LayoutContext layoutContext) {
+ LengthRangeProperty ipd = getInlineContainer().getInlineProgressionDimension();
+ Property optimum = ipd.getOptimum(this);
+ if (optimum.isAuto()) {
+ contentAreaIPD = layoutContext.getRefIPD();
+ InlineLevelEventProducer eventProducer = InlineLevelEventProducer.Provider.get(
+ fobj.getUserAgent().getEventBroadcaster());
+ eventProducer.inlineContainerAutoIPDNotSupported(this, contentAreaIPD / 1000f);
+ } else {
+ contentAreaIPD = optimum.getLength().getValue(this);
+ }
+ }
+
+ private List<ListElement> getChildKnuthElements(LayoutContext layoutContext, int alignment) {
+ List<ListElement> allChildElements = new LinkedList<ListElement>();
+ LayoutManager childLM;
+ while ((childLM = getChildLM()) != null) {
+ LayoutContext childLC = LayoutContext.offspringOf(layoutContext);
+ childLC.setRefIPD(contentAreaIPD);
+ @SuppressWarnings("unchecked")
+ List<ListElement> childElements = childLM.getNextKnuthElements(childLC, alignment);
+ allChildElements.addAll(childElements);
+ }
+ handleIPDOverflow();
+ wrapPositions(allChildElements);
+ SpaceResolver.resolveElementList(allChildElements);
+ SpaceResolver.performConditionalsNotification(allChildElements, 0, allChildElements.size() - 1, -1);
+ return allChildElements;
+ }
+
+ private void determineBPD() {
+ LengthRangeProperty bpd = getInlineContainer().getBlockProgressionDimension();
+ Property optimum = bpd.getOptimum(this);
+ int actualBPD = ElementListUtils.calcContentLength(childElements);
+ if (optimum.isAuto()) {
+ contentAreaBPD = actualBPD;
+ } else {
+ double bpdValue = optimum.getLength().getNumericValue(this);
+ if (bpdValue < 0) {
+ contentAreaBPD = actualBPD;
+ } else {
+ contentAreaBPD = (int) Math.round(bpdValue);
+ if (contentAreaBPD < actualBPD) {
+ BlockLevelEventProducer eventProducer = getBlockLevelEventProducer();
+ eventProducer.viewportBPDOverflow(this, fobj.getName(),
+ actualBPD - contentAreaBPD, needClip(), canRecoverFromOverflow(),
+ fobj.getLocator());
+ }
+ }
+ }
+ }
+
+ protected AlignmentContext makeAlignmentContext(LayoutContext context) {
+ InlineContainer ic = (InlineContainer) fobj;
+ AlignmentContext ac = new AlignmentContext(contentAreaBPD,
+ ic.getAlignmentAdjust(), ic.getAlignmentBaseline(),
+ ic.getBaselineShift(), ic.getDominantBaseline(),
+ context.getAlignmentContext());
+ int baselineOffset = getAlignmentPoint(ac.getDominantBaselineIdentifier());
+ ac.resizeLine(contentAreaBPD, baselineOffset);
+ return ac;
+ }
+
+ private void handleIPDOverflow() {
+ if (ipdOverflow > 0) {
+ BlockLevelEventProducer eventProducer = getBlockLevelEventProducer();
+ eventProducer.viewportIPDOverflow(this, fobj.getName(),
+ ipdOverflow, needClip(), canRecoverFromOverflow(),
+ fobj.getLocator());
+ }
+ }
+
+ private void wrapPositions(List<ListElement> elements) {
+ for (ListElement element : elements) {
+ Position position = new NonLeafPosition(this, element.getPosition());
+ notifyPos(position);
+ element.setPosition(position);
+ }
+ }
+
+ private BlockLevelEventProducer getBlockLevelEventProducer() {
+ return BlockLevelEventProducer.Provider.get(fobj.getUserAgent().getEventBroadcaster());
+ }
+
+ private boolean canRecoverFromOverflow() {
+ return getInlineContainer().getOverflow() != EN_ERROR_IF_OVERFLOW;
+ }
+
+ private int getAlignmentPoint(int dominantBaseline) {
+ Length alignmentAdjust = getInlineContainer().getAlignmentAdjust();
+ int baseline = alignmentAdjust.getEnum();
+ if (baseline == Constants.EN_AUTO) {
+ return getInlineContainerBaselineOffset(getInlineContainer().getAlignmentBaseline());
+ } else if (baseline == Constants.EN_BASELINE) {
+ return getInlineContainerBaselineOffset(dominantBaseline);
+ } else if (baseline != 0) {
+ return getInlineContainerBaselineOffset(baseline);
+ } else {
+ int baselineOffset = getInlineContainerBaselineOffset(dominantBaseline);
+ int lineHeight = getInlineContainer().getLineHeight().getOptimum(this).getLength().getValue(this);
+ int adjust = alignmentAdjust.getValue(
+ new SimplePercentBaseContext(null, LengthBase.ALIGNMENT_ADJUST, lineHeight));
+ return baselineOffset + adjust;
+ }
+ }
+
+ private int getInlineContainerBaselineOffset(int property) {
+ switch (property) {
+ case Constants.EN_BEFORE_EDGE:
+ case Constants.EN_TEXT_BEFORE_EDGE:
+ return 0;
+ case Constants.EN_AFTER_EDGE:
+ case Constants.EN_TEXT_AFTER_EDGE:
+ return contentAreaBPD;
+ case Constants.EN_MIDDLE:
+ case Constants.EN_CENTRAL:
+ case Constants.EN_MATHEMATICAL:
+ return contentAreaBPD / 2;
+ case Constants.EN_IDEOGRAPHIC:
+ return contentAreaBPD * 7 / 10;
+ case Constants.EN_ALPHABETIC:
+ return contentAreaBPD * 6 / 10;
+ case Constants.EN_HANGING:
+ return contentAreaBPD * 2 / 10;
+ case Constants.EN_AUTO:
+ case Constants.EN_BASELINE:
+ return hasLineAreaDescendant() ? getBaselineOffset() : contentAreaBPD;
+ default:
+ throw new AssertionError("Unknown baseline value: " + property);
+ }
+ }
+
+ @Override
+ public void addAreas(PositionIterator posIter, LayoutContext context) {
+ Position inlineContainerPosition = null;
+ while (posIter.hasNext()) {
+ /*
+ * Should iterate only once, but hasNext must be called twice for its
+ * side-effects to apply and the iterator to switch to the next LM.
+ */
+ assert inlineContainerPosition == null;
+ inlineContainerPosition = posIter.next();
+ assert inlineContainerPosition.getLM() == this;
+ }
+ assert inlineContainerPosition != null;
+ KnuthPossPosIter childPosIter = new KnuthPossPosIter(childElements);
+ AreaAdditionUtil.addAreas(this, childPosIter, context);
+ }
+
+ @Override
+ public Area getParentArea(Area childArea) {
+ if (referenceArea == null) {
+ referenceArea = new Container();
+ referenceArea.addTrait(Trait.IS_REFERENCE_AREA, Boolean.TRUE);
+ TraitSetter.setProducerID(referenceArea, fobj.getId());
+ referenceArea.setIPD(contentAreaIPD);
+ currentViewport = new InlineViewport(referenceArea);
+ currentViewport.addTrait(Trait.IS_VIEWPORT_AREA, Boolean.TRUE);
+ TraitSetter.setProducerID(currentViewport, fobj.getId());
+ currentViewport.setBlockProgressionOffset(alignmentContext.getOffset());
+ currentViewport.setIPD(getContentAreaIPD());
+ currentViewport.setBPD(getContentAreaBPD());
+ TraitSetter.addBackground(currentViewport, borderProps, this);
+ currentViewport.setClip(needClip());
+ currentViewport.setContentPosition(
+ new Rectangle2D.Float(0, 0, getContentAreaIPD(), getContentAreaBPD()));
+ getParent().addChildArea(currentViewport);
+ }
+ return referenceArea;
+ }
+
+ @Override
+ public int getContentAreaIPD() {
+ return contentAreaIPD;
+ }
+
+ @Override
+ public int getContentAreaBPD() {
+ return contentAreaBPD;
+ }
+
+ @Override
+ public void addChildArea(Area childArea) {
+ referenceArea.addChildArea(childArea);
+ }
+
+ private boolean needClip() {
+ int overflow = getInlineContainer().getOverflow();
+ return (overflow == EN_HIDDEN || overflow == EN_ERROR_IF_OVERFLOW);
+ }
+
+ public boolean handleOverflow(int milliPoints) {
+ ipdOverflow = Math.max(ipdOverflow, milliPoints);
+ return true;
+ }
+
+ public List addALetterSpaceTo(List oldList) {
+ return oldList;
+ }
+
+ public List addALetterSpaceTo(List oldList, int depth) {
+ return oldList;
+ }
+
+ public String getWordChars(Position pos) {
+ return "";
+ }
+
+ public void hyphenate(Position pos, HyphContext hyphContext) {
+ }
+
+ public boolean applyChanges(List oldList) {
+ return false;
+ }
+
+ public boolean applyChanges(List oldList, int depth) {
+ return false;
+ }
+
+ public List getChangedKnuthElements(List oldList, int alignment, int depth) {
+ return oldList;
+ }
+
+}
diff --git a/src/java/org/apache/fop/layoutmgr/inline/InlineLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/InlineLayoutManager.java
index dd80db1d1..61dcf45c9 100644
--- a/src/java/org/apache/fop/layoutmgr/inline/InlineLayoutManager.java
+++ b/src/java/org/apache/fop/layoutmgr/inline/InlineLayoutManager.java
@@ -213,6 +213,7 @@ public class InlineLayoutManager extends InlineStackingLayoutManager {
}
if (fobj instanceof Inline || fobj instanceof BasicLink) {
TraitSetter.setProducerID(area, fobj.getId());
+ TraitSetter.setLayer(area, fobj.getLayer());
}
return area;
}
diff --git a/src/java/org/apache/fop/layoutmgr/inline/InlineLevelEventProducer.java b/src/java/org/apache/fop/layoutmgr/inline/InlineLevelEventProducer.java
index 15284ae0a..332e14935 100644
--- a/src/java/org/apache/fop/layoutmgr/inline/InlineLevelEventProducer.java
+++ b/src/java/org/apache/fop/layoutmgr/inline/InlineLevelEventProducer.java
@@ -67,4 +67,13 @@ public interface InlineLevelEventProducer extends EventProducer {
*/
void lineOverflows(Object source, String elementName, int line, int overflowLength, Locator loc);
+ /**
+ * Auto IPD on inline-container is not supported.
+ *
+ * @param source the event source
+ * @param fallback the value in points that will be used as a fallback
+ * @event.severity WARN
+ */
+ void inlineContainerAutoIPDNotSupported(Object source, float fallback);
+
}
diff --git a/src/java/org/apache/fop/layoutmgr/inline/InlineLevelEventProducer.xml b/src/java/org/apache/fop/layoutmgr/inline/InlineLevelEventProducer.xml
index 66d352eb7..8d699f6bc 100644
--- a/src/java/org/apache/fop/layoutmgr/inline/InlineLevelEventProducer.xml
+++ b/src/java/org/apache/fop/layoutmgr/inline/InlineLevelEventProducer.xml
@@ -20,4 +20,5 @@
<message key="locator">[ (See position {loc})| (See {#gatherContextInfo})| (No context info available)]</message>
<message key="leaderWithoutContent">fo:leader is set to "use-content" but has no content.{{locator}}</message>
<message key="lineOverflows">The contents of {elementName} line {line} exceed the available area in the inline-progression direction by {overflowLength,choice,50000#{overflowLength} millipoints|50000&lt;more than 50 points}.{{locator}}</message>
+ <message key="inlineContainerAutoIPDNotSupported">A value of "auto" for the inline-progression-dimension property on fo:inline-container is not supported. Falling back to {fallback}pt.{{locator}}</message>
</catalogue>
diff --git a/src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java
index b3987a075..25d8c0872 100644
--- a/src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java
+++ b/src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java
@@ -160,6 +160,8 @@ public class LineLayoutManager extends InlineStackingLayoutManager
private final int follow;
private AlignmentContext alignmentContext;
+ private int baselineOffset = -1;
+
private List<KnuthSequence> knuthParagraphs;
private LineLayoutPossibilities lineLayouts;
@@ -556,7 +558,6 @@ public class LineLayoutManager extends InlineStackingLayoutManager
private int constantLineHeight = 12000;
-
/**
* Create a new Line Layout Manager.
* This is used by the block layout manager to create
@@ -939,7 +940,11 @@ public class LineLayoutManager extends InlineStackingLayoutManager
while (listIter.hasNext()) {
ListElement tempElement;
tempElement = (ListElement) listIter.next();
- if (tempElement.getLayoutManager() != this) {
+ LayoutManager lm = tempElement.getLayoutManager();
+ if (baselineOffset < 0 && lm != null && lm.hasLineAreaDescendant()) {
+ baselineOffset = lm.getBaselineOffset();
+ }
+ if (lm != this) {
tempElement.setPosition(notifyPos(new NonLeafPosition(this,
tempElement.getPosition())));
}
@@ -987,6 +992,9 @@ public class LineLayoutManager extends InlineStackingLayoutManager
}
startIndex = endIndex + 1;
LineBreakPosition lbp = (LineBreakPosition) llPoss.getChosenPosition(i);
+ if (baselineOffset < 0) {
+ baselineOffset = lbp.spaceBefore + lbp.baseline;
+ }
returnList.add(new KnuthBlockBox(
lbp.lineHeight + lbp.spaceBefore + lbp.spaceAfter,
footnoteList, lbp, false));
@@ -1424,6 +1432,16 @@ public class LineLayoutManager extends InlineStackingLayoutManager
}
}
+ @Override
+ public boolean hasLineAreaDescendant() {
+ return true;
+ }
+
+ @Override
+ public int getBaselineOffset() {
+ return baselineOffset;
+ }
+
/**
* Add the areas with the break points.
*
diff --git a/src/java/org/apache/fop/layoutmgr/list/ListBlockLayoutManager.java b/src/java/org/apache/fop/layoutmgr/list/ListBlockLayoutManager.java
index 61d8a891d..062a67b38 100644
--- a/src/java/org/apache/fop/layoutmgr/list/ListBlockLayoutManager.java
+++ b/src/java/org/apache/fop/layoutmgr/list/ListBlockLayoutManager.java
@@ -28,16 +28,15 @@ import org.apache.commons.logging.LogFactory;
import org.apache.fop.area.Area;
import org.apache.fop.area.Block;
import org.apache.fop.fo.flow.ListBlock;
+import org.apache.fop.fo.properties.CommonBorderPaddingBackground;
import org.apache.fop.fo.properties.KeepProperty;
-import org.apache.fop.layoutmgr.BlockStackingLayoutManager;
-import org.apache.fop.layoutmgr.ConditionalElementListener;
import org.apache.fop.layoutmgr.ElementListUtils;
import org.apache.fop.layoutmgr.LayoutContext;
import org.apache.fop.layoutmgr.LayoutManager;
import org.apache.fop.layoutmgr.NonLeafPosition;
import org.apache.fop.layoutmgr.Position;
import org.apache.fop.layoutmgr.PositionIterator;
-import org.apache.fop.layoutmgr.RelSide;
+import org.apache.fop.layoutmgr.SpacedBorderedPaddedBlockLayoutManager;
import org.apache.fop.layoutmgr.TraitSetter;
import org.apache.fop.traits.MinOptMax;
import org.apache.fop.traits.SpaceVal;
@@ -47,21 +46,13 @@ import org.apache.fop.traits.SpaceVal;
* A list block contains list items which are stacked within
* the list block area..
*/
-public class ListBlockLayoutManager extends BlockStackingLayoutManager
- implements ConditionalElementListener {
+public class ListBlockLayoutManager extends SpacedBorderedPaddedBlockLayoutManager {
/** logging instance */
private static Log log = LogFactory.getLog(ListBlockLayoutManager.class);
private Block curBlockArea;
- private boolean discardBorderBefore;
- private boolean discardBorderAfter;
- private boolean discardPaddingBefore;
- private boolean discardPaddingAfter;
- private MinOptMax effSpaceBefore;
- private MinOptMax effSpaceAfter;
-
/**
* Create a new list block layout manager.
* @param node list-block to create the layout manager for
@@ -70,6 +61,11 @@ public class ListBlockLayoutManager extends BlockStackingLayoutManager
super(node);
}
+ @Override
+ protected CommonBorderPaddingBackground getCommonBorderPaddingBackground() {
+ return getListBlockFO().getCommonBorderPaddingBackground();
+ }
+
/**
* Convenience method.
* @return the ListBlock node
@@ -242,6 +238,8 @@ public class ListBlockLayoutManager extends BlockStackingLayoutManager
int contentIPD = referenceIPD - getIPIndents();
curBlockArea.setIPD(contentIPD);
+ curBlockArea.setBidiLevel(getListBlockFO().getBidiLevel());
+
setCurrentArea(curBlockArea);
}
return curBlockArea;
@@ -277,50 +275,5 @@ public class ListBlockLayoutManager extends BlockStackingLayoutManager
return getListBlockFO().getKeepWithNext();
}
- /** {@inheritDoc} */
- public void notifySpace(RelSide side, MinOptMax effectiveLength) {
- if (RelSide.BEFORE == side) {
- if (log.isDebugEnabled()) {
- log.debug(this + ": Space " + side + ", "
- + this.effSpaceBefore + "-> " + effectiveLength);
- }
- this.effSpaceBefore = effectiveLength;
- } else {
- if (log.isDebugEnabled()) {
- log.debug(this + ": Space " + side + ", "
- + this.effSpaceAfter + "-> " + effectiveLength);
- }
- this.effSpaceAfter = effectiveLength;
- }
- }
-
- /** {@inheritDoc} */
- public void notifyBorder(RelSide side, MinOptMax effectiveLength) {
- if (effectiveLength == null) {
- if (RelSide.BEFORE == side) {
- this.discardBorderBefore = true;
- } else {
- this.discardBorderAfter = true;
- }
- }
- if (log.isDebugEnabled()) {
- log.debug(this + ": Border " + side + " -> " + effectiveLength);
- }
- }
-
- /** {@inheritDoc} */
- public void notifyPadding(RelSide side, MinOptMax effectiveLength) {
- if (effectiveLength == null) {
- if (RelSide.BEFORE == side) {
- this.discardPaddingBefore = true;
- } else {
- this.discardPaddingAfter = true;
- }
- }
- if (log.isDebugEnabled()) {
- log.debug(this + ": Padding " + side + " -> " + effectiveLength);
- }
- }
-
}
diff --git a/src/java/org/apache/fop/layoutmgr/list/ListItemContentLayoutManager.java b/src/java/org/apache/fop/layoutmgr/list/ListItemContentLayoutManager.java
index f017da381..b16c9dfc4 100644
--- a/src/java/org/apache/fop/layoutmgr/list/ListItemContentLayoutManager.java
+++ b/src/java/org/apache/fop/layoutmgr/list/ListItemContentLayoutManager.java
@@ -181,6 +181,7 @@ public class ListItemContentLayoutManager extends BlockStackingLayoutManager imp
//TODO: Check - itemIPD never set?
curBlockArea.setIPD(itemIPD);
//curBlockArea.setHeight();
+ curBlockArea.setBidiLevel(getPartFO().getBidiLevel());
TraitSetter.setProducerID(curBlockArea, getPartFO().getId());
diff --git a/src/java/org/apache/fop/layoutmgr/list/ListItemLayoutManager.java b/src/java/org/apache/fop/layoutmgr/list/ListItemLayoutManager.java
index 083e4ee1b..773506632 100644
--- a/src/java/org/apache/fop/layoutmgr/list/ListItemLayoutManager.java
+++ b/src/java/org/apache/fop/layoutmgr/list/ListItemLayoutManager.java
@@ -32,12 +32,11 @@ import org.apache.fop.area.Block;
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.properties.CommonBorderPaddingBackground;
import org.apache.fop.fo.properties.KeepProperty;
-import org.apache.fop.layoutmgr.BlockStackingLayoutManager;
import org.apache.fop.layoutmgr.BreakElement;
import org.apache.fop.layoutmgr.BreakOpportunity;
import org.apache.fop.layoutmgr.BreakOpportunityHelper;
-import org.apache.fop.layoutmgr.ConditionalElementListener;
import org.apache.fop.layoutmgr.ElementListObserver;
import org.apache.fop.layoutmgr.ElementListUtils;
import org.apache.fop.layoutmgr.FootnoteBodyLayoutManager;
@@ -53,10 +52,9 @@ import org.apache.fop.layoutmgr.ListElement;
import org.apache.fop.layoutmgr.NonLeafPosition;
import org.apache.fop.layoutmgr.Position;
import org.apache.fop.layoutmgr.PositionIterator;
-import org.apache.fop.layoutmgr.RelSide;
import org.apache.fop.layoutmgr.SpaceResolver;
+import org.apache.fop.layoutmgr.SpacedBorderedPaddedBlockLayoutManager;
import org.apache.fop.layoutmgr.TraitSetter;
-import org.apache.fop.traits.MinOptMax;
import org.apache.fop.traits.SpaceVal;
import org.apache.fop.util.BreakUtil;
@@ -64,8 +62,8 @@ import org.apache.fop.util.BreakUtil;
* LayoutManager for a list-item FO.
* The list item contains a list item label and a list item body.
*/
-public class ListItemLayoutManager extends BlockStackingLayoutManager implements ConditionalElementListener,
- BreakOpportunity {
+public class ListItemLayoutManager extends SpacedBorderedPaddedBlockLayoutManager
+ implements BreakOpportunity {
/** logging instance */
private static Log log = LogFactory.getLog(ListItemLayoutManager.class);
@@ -78,13 +76,6 @@ public class ListItemLayoutManager extends BlockStackingLayoutManager implements
private List<ListElement> labelList = null;
private List<ListElement> bodyList = null;
- private boolean discardBorderBefore;
- private boolean discardBorderAfter;
- private boolean discardPaddingBefore;
- private boolean discardPaddingAfter;
- private MinOptMax effSpaceBefore;
- private MinOptMax effSpaceAfter;
-
private Keep keepWithNextPendingOnLabel;
private Keep keepWithNextPendingOnBody;
@@ -145,6 +136,11 @@ public class ListItemLayoutManager extends BlockStackingLayoutManager implements
setBody(node.getBody());
}
+ @Override
+ protected CommonBorderPaddingBackground getCommonBorderPaddingBackground() {
+ return getListItemFO().getCommonBorderPaddingBackground();
+ }
+
/**
* Convenience method.
* @return the ListBlock node
@@ -475,6 +471,23 @@ public class ListItemLayoutManager extends BlockStackingLayoutManager implements
return returnedList;
}
+
+ @Override
+ public boolean hasLineAreaDescendant() {
+ return label.hasLineAreaDescendant() || body.hasLineAreaDescendant();
+ }
+
+ @Override
+ public int getBaselineOffset() {
+ if (label.hasLineAreaDescendant()) {
+ return label.getBaselineOffset();
+ } else if (body.hasLineAreaDescendant()) {
+ return body.getBaselineOffset();
+ } else {
+ throw newNoLineAreaDescendantException();
+ }
+ }
+
/**
* Add the areas for the break points.
*
@@ -615,6 +628,8 @@ public class ListItemLayoutManager extends BlockStackingLayoutManager implements
int contentIPD = referenceIPD - getIPIndents();
curBlockArea.setIPD(contentIPD);
+ curBlockArea.setBidiLevel(fo.getBidiLevel());
+
setCurrentArea(curBlockArea);
}
return curBlockArea;
@@ -653,51 +668,6 @@ public class ListItemLayoutManager extends BlockStackingLayoutManager implements
}
/** {@inheritDoc} */
- public void notifySpace(RelSide side, MinOptMax effectiveLength) {
- if (RelSide.BEFORE == side) {
- if (log.isDebugEnabled()) {
- log.debug(this + ": Space " + side + ", "
- + this.effSpaceBefore + "-> " + effectiveLength);
- }
- this.effSpaceBefore = effectiveLength;
- } else {
- if (log.isDebugEnabled()) {
- log.debug(this + ": Space " + side + ", "
- + this.effSpaceAfter + "-> " + effectiveLength);
- }
- this.effSpaceAfter = effectiveLength;
- }
- }
-
- /** {@inheritDoc} */
- public void notifyBorder(RelSide side, MinOptMax effectiveLength) {
- if (effectiveLength == null) {
- if (RelSide.BEFORE == side) {
- this.discardBorderBefore = true;
- } else {
- this.discardBorderAfter = true;
- }
- }
- if (log.isDebugEnabled()) {
- log.debug(this + ": Border " + side + " -> " + effectiveLength);
- }
- }
-
- /** {@inheritDoc} */
- public void notifyPadding(RelSide side, MinOptMax effectiveLength) {
- if (effectiveLength == null) {
- if (RelSide.BEFORE == side) {
- this.discardPaddingBefore = true;
- } else {
- this.discardPaddingAfter = true;
- }
- }
- if (log.isDebugEnabled()) {
- log.debug(this + ": Padding " + side + " -> " + effectiveLength);
- }
- }
-
- /** {@inheritDoc} */
@Override
public void reset() {
super.reset();
diff --git a/src/java/org/apache/fop/layoutmgr/table/TableAndCaptionLayoutManager.java b/src/java/org/apache/fop/layoutmgr/table/TableAndCaptionLayoutManager.java
index 0582a0283..aaa896ce3 100644
--- a/src/java/org/apache/fop/layoutmgr/table/TableAndCaptionLayoutManager.java
+++ b/src/java/org/apache/fop/layoutmgr/table/TableAndCaptionLayoutManager.java
@@ -184,6 +184,7 @@ public class TableAndCaptionLayoutManager extends BlockStackingLayoutManager {
Area parentArea = parentLayoutManager.getParentArea(curBlockArea);
int referenceIPD = parentArea.getIPD();
curBlockArea.setIPD(referenceIPD);
+ curBlockArea.setBidiLevel(getTableAndCaptionFO().getBidiLevel());
// Get reference IPD from parentArea
setCurrentArea(curBlockArea); // ??? for generic operations
}
diff --git a/src/java/org/apache/fop/layoutmgr/table/TableCaptionLayoutManager.java b/src/java/org/apache/fop/layoutmgr/table/TableCaptionLayoutManager.java
index 66f7ad9f2..8823c0fae 100644
--- a/src/java/org/apache/fop/layoutmgr/table/TableCaptionLayoutManager.java
+++ b/src/java/org/apache/fop/layoutmgr/table/TableCaptionLayoutManager.java
@@ -180,6 +180,7 @@ public class TableCaptionLayoutManager extends BlockStackingLayoutManager {
Area parentArea = parentLayoutManager.getParentArea(curBlockArea);
int referenceIPD = parentArea.getIPD();
curBlockArea.setIPD(referenceIPD);
+ curBlockArea.setBidiLevel(getTableCaptionFO().getBidiLevel());
// Get reference IPD from parentArea
setCurrentArea(curBlockArea); // ??? for generic operations
}
diff --git a/src/java/org/apache/fop/layoutmgr/table/TableCellLayoutManager.java b/src/java/org/apache/fop/layoutmgr/table/TableCellLayoutManager.java
index c8f2cea85..b2851c1b0 100644
--- a/src/java/org/apache/fop/layoutmgr/table/TableCellLayoutManager.java
+++ b/src/java/org/apache/fop/layoutmgr/table/TableCellLayoutManager.java
@@ -117,6 +117,7 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager
*/
public TableCellLayoutManager(TableCell node, PrimaryGridUnit pgu) {
super(node);
+ setGeneratesBlockArea(true);
this.primaryGridUnit = pgu;
this.isDescendantOfTableHeader = node.getParent().getParent() instanceof TableHeader
|| node.getParent() instanceof TableHeader;
@@ -441,12 +442,14 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager
Block[][] blocks = new Block[getTableCell().getNumberRowsSpanned()][getTableCell()
.getNumberColumnsSpanned()];
GridUnit[] gridUnits = (GridUnit[]) primaryGridUnit.getRows().get(startRow);
+ int level = getTableCell().getBidiLevelRecursive();
for (int x = 0; x < getTableCell().getNumberColumnsSpanned(); x++) {
GridUnit gu = gridUnits[x];
BorderInfo border = gu.getBorderBefore(borderBeforeWhich);
int borderWidth = border.getRetainedWidth() / 2;
if (borderWidth > 0) {
- addBorder(blocks, startRow, x, Trait.BORDER_BEFORE, border, firstOnPage);
+ addBorder(blocks, startRow, x, Trait.BORDER_BEFORE, border,
+ firstOnPage, level);
adjustYOffset(blocks[startRow][x], -borderWidth);
adjustBPD(blocks[startRow][x], -borderWidth);
}
@@ -457,7 +460,8 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager
BorderInfo border = gu.getBorderAfter(borderAfterWhich);
int borderWidth = border.getRetainedWidth() / 2;
if (borderWidth > 0) {
- addBorder(blocks, endRow, x, Trait.BORDER_AFTER, border, lastOnPage);
+ addBorder(blocks, endRow, x, Trait.BORDER_AFTER, border,
+ lastOnPage, level);
adjustBPD(blocks[endRow][x], -borderWidth);
}
}
@@ -466,7 +470,8 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager
BorderInfo border = gridUnits[0].getBorderStart();
int borderWidth = border.getRetainedWidth() / 2;
if (borderWidth > 0) {
- addBorder(blocks, y, 0, Trait.BORDER_START, border, inFirstColumn);
+ addBorder(blocks, y, 0, Trait.BORDER_START, border,
+ inFirstColumn, level);
adjustXOffset(blocks[y][0], borderWidth);
adjustIPD(blocks[y][0], -borderWidth);
}
@@ -474,7 +479,7 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager
borderWidth = border.getRetainedWidth() / 2;
if (borderWidth > 0) {
addBorder(blocks, y, gridUnits.length - 1, Trait.BORDER_END, border,
- inLastColumn);
+ inLastColumn, level);
adjustIPD(blocks[y][gridUnits.length - 1], -borderWidth);
}
}
@@ -511,10 +516,12 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager
if (getTableCell().getDisplayAlign() == EN_CENTER) {
Block space = new Block();
space.setBPD((cellBPD - usedBPD) / 2);
+ space.setBidiLevel(getTableCell().getBidiLevelRecursive());
curBlockArea.addBlock(space);
} else if (getTableCell().getDisplayAlign() == EN_AFTER) {
Block space = new Block();
space.setBPD(cellBPD - usedBPD);
+ space.setBidiLevel(getTableCell().getBidiLevelRecursive());
curBlockArea.addBlock(space);
}
}
@@ -590,11 +597,12 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager
}
private void addBorder(Block[][] blocks, int i, int j, Integer side, BorderInfo border,
- boolean outer) {
+ boolean outer, int level) {
if (blocks[i][j] == null) {
blocks[i][j] = new Block();
blocks[i][j].addTrait(Trait.IS_REFERENCE_AREA, Boolean.TRUE);
blocks[i][j].setPositioning(Block.ABSOLUTE);
+ blocks[i][j].setBidiLevel(level);
}
blocks[i][j].addTrait(side, BorderProps.makeRectangular(border.getStyle(),
border.getRetainedWidth(), border.getColor(),
@@ -629,6 +637,7 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager
block.setBPD(bpd);
block.setXOffset(xoffset + startIndent - paddingStart);
block.setYOffset(yoffset + borderBeforeWidth);
+ block.setBidiLevel(getTableCell().getBidiLevelRecursive());
return block;
}
@@ -654,6 +663,7 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager
curBlockArea.setXOffset(xoffset + startIndent);
curBlockArea.setYOffset(yoffset);
curBlockArea.setIPD(cellIPD);
+ curBlockArea.setBidiLevel(getTableCell().getBidiLevelRecursive());
/*Area parentArea =*/ parentLayoutManager.getParentArea(curBlockArea);
// Get reference IPD from parentArea
diff --git a/src/java/org/apache/fop/layoutmgr/table/TableLayoutManager.java b/src/java/org/apache/fop/layoutmgr/table/TableLayoutManager.java
index 7f1754064..afb6547c0 100644
--- a/src/java/org/apache/fop/layoutmgr/table/TableLayoutManager.java
+++ b/src/java/org/apache/fop/layoutmgr/table/TableLayoutManager.java
@@ -39,12 +39,11 @@ import org.apache.fop.fo.flow.Markers;
import org.apache.fop.fo.flow.RetrieveTableMarker;
import org.apache.fop.fo.flow.table.Table;
import org.apache.fop.fo.flow.table.TableColumn;
+import org.apache.fop.fo.properties.CommonBorderPaddingBackground;
import org.apache.fop.fo.properties.KeepProperty;
import org.apache.fop.layoutmgr.BlockLevelEventProducer;
-import org.apache.fop.layoutmgr.BlockStackingLayoutManager;
import org.apache.fop.layoutmgr.BreakElement;
import org.apache.fop.layoutmgr.BreakOpportunity;
-import org.apache.fop.layoutmgr.ConditionalElementListener;
import org.apache.fop.layoutmgr.KnuthElement;
import org.apache.fop.layoutmgr.KnuthGlue;
import org.apache.fop.layoutmgr.LayoutContext;
@@ -52,7 +51,7 @@ import org.apache.fop.layoutmgr.LeafPosition;
import org.apache.fop.layoutmgr.ListElement;
import org.apache.fop.layoutmgr.Position;
import org.apache.fop.layoutmgr.PositionIterator;
-import org.apache.fop.layoutmgr.RelSide;
+import org.apache.fop.layoutmgr.SpacedBorderedPaddedBlockLayoutManager;
import org.apache.fop.layoutmgr.TraitSetter;
import org.apache.fop.traits.MinOptMax;
import org.apache.fop.traits.SpaceVal;
@@ -66,8 +65,8 @@ import org.apache.fop.util.BreakUtil;
* The table then creates areas for the columns, bodies and rows
* the render background.
*/
-public class TableLayoutManager extends BlockStackingLayoutManager
- implements ConditionalElementListener, BreakOpportunity {
+public class TableLayoutManager extends SpacedBorderedPaddedBlockLayoutManager
+ implements BreakOpportunity {
/**
* logging instance
@@ -82,13 +81,6 @@ public class TableLayoutManager extends BlockStackingLayoutManager
private double tableUnit;
private boolean autoLayout = true;
- private boolean discardBorderBefore;
- private boolean discardBorderAfter;
- private boolean discardPaddingBefore;
- private boolean discardPaddingAfter;
- private MinOptMax effSpaceBefore;
- private MinOptMax effSpaceAfter;
-
private int halfBorderSeparationBPD;
private int halfBorderSeparationIPD;
@@ -132,6 +124,13 @@ public class TableLayoutManager extends BlockStackingLayoutManager
this.columns = new ColumnSetup(node);
}
+
+ @Override
+ protected CommonBorderPaddingBackground getCommonBorderPaddingBackground() {
+ return getTable().getCommonBorderPaddingBackground();
+ }
+
+
/** @return the table FO */
public Table getTable() {
return (Table)this.fobj;
@@ -522,51 +521,6 @@ public class TableLayoutManager extends BlockStackingLayoutManager
}
/** {@inheritDoc} */
- public void notifySpace(RelSide side, MinOptMax effectiveLength) {
- if (RelSide.BEFORE == side) {
- if (log.isDebugEnabled()) {
- log.debug(this + ": Space " + side + ", "
- + this.effSpaceBefore + "-> " + effectiveLength);
- }
- this.effSpaceBefore = effectiveLength;
- } else {
- if (log.isDebugEnabled()) {
- log.debug(this + ": Space " + side + ", "
- + this.effSpaceAfter + "-> " + effectiveLength);
- }
- this.effSpaceAfter = effectiveLength;
- }
- }
-
- /** {@inheritDoc} */
- public void notifyBorder(RelSide side, MinOptMax effectiveLength) {
- if (effectiveLength == null) {
- if (RelSide.BEFORE == side) {
- this.discardBorderBefore = true;
- } else {
- this.discardBorderAfter = true;
- }
- }
- if (log.isDebugEnabled()) {
- log.debug(this + ": Border " + side + " -> " + effectiveLength);
- }
- }
-
- /** {@inheritDoc} */
- public void notifyPadding(RelSide side, MinOptMax effectiveLength) {
- if (effectiveLength == null) {
- if (RelSide.BEFORE == side) {
- this.discardPaddingBefore = true;
- } else {
- this.discardPaddingAfter = true;
- }
- }
- if (log.isDebugEnabled()) {
- log.debug(this + ": Padding " + side + " -> " + effectiveLength);
- }
- }
-
- /** {@inheritDoc} */
public void reset() {
super.reset();
curBlockArea = null;
diff --git a/src/java/org/apache/fop/pdf/AbstractPDFStream.java b/src/java/org/apache/fop/pdf/AbstractPDFStream.java
index 41eed4885..13bd1bda1 100644
--- a/src/java/org/apache/fop/pdf/AbstractPDFStream.java
+++ b/src/java/org/apache/fop/pdf/AbstractPDFStream.java
@@ -143,7 +143,7 @@ public abstract class AbstractPDFStream extends PDFObject {
*/
protected int outputStreamData(StreamCache encodedStream, OutputStream out) throws IOException {
int length = 0;
- byte[] p = encode("stream\n");
+ byte[] p = encode("\nstream\n");
out.write(p);
length += p.length;
@@ -186,7 +186,7 @@ public abstract class AbstractPDFStream extends PDFObject {
throws IOException {
int bytesWritten = 0;
//Stream header
- byte[] buf = encode("stream\n");
+ byte[] buf = encode("\nstream\n");
out.write(buf);
bytesWritten += buf.length;
diff --git a/src/java/org/apache/fop/pdf/PDFCFFStreamType0C.java b/src/java/org/apache/fop/pdf/PDFCFFStreamType0C.java
new file mode 100644
index 000000000..53f0b36b4
--- /dev/null
+++ b/src/java/org/apache/fop/pdf/PDFCFFStreamType0C.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.pdf;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * PDFStream for embeddable OpenType CFF fonts.
+ */
+public class PDFCFFStreamType0C extends AbstractPDFFontStream {
+
+ private byte[] cffData;
+ private boolean fullEmbed;
+
+ /**
+ * Main constructor
+ * @param fullEmbed Determines whether the font is fully embedded
+ */
+ public PDFCFFStreamType0C(boolean fullEmbed) {
+ super();
+ this.fullEmbed = fullEmbed;
+ }
+
+ protected int getSizeHint() throws IOException {
+ if (this.cffData != null) {
+ return cffData.length;
+ } else {
+ return 0; //no hint available
+ }
+ }
+
+ /** {@inheritDoc} */
+ protected void outputRawStreamData(OutputStream out) throws IOException {
+ out.write(this.cffData);
+ }
+
+ /** {@inheritDoc} */
+ protected void populateStreamDict(Object lengthEntry) {
+ String type = (fullEmbed) ? "OpenType" : "CIDFontType0C";
+ put("Subtype", new PDFName(type));
+ super.populateStreamDict(lengthEntry);
+ }
+
+ /**
+ * Sets the CFF font data.
+ * @param data the font payload
+ * @param size size of the payload
+ * @throws IOException in case of an I/O problem
+ */
+ public void setData(byte[] data, int size) throws IOException {
+ this.cffData = new byte[size];
+ System.arraycopy(data, 0, this.cffData, 0, size);
+ }
+
+}
+
diff --git a/src/java/org/apache/fop/pdf/PDFDictionary.java b/src/java/org/apache/fop/pdf/PDFDictionary.java
index 6bacd31a3..ae0b950fd 100644
--- a/src/java/org/apache/fop/pdf/PDFDictionary.java
+++ b/src/java/org/apache/fop/pdf/PDFDictionary.java
@@ -131,7 +131,7 @@ public class PDFDictionary extends PDFObject {
} else {
textBuffer.append('\n');
}
- textBuffer.append(">>\n");
+ textBuffer.append(">>");
}
}
diff --git a/src/java/org/apache/fop/pdf/PDFDocument.java b/src/java/org/apache/fop/pdf/PDFDocument.java
index ff9f61201..bcd54fcb9 100644
--- a/src/java/org/apache/fop/pdf/PDFDocument.java
+++ b/src/java/org/apache/fop/pdf/PDFDocument.java
@@ -155,6 +155,12 @@ public class PDFDocument {
private List<PDFLaunch> launches = new ArrayList<PDFLaunch>();
+ private List<PDFLayer> layers;
+
+ private List<PDFNavigator> navigators;
+
+ private List<PDFNavigatorAction> navigatorActions;
+
private PDFFactory factory;
private FileIDGenerator fileIDGenerator;
@@ -477,6 +483,24 @@ public class PDFDocument {
if (obj instanceof PDFGoToRemote) {
this.gotoremotes.add((PDFGoToRemote) obj);
}
+ if (obj instanceof PDFLayer) {
+ if (this.layers == null) {
+ this.layers = new ArrayList<PDFLayer>();
+ }
+ this.layers.add((PDFLayer) obj);
+ }
+ if (obj instanceof PDFNavigator) {
+ if (this.navigators == null) {
+ this.navigators = new ArrayList<PDFNavigator>();
+ }
+ this.navigators.add((PDFNavigator) obj);
+ }
+ if (obj instanceof PDFNavigatorAction) {
+ if (this.navigatorActions == null) {
+ this.navigatorActions = new ArrayList<PDFNavigatorAction>();
+ }
+ this.navigatorActions.add((PDFNavigatorAction) obj);
+ }
}
/**
@@ -890,6 +914,34 @@ public class PDFDocument {
}
/**
+ *
+ */
+ public PDFReference resolveExtensionReference(String id) {
+ if (layers != null) {
+ for (PDFLayer layer : layers) {
+ if (layer.hasId(id)) {
+ return layer.makeReference();
+ }
+ }
+ }
+ if (navigators != null) {
+ for (PDFNavigator navigator : navigators) {
+ if (navigator.hasId(id)) {
+ return navigator.makeReference();
+ }
+ }
+ }
+ if (navigatorActions != null) {
+ for (PDFNavigatorAction action : navigatorActions) {
+ if (action.hasId(id)) {
+ return action.makeReference();
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
* Writes out the entire document
*
* @param stream the OutputStream to output the document to
@@ -1009,7 +1061,7 @@ public class PDFDocument {
streamIndirectObjects(trailerObjects, stream);
TrailerDictionary trailerDictionary = createTrailerDictionary();
long startxref = trailerOutputHelper.outputCrossReferenceObject(stream, trailerDictionary);
- String trailer = "startxref\n" + startxref + "\n%%EOF\n";
+ String trailer = "\nstartxref\n" + startxref + "\n%%EOF\n";
stream.write(encode(trailer));
}
diff --git a/src/java/org/apache/fop/pdf/PDFFactory.java b/src/java/org/apache/fop/pdf/PDFFactory.java
index 1756f1d56..070630274 100644
--- a/src/java/org/apache/fop/pdf/PDFFactory.java
+++ b/src/java/org/apache/fop/pdf/PDFFactory.java
@@ -56,6 +56,8 @@ 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.OFFontLoader;
+import org.apache.fop.fonts.truetype.OTFSubSetFile;
import org.apache.fop.fonts.truetype.TTFSubSetFile;
import org.apache.fop.fonts.type1.PFBData;
import org.apache.fop.fonts.type1.PFBParser;
@@ -308,12 +310,7 @@ public class PDFFactory {
theFunctionDataStream,
theFilter);
- PDFFunction oldfunc = getDocument().findFunction(function);
- if (oldfunc == null) {
- getDocument().registerObject(function);
- } else {
- function = oldfunc;
- }
+ function = registerFunction(function);
return (function);
}
@@ -350,12 +347,7 @@ public class PDFFactory {
PDFFunction function = new PDFFunction(theFunctionType, theDomain,
theRange, theCZero, theCOne,
theInterpolationExponentN);
- PDFFunction oldfunc = getDocument().findFunction(function);
- if (oldfunc == null) {
- getDocument().registerObject(function);
- } else {
- function = oldfunc;
- }
+ function = registerFunction(function);
return (function);
}
@@ -405,12 +397,7 @@ public class PDFFactory {
theRange, theFunctions,
theBounds, theEncode);
- PDFFunction oldfunc = getDocument().findFunction(function);
- if (oldfunc == null) {
- getDocument().registerObject(function);
- } else {
- function = oldfunc;
- }
+ function = registerFunction(function);
return (function);
}
@@ -432,14 +419,23 @@ public class PDFFactory {
theRange,
theFunctionDataStream);
+ function = registerFunction(function);
+ return (function);
+
+ }
+
+ /**
+ * Registers a function against the document
+ * @param function The function to register
+ */
+ public PDFFunction registerFunction(PDFFunction function) {
PDFFunction oldfunc = getDocument().findFunction(function);
if (oldfunc == null) {
getDocument().registerObject(function);
} else {
function = oldfunc;
}
- return (function);
-
+ return function;
}
/* ========================= shadings ================================== */
@@ -479,20 +475,7 @@ public class PDFFactory {
theBBox, theAntiAlias, theDomain,
theMatrix, theFunction);
- PDFShading oldshad = getDocument().findShading(shading);
- if (oldshad == null) {
- getDocument().registerObject(shading);
- } else {
- shading = oldshad;
- }
-
- // add this shading to resources
- if (res != null) {
- res.getPDFResources().addShading(shading);
- } else {
- getDocument().getResources().addShading(shading);
- }
-
+ shading = registerShading(res, shading);
return (shading);
}
@@ -532,18 +515,7 @@ public class PDFFactory {
theDomain, theFunction,
theExtend);
- PDFShading oldshad = getDocument().findShading(shading);
- if (oldshad == null) {
- getDocument().registerObject(shading);
- } else {
- shading = oldshad;
- }
-
- if (res != null) {
- res.getPDFResources().addShading(shading);
- } else {
- getDocument().getResources().addShading(shading);
- }
+ shading = registerShading(res, shading);
return (shading);
}
@@ -589,18 +561,7 @@ public class PDFFactory {
theBitsPerFlag, theDecode,
theFunction);
- PDFShading oldshad = getDocument().findShading(shading);
- if (oldshad == null) {
- getDocument().registerObject(shading);
- } else {
- shading = oldshad;
- }
-
- if (res != null) {
- res.getPDFResources().addShading(shading);
- } else {
- getDocument().getResources().addShading(shading);
- }
+ shading = registerShading(res, shading);
return (shading);
}
@@ -643,6 +604,17 @@ public class PDFFactory {
theBitsPerComponent, theDecode,
theVerticesPerRow, theFunction);
+ shading = registerShading(res, shading);
+
+ return (shading);
+ }
+
+ /**
+ * Registers a shading object against the document
+ * @param res The PDF resource context
+ * @param shading The shading object to be registered
+ */
+ public PDFShading registerShading(PDFResourceContext res, PDFShading shading) {
PDFShading oldshad = getDocument().findShading(shading);
if (oldshad == null) {
getDocument().registerObject(shading);
@@ -650,13 +622,13 @@ public class PDFFactory {
shading = oldshad;
}
+ // add this shading to resources
if (res != null) {
res.getPDFResources().addShading(shading);
} else {
getDocument().getResources().addShading(shading);
}
-
- return (shading);
+ return shading;
}
/* ========================= patterns ================================== */
@@ -705,6 +677,22 @@ public class PDFFactory {
return (pattern);
}
+ public PDFPattern registerPattern(PDFResourceContext res, PDFPattern pattern) {
+ PDFPattern oldpatt = getDocument().findPattern(pattern);
+ if (oldpatt == null) {
+ getDocument().registerObject(pattern);
+ } else {
+ pattern = oldpatt;
+ }
+
+ if (res != null) {
+ res.getPDFResources().addPattern(pattern);
+ } else {
+ getDocument().getResources().addPattern(pattern);
+ }
+ return pattern;
+ }
+
/**
* Make a smooth shading pattern
*
@@ -1387,15 +1375,15 @@ public class PDFFactory {
int firstChar = singleByteFont.getFirstChar();
int lastChar = singleByteFont.getLastChar();
nonBase14.setWidthMetrics(firstChar,
- lastChar,
- new PDFArray(null, metrics.getWidths()));
+ lastChar,
+ new PDFArray(null, metrics.getWidths()));
//Handle encoding
SingleByteEncoding mapping = singleByteFont.getEncoding();
if (singleByteFont.isSymbolicFont()) {
//no encoding, use the font's encoding
if (forceToUnicode) {
- generateToUnicodeCmap(nonBase14, mapping);
+ generateToUnicodeCmap(nonBase14, mapping);
}
} else if (PDFEncoding.isPredefinedEncoding(mapping.getName())) {
font.setEncoding(mapping.getName());
@@ -1403,7 +1391,7 @@ public class PDFFactory {
//believed.
} else {
Object pdfEncoding = createPDFEncoding(mapping,
- singleByteFont.getFontName());
+ singleByteFont.getFontName());
if (pdfEncoding instanceof PDFEncoding) {
font.setEncoding((PDFEncoding)pdfEncoding);
} else {
@@ -1518,7 +1506,8 @@ public class PDFFactory {
// Check if the font is embeddable
if (desc.isEmbeddable()) {
- AbstractPDFStream stream = makeFontFile(desc);
+ AbstractPDFStream stream = makeFontFile(desc, fontPrefix);
+
if (stream != null) {
descriptor.setFontFile(desc.getFontType(), stream);
getDocument().registerObject(stream);
@@ -1564,7 +1553,7 @@ public class PDFFactory {
* @param desc FontDescriptor of the font.
* @return PDFStream The embedded font file
*/
- public AbstractPDFStream makeFontFile(FontDescriptor desc) {
+ public AbstractPDFStream makeFontFile(FontDescriptor desc, String fontPrefix) {
if (desc.getFontType() == FontType.OTHER) {
throw new IllegalArgumentException("Trying to embed unsupported font type: "
+ desc.getFontType());
@@ -1578,20 +1567,24 @@ public class PDFFactory {
if (in == null) {
return null;
} else {
- AbstractPDFStream embeddedFont;
+ AbstractPDFStream embeddedFont = null;
if (desc.getFontType() == FontType.TYPE0) {
MultiByteFont mbfont = (MultiByteFont) font;
FontFileReader reader = new FontFileReader(in);
byte[] fontBytes;
+ String header = OFFontLoader.readHeader(reader);
+ boolean isCFF = mbfont.isOTFFile();
if (font.getEmbeddingMode() == EmbeddingMode.FULL) {
fontBytes = reader.getAllBytes();
+ if (isCFF) {
+ //Ensure version 1.6 for full OTF CFF embedding
+ document.setPDFVersion(Version.V1_6);
+ }
} else {
- TTFSubSetFile ttfFile = new TTFSubSetFile();
- ttfFile.readFont(reader, mbfont.getTTCName(), mbfont.getUsedGlyphs());
- fontBytes = ttfFile.getFontSubset();
+ fontBytes = getFontSubsetBytes(reader, mbfont, header, fontPrefix, desc,
+ isCFF);
}
- embeddedFont = new PDFTTFStream(fontBytes.length);
- ((PDFTTFStream) embeddedFont).setData(fontBytes, fontBytes.length);
+ embeddedFont = getFontStream(font, fontBytes, isCFF);
} else if (desc.getFontType() == FontType.TYPE1) {
PFBParser parser = new PFBParser();
PFBData pfb = parser.parsePFB(in);
@@ -1621,6 +1614,32 @@ public class PDFFactory {
}
}
+ private byte[] getFontSubsetBytes(FontFileReader reader, MultiByteFont mbfont, String header,
+ String fontPrefix, FontDescriptor desc, boolean isCFF) throws IOException {
+ if (isCFF) {
+ OTFSubSetFile otfFile = new OTFSubSetFile();
+ otfFile.readFont(reader, fontPrefix + desc.getEmbedFontName(), header, mbfont);
+ return otfFile.getFontSubset();
+ } else {
+ TTFSubSetFile otfFile = new TTFSubSetFile();
+ otfFile.readFont(reader, mbfont.getTTCName(), header, mbfont.getUsedGlyphs());
+ return otfFile.getFontSubset();
+ }
+ }
+
+ private AbstractPDFStream getFontStream(CustomFont font, byte[] fontBytes, boolean isCFF)
+ throws IOException {
+ AbstractPDFStream embeddedFont;
+ if (isCFF) {
+ embeddedFont = new PDFCFFStreamType0C(font.getEmbeddingMode() == EmbeddingMode.FULL);
+ ((PDFCFFStreamType0C) embeddedFont).setData(fontBytes, fontBytes.length);
+ } else {
+ embeddedFont = new PDFTTFStream(fontBytes.length);
+ ((PDFTTFStream) embeddedFont).setData(fontBytes, fontBytes.length);
+ }
+ return embeddedFont;
+ }
+
private CustomFont getCustomFont(FontDescriptor desc) {
Typeface tempFont;
if (desc instanceof LazyFont) {
@@ -1788,4 +1807,28 @@ public class PDFFactory {
return obj;
}
+ public PDFLayer makeLayer(String id) {
+ PDFLayer layer = new PDFLayer(id);
+ getDocument().registerObject(layer);
+ return layer;
+ }
+
+ public PDFSetOCGStateAction makeSetOCGStateAction(String id) {
+ PDFSetOCGStateAction action = new PDFSetOCGStateAction(id);
+ getDocument().registerObject(action);
+ return action;
+ }
+
+ public PDFTransitionAction makeTransitionAction(String id) {
+ PDFTransitionAction action = new PDFTransitionAction(id);
+ getDocument().registerObject(action);
+ return action;
+ }
+
+ public PDFNavigator makeNavigator(String id) {
+ PDFNavigator navigator = new PDFNavigator(id);
+ getDocument().registerObject(navigator);
+ return navigator;
+ }
+
}
diff --git a/src/java/org/apache/fop/pdf/PDFFontDescriptor.java b/src/java/org/apache/fop/pdf/PDFFontDescriptor.java
index ec4e99101..73dbebc3f 100644
--- a/src/java/org/apache/fop/pdf/PDFFontDescriptor.java
+++ b/src/java/org/apache/fop/pdf/PDFFontDescriptor.java
@@ -102,6 +102,8 @@ public class PDFFontDescriptor extends PDFDictionary {
public void setFontFile(FontType subtype, AbstractPDFStream fontfile) {
if (subtype == FontType.TYPE1) {
put("FontFile", fontfile);
+ } else if (fontfile instanceof PDFCFFStreamType0C) {
+ put("FontFile3", fontfile);
} else {
put("FontFile2", fontfile);
}
diff --git a/src/java/org/apache/fop/pdf/PDFFunction.java b/src/java/org/apache/fop/pdf/PDFFunction.java
index f424569b9..09cbd9708 100644
--- a/src/java/org/apache/fop/pdf/PDFFunction.java
+++ b/src/java/org/apache/fop/pdf/PDFFunction.java
@@ -22,6 +22,10 @@ package org.apache.fop.pdf;
// Java...
import java.util.List;
+import org.apache.fop.render.shading.Function;
+import org.apache.fop.render.shading.FunctionDelegate;
+import org.apache.fop.render.shading.FunctionPattern;
+
/**
* class representing a PDF Function.
*
@@ -33,126 +37,9 @@ import java.util.List;
*
* All PDF Functions have a FunctionType (0,2,3, or 4), a Domain, and a Range.
*/
-public class PDFFunction extends PDFObject {
- // Guts common to all function types
-
- /**
- * Required: The Type of function (0,2,3,4) default is 0.
- */
- protected int functionType = 0; // Default
-
- /**
- * Required: 2 * m Array of Double numbers which are possible inputs to the function
- */
- protected List domain = null;
-
- /**
- * Required: 2 * n Array of Double numbers which are possible outputs to the function
- */
- protected List range = null;
-
- /* ********************TYPE 0***************************** */
- // FunctionType 0 specific function guts
-
- /**
- * Required: Array containing the Integer size of the Domain and Range, respectively.
- * Note: This is really more like two seperate integers, sizeDomain, and sizeRange,
- * but since they're expressed as an array in PDF, my implementation reflects that.
- */
- protected List size = null;
-
- /**
- * Required for Type 0: Number of Bits used to represent each sample value.
- * Limited to 1,2,4,8,12,16,24, or 32
- */
- protected int bitsPerSample = 1;
-
- /**
- * Optional for Type 0: order of interpolation between samples.
- * Limited to linear (1) or cubic (3). Default is 1
- */
- protected int order = 1;
-
- /**
- * Optional for Type 0: A 2 * m array of Doubles which provides a
- * linear mapping of input values to the domain.
- *
- * Required for Type 3: A 2 * k array of Doubles that, taken
- * in pairs, map each subset of the domain defined by Domain
- * and the Bounds array to the domain of the corresponding function.
- * Should be two values per function, usually (0,1),
- * as in [0 1 0 1] for 2 functions.
- */
- protected List encode = null;
-
- /**
- * Optional for Type 0: A 2 * n array of Doubles which provides
- * a linear mapping of sample values to the range. Defaults to Range.
- */
- protected List decode = null;
-
- /**
- * Optional For Type 0: A stream of sample values
- */
-
- /**
- * Required For Type 4: Postscript Calculator function
- * composed of arithmetic, boolean, and stack operators + boolean constants
- */
- protected StringBuffer functionDataStream = null;
-
- /**
- * Required (possibly) For Type 0: A vector of Strings for the
- * various filters to be used to decode the stream.
- * These are how the string is compressed. Flate, LZW, etc.
- */
- protected List filter = null;
- /* *************************TYPE 2************************** */
-
- /**
- * Required For Type 2: An Array of n Doubles defining
- * the function result when x=0. Default is [0].
- */
- protected List cZero = null;
-
- /**
- * Required For Type 2: An Array of n Doubles defining
- * the function result when x=1. Default is [1].
- */
- protected List cOne = null;
+public class PDFFunction extends PDFObject implements Function {
- /**
- * Required for Type 2: The interpolation exponent.
- * Each value x will return n results.
- * Must be greater than 0.
- */
- protected double interpolationExponentN = 1;
-
- /* *************************TYPE 3************************** */
-
- /**
- * Required for Type 3: An vector of PDFFunctions which
- * form an array of k single input functions making up
- * the stitching function.
- */
- protected List functions = null;
-
- /**
- * Optional for Type 3: An array of (k-1) Doubles that,
- * in combination with Domain, define the intervals to which
- * each function from the Functions array apply. Bounds
- * elements must be in order of increasing magnitude,
- * and each value must be within the value of Domain.
- * k is the number of functions.
- * If you pass null, it will output (1/k) in an array of k-1 elements.
- * This makes each function responsible for an equal amount of the stitching function.
- * It makes the gradient even.
- */
- protected List bounds = null;
- // See encode above, as it's also part of Type 3 Functions.
-
- /* *************************TYPE 4************************** */
- // See 'data' above.
+ private FunctionDelegate delegate;
/**
* create an complete Function object of Type 0, A Sampled function.
@@ -211,26 +98,13 @@ public class PDFFunction extends PDFObject {
* @param theFunctionType This is the type of function (0,2,3, or 4).
* It should be 0 as this is the constructor for sampled functions.
*/
- public PDFFunction(int theFunctionType, List theDomain,
- List theRange, List theSize, int theBitsPerSample,
- int theOrder, List theEncode, List theDecode,
- StringBuffer theFunctionDataStream, List theFilter) {
- super();
-
- this.functionType = 0; // dang well better be 0;
- this.size = theSize;
- this.bitsPerSample = theBitsPerSample;
- this.order = theOrder; // int
- this.encode = theEncode; // vector of int
- this.decode = theDecode; // vector of int
- this.functionDataStream = theFunctionDataStream;
- this.filter = theFilter; // vector of Strings
-
- // the domain and range are actually two dimensional arrays.
- // so if there's not an even number of items, bad stuff
- // happens.
- this.domain = theDomain;
- this.range = theRange;
+ public PDFFunction(int theFunctionType, List<Double> theDomain,
+ List<Double> theRange, List<Double> theSize, int theBitsPerSample,
+ int theOrder, List<Double> theEncode, List<Double> theDecode,
+ StringBuffer theFunctionDataStream, List<String> theFilter) {
+ delegate = new FunctionDelegate(this, theFunctionType, theDomain, theRange,
+ theSize, theBitsPerSample, theOrder, theEncode, theDecode,
+ theFunctionDataStream, theFilter);
}
/**
@@ -260,20 +134,11 @@ public class PDFFunction extends PDFObject {
* PDF Spec page 268
* @param theFunctionType The type of the function, which should be 2.
*/
- public PDFFunction(int theFunctionType, List theDomain,
- List theRange, List theCZero, List theCOne,
+ public PDFFunction(int theFunctionType, List<Double> theDomain,
+ List<Double> theRange, List<Double> theCZero, List<Double> theCOne,
double theInterpolationExponentN) {
- super();
-
- this.functionType = 2; // dang well better be 2;
-
- this.cZero = theCZero;
- this.cOne = theCOne;
- this.interpolationExponentN = theInterpolationExponentN;
-
-
- this.domain = theDomain;
- this.range = theRange;
+ delegate = new FunctionDelegate(this, theFunctionType, theDomain, theRange,
+ theCZero, theCOne, theInterpolationExponentN);
}
@@ -312,18 +177,11 @@ public class PDFFunction extends PDFObject {
* @param theFunctionType This is the function type. It should be 3,
* for a stitching function.
*/
- public PDFFunction(int theFunctionType, List theDomain,
- List theRange, List theFunctions,
- List theBounds, List theEncode) {
- super();
-
- this.functionType = 3; // dang well better be 3;
-
- this.functions = theFunctions;
- this.bounds = theBounds;
- this.encode = theEncode;
- this.domain = theDomain;
- this.range = theRange;
+ public PDFFunction(int theFunctionType, List<Double> theDomain,
+ List<Double> theRange, List<Function> theFunctions,
+ List<Double> theBounds, List<Double> theEncode) {
+ delegate = new FunctionDelegate(this, theFunctionType, theDomain, theRange,
+ theFunctions, theBounds, theEncode);
}
@@ -349,20 +207,12 @@ public class PDFFunction extends PDFObject {
* @param theFunctionType The type of function which should be 4, as this is
* a Postscript calculator function
*/
- public PDFFunction(int theFunctionType, List theDomain,
- List theRange, StringBuffer theFunctionDataStream) {
- super();
-
- this.functionType = 4; // dang well better be 4;
- this.functionDataStream = theFunctionDataStream;
-
- this.domain = theDomain;
-
- this.range = theRange;
-
+ public PDFFunction(int theFunctionType, List<Double> theDomain,
+ List<Double> theRange, StringBuffer theFunctionDataStream) {
+ delegate = new FunctionDelegate(this, theFunctionType, theDomain, theRange,
+ theFunctionDataStream);
}
-
/**
* represent as PDF. Whatever the FunctionType is, the correct
* representation spits out. The sets of required and optional
@@ -375,319 +225,13 @@ public class PDFFunction extends PDFObject {
* @return the PDF string.
*/
public byte[] toPDF() {
- int vectorSize = 0;
- int numberOfFunctions = 0;
- int tempInt = 0;
- StringBuffer p = new StringBuffer(256);
- p.append("<< \n/FunctionType " + this.functionType + " \n");
-
- // FunctionType 0
- if (this.functionType == 0) {
- if (this.domain != null) {
- // DOMAIN
- p.append("/Domain [ ");
- vectorSize = this.domain.size();
- for (tempInt = 0; tempInt < vectorSize; tempInt++) {
- p.append(PDFNumber.doubleOut((Double)this.domain.get(tempInt))
- + " ");
- }
-
- p.append("] \n");
- } else {
- p.append("/Domain [ 0 1 ] \n");
- }
-
- // SIZE
- if (this.size != null) {
- p.append("/Size [ ");
- vectorSize = this.size.size();
- for (tempInt = 0; tempInt < vectorSize; tempInt++) {
- p.append(PDFNumber.doubleOut((Double)this.size.get(tempInt))
- + " ");
- }
- p.append("] \n");
- }
- // ENCODE
- if (this.encode != null) {
- p.append("/Encode [ ");
- vectorSize = this.encode.size();
- for (tempInt = 0; tempInt < vectorSize; tempInt++) {
- p.append(PDFNumber.doubleOut((Double)this.encode.get(tempInt))
- + " ");
- }
- p.append("] \n");
- } else {
- p.append("/Encode [ ");
- vectorSize = this.functions.size();
- for (tempInt = 0; tempInt < vectorSize; tempInt++) {
- p.append("0 1 ");
- }
- p.append("] \n");
-
- }
-
- // BITSPERSAMPLE
- p.append("/BitsPerSample " + this.bitsPerSample);
-
- // ORDER (optional)
- if (this.order == 1 || this.order == 3) {
- p.append(" \n/Order " + this.order + " \n");
- }
-
- // RANGE
- if (this.range != null) {
- p.append("/Range [ ");
- vectorSize = this.range.size();
- for (tempInt = 0; tempInt < vectorSize; tempInt++) {
- p.append(PDFNumber.doubleOut((Double)this.range.get(tempInt))
- + " ");
- }
-
- p.append("] \n");
- }
-
- // DECODE
- if (this.decode != null) {
- p.append("/Decode [ ");
- vectorSize = this.decode.size();
- for (tempInt = 0; tempInt < vectorSize; tempInt++) {
- p.append(PDFNumber.doubleOut((Double)this.decode.get(tempInt))
- + " ");
- }
-
- p.append("] \n");
- }
-
- // LENGTH
- if (this.functionDataStream != null) {
- p.append("/Length " + (this.functionDataStream.length() + 1)
- + " \n");
- }
-
- // FILTER?
- if (this.filter != null) { // if there's a filter
- vectorSize = this.filter.size();
- p.append("/Filter ");
- if (vectorSize == 1) {
- p.append("/" + ((String)this.filter.get(0))
- + " \n");
- } else {
- p.append("[ ");
- for (tempInt = 0; tempInt < vectorSize; tempInt++) {
- p.append("/" + ((String)this.filter.get(0))
- + " ");
- }
- p.append("] \n");
- }
- }
- p.append(">>");
-
- // stream representing the function
- if (this.functionDataStream != null) {
- p.append("\nstream\n" + this.functionDataStream
- + "\nendstream");
- }
-
- // end of if FunctionType 0
-
- } else if (this.functionType == 2) {
- // DOMAIN
- if (this.domain != null) {
- p.append("/Domain [ ");
- vectorSize = this.domain.size();
- for (tempInt = 0; tempInt < vectorSize; tempInt++) {
- p.append(PDFNumber.doubleOut((Double)this.domain.get(tempInt))
- + " ");
- }
-
- p.append("] \n");
- } else {
- p.append("/Domain [ 0 1 ] \n");
- }
-
-
- // RANGE
- if (this.range != null) {
- p.append("/Range [ ");
- vectorSize = this.range.size();
- for (tempInt = 0; tempInt < vectorSize; tempInt++) {
- p.append(PDFNumber.doubleOut((Double)this.range.get(tempInt))
- + " ");
- }
-
- p.append("] \n");
- }
-
- // FunctionType, C0, C1, N are required in PDF
-
- // C0
- if (this.cZero != null) {
- p.append("/C0 [ ");
- vectorSize = this.cZero.size();
- for (tempInt = 0; tempInt < vectorSize; tempInt++) {
- p.append(PDFNumber.doubleOut((Double)this.cZero.get(tempInt))
- + " ");
- }
- p.append("] \n");
- }
-
- // C1
- if (this.cOne != null) {
- p.append("/C1 [ ");
- vectorSize = this.cOne.size();
- for (tempInt = 0; tempInt < vectorSize; tempInt++) {
- p.append(PDFNumber.doubleOut((Double)this.cOne.get(tempInt))
- + " ");
- }
- p.append("] \n");
- }
-
- // N: The interpolation Exponent
- p.append("/N "
- + PDFNumber.doubleOut(new Double(this.interpolationExponentN))
- + " \n");
-
- p.append(">>");
-
- } else if (this.functionType
- == 3) { // fix this up when my eyes uncross
- // DOMAIN
- if (this.domain != null) {
- p.append("/Domain [ ");
- vectorSize = this.domain.size();
- for (tempInt = 0; tempInt < vectorSize; tempInt++) {
- p.append(PDFNumber.doubleOut((Double)this.domain.get(tempInt))
- + " ");
- }
- p.append("] \n");
- } else {
- p.append("/Domain [ 0 1 ] \n");
- }
-
- // RANGE
- if (this.range != null) {
- p.append("/Range [ ");
- vectorSize = this.range.size();
- for (tempInt = 0; tempInt < vectorSize; tempInt++) {
- p.append(PDFNumber.doubleOut((Double)this.range.get(tempInt))
- + " ");
- }
-
- p.append("] \n");
- }
-
- // FUNCTIONS
- if (this.functions != null) {
- p.append("/Functions [ ");
- numberOfFunctions = this.functions.size();
- for (tempInt = 0; tempInt < numberOfFunctions; tempInt++) {
- p.append(((PDFFunction)this.functions.get(tempInt)).referencePDF()
- + " ");
-
- }
- p.append("] \n");
- }
-
-
- // ENCODE
- if (this.encode != null) {
- p.append("/Encode [ ");
- vectorSize = this.encode.size();
- for (tempInt = 0; tempInt < vectorSize; tempInt++) {
- p.append(PDFNumber.doubleOut((Double)this.encode.get(tempInt))
- + " ");
- }
-
- p.append("] \n");
- } else {
- p.append("/Encode [ ");
- vectorSize = this.functions.size();
- for (tempInt = 0; tempInt < vectorSize; tempInt++) {
- p.append("0 1 ");
- }
- p.append("] \n");
-
- }
-
-
- // BOUNDS, required, but can be empty
- p.append("/Bounds [ ");
- if (this.bounds != null) {
-
- vectorSize = this.bounds.size();
- for (tempInt = 0; tempInt < vectorSize; tempInt++) {
- p.append(PDFNumber.doubleOut((Double)this.bounds.get(tempInt))
- + " ");
- }
-
- } else {
- if (this.functions != null) {
- // if there are n functions,
- // there must be n-1 bounds.
- // so let each function handle an equal portion
- // of the whole. e.g. if there are 4, then [ 0.25 0.25 0.25 ]
-
- String functionsFraction = PDFNumber.doubleOut(new Double(1.0
- / ((double)numberOfFunctions)));
-
- for (tempInt = 0; tempInt + 1 < numberOfFunctions;
- tempInt++) {
-
- p.append(functionsFraction + " ");
- }
- functionsFraction = null; // clean reference.
-
- }
-
- }
- p.append("]\n>>");
- } else if (this.functionType
- == 4) { // fix this up when my eyes uncross
- // DOMAIN
- if (this.domain != null) {
- p.append("/Domain [ ");
- vectorSize = this.domain.size();
- for (tempInt = 0; tempInt < vectorSize; tempInt++) {
- p.append(PDFNumber.doubleOut((Double)this.domain.get(tempInt))
- + " ");
- }
-
- p.append("] \n");
- } else {
- p.append("/Domain [ 0 1 ] \n");
- }
-
- // RANGE
- if (this.range != null) {
- p.append("/Range [ ");
- vectorSize = this.range.size();
- for (tempInt = 0; tempInt < vectorSize; tempInt++) {
- p.append(PDFNumber.doubleOut((Double)this.range.get(tempInt))
- + " ");
- }
-
- p.append("] \n");
- }
-
- // LENGTH
- if (this.functionDataStream != null) {
- p.append("/Length " + (this.functionDataStream.length() + 1)
- + " \n");
- }
-
- p.append(">>");
-
- // stream representing the function
- if (this.functionDataStream != null) {
- p.append("\nstream\n{ " + this.functionDataStream
- + " }\nendstream");
- }
-
-
- }
+ return toByteString();
+ }
- return encode(p.toString());
+ public byte[] toByteString() {
+ FunctionPattern pattern = new FunctionPattern(this);
+ return encode(pattern.toWriteableString());
}
/** {@inheritDoc} */
@@ -702,96 +246,155 @@ public class PDFFunction extends PDFObject {
return false;
}
PDFFunction func = (PDFFunction)obj;
- if (functionType != func.functionType) {
+ if (delegate.getFunctionType() != func.getFunctionType()) {
return false;
}
- if (bitsPerSample != func.bitsPerSample) {
+ if (delegate.getBitsPerSample() != func.getBitsPerSample()) {
return false;
}
- if (order != func.order) {
+ if (delegate.getOrder() != func.getOrder()) {
return false;
}
- if (interpolationExponentN != func.interpolationExponentN) {
+ if (delegate.getInterpolationExponentN() != func.getInterpolationExponentN()) {
return false;
}
- if (domain != null) {
- if (!domain.equals(func.domain)) {
+ if (delegate.getDomain() != null) {
+ if (!delegate.getDomain().equals(func.getDomain())) {
return false;
}
- } else if (func.domain != null) {
+ } else if (func.getDomain() != null) {
return false;
}
- if (range != null) {
- if (!range.equals(func.range)) {
+ if (delegate.getRange() != null) {
+ if (!delegate.getRange().equals(func.getRange())) {
return false;
}
- } else if (func.range != null) {
+ } else if (func.getRange() != null) {
return false;
}
- if (size != null) {
- if (!size.equals(func.size)) {
+ if (delegate.getSize() != null) {
+ if (!delegate.getSize().equals(func.getSize())) {
return false;
}
- } else if (func.size != null) {
+ } else if (func.getSize() != null) {
return false;
}
- if (encode != null) {
- if (!encode.equals(func.encode)) {
+ if (delegate.getEncode() != null) {
+ if (!delegate.getEncode().equals(func.getEncode())) {
return false;
}
- } else if (func.encode != null) {
+ } else if (func.getEncode() != null) {
return false;
}
- if (decode != null) {
- if (!decode.equals(func.decode)) {
+ if (delegate.getDecode() != null) {
+ if (!delegate.getDecode().equals(func.getDecode())) {
return false;
}
- } else if (func.decode != null) {
+ } else if (func.getDecode() != null) {
return false;
}
- if (functionDataStream != null) {
- if (!functionDataStream.equals(func.functionDataStream)) {
+ if (delegate.getDataStream() != null) {
+ if (!delegate.getDataStream().equals(func.getDataStream())) {
return false;
}
- } else if (func.functionDataStream != null) {
+ } else if (func.getDataStream() != null) {
return false;
}
- if (filter != null) {
- if (!filter.equals(func.filter)) {
+ if (delegate.getFilter() != null) {
+ if (!delegate.getFilter().equals(func.getFilter())) {
return false;
}
- } else if (func.filter != null) {
+ } else if (func.getFilter() != null) {
return false;
}
- if (cZero != null) {
- if (!cZero.equals(func.cZero)) {
+ if (delegate.getCZero() != null) {
+ if (!delegate.getCZero().equals(func.getCZero())) {
return false;
}
- } else if (func.cZero != null) {
+ } else if (func.getCZero() != null) {
return false;
}
- if (cOne != null) {
- if (!cOne.equals(func.cOne)) {
+ if (delegate.getCOne() != null) {
+ if (!delegate.getCOne().equals(func.getCOne())) {
return false;
}
- } else if (func.cOne != null) {
+ } else if (func.getCOne() != null) {
return false;
}
- if (functions != null) {
- if (!functions.equals(func.functions)) {
+ if (delegate.getFunctions() != null) {
+ if (!delegate.getFunctions().equals(func.getFunctions())) {
return false;
}
- } else if (func.functions != null) {
+ } else if (func.getFunctions() != null) {
return false;
}
- if (bounds != null) {
- if (!bounds.equals(func.bounds)) {
+ if (delegate.getBounds() != null) {
+ if (!delegate.getBounds().equals(func.getBounds())) {
return false;
}
- } else if (func.bounds != null) {
+ } else if (func.getBounds() != null) {
return false;
}
return true;
}
+ public int getFunctionType() {
+ return delegate.getFunctionType();
+ }
+
+ public List<Double> getBounds() {
+ return delegate.getBounds();
+ }
+
+ public List<Double> getDomain() {
+ return delegate.getDomain();
+ }
+
+ public List<Double> getSize() {
+ return delegate.getSize();
+ }
+
+ public List<String> getFilter() {
+ return delegate.getFilter();
+ }
+
+ public List<Double> getEncode() {
+ return delegate.getEncode();
+ }
+
+ public List<Function> getFunctions() {
+ return delegate.getFunctions();
+ }
+
+ public int getBitsPerSample() {
+ return delegate.getBitsPerSample();
+ }
+
+ public double getInterpolationExponentN() {
+ return delegate.getInterpolationExponentN();
+ }
+
+ public int getOrder() {
+ return delegate.getOrder();
+ }
+
+ public List<Double> getRange() {
+ return delegate.getRange();
+ }
+
+ public List<Double> getDecode() {
+ return delegate.getDecode();
+ }
+
+ public StringBuffer getDataStream() {
+ return delegate.getDataStream();
+ }
+
+ public List<Double> getCZero() {
+ return delegate.getCZero();
+ }
+
+ public List<Double> getCOne() {
+ return delegate.getCOne();
+ }
}
diff --git a/src/java/org/apache/fop/layoutmgr/inline/ICLayoutManager.java b/src/java/org/apache/fop/pdf/PDFIdentifiedDictionary.java
index 7fe90f63c..c2d033aec 100644
--- a/src/java/org/apache/fop/layoutmgr/inline/ICLayoutManager.java
+++ b/src/java/org/apache/fop/pdf/PDFIdentifiedDictionary.java
@@ -17,38 +17,26 @@
/* $Id$ */
-package org.apache.fop.layoutmgr.inline;
+package org.apache.fop.pdf;
-// Java
-import java.util.List;
-
-// FOP
-import org.apache.fop.area.inline.InlineArea;
-import org.apache.fop.fo.flow.InlineContainer;
/**
- * This creates a single inline container area after
- * laying out the child block areas. All footnotes, floats
- * and id areas are maintained for later retrieval.
+ * Identified Dictionary.
*/
-public class ICLayoutManager extends LeafNodeLayoutManager {
- private List childrenLM;
-
- /**
- * Construct inline container layout manager.
- * @param node inline container FO node
- * @param childLM child layout manager
- */
- public ICLayoutManager(InlineContainer node, List childLM) {
- super(node);
- childrenLM = childLM;
+public class PDFIdentifiedDictionary extends PDFDictionary {
+
+ private final String id;
+
+ public PDFIdentifiedDictionary(String id) {
+ this.id = id;
}
- /**
- * @param index an integer
- * @return an inline area or null
- */
- public InlineArea get(int index) {
- return null;
+ public String getId() {
+ return this.id;
+ }
+
+ public boolean hasId(String id) {
+ return (this.id != null) && (id != null) && this.id.equals(id);
}
}
+
diff --git a/src/java/org/apache/fop/pdf/PDFLayer.java b/src/java/org/apache/fop/pdf/PDFLayer.java
new file mode 100644
index 000000000..f8f434e87
--- /dev/null
+++ b/src/java/org/apache/fop/pdf/PDFLayer.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.pdf;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * Optional Content Group Dictionary, which we will call a 'layer'.
+ */
+public class PDFLayer extends PDFIdentifiedDictionary {
+
+ public abstract static class Resolver {
+ private boolean resolved;
+ private PDFLayer layer;
+ private Object extension;
+ public Resolver(PDFLayer layer, Object extension) {
+ this.layer = layer;
+ this.extension = extension;
+ }
+ public PDFLayer getLayer() {
+ return layer;
+ }
+ public Object getExtension() {
+ return extension;
+ }
+ public void resolve() {
+ if (!resolved) {
+ performResolution();
+ resolved = true;
+ }
+ }
+ protected void performResolution() {
+ }
+ }
+
+ private Resolver resolver;
+
+ public PDFLayer(String id) {
+ super(id);
+ put("Type", new PDFName("OCG"));
+ }
+
+ @Override
+ public int output(OutputStream stream) throws IOException {
+ if (resolver != null) {
+ resolver.resolve();
+ }
+ return super.output(stream);
+ }
+
+ public void setResolver(Resolver resolver) {
+ this.resolver = resolver;
+ }
+
+ public void populate(Object name, Object intent, Object usage) {
+ if (name != null) {
+ put("Name", name);
+ }
+ if (intent != null) {
+ put("Intent", intent);
+ }
+ if (usage != null) {
+ put("Usage", usage);
+ }
+ }
+
+}
+
diff --git a/src/java/org/apache/fop/pdf/PDFNavigator.java b/src/java/org/apache/fop/pdf/PDFNavigator.java
new file mode 100644
index 000000000..fdb97469b
--- /dev/null
+++ b/src/java/org/apache/fop/pdf/PDFNavigator.java
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.pdf;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * Navigation Node Dictionary, which we call a 'navigator'.
+ * This class is used to for sub-page navigation.
+ */
+public class PDFNavigator extends PDFIdentifiedDictionary {
+
+ public abstract static class Resolver {
+ private boolean resolved;
+ private PDFNavigator navigator;
+ private Object extension;
+ public Resolver(PDFNavigator navigator, Object extension) {
+ this.navigator = navigator;
+ this.extension = extension;
+ }
+ public PDFNavigator getNavigator() {
+ return navigator;
+ }
+ public Object getExtension() {
+ return extension;
+ }
+ public void resolve() {
+ if (!resolved) {
+ performResolution();
+ resolved = true;
+ }
+ }
+ protected void performResolution() {
+ }
+ }
+
+ private Resolver resolver;
+
+ public PDFNavigator(String id) {
+ super(id);
+ put("Type", new PDFName("NavNode"));
+ }
+
+ @Override
+ public int output(OutputStream stream) throws IOException {
+ if (resolver != null) {
+ resolver.resolve();
+ }
+ return super.output(stream);
+ }
+
+ public void setResolver(Resolver resolver) {
+ this.resolver = resolver;
+ }
+
+ public void populate(Object nextAction, Object nextNode, Object prevAction, Object prevNode, Object duration) {
+ if (nextAction != null) {
+ put("NA", nextAction);
+ }
+ if (nextNode != null) {
+ put("Next", nextNode);
+ }
+ if (prevAction != null) {
+ put("PA", prevAction);
+ }
+ if (prevNode != null) {
+ put("Prev", prevNode);
+ }
+ if (duration != null) {
+ put("Dur", duration);
+ }
+ }
+
+}
+
diff --git a/src/java/org/apache/fop/pdf/PDFNavigatorAction.java b/src/java/org/apache/fop/pdf/PDFNavigatorAction.java
new file mode 100644
index 000000000..ba32269b5
--- /dev/null
+++ b/src/java/org/apache/fop/pdf/PDFNavigatorAction.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.pdf;
+
+public abstract class PDFNavigatorAction extends PDFIdentifiedDictionary {
+
+ protected PDFNavigatorAction(String id) {
+ super(id);
+ }
+
+}
diff --git a/src/java/org/apache/fop/pdf/PDFNumber.java b/src/java/org/apache/fop/pdf/PDFNumber.java
index 1c31f8e9d..754194886 100644
--- a/src/java/org/apache/fop/pdf/PDFNumber.java
+++ b/src/java/org/apache/fop/pdf/PDFNumber.java
@@ -29,6 +29,14 @@ public class PDFNumber extends PDFObject {
private Number number;
+ public PDFNumber() {
+ this.number = Integer.valueOf(0);
+ }
+
+ public PDFNumber(Number number) {
+ this.number = number;
+ }
+
/**
* Returns the number.
* @return the number
diff --git a/src/java/org/apache/fop/pdf/PDFPaintingState.java b/src/java/org/apache/fop/pdf/PDFPaintingState.java
index 29d022f61..f6528a30c 100644
--- a/src/java/org/apache/fop/pdf/PDFPaintingState.java
+++ b/src/java/org/apache/fop/pdf/PDFPaintingState.java
@@ -44,8 +44,6 @@ import org.apache.fop.util.AbstractPaintingState;
* previous state then the necessary values can be overridden.
* The current transform behaves differently to other values as the
* matrix is combined with the current resolved value.
- * It is impossible to optimise the result without analysing the all
- * the possible combinations after completing.
*/
public class PDFPaintingState extends org.apache.fop.util.AbstractPaintingState {
@@ -187,6 +185,36 @@ public class PDFPaintingState extends org.apache.fop.util.AbstractPaintingState
return newState;
}
+ public void setLayer(String layer) {
+ getPDFData().setLayer(layer);
+ }
+
+ public String getLayer() {
+ return getPDFData().getLayer();
+ }
+
+ public boolean getLayerChanged() {
+ String layerCurrent = getLayer();
+ if (layerCurrent == null) {
+ return false;
+ } else if (getStateStack().isEmpty()) {
+ return true;
+ } else {
+ for (int i = getStackLevel(); i > 0; --i) {
+ String layerPrev = ((PDFData) getStateStack().get(i - 1)).getLayer();
+ if (layerPrev == null) {
+ continue;
+ } else {
+ // Both current and prior are set, so, if same, then we know layer
+ // didn't change (and can stop search), otherwise it did change.
+ return !layerCurrent.equals(layerPrev);
+ }
+ }
+ // Current layer set, but no prior saved layer set, so must have changed.
+ return true;
+ }
+ }
+
/** {@inheritDoc} */
@Override
protected AbstractData instantiateData() {
@@ -209,7 +237,7 @@ public class PDFPaintingState extends org.apache.fop.util.AbstractPaintingState
AbstractData data = getData();
AbstractData copy = (AbstractData)data.clone();
data.clearTransform();
- getStateStack().add(copy);
+ getStateStack().push(copy);
}
private PDFData getPDFData() {
diff --git a/src/java/org/apache/fop/pdf/PDFPattern.java b/src/java/org/apache/fop/pdf/PDFPattern.java
index 46a6a7378..df4b0233d 100644
--- a/src/java/org/apache/fop/pdf/PDFPattern.java
+++ b/src/java/org/apache/fop/pdf/PDFPattern.java
@@ -23,6 +23,9 @@ import java.io.IOException;
import java.io.OutputStream;
import java.util.List;
+import org.apache.fop.render.shading.Pattern;
+import org.apache.fop.render.shading.Shading;
+
/**
* class representing a PDF Function.
*
@@ -33,7 +36,7 @@ import java.util.List;
*
* All PDF Functions have a FunctionType (0,2,3, or 4), a Domain, and a Range.
*/
-public class PDFPattern extends PDFPathPaint {
+public class PDFPattern extends PDFPathPaint implements Pattern {
/**
* The resources associated with this pattern
@@ -146,13 +149,14 @@ public class PDFPattern extends PDFPathPaint {
* @param theExtGState optional: the extended graphics state, if used.
* @param theMatrix Optional:List of Doubles that specify the matrix.
*/
- public PDFPattern(int thePatternType, PDFShading theShading,
+ public PDFPattern(int thePatternType, Shading theShading,
List theXUID, StringBuffer theExtGState,
List theMatrix) {
super();
this.patternType = 2; // thePatternType;
- this.shading = theShading;
+ assert theShading instanceof PDFShading;
+ this.shading = (PDFShading)theShading;
this.xUID = theXUID;
// this isn't really implemented, so it should always be null.
// I just don't want to have to add a new parameter once it is implemented.
@@ -259,7 +263,7 @@ public class PDFPattern extends PDFPathPaint {
vectorSize = this.xUID.size();
p.append("/XUID [ ");
for (tempInt = 0; tempInt < vectorSize; tempInt++) {
- p.append(((Integer)this.xUID.get(tempInt)) + " ");
+ p.append((this.xUID.get(tempInt)) + " ");
}
p.append("] \n");
}
@@ -269,13 +273,14 @@ public class PDFPattern extends PDFPathPaint {
pdfStream = new PDFStream();
pdfStream.setDocument(getDocumentSafely());
pdfStream.add(this.patternDataStream.toString());
+ pdfStream.setObjectNumber(getObjectNumber());
pdfStream.getFilterList().addDefaultFilters(
getDocument().getFilterMap(),
PDFFilterList.CONTENT_FILTER);
+ getDocument().applyEncryption(pdfStream);
encodedStream = pdfStream.encodeStream();
p.append(pdfStream.getFilterList().buildFilterDictEntries());
- p.append("/Length " + (encodedStream.getSize() + 1)
- + " \n");
+ p.append("/Length " + encodedStream.getSize() + " \n");
}
} else {
@@ -289,7 +294,7 @@ public class PDFPattern extends PDFPathPaint {
vectorSize = this.xUID.size();
p.append("/XUID [ ");
for (tempInt = 0; tempInt < vectorSize; tempInt++) {
- p.append(((Integer)this.xUID.get(tempInt)) + " ");
+ p.append((this.xUID.get(tempInt)) + " ");
}
p.append("] \n");
}
diff --git a/src/java/org/apache/fop/pdf/PDFResources.java b/src/java/org/apache/fop/pdf/PDFResources.java
index cded7c00a..6d09d5738 100644
--- a/src/java/org/apache/fop/pdf/PDFResources.java
+++ b/src/java/org/apache/fop/pdf/PDFResources.java
@@ -37,8 +37,8 @@ import org.apache.fop.fonts.base14.ZapfDingbats;
/**
* Class representing a /Resources object.
*
- * /Resources object contain a list of references to the fonts for the
- * document
+ * /Resources object contain a list of references to the fonts, patterns,
+ * shadings, etc., for the document.
*/
public class PDFResources extends PDFDictionary {
@@ -73,6 +73,9 @@ public class PDFResources extends PDFDictionary {
/** Map of ICC color spaces (key: ICC profile description) */
protected Map<String, PDFICCBasedColorSpace> iccColorSpaces = new LinkedHashMap<String, PDFICCBasedColorSpace>();
+ /** Named properties */
+ protected Map<String, PDFReference> properties = new LinkedHashMap<String, PDFReference>();
+
/**
* create a /Resources object.
*
@@ -191,6 +194,25 @@ public class PDFResources extends PDFDictionary {
return cs;
}
+ /**
+ * Add a named property.
+ *
+ * @param name name of property
+ * @param property reference to property value
+ */
+ public void addProperty(String name, PDFReference property) {
+ this.properties.put(name, property);
+ }
+
+ /**
+ * Get a named property.
+ *
+ * @param name name of property
+ */
+ public PDFReference getProperty(String name) {
+ return this.properties.get(name);
+ }
+
@Override
public int output(OutputStream stream) throws IOException {
populateDictionary();
@@ -253,6 +275,14 @@ public class PDFResources extends PDFDictionary {
}
put("ColorSpace", dict);
}
+
+ if (!properties.isEmpty()) {
+ PDFDictionary dict = new PDFDictionary(this);
+ for (String name : properties.keySet()) {
+ dict.put(name, properties.get(name));
+ }
+ put("Properties", dict);
+ }
}
}
diff --git a/src/java/org/apache/fop/pdf/PDFSetOCGStateAction.java b/src/java/org/apache/fop/pdf/PDFSetOCGStateAction.java
new file mode 100644
index 000000000..a47c5cd59
--- /dev/null
+++ b/src/java/org/apache/fop/pdf/PDFSetOCGStateAction.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.pdf;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+public class PDFSetOCGStateAction extends PDFNavigatorAction {
+
+ public abstract static class Resolver {
+ private boolean resolved;
+ private PDFSetOCGStateAction action;
+ private Object extension;
+ public Resolver(PDFSetOCGStateAction action, Object extension) {
+ this.action = action;
+ this.extension = extension;
+ }
+ public PDFSetOCGStateAction getAction() {
+ return action;
+ }
+ public Object getExtension() {
+ return extension;
+ }
+ public void resolve() {
+ if (!resolved) {
+ performResolution();
+ resolved = true;
+ }
+ }
+ protected void performResolution() {
+ }
+ }
+
+ private Resolver resolver;
+
+ public PDFSetOCGStateAction(String id) {
+ super(id);
+ put("Type", new PDFName("Action"));
+ put("S", new PDFName("SetOCGState"));
+ }
+
+ @Override
+ public int output(OutputStream stream) throws IOException {
+ if (resolver != null) {
+ resolver.resolve();
+ }
+ return super.output(stream);
+ }
+
+ public void setResolver(Resolver resolver) {
+ this.resolver = resolver;
+ }
+
+ public void populate(Object state, Object preserveRB, Object nextAction) {
+ if (state != null) {
+ put("State", state);
+ }
+ if (preserveRB != null) {
+ put("PreserveRB", preserveRB);
+ }
+ if (nextAction != null) {
+ put("Next", nextAction);
+ }
+ }
+}
diff --git a/src/java/org/apache/fop/pdf/PDFShading.java b/src/java/org/apache/fop/pdf/PDFShading.java
index 62012b9b2..3f7b2b4b0 100644
--- a/src/java/org/apache/fop/pdf/PDFShading.java
+++ b/src/java/org/apache/fop/pdf/PDFShading.java
@@ -22,6 +22,10 @@ package org.apache.fop.pdf;
// Java...
import java.util.List;
+import org.apache.fop.render.shading.Function;
+import org.apache.fop.render.shading.Shading;
+import org.apache.fop.render.shading.ShadingPattern;
+
/**
* class representing a PDF Smooth Shading object.
*
@@ -32,7 +36,7 @@ import java.util.List;
*
* All PDF Functions have a shadingType (0,2,3, or 4), a Domain, and a Range.
*/
-public class PDFShading extends PDFObject {
+public class PDFShading extends PDFObject implements Shading {
// Guts common to all function types
/**
@@ -205,7 +209,7 @@ public class PDFShading extends PDFObject {
public PDFShading(int theShadingType, PDFDeviceColorSpace theColorSpace,
List theBackground, List theBBox,
boolean theAntiAlias, List theCoords,
- List theDomain, PDFFunction theFunction,
+ List theDomain, Function theFunction,
List theExtend) {
super();
this.shadingType = theShadingType; // 2 or 3
@@ -216,7 +220,8 @@ public class PDFShading extends PDFObject {
this.coords = theCoords;
this.domain = theDomain;
- this.function = theFunction;
+ assert theFunction instanceof PDFFunction;
+ this.function = (PDFFunction)theFunction;
this.extend = theExtend;
}
@@ -335,197 +340,8 @@ public class PDFShading extends PDFObject {
* @return the PDF string.
*/
public String toPDFString() {
- int vectorSize;
- int tempInt;
- StringBuffer p = new StringBuffer(128);
- p.append("<<\n/ShadingType " + this.shadingType + " \n");
- if (this.colorSpace != null) {
- p.append("/ColorSpace /"
- + this.colorSpace.getName() + " \n");
- }
-
- if (this.background != null) {
- p.append("/Background [ ");
- vectorSize = this.background.size();
- for (tempInt = 0; tempInt < vectorSize; tempInt++) {
- p.append(PDFNumber.doubleOut((Double)this.background.get(tempInt))
- + " ");
- }
- p.append("] \n");
- }
-
- if (this.bBox
- != null) { // I've never seen an example, so I guess this is right.
- p.append("/BBox [ ");
- vectorSize = this.bBox.size();
- for (tempInt = 0; tempInt < vectorSize; tempInt++) {
- p.append(PDFNumber.doubleOut((Double)this.bBox.get(tempInt))
- + " ");
- }
- p.append("] \n");
- }
-
- if (this.antiAlias) {
- p.append("/AntiAlias " + this.antiAlias + " \n");
- }
-
- // Here's where we differentiate based on what type it is.
- if (this.shadingType == 1) { // function based shading
- if (this.domain != null) {
- p.append("/Domain [ ");
- vectorSize = this.domain.size();
- for (tempInt = 0; tempInt < vectorSize; tempInt++) {
- p.append(PDFNumber.doubleOut((Double)this.domain.get(tempInt))
- + " ");
- }
- p.append("] \n");
- } else {
- p.append("/Domain [ 0 1 ] \n");
- }
-
- if (this.matrix != null) {
- p.append("/Matrix [ ");
- vectorSize = this.matrix.size();
- for (tempInt = 0; tempInt < vectorSize; tempInt++) {
- p.append(PDFNumber.doubleOut((Double)this.matrix.get(tempInt))
- + " ");
- }
- p.append("] \n");
- }
-
- if (this.function != null) {
- p.append("/Function ");
- p.append(this.function.referencePDF() + " \n");
- }
- } else if ((this.shadingType == 2)
- || (this.shadingType
- == 3)) { // 2 is axial shading (linear gradient)
- // 3 is radial shading (circular gradient)
- if (this.coords != null) {
- p.append("/Coords [ ");
- vectorSize = this.coords.size();
- for (tempInt = 0; tempInt < vectorSize; tempInt++) {
- p.append(PDFNumber.doubleOut((Double)this.coords.get(tempInt))
- + " ");
- }
- p.append("] \n");
- }
-
- // DOMAIN
- if (this.domain != null) {
- p.append("/Domain [ ");
- vectorSize = this.domain.size();
- for (tempInt = 0; tempInt < vectorSize; tempInt++) {
- p.append(PDFNumber.doubleOut((Double)this.domain.get(tempInt))
- + " ");
- }
- p.append("] \n");
- } else {
- p.append("/Domain [ 0 1 ] \n");
- }
-
- if (this.extend != null) {
- p.append("/Extend [ ");
- vectorSize = this.extend.size();
- for (tempInt = 0; tempInt < vectorSize; tempInt++) {
- p.append(((Boolean)this.extend.get(tempInt)) + " ");
- }
-
- p.append("] \n");
- } else {
- p.append("/Extend [ true true ] \n");
- }
-
-
- if (this.function != null) {
- p.append("/Function ");
- p.append(this.function.referencePDF() + " \n");
- }
-
-
- } else if ((this.shadingType == 4) || (this.shadingType == 6)
- || (this.shadingType
- == 7)) { // 4:Free-form Gouraud-shaded triangle meshes
- // 6:coons patch meshes
- // 7://tensor product patch meshes (which no one ever uses)
- if (this.bitsPerCoordinate > 0) {
- p.append("/BitsPerCoordinate " + this.bitsPerCoordinate
- + " \n");
- } else {
- p.append("/BitsPerCoordinate 1 \n");
- }
-
- if (this.bitsPerComponent > 0) {
- p.append("/BitsPerComponent " + this.bitsPerComponent
- + " \n");
- } else {
- p.append("/BitsPerComponent 1 \n");
- }
-
- if (this.bitsPerFlag > 0) {
- p.append("/BitsPerFlag " + this.bitsPerFlag + " \n");
- } else {
- p.append("/BitsPerFlag 2 \n");
- }
-
- if (this.decode != null) {
- p.append("/Decode [ ");
- vectorSize = this.decode.size();
- for (tempInt = 0; tempInt < vectorSize; tempInt++) {
- p.append(((Boolean)this.decode.get(tempInt)) + " ");
- }
-
- p.append("] \n");
- }
-
- if (this.function != null) {
- p.append("/Function ");
- p.append(this.function.referencePDF() + " \n");
- }
-
- } else if (this.shadingType
- == 5) { // Lattice Free form gouraud-shaded triangle mesh
-
- if (this.bitsPerCoordinate > 0) {
- p.append("/BitsPerCoordinate " + this.bitsPerCoordinate
- + " \n");
- } else {
- p.append("/BitsPerCoordinate 1 \n");
- }
-
- if (this.bitsPerComponent > 0) {
- p.append("/BitsPerComponent " + this.bitsPerComponent
- + " \n");
- } else {
- p.append("/BitsPerComponent 1 \n");
- }
-
- if (this.decode != null) {
- p.append("/Decode [ ");
- vectorSize = this.decode.size();
- for (tempInt = 0; tempInt < vectorSize; tempInt++) {
- p.append(((Boolean)this.decode.get(tempInt)) + " ");
- }
-
- p.append("] \n");
- }
-
- if (this.function != null) {
- p.append("/Function ");
- p.append(this.function.referencePDF() + " \n");
- }
-
- if (this.verticesPerRow > 0) {
- p.append("/VerticesPerRow " + this.verticesPerRow + " \n");
- } else {
- p.append("/VerticesPerRow 2 \n");
- }
-
- }
-
- p.append(">>");
-
- return (p.toString());
+ ShadingPattern pattern = new ShadingPattern(this);
+ return pattern.toString(colorSpace, shadingType, background, bBox, antiAlias);
}
/** {@inheritDoc} */
@@ -623,4 +439,173 @@ public class PDFShading extends PDFObject {
}
return true;
}
+
+ /**
+ * A method to write a type 1 shading object
+ * @param p The StringBuffer to write the shading object
+ * @return Returns the StringBuffer to which the shading object was written
+ */
+ public StringBuffer handleShadingType1(StringBuffer p) {
+ if (this.domain != null) {
+ p.append("/Domain [ ");
+ for (int domainIndex = 0; domainIndex < domain.size(); domainIndex++) {
+ p.append(PDFNumber.doubleOut((Double)this.domain.get(domainIndex))
+ + " ");
+ }
+ p.append("] \n");
+ } else {
+ p.append("/Domain [ 0 1 ] \n");
+ }
+
+ if (this.matrix != null) {
+ p.append("/Matrix [ ");
+ for (int matrixIndex = 0; matrixIndex < matrix.size(); matrixIndex++) {
+ p.append(PDFNumber.doubleOut((Double)this.matrix.get(matrixIndex))
+ + " ");
+ }
+ p.append("] \n");
+ }
+
+ if (this.function != null) {
+ p.append("/Function ");
+ p.append(this.function.referencePDF() + " \n");
+ }
+ return p;
+ }
+
+ /**
+ * A method to write a type 2 or 3 shading object
+ * @param p The StringBuffer to write the shading object
+ * @return Returns the StringBuffer to which the shading object was written
+ */
+ public StringBuffer handleShadingType2or3(StringBuffer p) {
+ // 3 is radial shading (circular gradient)
+ if (this.coords != null) {
+ p.append("/Coords [ ");
+ for (int coordIndex = 0; coordIndex < coords.size(); coordIndex++) {
+ p.append(PDFNumber.doubleOut((Double)this.coords.get(coordIndex))
+ + " ");
+ }
+ p.append("] \n");
+ }
+
+ // DOMAIN
+ if (this.domain != null) {
+ p.append("/Domain [ ");
+ for (int domainIndex = 0; domainIndex < domain.size(); domainIndex++) {
+ p.append(PDFNumber.doubleOut((Double)this.domain.get(domainIndex))
+ + " ");
+ }
+ p.append("] \n");
+ } else {
+ p.append("/Domain [ 0 1 ] \n");
+ }
+
+ if (this.extend != null) {
+ p.append("/Extend [ ");
+ for (int extendIndex = 0; extendIndex < extend.size(); extendIndex++) {
+ p.append((this.extend.get(extendIndex)) + " ");
+ }
+
+ p.append("] \n");
+ } else {
+ p.append("/Extend [ true true ] \n");
+ }
+
+
+ if (this.function != null) {
+ p.append("/Function ");
+ p.append(this.function.referencePDF() + " \n");
+ }
+
+ return p;
+ }
+
+ /**
+ * A method to write a type 4, 6 or 7 shading object
+ * @param p The StringBuffer to write the shading object
+ * @return Returns the StringBuffer to which the shading object was written
+ */
+ public StringBuffer handleShadingType4or6or7(StringBuffer p) {
+ // 6:coons patch meshes
+ // 7://tensor product patch meshes (which no one ever uses)
+ if (this.bitsPerCoordinate > 0) {
+ p.append("/BitsPerCoordinate " + this.bitsPerCoordinate
+ + " \n");
+ } else {
+ p.append("/BitsPerCoordinate 1 \n");
+ }
+
+ if (this.bitsPerComponent > 0) {
+ p.append("/BitsPerComponent " + this.bitsPerComponent
+ + " \n");
+ } else {
+ p.append("/BitsPerComponent 1 \n");
+ }
+
+ if (this.bitsPerFlag > 0) {
+ p.append("/BitsPerFlag " + this.bitsPerFlag + " \n");
+ } else {
+ p.append("/BitsPerFlag 2 \n");
+ }
+
+ if (this.decode != null) {
+ p.append("/Decode [ ");
+ for (int decodeIndex = 0; decodeIndex < decode.size(); decodeIndex++) {
+ p.append((this.decode.get(decodeIndex)) + " ");
+ }
+
+ p.append("] \n");
+ }
+
+ if (this.function != null) {
+ p.append("/Function ");
+ p.append(this.function.referencePDF() + " \n");
+ }
+
+ return p;
+ }
+
+ /**
+ * A method to write a type 5 shading object
+ * @param p The StringBuffer to write the shading object
+ * @return Returns the StringBuffer to which the shading object was written
+ */
+ public StringBuffer handleShadingType5(StringBuffer p) {
+ if (this.bitsPerCoordinate > 0) {
+ p.append("/BitsPerCoordinate " + this.bitsPerCoordinate
+ + " \n");
+ } else {
+ p.append("/BitsPerCoordinate 1 \n");
+ }
+
+ if (this.bitsPerComponent > 0) {
+ p.append("/BitsPerComponent " + this.bitsPerComponent
+ + " \n");
+ } else {
+ p.append("/BitsPerComponent 1 \n");
+ }
+
+ if (this.decode != null) {
+ p.append("/Decode [ ");
+ for (int decodeIndex = 0; decodeIndex < decode.size(); decodeIndex++) {
+ p.append((this.decode.get(decodeIndex)) + " ");
+ }
+
+ p.append("] \n");
+ }
+
+ if (this.function != null) {
+ p.append("/Function ");
+ p.append(this.function.referencePDF() + " \n");
+ }
+
+ if (this.verticesPerRow > 0) {
+ p.append("/VerticesPerRow " + this.verticesPerRow + " \n");
+ } else {
+ p.append("/VerticesPerRow 2 \n");
+ }
+
+ return p;
+ }
}
diff --git a/src/java/org/apache/fop/pdf/PDFTransitionAction.java b/src/java/org/apache/fop/pdf/PDFTransitionAction.java
new file mode 100644
index 000000000..01f8fcf21
--- /dev/null
+++ b/src/java/org/apache/fop/pdf/PDFTransitionAction.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.pdf;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+public class PDFTransitionAction extends PDFNavigatorAction {
+
+ public abstract static class Resolver {
+ private boolean resolved;
+ private PDFTransitionAction action;
+ private Object extension;
+ public Resolver(PDFTransitionAction action, Object extension) {
+ this.action = action;
+ this.extension = extension;
+ }
+ public PDFTransitionAction getAction() {
+ return action;
+ }
+ public Object getExtension() {
+ return extension;
+ }
+ public void resolve() {
+ if (!resolved) {
+ performResolution();
+ resolved = true;
+ }
+ }
+ protected void performResolution() {
+ }
+ }
+
+ private Resolver resolver;
+
+ public PDFTransitionAction(String id) {
+ super(id);
+ put("Type", new PDFName("Action"));
+ put("S", new PDFName("Trans"));
+ }
+
+ @Override
+ public int output(OutputStream stream) throws IOException {
+ if (resolver != null) {
+ resolver.resolve();
+ }
+ return super.output(stream);
+ }
+
+ public void setResolver(Resolver resolver) {
+ this.resolver = resolver;
+ }
+
+ public void populate(Object transition, Object nextAction) {
+ if (transition != null) {
+ put("Trans", transition);
+ }
+ if (nextAction != null) {
+ put("Next", nextAction);
+ }
+ }
+}
diff --git a/src/java/org/apache/fop/render/AbstractRenderer.java b/src/java/org/apache/fop/render/AbstractRenderer.java
index e274e5c4b..9a94e0cc6 100644
--- a/src/java/org/apache/fop/render/AbstractRenderer.java
+++ b/src/java/org/apache/fop/render/AbstractRenderer.java
@@ -28,6 +28,7 @@ import java.io.OutputStream;
import java.util.List;
import java.util.Locale;
import java.util.Set;
+import java.util.Stack;
import org.w3c.dom.Document;
@@ -112,8 +113,12 @@ public abstract class AbstractRenderer
/** the currently active PageViewport */
protected PageViewport currentPageViewport;
+ /* warned XML handlers */
private Set warnedXMLHandlers;
+ /* layers stack */
+ private Stack<String> layers;
+
/** {@inheritDoc} */
public abstract void setupFontInfo(FontInfo fontInfo) throws FOPException;
@@ -471,6 +476,10 @@ public abstract class AbstractRenderer
* @param children The children to render within the block viewport
*/
protected void renderBlockViewport(BlockViewport bv, List children) {
+ boolean inNewLayer = false;
+ if (maybeStartLayer(bv)) {
+ inNewLayer = true;
+ }
// clip and position viewport if necessary
if (bv.getPositioning() == Block.ABSOLUTE) {
// save positions
@@ -506,6 +515,7 @@ public abstract class AbstractRenderer
currentIPPosition = saveIP;
currentBPPosition = saveBP + bv.getAllocBPD();
}
+ maybeEndLayer(bv, inNewLayer);
}
/**
@@ -573,6 +583,10 @@ public abstract class AbstractRenderer
protected void renderBlock(Block block) {
assert block != null;
List children = block.getChildAreas();
+ boolean inNewLayer = false;
+ if (maybeStartLayer(block)) {
+ inNewLayer = true;
+ }
if (block instanceof BlockViewport) {
if (children != null) {
renderBlockViewport((BlockViewport) block, children);
@@ -607,6 +621,45 @@ public abstract class AbstractRenderer
currentBPPosition = saveBP + block.getAllocBPD();
}
}
+ maybeEndLayer(block, inNewLayer);
+ }
+
+ /**
+ * Establish new optional content group layer.
+ * @param layer name of layer
+ */
+ protected abstract void startLayer(String layer);
+
+ /**
+ * Finish current optional content group layer.
+ */
+ protected abstract void endLayer();
+
+ protected boolean maybeStartLayer(Area area) {
+ String layer = (String) area.getTrait(Trait.LAYER);
+ if (layer != null) {
+ if (layers == null) {
+ layers = new Stack<String>();
+ }
+ if (layers.empty() || !layers.peek().equals(layer)) {
+ layers.push(layer);
+ startLayer(layer);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ protected void maybeEndLayer(Area area, boolean inNewLayer) {
+ if (inNewLayer) {
+ assert layers != null;
+ assert !layers.empty();
+ String layer = (String) area.getTrait(Trait.LAYER);
+ assert layer != null;
+ assert layers.peek().equals(layer);
+ endLayer();
+ layers.pop();
+ }
}
/**
@@ -746,6 +799,10 @@ public abstract class AbstractRenderer
* @param ip the inline parent to render
*/
protected void renderInlineParent(InlineParent ip) {
+ boolean inNewLayer = false;
+ if (maybeStartLayer(ip)) {
+ inNewLayer = true;
+ }
int level = ip.getBidiLevel();
List children = ip.getChildAreas();
renderInlineAreaBackAndBorders(ip);
@@ -782,6 +839,7 @@ public abstract class AbstractRenderer
}
currentIPPosition = saveIP + ip.getAllocIPD();
currentBPPosition = saveBP;
+ maybeEndLayer(ip, inNewLayer);
}
/**
diff --git a/src/java/org/apache/fop/render/afp/AFPFontConfig.java b/src/java/org/apache/fop/render/afp/AFPFontConfig.java
index aef0b666c..149973edd 100644
--- a/src/java/org/apache/fop/render/afp/AFPFontConfig.java
+++ b/src/java/org/apache/fop/render/afp/AFPFontConfig.java
@@ -316,8 +316,8 @@ public final class AFPFontConfig implements FontConfig {
private final String characterset;
- private CIDKeyedFontConfig(List<FontTriplet> triplets, String type, String codePage,
- String encoding, String characterset, String name, CharacterSetType charsetType, boolean embeddable, String uri) {
+ private CIDKeyedFontConfig(List<FontTriplet> triplets, String type, String codePage, String encoding,
+ String characterset, String name, CharacterSetType charsetType, boolean embeddable, String uri) {
super(triplets, type, codePage, encoding, name, embeddable, uri);
this.characterset = characterset;
this.charsetType = charsetType;
diff --git a/src/java/org/apache/fop/render/afp/AFPPainter.java b/src/java/org/apache/fop/render/afp/AFPPainter.java
index 12713bb24..aaa702afa 100644
--- a/src/java/org/apache/fop/render/afp/AFPPainter.java
+++ b/src/java/org/apache/fop/render/afp/AFPPainter.java
@@ -164,7 +164,7 @@ public class AFPPainter extends AbstractIFPainter<AFPDocumentHandler> {
}
/** {@inheritDoc} */
- public void startGroup(AffineTransform transform) throws IFException {
+ public void startGroup(AffineTransform transform, String layer) throws IFException {
try {
saveGraphicsState();
concatenateTransformationMatrix(transform);
diff --git a/src/java/org/apache/fop/render/afp/AFPRendererConfig.java b/src/java/org/apache/fop/render/afp/AFPRendererConfig.java
index 90cc6e767..a943f5aad 100644
--- a/src/java/org/apache/fop/render/afp/AFPRendererConfig.java
+++ b/src/java/org/apache/fop/render/afp/AFPRendererConfig.java
@@ -103,7 +103,8 @@ public final class AFPRendererConfig implements RendererConfig {
}
}
- private final EnumMap<AFPRendererOption, Object> params = new EnumMap<AFPRendererOption, Object>(AFPRendererOption.class);
+ private final EnumMap<AFPRendererOption, Object> params
+ = new EnumMap<AFPRendererOption, Object>(AFPRendererOption.class);
private final EnumMap<ImagesModeOptions, Object> imageModeParams
= new EnumMap<ImagesModeOptions, Object>(ImagesModeOptions.class);
diff --git a/src/java/org/apache/fop/render/intermediate/AbstractIFPainter.java b/src/java/org/apache/fop/render/intermediate/AbstractIFPainter.java
index 2dd046fa9..f69fe2091 100644
--- a/src/java/org/apache/fop/render/intermediate/AbstractIFPainter.java
+++ b/src/java/org/apache/fop/render/intermediate/AbstractIFPainter.java
@@ -126,8 +126,8 @@ public abstract class AbstractIFPainter<T extends IFDocumentHandler> implements
}
/** {@inheritDoc} */
- public void startGroup(AffineTransform[] transforms) throws IFException {
- startGroup(combine(transforms));
+ public void startGroup(AffineTransform[] transforms, String layer) throws IFException {
+ startGroup(combine(transforms), layer);
}
/**
diff --git a/src/java/org/apache/fop/render/intermediate/AbstractXMLWritingIFDocumentHandler.java b/src/java/org/apache/fop/render/intermediate/AbstractXMLWritingIFDocumentHandler.java
index fcf8554a6..0c3305797 100644
--- a/src/java/org/apache/fop/render/intermediate/AbstractXMLWritingIFDocumentHandler.java
+++ b/src/java/org/apache/fop/render/intermediate/AbstractXMLWritingIFDocumentHandler.java
@@ -54,10 +54,10 @@ public abstract class AbstractXMLWritingIFDocumentHandler extends AbstractIFDocu
if (result instanceof SAXResult) {
SAXResult saxResult = (SAXResult)result;
this.handler = new GenerationHelperContentHandler(
- saxResult.getHandler(), getMainNamespace());
+ saxResult.getHandler(), getMainNamespace(), getContext());
} else {
this.handler = new GenerationHelperContentHandler(
- createContentHandler(result), getMainNamespace());
+ createContentHandler(result), getMainNamespace(), getContext());
}
}
diff --git a/src/java/org/apache/fop/render/intermediate/IFContext.java b/src/java/org/apache/fop/render/intermediate/IFContext.java
index fda0cff3b..7464e26e0 100644
--- a/src/java/org/apache/fop/render/intermediate/IFContext.java
+++ b/src/java/org/apache/fop/render/intermediate/IFContext.java
@@ -55,6 +55,8 @@ public class IFContext {
private boolean hyphenated;
+ private int pageIndex = -1;
+
/**
* Main constructor.
* @param ua the user agent
@@ -216,4 +218,20 @@ public class IFContext {
return hyphenated;
}
+ /**
+ * Record current page index.
+ * @param pageIndex a zero based page index or -1 (no page)
+ */
+ public void setPageIndex(int pageIndex) {
+ this.pageIndex = pageIndex;
+ }
+
+ /**
+ * Obtain current page index.
+ * @return a zero based page index or -1 (no page)
+ */
+ public int getPageIndex() {
+ return this.pageIndex;
+ }
+
}
diff --git a/src/java/org/apache/fop/render/intermediate/IFGraphicContext.java b/src/java/org/apache/fop/render/intermediate/IFGraphicContext.java
index 868615360..c1742be1f 100644
--- a/src/java/org/apache/fop/render/intermediate/IFGraphicContext.java
+++ b/src/java/org/apache/fop/render/intermediate/IFGraphicContext.java
@@ -89,6 +89,7 @@ public class IFGraphicContext extends GraphicContext {
public static class Group {
private AffineTransform[] transforms;
+ private String layer;
/**
* Construct a Group.
@@ -106,6 +107,16 @@ public class IFGraphicContext extends GraphicContext {
this(new AffineTransform[] {transform});
}
+ /**
+ * Construct a layer Group, i.e., a Group with no transforms
+ * but with a optional content group layer label.
+ * @param layer a layer label
+ */
+ public Group(String layer) {
+ this();
+ this.layer = layer;
+ }
+
/** Default constructor. */
public Group() {
this(EMPTY_TRANSFORM_ARRAY);
@@ -116,12 +127,17 @@ public class IFGraphicContext extends GraphicContext {
return this.transforms;
}
+ /** @return layer */
+ public String getLayer() {
+ return this.layer;
+ }
+
/**
* @param painter a painter
* @throws IFException in not caught
*/
public void start(IFPainter painter) throws IFException {
- painter.startGroup(transforms);
+ painter.startGroup(transforms, layer);
}
/**
@@ -136,6 +152,11 @@ public class IFGraphicContext extends GraphicContext {
public String toString() {
StringBuffer sb = new StringBuffer("group: ");
IFUtil.toString(getTransforms(), sb);
+ if ((layer != null) && (layer.length() > 0)) {
+ sb.append(" layer(");
+ sb.append(layer);
+ sb.append(')');
+ }
return sb.toString();
}
diff --git a/src/java/org/apache/fop/render/intermediate/IFPainter.java b/src/java/org/apache/fop/render/intermediate/IFPainter.java
index 599292287..d43b5cb85 100644
--- a/src/java/org/apache/fop/render/intermediate/IFPainter.java
+++ b/src/java/org/apache/fop/render/intermediate/IFPainter.java
@@ -113,19 +113,21 @@ public interface IFPainter {
/**
* Starts a new group of graphical elements. Corresponds to SVG's g element.
* @param transforms a series of transformation matrices establishing the new coordinate system
+ * @param layer an optional layer label (or null if none)
* @throws IFException if an error occurs while handling this element
*/
- void startGroup(AffineTransform[] transforms) throws IFException;
+ void startGroup(AffineTransform[] transforms, String layer) throws IFException;
/**
* Starts a new group of graphical elements. Corresponds to SVG's g element.
* @param transform the transformation matrix establishing the new coordinate system
+ * @param layer an optional layer label (or null if none)
* @throws IFException if an error occurs while handling this element
*/
- void startGroup(AffineTransform transform) throws IFException;
+ void startGroup(AffineTransform transform, String layer) throws IFException;
/**
- * Ends the current group and restores the previous coordinate system.
+ * Ends the current group and restores the previous coordinate system (and layer).
* @throws IFException if an error occurs while handling this element
*/
void endGroup() throws IFException;
diff --git a/src/java/org/apache/fop/render/intermediate/IFParser.java b/src/java/org/apache/fop/render/intermediate/IFParser.java
index 1af1d4a06..519726291 100644
--- a/src/java/org/apache/fop/render/intermediate/IFParser.java
+++ b/src/java/org/apache/fop/render/intermediate/IFParser.java
@@ -592,7 +592,8 @@ public class IFParser implements IFConstants {
String transform = attributes.getValue("transform");
AffineTransform[] transforms
= AffineTransformArrayParser.createAffineTransform(transform);
- painter.startGroup(transforms);
+ String layer = attributes.getValue("layer");
+ painter.startGroup(transforms, layer);
}
public void endElement() throws IFException {
@@ -800,8 +801,8 @@ public class IFParser implements IFConstants {
}
painter.drawImage(uri, new Rectangle(x, y, width, height));
}
- resetForeignAttributes();
resetStructureTreeElement();
+ resetForeignAttributes();
inForeignObject = false;
}
diff --git a/src/java/org/apache/fop/render/intermediate/IFRenderer.java b/src/java/org/apache/fop/render/intermediate/IFRenderer.java
index 9e6aae700..e40a8f6b3 100644
--- a/src/java/org/apache/fop/render/intermediate/IFRenderer.java
+++ b/src/java/org/apache/fop/render/intermediate/IFRenderer.java
@@ -571,6 +571,7 @@ public class IFRenderer extends AbstractPathOrientedRenderer {
Dimension dim = new Dimension(viewArea.width, viewArea.height);
establishForeignAttributes(page.getForeignAttributes());
+ documentHandler.getContext().setPageIndex(page.getPageIndex());
documentHandler.startPage(page.getPageIndex(), page.getPageNumberString(),
page.getSimplePageMasterName(), dim);
resetForeignAttributes();
@@ -598,6 +599,7 @@ public class IFRenderer extends AbstractPathOrientedRenderer {
establishForeignAttributes(page.getForeignAttributes());
documentHandler.endPage();
+ documentHandler.getContext().setPageIndex(-1);
resetForeignAttributes();
} catch (IFException e) {
handleIFException(e);
@@ -740,6 +742,12 @@ public class IFRenderer extends AbstractPathOrientedRenderer {
protected void renderBlockViewport(BlockViewport bv, List children) {
//Essentially the same code as in the super class but optimized for the IF
+ // Handle new layer.
+ boolean inNewLayer = false;
+ if (maybeStartLayer(bv)) {
+ inNewLayer = true;
+ }
+
//This is the content-rect
Dimension dim = new Dimension(bv.getIPD(), bv.getBPD());
viewportDimensionStack.push(dim);
@@ -840,6 +848,7 @@ public class IFRenderer extends AbstractPathOrientedRenderer {
currentBPPosition += bv.getAllocBPD();
}
viewportDimensionStack.pop();
+ maybeEndLayer(bv, inNewLayer);
}
/** {@inheritDoc} */
@@ -847,7 +856,7 @@ public class IFRenderer extends AbstractPathOrientedRenderer {
StructureTreeElement structElem
= (StructureTreeElement) viewport.getTrait(Trait.STRUCTURE_TREE_ELEMENT);
establishStructureTreeElement(structElem);
- pushdID(viewport);
+ pushID(viewport);
Dimension dim = new Dimension(viewport.getIPD(), viewport.getBPD());
viewportDimensionStack.push(dim);
super.renderInlineViewport(viewport);
@@ -894,9 +903,26 @@ public class IFRenderer extends AbstractPathOrientedRenderer {
}
/** {@inheritDoc} */
+ protected void startLayer(String layer) {
+ if (log.isTraceEnabled()) {
+ log.trace("startLayer() layer=" + layer);
+ }
+ saveGraphicsState();
+ pushGroup(new IFGraphicContext.Group(layer));
+ }
+
+ /** {@inheritDoc} */
+ protected void endLayer() {
+ if (log.isTraceEnabled()) {
+ log.trace("endLayer()");
+ }
+ restoreGraphicsState();
+ }
+
+ /** {@inheritDoc} */
protected void renderInlineArea(InlineArea inlineArea) {
saveInlinePosIfTargetable(inlineArea);
- pushdID(inlineArea);
+ pushID(inlineArea);
super.renderInlineArea(inlineArea);
popID(inlineArea);
}
@@ -963,7 +989,7 @@ public class IFRenderer extends AbstractPathOrientedRenderer {
log.trace("renderBlock() " + block);
}
saveBlockPosIfTargetable(block);
- pushdID(block);
+ pushID(block);
IFContext context = documentHandler.getContext();
Locale oldLocale = context.getLanguage();
context.setLanguage(block.getLocale());
@@ -975,7 +1001,7 @@ public class IFRenderer extends AbstractPathOrientedRenderer {
popID(block);
}
- private void pushdID(Area area) {
+ private void pushID(Area area) {
String prodID = (String) area.getTrait(Trait.PROD_ID);
if (prodID != null) {
ids.push(prodID);
diff --git a/src/java/org/apache/fop/render/intermediate/IFSerializer.java b/src/java/org/apache/fop/render/intermediate/IFSerializer.java
index 62e4cc67d..395e79719 100644
--- a/src/java/org/apache/fop/render/intermediate/IFSerializer.java
+++ b/src/java/org/apache/fop/render/intermediate/IFSerializer.java
@@ -303,6 +303,7 @@ implements IFConstants, IFPainter, IFDocumentNavigationHandler {
addAttribute(atts, "width", Integer.toString(size.width));
addAttribute(atts, "height", Integer.toString(size.height));
addForeignAttributes(atts);
+ getContext().setPageIndex(index);
handler.startElement(EL_PAGE, atts);
} catch (SAXException e) {
throw new IFException("SAX error in startPage()", e);
@@ -379,6 +380,7 @@ implements IFConstants, IFPainter, IFDocumentNavigationHandler {
public void endPage() throws IFException {
try {
handler.endElement(EL_PAGE);
+ getContext().setPageIndex(-1);
} catch (SAXException e) {
throw new IFException("SAX error in endPage()", e);
}
@@ -426,21 +428,24 @@ implements IFConstants, IFPainter, IFDocumentNavigationHandler {
}
/** {@inheritDoc} */
- public void startGroup(AffineTransform[] transforms) throws IFException {
- startGroup(IFUtil.toString(transforms));
+ public void startGroup(AffineTransform[] transforms, String layer) throws IFException {
+ startGroup(IFUtil.toString(transforms), layer);
}
/** {@inheritDoc} */
- public void startGroup(AffineTransform transform) throws IFException {
- startGroup(IFUtil.toString(transform));
+ public void startGroup(AffineTransform transform, String layer) throws IFException {
+ startGroup(IFUtil.toString(transform), layer);
}
- private void startGroup(String transform) throws IFException {
+ private void startGroup(String transform, String layer) throws IFException {
try {
AttributesImpl atts = new AttributesImpl();
if (transform != null && transform.length() > 0) {
addAttribute(atts, "transform", transform);
}
+ if (layer != null && layer.length() > 0) {
+ addAttribute(atts, "layer", layer);
+ }
handler.startElement(EL_GROUP, atts);
} catch (SAXException e) {
throw new IFException("SAX error in startGroup()", e);
diff --git a/src/java/org/apache/fop/render/java2d/Java2DPainter.java b/src/java/org/apache/fop/render/java2d/Java2DPainter.java
index 07440ff0b..328d1a4f8 100644
--- a/src/java/org/apache/fop/render/java2d/Java2DPainter.java
+++ b/src/java/org/apache/fop/render/java2d/Java2DPainter.java
@@ -143,7 +143,7 @@ public class Java2DPainter extends AbstractIFPainter<Java2DDocumentHandler> {
}
/** {@inheritDoc} */
- public void startGroup(AffineTransform transform) throws IFException {
+ public void startGroup(AffineTransform transform, String layer) throws IFException {
saveGraphicsState();
try {
concatenateTransformationMatrix(transform);
diff --git a/src/java/org/apache/fop/render/java2d/Java2DRenderer.java b/src/java/org/apache/fop/render/java2d/Java2DRenderer.java
index a98aff353..d343c55d8 100644
--- a/src/java/org/apache/fop/render/java2d/Java2DRenderer.java
+++ b/src/java/org/apache/fop/render/java2d/Java2DRenderer.java
@@ -476,6 +476,14 @@ public abstract class Java2DRenderer extends AbstractPathOrientedRenderer implem
}
/** {@inheritDoc} */
+ protected void startLayer(String layer) {
+ }
+
+ /** {@inheritDoc} */
+ protected void endLayer() {
+ }
+
+ /** {@inheritDoc} */
protected List breakOutOfStateStack() {
log.debug("Block.FIXED --> break out");
List breakOutList;
diff --git a/src/java/org/apache/fop/render/pcl/PCLPainter.java b/src/java/org/apache/fop/render/pcl/PCLPainter.java
index c51ee834f..f934eed8c 100644
--- a/src/java/org/apache/fop/render/pcl/PCLPainter.java
+++ b/src/java/org/apache/fop/render/pcl/PCLPainter.java
@@ -126,7 +126,7 @@ public class PCLPainter extends AbstractIFPainter<PCLDocumentHandler> implements
}
/** {@inheritDoc} */
- public void startGroup(AffineTransform transform) throws IFException {
+ public void startGroup(AffineTransform transform, String layer) throws IFException {
saveGraphicsState();
try {
concatenateTransformationMatrix(transform);
diff --git a/src/java/org/apache/fop/render/pdf/AbstractImageAdapter.java b/src/java/org/apache/fop/render/pdf/AbstractImageAdapter.java
index 58299c528..471efc567 100644
--- a/src/java/org/apache/fop/render/pdf/AbstractImageAdapter.java
+++ b/src/java/org/apache/fop/render/pdf/AbstractImageAdapter.java
@@ -23,6 +23,8 @@ import java.awt.color.ICC_Profile;
import java.awt.image.DataBufferByte;
import java.awt.image.IndexColorModel;
import java.awt.image.Raster;
+import java.io.IOException;
+import java.util.Arrays;
import org.apache.commons.io.output.ByteArrayOutputStream;
import org.apache.commons.logging.Log;
@@ -239,24 +241,40 @@ public abstract class AbstractImageAdapter implements PDFImage {
+ " The image may not be handled correctly." + " Base color space: "
+ icm.getColorSpace() + " Image: " + image.getInfo());
}
- indexed.add(new PDFName(toPDFColorSpace(icm.getColorSpace()).getName()));
+ ByteArrayOutputStream baout = new ByteArrayOutputStream();
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));
+ boolean isDeviceGray = false;
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);
+ byte[] reds = new byte[c];
+ byte[] greens = new byte[c];
+ byte[] blues = new byte[c];
+ icm.getReds(reds);
+ icm.getGreens(greens);
+ icm.getBlues(blues);
+ isDeviceGray = Arrays.equals(reds, blues) && Arrays.equals(blues, greens);
+ if (isDeviceGray) {
+ indexed.add(new PDFName("DeviceGray"));
+ try {
+ baout.write(blues);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ } else {
+ indexed.add(new PDFName(toPDFColorSpace(icm.getColorSpace()).getName()));
+ for (int i = 0; i < c; i++) {
+ int entry = palette[i];
+ baout.write((entry & 0xFF0000) >> 16);
+ baout.write((entry & 0xFF00) >> 8);
+ baout.write(entry & 0xFF);
+ }
}
+ indexed.add(hival);
+
indexed.add(baout.toByteArray());
dict.put("ColorSpace", indexed);
diff --git a/src/java/org/apache/fop/render/pdf/PDFContentGenerator.java b/src/java/org/apache/fop/render/pdf/PDFContentGenerator.java
index dde6b0ef3..ac7b1d905 100644
--- a/src/java/org/apache/fop/render/pdf/PDFContentGenerator.java
+++ b/src/java/org/apache/fop/render/pdf/PDFContentGenerator.java
@@ -30,6 +30,7 @@ import org.apache.fop.pdf.PDFDocument;
import org.apache.fop.pdf.PDFFilterList;
import org.apache.fop.pdf.PDFNumber;
import org.apache.fop.pdf.PDFPaintingState;
+import org.apache.fop.pdf.PDFReference;
import org.apache.fop.pdf.PDFResourceContext;
import org.apache.fop.pdf.PDFStream;
import org.apache.fop.pdf.PDFText;
@@ -55,7 +56,7 @@ public class PDFContentGenerator {
private PDFColorHandler colorHandler;
/** drawing state */
- protected PDFPaintingState currentState = null;
+ protected PDFPaintingState currentState;
/** Text generation utility holding the current font status */
protected PDFTextUtil textutil;
@@ -156,15 +157,23 @@ public class PDFContentGenerator {
*/
protected void comment(String text) {
if (WRITE_COMMENTS) {
- currentStream.add("% " + text + "\n");
+ getStream().add("% " + text + "\n");
}
}
/** Save graphics state. */
protected void saveGraphicsState() {
endTextObject();
- currentState.save();
- currentStream.add("q\n");
+ getState().save();
+ getStream().add("q\n");
+ }
+
+ /** Save graphics state with optional layer. */
+ protected void saveGraphicsState(String layer) {
+ endTextObject();
+ getState().save();
+ maybeBeginLayer(layer);
+ getStream().add("q\n");
}
/**
@@ -174,9 +183,9 @@ public class PDFContentGenerator {
*/
protected void saveGraphicsState(String structElemType, int sequenceNum) {
endTextObject();
- currentState.save();
+ getState().save();
beginMarkedContentSequence(structElemType, sequenceNum);
- currentStream.add("q\n");
+ getStream().add("q\n");
}
/**
@@ -208,18 +217,18 @@ public class PDFContentGenerator {
if (structElemType != null) {
String actualTextProperty = actualText == null ? ""
: " /ActualText " + PDFText.escapeText(actualText);
- currentStream.add(structElemType + " <</MCID " + String.valueOf(mcid)
+ getStream().add(structElemType + " <</MCID " + String.valueOf(mcid)
+ actualTextProperty + ">>\n"
+ "BDC\n");
} else {
- currentStream.add("/Artifact\nBMC\n");
+ getStream().add("/Artifact\nBMC\n");
this.inArtifactMode = true;
}
this.inMarkedContentSequence = true;
}
void endMarkedContentSequence() {
- currentStream.add("EMC\n");
+ getStream().add("EMC\n");
this.inMarkedContentSequence = false;
this.inArtifactMode = false;
}
@@ -231,9 +240,10 @@ public class PDFContentGenerator {
*/
protected void restoreGraphicsState(boolean popState) {
endTextObject();
- currentStream.add("Q\n");
+ getStream().add("Q\n");
+ maybeEndLayer();
if (popState) {
- currentState.restore();
+ getState().restore();
}
}
@@ -251,11 +261,42 @@ public class PDFContentGenerator {
*/
protected void restoreGraphicsStateAccess() {
endTextObject();
- currentStream.add("Q\n");
+ getStream().add("Q\n");
if (this.inMarkedContentSequence) {
endMarkedContentSequence();
}
- currentState.restore();
+ getState().restore();
+ }
+
+ private void maybeBeginLayer(String layer) {
+ if ((layer != null) && (layer.length() > 0)) {
+ getState().setLayer(layer);
+ beginOptionalContent(layer);
+ }
+ }
+
+ private void maybeEndLayer() {
+ if (getState().getLayerChanged()) {
+ endOptionalContent();
+ }
+ }
+
+ private int ocNameIndex = 0;
+
+ private void beginOptionalContent(String layerId) {
+ String name;
+ PDFReference layer = document.resolveExtensionReference(layerId);
+ if (layer != null) {
+ name = "oc" + ++ocNameIndex;
+ document.getResources().addProperty(name, layer);
+ } else {
+ name = "unknown";
+ }
+ getStream().add("/OC /" + name + " BDC\n");
+ }
+
+ private void endOptionalContent() {
+ getStream().add("EMC\n");
}
/** Indicates the beginning of a text object. */
@@ -310,8 +351,8 @@ public class PDFContentGenerator {
public void concatenate(AffineTransform transform) {
this.transform = transform;
if (!transform.isIdentity()) {
- currentState.concatenate(transform);
- currentStream.add(CTMHelper.toPDFString(transform, false) + " cm\n");
+ getState().concatenate(transform);
+ getStream().add(CTMHelper.toPDFString(transform, false) + " cm\n");
}
}
@@ -333,7 +374,7 @@ public class PDFContentGenerator {
* @param content the PDF content
*/
public void add(String content) {
- currentStream.add(content);
+ getStream().add(content);
}
/**
@@ -350,9 +391,9 @@ public class PDFContentGenerator {
* @param width line width in points
*/
public void updateLineWidth(float width) {
- if (currentState.setLineWidth(width)) {
+ if (getState().setLineWidth(width)) {
//Only write if value has changed WRT the current line width
- currentStream.add(format(width) + " w\n");
+ getStream().add(format(width) + " w\n");
}
}
@@ -362,7 +403,7 @@ public class PDFContentGenerator {
*/
public void updateCharacterSpacing(float value) {
if (getState().setCharacterSpacing(value)) {
- currentStream.add(format(value) + " Tc\n");
+ getStream().add(format(value) + " Tc\n");
}
}
@@ -400,7 +441,7 @@ public class PDFContentGenerator {
if (pdf != null) {
colorHandler.establishColor(pdf, col, fill);
} else {
- setColor(col, fill, this.currentStream);
+ setColor(col, fill, getStream());
}
}
diff --git a/src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java b/src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java
index dd9320571..648cdce7a 100644
--- a/src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java
+++ b/src/java/org/apache/fop/render/pdf/PDFDocumentHandler.java
@@ -50,7 +50,8 @@ import org.apache.fop.render.intermediate.IFDocumentNavigationHandler;
import org.apache.fop.render.intermediate.IFException;
import org.apache.fop.render.intermediate.IFPainter;
import org.apache.fop.render.pdf.PDFRendererConfig.PDFRendererConfigParser;
-import org.apache.fop.render.pdf.extensions.PDFEmbeddedFileExtensionAttachment;
+import org.apache.fop.render.pdf.extensions.PDFDictionaryAttachment;
+import org.apache.fop.render.pdf.extensions.PDFEmbeddedFileAttachment;
/**
* {@link org.apache.fop.render.intermediate.IFDocumentHandler} implementation that produces PDF.
@@ -296,17 +297,21 @@ public class PDFDocumentHandler extends AbstractBinaryWritingIFDocumentHandler {
} else if (extension instanceof Metadata) {
XMPMetadata wrapper = new XMPMetadata(((Metadata) extension));
pdfUtil.renderXMPMetadata(wrapper);
- } else if (extension instanceof PDFEmbeddedFileExtensionAttachment) {
- PDFEmbeddedFileExtensionAttachment embeddedFile
- = (PDFEmbeddedFileExtensionAttachment)extension;
+ } else if (extension instanceof PDFEmbeddedFileAttachment) {
+ PDFEmbeddedFileAttachment embeddedFile
+ = (PDFEmbeddedFileAttachment)extension;
try {
pdfUtil.addEmbeddedFile(embeddedFile);
} catch (IOException ioe) {
throw new IFException("Error adding embedded file: " + embeddedFile.getSrc(), ioe);
}
- } else {
+ } else if (extension instanceof PDFDictionaryAttachment) {
+ pdfUtil.renderDictionaryExtension((PDFDictionaryAttachment) extension, currentPage);
+ } else if (extension != null) {
log.debug("Don't know how to handle extension object. Ignoring: "
+ extension + " (" + extension.getClass().getName() + ")");
+ } else {
+ log.debug("Ignoring null extension object.");
}
}
diff --git a/src/java/org/apache/fop/render/pdf/PDFPainter.java b/src/java/org/apache/fop/render/pdf/PDFPainter.java
index d9fdd399c..f85328b8b 100644
--- a/src/java/org/apache/fop/render/pdf/PDFPainter.java
+++ b/src/java/org/apache/fop/render/pdf/PDFPainter.java
@@ -143,8 +143,8 @@ public class PDFPainter extends AbstractIFPainter<PDFDocumentHandler> {
}
/** {@inheritDoc} */
- public void startGroup(AffineTransform transform) throws IFException {
- generator.saveGraphicsState();
+ public void startGroup(AffineTransform transform, String layer) throws IFException {
+ generator.saveGraphicsState(layer);
generator.concatenate(toPoints(transform));
}
diff --git a/src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java b/src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java
index c3b46794d..4352dae6c 100644
--- a/src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java
+++ b/src/java/org/apache/fop/render/pdf/PDFRenderingUtil.java
@@ -27,6 +27,7 @@ import java.io.OutputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.EnumMap;
+import java.util.List;
import java.util.Map;
import org.apache.commons.io.IOUtils;
@@ -55,16 +56,33 @@ import org.apache.fop.pdf.PDFFileSpec;
import org.apache.fop.pdf.PDFICCBasedColorSpace;
import org.apache.fop.pdf.PDFICCStream;
import org.apache.fop.pdf.PDFInfo;
+import org.apache.fop.pdf.PDFLayer;
import org.apache.fop.pdf.PDFMetadata;
+import org.apache.fop.pdf.PDFName;
import org.apache.fop.pdf.PDFNames;
+import org.apache.fop.pdf.PDFNavigator;
+import org.apache.fop.pdf.PDFNull;
+import org.apache.fop.pdf.PDFNumber;
import org.apache.fop.pdf.PDFOutputIntent;
+import org.apache.fop.pdf.PDFPage;
import org.apache.fop.pdf.PDFPageLabels;
import org.apache.fop.pdf.PDFReference;
+import org.apache.fop.pdf.PDFSetOCGStateAction;
import org.apache.fop.pdf.PDFText;
+import org.apache.fop.pdf.PDFTransitionAction;
import org.apache.fop.pdf.PDFXMode;
import org.apache.fop.pdf.Version;
import org.apache.fop.pdf.VersionController;
-import org.apache.fop.render.pdf.extensions.PDFEmbeddedFileExtensionAttachment;
+import org.apache.fop.render.pdf.extensions.PDFActionExtension;
+import org.apache.fop.render.pdf.extensions.PDFArrayExtension;
+import org.apache.fop.render.pdf.extensions.PDFCollectionEntryExtension;
+import org.apache.fop.render.pdf.extensions.PDFDictionaryAttachment;
+import org.apache.fop.render.pdf.extensions.PDFDictionaryExtension;
+import org.apache.fop.render.pdf.extensions.PDFDictionaryType;
+import org.apache.fop.render.pdf.extensions.PDFEmbeddedFileAttachment;
+import org.apache.fop.render.pdf.extensions.PDFObjectType;
+import org.apache.fop.render.pdf.extensions.PDFPageExtension;
+import org.apache.fop.render.pdf.extensions.PDFReferenceExtension;
import static org.apache.fop.render.pdf.PDFEncryptionOption.ENCRYPTION_PARAMS;
import static org.apache.fop.render.pdf.PDFEncryptionOption.NO_ACCESSCONTENT;
@@ -250,6 +268,274 @@ class PDFRenderingUtil {
}
}
+ public void renderDictionaryExtension(PDFDictionaryAttachment attachment, PDFPage currentPage) {
+ PDFDictionaryExtension extension = attachment.getExtension();
+ PDFDictionaryType type = extension.getDictionaryType();
+ if (type == PDFDictionaryType.Action) {
+ addNavigatorAction(extension);
+ } else if (type == PDFDictionaryType.Layer) {
+ addLayer(extension);
+ } else if (type == PDFDictionaryType.Navigator) {
+ addNavigator(extension);
+ } else {
+ renderDictionaryExtension(extension, currentPage);
+ }
+ }
+
+ public void addLayer(PDFDictionaryExtension extension) {
+ assert extension.getDictionaryType() == PDFDictionaryType.Layer;
+ String id = extension.getProperty(PDFDictionaryExtension.PROPERTY_ID);
+ if ((id != null) && (id.length() > 0)) {
+ PDFLayer layer = pdfDoc.getFactory().makeLayer(id);
+ layer.setResolver(new PDFLayer.Resolver(layer, extension) {
+ public void performResolution() {
+ PDFDictionaryExtension extension = (PDFDictionaryExtension) getExtension();
+ Object name = extension.findEntryValue("Name");
+ Object intent = extension.findEntryValue("Intent");
+ Object usage = makeDictionary(extension.findEntryValue("Usage"));
+ getLayer().populate(name, intent, usage);
+ }
+ });
+ }
+ }
+
+ public void addNavigatorAction(PDFDictionaryExtension extension) {
+ assert extension.getDictionaryType() == PDFDictionaryType.Action;
+ String id = extension.getProperty(PDFDictionaryExtension.PROPERTY_ID);
+ if ((id != null) && (id.length() > 0)) {
+ String type = extension.getProperty(PDFActionExtension.PROPERTY_TYPE);
+ if (type != null) {
+ if (type.equals("SetOCGState")) {
+ PDFSetOCGStateAction action = pdfDoc.getFactory().makeSetOCGStateAction(id);
+ action.setResolver(new PDFSetOCGStateAction.Resolver(action, extension) {
+ public void performResolution() {
+ PDFDictionaryExtension extension = (PDFDictionaryExtension) getExtension();
+ Object state = makeArray(extension.findEntryValue("State"));
+ Object preserveRB = extension.findEntryValue("PreserveRB");
+ Object nextAction = makeDictionaryOrArray(extension.findEntryValue("Next"));
+ getAction().populate(state, preserveRB, nextAction);
+ }
+ });
+ } else if (type.equals("Trans")) {
+ PDFTransitionAction action = pdfDoc.getFactory().makeTransitionAction(id);
+ action.setResolver(new PDFTransitionAction.Resolver(action, extension) {
+ public void performResolution() {
+ PDFDictionaryExtension extension = (PDFDictionaryExtension) getExtension();
+ Object transition = makeDictionary(extension.findEntryValue("Trans"));
+ Object nextAction = makeDictionaryOrArray(extension.findEntryValue("Next"));
+ getAction().populate(transition, nextAction);
+ }
+ });
+ } else {
+ throw new UnsupportedOperationException();
+ }
+ }
+ }
+ }
+
+ public void addNavigator(PDFDictionaryExtension extension) {
+ assert extension.getDictionaryType() == PDFDictionaryType.Navigator;
+ String id = extension.getProperty(PDFDictionaryExtension.PROPERTY_ID);
+ if ((id != null) && (id.length() > 0)) {
+ PDFNavigator navigator = pdfDoc.getFactory().makeNavigator(id);
+ navigator.setResolver(new PDFNavigator.Resolver(navigator, extension) {
+ public void performResolution() {
+ PDFDictionaryExtension extension = (PDFDictionaryExtension) getExtension();
+ Object nextAction = makeDictionary(extension.findEntryValue("NA"));
+ Object next = makeDictionary(extension.findEntryValue("Next"));
+ Object prevAction = makeDictionary(extension.findEntryValue("PA"));
+ Object prev = makeDictionary(extension.findEntryValue("Prev"));
+ Object duration = extension.findEntryValue("Dur");
+ getNavigator().populate(nextAction, next, prevAction, prev, duration);
+ }
+ });
+ }
+ }
+
+ private Object makeArray(Object value) {
+ if (value == null) {
+ return null;
+ } else if (value instanceof PDFReferenceExtension) {
+ return resolveReference((PDFReferenceExtension) value);
+ } else if (value instanceof List<?>) {
+ return populateArray(new PDFArray(), (List<?>) value);
+ } else {
+ throw new IllegalArgumentException();
+ }
+ }
+
+ private Object populateArray(PDFArray array, List<?> entries) {
+ for (PDFCollectionEntryExtension entry : (List<PDFCollectionEntryExtension>) entries) {
+ PDFObjectType type = entry.getType();
+ if (type == PDFObjectType.Array) {
+ array.add(makeArray(entry.getValue()));
+ } else if (type == PDFObjectType.Boolean) {
+ array.add(entry.getValueAsBoolean());
+ } else if (type == PDFObjectType.Dictionary) {
+ array.add(makeDictionary(entry.getValue()));
+ } else if (type == PDFObjectType.Name) {
+ array.add(new PDFName(entry.getValueAsString()));
+ } else if (type == PDFObjectType.Number) {
+ array.add(new PDFNumber(entry.getValueAsNumber()));
+ } else if (type == PDFObjectType.Reference) {
+ array.add(resolveReference((PDFReferenceExtension) entry));
+ } else if (type == PDFObjectType.String) {
+ array.add(entry.getValue());
+ }
+ }
+ return array;
+ }
+
+ private Object makeDictionary(Object value) {
+ if (value == null) {
+ return null;
+ } else if (value instanceof PDFReferenceExtension) {
+ return resolveReference((PDFReferenceExtension) value);
+ } else if (value instanceof List<?>) {
+ return populateDictionary(new PDFDictionary(), (List<?>) value);
+ } else {
+ throw new IllegalArgumentException();
+ }
+ }
+
+ private Object populateDictionary(PDFDictionary dictionary, List<?> entries) {
+ for (PDFCollectionEntryExtension entry : (List<PDFCollectionEntryExtension>) entries) {
+ PDFObjectType type = entry.getType();
+ String key = entry.getKey();
+ if (type == PDFObjectType.Array) {
+ dictionary.put(key, makeArray(entry.getValue()));
+ } else if (type == PDFObjectType.Boolean) {
+ dictionary.put(key, entry.getValueAsBoolean());
+ } else if (type == PDFObjectType.Dictionary) {
+ dictionary.put(key, makeDictionary(entry.getValue()));
+ } else if (type == PDFObjectType.Name) {
+ dictionary.put(key, new PDFName(entry.getValueAsString()));
+ } else if (type == PDFObjectType.Number) {
+ dictionary.put(key, new PDFNumber(entry.getValueAsNumber()));
+ } else if (type == PDFObjectType.Reference) {
+ dictionary.put(key, resolveReference((PDFReferenceExtension) entry));
+ } else if (type == PDFObjectType.String) {
+ dictionary.put(key, entry.getValue());
+ }
+ }
+ return dictionary;
+ }
+
+ private Object makeDictionaryOrArray(Object value) {
+ if (value == null) {
+ return null;
+ } else if (value instanceof PDFReferenceExtension) {
+ return resolveReference((PDFReferenceExtension) value);
+ } else if (value instanceof List<?>) {
+ if (hasKeyedEntry((List<?>) value)) {
+ return populateDictionary(new PDFDictionary(), (List<?>) value);
+ } else {
+ return populateArray(new PDFArray(), (List<?>) value);
+ }
+ } else {
+ throw new IllegalArgumentException();
+ }
+ }
+
+ private boolean hasKeyedEntry(List<?> entries) {
+ for (PDFCollectionEntryExtension entry : (List<PDFCollectionEntryExtension>) entries) {
+ if (entry.getKey() != null) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public void renderDictionaryExtension(PDFDictionaryExtension extension, PDFPage currentPage) {
+ PDFDictionaryType type = extension.getDictionaryType();
+ if (type == PDFDictionaryType.Catalog) {
+ augmentDictionary(pdfDoc.getRoot(), extension);
+ } else if (type == PDFDictionaryType.Page) {
+ assert extension instanceof PDFPageExtension;
+ if (((PDFPageExtension) extension).matchesPageNumber(currentPage.getPageIndex() + 1)) {
+ augmentDictionary(currentPage, extension);
+ }
+ } else {
+ throw new IllegalStateException();
+ }
+ }
+
+ private PDFDictionary augmentDictionary(PDFDictionary dictionary, PDFDictionaryExtension extension) {
+ for (PDFCollectionEntryExtension entry : extension.getEntries()) {
+ if (entry instanceof PDFDictionaryExtension) {
+ dictionary.put(entry.getKey(),
+ augmentDictionary(new PDFDictionary(dictionary), (PDFDictionaryExtension) entry));
+ } else if (entry instanceof PDFArrayExtension) {
+ dictionary.put(entry.getKey(), augmentArray(new PDFArray(dictionary), (PDFArrayExtension) entry));
+ } else {
+ augmentDictionary(dictionary, entry);
+ }
+ }
+ return dictionary;
+ }
+
+ private void augmentDictionary(PDFDictionary dictionary, PDFCollectionEntryExtension entry) {
+ PDFObjectType type = entry.getType();
+ String key = entry.getKey();
+ if (type == PDFObjectType.Boolean) {
+ dictionary.put(key, entry.getValueAsBoolean());
+ } else if (type == PDFObjectType.Name) {
+ dictionary.put(key, new PDFName(entry.getValueAsString()));
+ } else if (type == PDFObjectType.Number) {
+ dictionary.put(key, new PDFNumber(entry.getValueAsNumber()));
+ } else if (type == PDFObjectType.Reference) {
+ assert entry instanceof PDFReferenceExtension;
+ dictionary.put(key, resolveReference((PDFReferenceExtension) entry));
+ } else if (type == PDFObjectType.String) {
+ dictionary.put(key, entry.getValueAsString());
+ } else {
+ throw new IllegalStateException();
+ }
+ }
+
+ private Object resolveReference(PDFReferenceExtension entry) {
+ PDFReference reference = (PDFReference) entry.getResolvedReference();
+ if (reference == null) {
+ reference = pdfDoc.resolveExtensionReference(entry.getReferenceId());
+ if (reference != null) {
+ entry.setResolvedReference(reference);
+ }
+ return reference;
+ }
+ return PDFNull.INSTANCE;
+ }
+
+ private PDFArray augmentArray(PDFArray array, PDFArrayExtension extension) {
+ for (PDFCollectionEntryExtension entry : extension.getEntries()) {
+ if (entry instanceof PDFDictionaryExtension) {
+ array.add(augmentDictionary(new PDFDictionary(array), (PDFDictionaryExtension) entry));
+ } else if (entry instanceof PDFArrayExtension) {
+ array.add(augmentArray(new PDFArray(array), (PDFArrayExtension) entry));
+ } else {
+ augmentArray(array, entry);
+ }
+ }
+ return array;
+ }
+
+ private void augmentArray(PDFArray array, PDFCollectionEntryExtension entry) {
+ PDFObjectType type = entry.getType();
+ if (type == PDFObjectType.Boolean) {
+ array.add(entry.getValueAsBoolean());
+ } else if (type == PDFObjectType.Name) {
+ array.add(new PDFName(entry.getValueAsString()));
+ } else if (type == PDFObjectType.Number) {
+ array.add(new PDFNumber(entry.getValueAsNumber()));
+ } else if (type == PDFObjectType.Reference) {
+ assert entry instanceof PDFReferenceExtension;
+ array.add(resolveReference((PDFReferenceExtension) entry));
+ } else if (type == PDFObjectType.String) {
+ array.add(entry.getValueAsString());
+ } else {
+ throw new IllegalStateException();
+ }
+ }
+
public PDFDocument setupPDFDocument(OutputStream out) throws IOException {
if (this.pdfDoc != null) {
throw new IllegalStateException("PDFDocument already set up");
@@ -315,7 +601,7 @@ class PDFRenderingUtil {
* @param embeddedFile the object representing the embedded file to be added
* @throws IOException if an I/O error occurs
*/
- public void addEmbeddedFile(PDFEmbeddedFileExtensionAttachment embeddedFile)
+ public void addEmbeddedFile(PDFEmbeddedFileAttachment embeddedFile)
throws IOException {
this.pdfDoc.getProfile().verifyEmbeddedFilesAllowed();
PDFNames names = this.pdfDoc.getRoot().getNames();
diff --git a/src/java/org/apache/fop/render/pdf/PDFStructureTreeBuilder.java b/src/java/org/apache/fop/render/pdf/PDFStructureTreeBuilder.java
index 4d34b8be4..985974b80 100644
--- a/src/java/org/apache/fop/render/pdf/PDFStructureTreeBuilder.java
+++ b/src/java/org/apache/fop/render/pdf/PDFStructureTreeBuilder.java
@@ -358,6 +358,7 @@ class PDFStructureTreeBuilder implements StructureTreeEventHandler {
}
public StructureTreeElement startNode(String name, Attributes attributes, StructureTreeElement parent) {
+ assert parent == null || parent instanceof PDFStructElem;
PDFStructElem parentElem = parent == null ? ancestors.getFirst() : (PDFStructElem) parent;
PDFStructElem structElem = createStructureElement(name, parentElem, attributes,
pdfFactory, eventBroadcaster);
diff --git a/src/java/org/apache/fop/render/pdf/extensions/AbstractPDFExtensionElement.java b/src/java/org/apache/fop/render/pdf/extensions/AbstractPDFExtensionElement.java
index 2b50112c0..b66fe4fad 100644
--- a/src/java/org/apache/fop/render/pdf/extensions/AbstractPDFExtensionElement.java
+++ b/src/java/org/apache/fop/render/pdf/extensions/AbstractPDFExtensionElement.java
@@ -19,10 +19,11 @@
package org.apache.fop.render.pdf.extensions;
-// FOP
import org.apache.fop.fo.FONode;
import org.apache.fop.fo.extensions.ExtensionAttachment;
+// CSOFF: LineLengthCheck
+
/**
* Base class for the PDF-specific extension elements.
*/
@@ -67,7 +68,9 @@ public abstract class AbstractPDFExtensionElement extends FONode {
* Instantiates extension attachment object.
* @return extension attachment
*/
- protected abstract ExtensionAttachment instantiateExtensionAttachment();
+ protected ExtensionAttachment instantiateExtensionAttachment() {
+ return null;
+ }
}
diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFActionElement.java b/src/java/org/apache/fop/render/pdf/extensions/PDFActionElement.java
new file mode 100644
index 000000000..e4a5747e6
--- /dev/null
+++ b/src/java/org/apache/fop/render/pdf/extensions/PDFActionElement.java
@@ -0,0 +1,64 @@
+/*
+ * 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.extensions;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.Locator;
+
+import org.apache.fop.apps.FOPException;
+import org.apache.fop.fo.Constants;
+import org.apache.fop.fo.FONode;
+import org.apache.fop.fo.PropertyList;
+
+// CSOFF: LineLengthCheck
+
+/**
+ * Extension element for pdf:action.
+ */
+public class PDFActionElement extends PDFDictionaryElement {
+
+ public static final String ATT_TYPE = PDFActionExtension.PROPERTY_TYPE;
+
+ /**
+ * Main constructor
+ * @param parent parent FO node
+ */
+ PDFActionElement(FONode parent) {
+ super(parent, PDFDictionaryType.Action);
+ }
+
+ @Override
+ public void processNode(String elementName, Locator locator, Attributes attlist, PropertyList propertyList) throws FOPException {
+ super.processNode(elementName, locator, attlist, propertyList);
+ String type = attlist.getValue(ATT_TYPE);
+ if (type != null) {
+ getDictionaryExtension().setProperty(PDFActionExtension.PROPERTY_TYPE, type);
+ }
+ }
+
+ @Override
+ public void startOfNode() throws FOPException {
+ super.startOfNode();
+ if (parent.getNameId() != Constants.FO_DECLARATIONS) {
+ invalidChildError(getLocator(), parent.getName(), getNamespaceURI(), getName(), "rule.childOfDeclarations");
+ }
+ }
+
+}
diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFActionExtension.java b/src/java/org/apache/fop/render/pdf/extensions/PDFActionExtension.java
new file mode 100644
index 000000000..778b8a203
--- /dev/null
+++ b/src/java/org/apache/fop/render/pdf/extensions/PDFActionExtension.java
@@ -0,0 +1,32 @@
+/*
+ * 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.extensions;
+
+// CSOFF: LineLengthCheck
+
+public class PDFActionExtension extends PDFDictionaryExtension {
+
+ public static final String PROPERTY_TYPE = "type";
+
+ PDFActionExtension() {
+ super(PDFDictionaryType.Action);
+ }
+
+}
diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFArrayElement.java b/src/java/org/apache/fop/render/pdf/extensions/PDFArrayElement.java
new file mode 100644
index 000000000..1f3ba22b2
--- /dev/null
+++ b/src/java/org/apache/fop/render/pdf/extensions/PDFArrayElement.java
@@ -0,0 +1,82 @@
+/*
+ * 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.extensions;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.Locator;
+
+import org.apache.fop.apps.FOPException;
+import org.apache.fop.fo.FONode;
+import org.apache.fop.fo.PropertyList;
+
+// CSOFF: LineLengthCheck
+
+/**
+ * Extension element for pdf:array.
+ */
+public class PDFArrayElement extends PDFCollectionEntryElement {
+
+ private PDFArrayExtension extension;
+
+ /**
+ * Main constructor
+ * @param parent parent FO node
+ */
+ PDFArrayElement(FONode parent) {
+ super(parent, PDFObjectType.Array, new PDFArrayExtension());
+ }
+
+ public PDFArrayExtension getArrayExtension() {
+ assert getExtension() instanceof PDFArrayExtension;
+ return (PDFArrayExtension) getExtension();
+ }
+
+ @Override
+ public void processNode(String elementName, Locator locator, Attributes attlist, PropertyList propertyList) throws FOPException {
+ super.processNode(elementName, locator, attlist, propertyList);
+ }
+
+ @Override
+ public void startOfNode() throws FOPException {
+ super.startOfNode();
+ }
+
+ @Override
+ protected void addChildNode(FONode child) throws FOPException {
+ PDFArrayExtension extension = getArrayExtension();
+ if (child instanceof PDFCollectionEntryElement) {
+ PDFCollectionEntryExtension entry = ((PDFCollectionEntryElement) child).getExtension();
+ if (entry.getKey() == null) {
+ extension.addEntry(entry);
+ }
+ }
+ }
+
+ @Override
+ public void endOfNode() throws FOPException {
+ super.endOfNode();
+ }
+
+ @Override
+ public String getLocalName() {
+ return PDFObjectType.Array.elementName();
+ }
+
+}
diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFArrayExtension.java b/src/java/org/apache/fop/render/pdf/extensions/PDFArrayExtension.java
new file mode 100644
index 000000000..80c6c94e4
--- /dev/null
+++ b/src/java/org/apache/fop/render/pdf/extensions/PDFArrayExtension.java
@@ -0,0 +1,84 @@
+/*
+ * 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.extensions;
+
+import java.util.List;
+import java.util.Map;
+
+// CSOFF: LineLengthCheck
+
+public class PDFArrayExtension extends PDFCollectionExtension {
+
+ private static final long serialVersionUID = -1L;
+
+ private Map<String, String> properties;
+ private List<PDFCollectionEntryExtension> entries;
+
+ PDFArrayExtension() {
+ super(PDFObjectType.Array);
+ this.properties = new java.util.HashMap<String, String>();
+ this.entries = new java.util.ArrayList<PDFCollectionEntryExtension>();
+ }
+
+ @Override
+ public void setValue(Object value) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Object getValue() {
+ return getEntries();
+ }
+
+ public void setProperty(String name, String value) {
+ properties.put(name, value);
+ }
+
+ public String getProperty(String name) {
+ return properties.get(name);
+ }
+
+ @Override
+ public void addEntry(PDFCollectionEntryExtension entry) {
+ if (entry.getKey() != null) {
+ throw new IllegalArgumentException();
+ } else {
+ entries.add(entry);
+ }
+ }
+
+ public List<PDFCollectionEntryExtension> getEntries() {
+ return entries;
+ }
+
+ public PDFCollectionEntryExtension getLastEntry() {
+ if (entries.size() > 0) {
+ return entries.get(entries.size() - 1);
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public String getElementName() {
+ return PDFObjectType.Array.elementName();
+ }
+
+}
diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFCatalogElement.java b/src/java/org/apache/fop/render/pdf/extensions/PDFCatalogElement.java
new file mode 100644
index 000000000..029357d22
--- /dev/null
+++ b/src/java/org/apache/fop/render/pdf/extensions/PDFCatalogElement.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.pdf.extensions;
+
+import org.apache.fop.apps.FOPException;
+import org.apache.fop.fo.Constants;
+import org.apache.fop.fo.FONode;
+
+// CSOFF: LineLengthCheck
+
+/**
+ * Extension element for pdf:catalog.
+ */
+public class PDFCatalogElement extends PDFDictionaryElement {
+
+ PDFCatalogElement(FONode parent) {
+ super(parent, PDFDictionaryType.Catalog);
+ }
+
+ @Override
+ public void startOfNode() throws FOPException {
+ super.startOfNode();
+ if (parent.getNameId() != Constants.FO_DECLARATIONS) {
+ invalidChildError(getLocator(), parent.getName(), getNamespaceURI(), getName(), "rule.childOfDeclarations");
+ }
+ }
+
+}
diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFCatalogExtension.java b/src/java/org/apache/fop/render/pdf/extensions/PDFCatalogExtension.java
new file mode 100644
index 000000000..381f6fbe8
--- /dev/null
+++ b/src/java/org/apache/fop/render/pdf/extensions/PDFCatalogExtension.java
@@ -0,0 +1,29 @@
+/*
+ * 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.extensions;
+
+// CSOFF: LineLengthCheck
+
+public class PDFCatalogExtension extends PDFDictionaryExtension {
+
+ PDFCatalogExtension() {
+ super(PDFDictionaryType.Catalog);
+ }
+}
diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFCollectionEntryElement.java b/src/java/org/apache/fop/render/pdf/extensions/PDFCollectionEntryElement.java
new file mode 100644
index 000000000..4185ceef0
--- /dev/null
+++ b/src/java/org/apache/fop/render/pdf/extensions/PDFCollectionEntryElement.java
@@ -0,0 +1,146 @@
+/*
+ * 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.extensions;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.Locator;
+
+import org.apache.fop.apps.FOPException;
+import org.apache.fop.fo.FONode;
+import org.apache.fop.fo.PropertyList;
+
+// CSOFF: LineLengthCheck
+
+/**
+ * Extension element for collection entries: pdf:{array,boolean,dictionary,name,number,reference,string}. The specific type
+ * of entry is established at construction type.
+ */
+public class PDFCollectionEntryElement extends AbstractPDFExtensionElement {
+
+ public static final String ATT_KEY = PDFCollectionEntryExtension.PROPERTY_KEY;
+
+ private PDFCollectionEntryExtension extension;
+ private StringBuffer characters;
+
+ PDFCollectionEntryElement(FONode parent, PDFObjectType type, PDFCollectionEntryExtension extension) {
+ super(parent);
+ this.extension = extension;
+ }
+
+ PDFCollectionEntryElement(FONode parent, PDFObjectType type) {
+ this(parent, type, createExtension(type));
+ }
+
+ private static PDFCollectionEntryExtension createExtension(PDFObjectType type) {
+ if (type == PDFObjectType.Reference) {
+ return new PDFReferenceExtension();
+ } else {
+ return new PDFCollectionEntryExtension(type);
+ }
+ }
+
+ public PDFCollectionEntryExtension getExtension() {
+ return extension;
+ }
+
+ @Override
+ public void processNode(String elementName, Locator locator, Attributes attlist, PropertyList propertyList) throws FOPException {
+ if (parent instanceof PDFDictionaryElement) {
+ String key = attlist.getValue(ATT_KEY);
+ if (key == null) {
+ missingPropertyError(ATT_KEY);
+ } else if (key.length() == 0) {
+ invalidPropertyValueError(ATT_KEY, key, null);
+ } else {
+ extension.setKey(key);
+ }
+ }
+ }
+
+ @Override
+ public void startOfNode() throws FOPException {
+ super.startOfNode();
+ if (parent instanceof PDFDictionaryElement) {
+ if (!PDFDictionaryType.hasValueOfElementName(parent.getLocalName())) {
+ invalidChildError(getLocator(), parent.getName(), getNamespaceURI(), getName(), null);
+ }
+ }
+ }
+
+ @Override
+ protected void characters(char[] data, int start, int length, PropertyList pList, Locator locator) throws FOPException {
+ if (capturePCData(extension.getType())) {
+ if (characters == null) {
+ characters = new StringBuffer((length < 16) ? 16 : length);
+ }
+ characters.append(data, start, length);
+ }
+ }
+
+ private boolean capturePCData(PDFObjectType type) {
+ if (type == PDFObjectType.Array) {
+ return false;
+ } else if (type == PDFObjectType.Dictionary) {
+ return false;
+ } else {
+ return (type != PDFObjectType.Reference);
+ }
+ }
+
+ @Override
+ public void endOfNode() throws FOPException {
+ if (capturePCData(extension.getType())) {
+ if (extension.getType() == PDFObjectType.Boolean) {
+ String value = (characters != null) ? characters.toString() : "";
+ if (!value.equals("true") && !value.equals("false")) {
+ invalidPropertyValueError("<value>", value, null);
+ }
+ extension.setValue(Boolean.valueOf(value));
+ } else if (extension.getType() == PDFObjectType.Name) {
+ String value = (characters != null) ? characters.toString() : "";
+ if (value.length() == 0) {
+ invalidPropertyValueError("<value>", value, null);
+ }
+ extension.setValue(value);
+ } else if (extension.getType() == PDFObjectType.Number) {
+ String value = (characters != null) ? characters.toString() : "";
+ try {
+ double d = Double.parseDouble(value);
+ if (Math.abs(Math.floor(d) - d) < 1E-10) {
+ extension.setValue(Long.valueOf((long) d));
+ } else {
+ extension.setValue(Double.valueOf(d));
+ }
+ } catch (NumberFormatException e) {
+ invalidPropertyValueError("<value>", value, null);
+ }
+ } else if (extension.getType() == PDFObjectType.String) {
+ String value = (characters != null) ? characters.toString() : "";
+ extension.setValue(value);
+ }
+ }
+ super.endOfNode();
+ }
+
+ @Override
+ public String getLocalName() {
+ return extension.getType().elementName();
+ }
+}
diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFCollectionEntryExtension.java b/src/java/org/apache/fop/render/pdf/extensions/PDFCollectionEntryExtension.java
new file mode 100644
index 000000000..d28f1602f
--- /dev/null
+++ b/src/java/org/apache/fop/render/pdf/extensions/PDFCollectionEntryExtension.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.pdf.extensions;
+
+// CSOFF: LineLengthCheck
+
+public class PDFCollectionEntryExtension extends PDFObjectExtension {
+
+ public static final String PROPERTY_KEY = "key";
+
+ /* Non-empty key if used as dictionary entry, otherwise must be null. */
+ private String key;
+
+ PDFCollectionEntryExtension(PDFObjectType type) {
+ super(type);
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public void setKey(String key) {
+ this.key = key;
+ }
+
+}
diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFCollectionExtension.java b/src/java/org/apache/fop/render/pdf/extensions/PDFCollectionExtension.java
new file mode 100644
index 000000000..228d69c79
--- /dev/null
+++ b/src/java/org/apache/fop/render/pdf/extensions/PDFCollectionExtension.java
@@ -0,0 +1,34 @@
+/*
+ * 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.extensions;
+
+// CSOFF: LineLengthCheck
+
+public abstract class PDFCollectionExtension extends PDFCollectionEntryExtension {
+
+ protected PDFCollectionExtension(PDFObjectType type) {
+ super(type);
+ }
+
+ public abstract void addEntry(PDFCollectionEntryExtension entry);
+
+ public abstract PDFCollectionEntryExtension getLastEntry();
+
+}
diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryAttachment.java b/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryAttachment.java
new file mode 100644
index 000000000..3832619ba
--- /dev/null
+++ b/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryAttachment.java
@@ -0,0 +1,166 @@
+/*
+ * 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.extensions;
+
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.AttributesImpl;
+
+import org.apache.fop.render.intermediate.IFContext;
+import org.apache.fop.util.GenerationHelperContentHandler;
+
+// CSOFF: LineLengthCheck
+
+public class PDFDictionaryAttachment extends PDFExtensionAttachment {
+
+ private static final long serialVersionUID = -5576832955238384505L;
+
+ private PDFDictionaryExtension extension;
+
+ public PDFDictionaryAttachment(PDFDictionaryExtension extension) {
+ this.extension = extension;
+ }
+
+ public PDFDictionaryExtension getExtension() {
+ return extension;
+ }
+
+ public void toSAX(ContentHandler handler) throws SAXException {
+ int pageNumber = 0;
+ if (extension instanceof PDFPageExtension) {
+ if (handler instanceof GenerationHelperContentHandler) {
+ Object context = ((GenerationHelperContentHandler) handler).getContentHandlerContext();
+ if (context instanceof IFContext) {
+ int pageIndex = ((IFContext) context).getPageIndex();
+ if ((pageIndex >= 0) && ((PDFPageExtension) extension).matchesPageNumber(pageIndex + 1)) {
+ pageNumber = pageIndex + 1;
+ } else {
+ pageNumber = -1;
+ }
+ }
+ }
+ }
+ if (pageNumber >= 0) {
+ toSAX(handler, extension);
+ }
+ }
+
+ private void toSAX(ContentHandler handler, PDFDictionaryExtension dictionary) throws SAXException {
+ AttributesImpl attributes = new AttributesImpl();
+ String ln = dictionary.getElementName();
+ String qn = PREFIX + ":" + ln;
+ attributes = extractIFAttributes(attributes, dictionary);
+ handler.startElement(CATEGORY, ln, qn, attributes);
+ for (PDFCollectionEntryExtension entry : dictionary.getEntries()) {
+ toSAX(handler, entry);
+ }
+ handler.endElement(CATEGORY, ln, qn);
+ }
+
+ private void toSAX(ContentHandler handler, PDFArrayExtension array) throws SAXException {
+ AttributesImpl attributes = new AttributesImpl();
+ String ln = array.getElementName();
+ String qn = PREFIX + ":" + ln;
+ attributes = extractIFAttributes(attributes, array);
+ handler.startElement(CATEGORY, ln, qn, attributes);
+ for (PDFCollectionEntryExtension entry : array.getEntries()) {
+ toSAX(handler, entry);
+ }
+ handler.endElement(CATEGORY, ln, qn);
+ }
+
+ private void toSAX(ContentHandler handler, PDFCollectionEntryExtension entry) throws SAXException {
+ if (entry instanceof PDFDictionaryExtension) {
+ toSAX(handler, (PDFDictionaryExtension) entry);
+ } else if (entry instanceof PDFArrayExtension) {
+ toSAX(handler, (PDFArrayExtension) entry);
+ } else {
+ AttributesImpl attributes = new AttributesImpl();
+ String ln = entry.getElementName();
+ String qn = PREFIX + ":" + ln;
+ attributes = extractIFAttributes(attributes, entry);
+ handler.startElement(CATEGORY, ln, qn, attributes);
+ if (!(entry instanceof PDFReferenceExtension)) {
+ char[] characters = entry.getValueAsXMLEscapedString().toCharArray();
+ if (characters.length > 0) {
+ handler.characters(characters, 0, characters.length);
+ }
+ }
+ handler.endElement(CATEGORY, ln, qn);
+ }
+ }
+
+ private static AttributesImpl extractIFAttributes(AttributesImpl attributes, PDFDictionaryExtension dictionary) {
+ PDFDictionaryType type = dictionary.getDictionaryType();
+ if (dictionary.usesIDAttribute()) {
+ String idName = PDFDictionaryElement.ATT_ID;
+ String id = dictionary.getProperty(PDFDictionaryExtension.PROPERTY_ID);
+ if (id != null) {
+ attributes.addAttribute(null, idName, idName, "ID", id);
+ }
+ }
+ if (type == PDFDictionaryType.Action) {
+ String actionTypeName = PDFActionElement.ATT_TYPE;
+ String actionType = dictionary.getProperty(PDFActionExtension.PROPERTY_TYPE);
+ if (actionType != null) {
+ attributes.addAttribute(null, actionTypeName, actionTypeName, "CDATA", actionType);
+ }
+ } else if (type == PDFDictionaryType.Page) {
+ String pageNumbersName = PDFPageExtension.PROPERTY_PAGE_NUMBERS;
+ String pageNumbers = dictionary.getProperty(pageNumbersName);
+ if (pageNumbers != null) {
+ attributes.addAttribute(null, pageNumbersName, pageNumbersName, "CDATA", pageNumbers);
+ }
+ } else if (type == PDFDictionaryType.Dictionary) {
+ String keyName = PDFCollectionEntryElement.ATT_KEY;
+ String key = dictionary.getKey();
+ if (key != null) {
+ attributes.addAttribute(null, keyName, keyName, "CDATA", key);
+ }
+ }
+ return attributes;
+ }
+
+ private static AttributesImpl extractIFAttributes(AttributesImpl attributes, PDFArrayExtension array) {
+ String keyName = PDFCollectionEntryExtension.PROPERTY_KEY;
+ String key = array.getKey();
+ if (key != null) {
+ attributes.addAttribute(null, keyName, keyName, "CDATA", key);
+ }
+ return attributes;
+ }
+
+ private static AttributesImpl extractIFAttributes(AttributesImpl attributes, PDFCollectionEntryExtension entry) {
+ String keyName = PDFCollectionEntryElement.ATT_KEY;
+ String key = entry.getKey();
+ if (key != null) {
+ attributes.addAttribute(null, keyName, keyName, "CDATA", key);
+ }
+ if (entry instanceof PDFReferenceExtension) {
+ String refid = ((PDFReferenceExtension) entry).getReferenceId();
+ if (refid != null) {
+ String refidName = PDFReferenceElement.ATT_REFID;
+ attributes.addAttribute(null, refidName, refidName, "IDREF", refid);
+ }
+ }
+ return attributes;
+ }
+
+}
diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryElement.java b/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryElement.java
new file mode 100644
index 000000000..9dc127da6
--- /dev/null
+++ b/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryElement.java
@@ -0,0 +1,145 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.pdf.extensions;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.Locator;
+
+import org.apache.fop.apps.FOPException;
+import org.apache.fop.fo.FONode;
+import org.apache.fop.fo.PropertyList;
+import org.apache.fop.fo.extensions.ExtensionAttachment;
+
+// CSOFF: LineLengthCheck
+
+/**
+ * Extension element for dictionaries: pdf:{catalog,page,dictionary}. The specific type
+ * of dictionary is established at construction type.
+ */
+public class PDFDictionaryElement extends PDFCollectionEntryElement {
+
+ public static final String ATT_ID = PDFDictionaryExtension.PROPERTY_ID;
+
+ /**
+ * Main constructor
+ * @param parent parent FO node
+ */
+ PDFDictionaryElement(FONode parent, PDFDictionaryType type) {
+ super(parent, PDFObjectType.Dictionary, createExtension(type));
+ }
+
+ private static PDFDictionaryExtension createExtension(PDFDictionaryType type) {
+ if (type == PDFDictionaryType.Action) {
+ return new PDFActionExtension();
+ } else if (type == PDFDictionaryType.Catalog) {
+ return new PDFCatalogExtension();
+ } else if (type == PDFDictionaryType.Layer) {
+ return new PDFLayerExtension();
+ } else if (type == PDFDictionaryType.Navigator) {
+ return new PDFNavigatorExtension();
+ } else if (type == PDFDictionaryType.Page) {
+ return new PDFPageExtension();
+ } else {
+ return new PDFDictionaryExtension(type);
+ }
+ }
+
+ public PDFDictionaryExtension getDictionaryExtension() {
+ assert getExtension() instanceof PDFDictionaryExtension;
+ return (PDFDictionaryExtension) getExtension();
+ }
+
+ @Override
+ public void processNode(String elementName, Locator locator, Attributes attlist, PropertyList propertyList) throws FOPException {
+ PDFDictionaryExtension extension = getDictionaryExtension();
+ if (extension.usesIDAttribute()) {
+ String id = attlist.getValue(ATT_ID);
+ if (id != null) {
+ extension.setProperty(PDFDictionaryExtension.PROPERTY_ID, id);
+ }
+ }
+ if (extension.getDictionaryType() == PDFDictionaryType.Dictionary) {
+ String key = attlist.getValue(ATT_KEY);
+ if (key == null) {
+ if (parent instanceof PDFDictionaryElement) {
+ missingPropertyError(ATT_KEY);
+ }
+ } else if (key.length() == 0) {
+ invalidPropertyValueError(ATT_KEY, key, null);
+ } else {
+ extension.setKey(key);
+ }
+ }
+ }
+
+ @Override
+ public void startOfNode() throws FOPException {
+ super.startOfNode();
+ String localName = getLocalName();
+ if (localName.equals("action")) {
+ // handled in PDFActionElement subclass
+ } else if (localName.equals("catalog")) {
+ // handled in PDFCatalogElement subclass
+ } else if (localName.equals("layer")) {
+ // handled in PDFLayerElement subclass
+ } else if (localName.equals("navigator")) {
+ // handled in PDFNavigattorElement subclass
+ } else if (localName.equals("page")) {
+ // handled in PDFPageElement subclass
+ } else if (localName.equals("dictionary")) {
+ if (!PDFDictionaryType.hasValueOfElementName(parent.getLocalName()) && !PDFObjectType.Array.elementName().equals(parent.getLocalName())) {
+ invalidChildError(getLocator(), parent.getName(), getNamespaceURI(), getName(), null);
+ }
+ } else {
+ throw new IllegalStateException("unknown name: " + localName);
+ }
+ }
+
+ @Override
+ protected void addChildNode(FONode child) throws FOPException {
+ PDFDictionaryExtension extension = getDictionaryExtension();
+ if (child instanceof PDFDictionaryElement) {
+ PDFDictionaryExtension entry = ((PDFDictionaryElement) child).getDictionaryExtension();
+ if (entry.getDictionaryType() == PDFDictionaryType.Dictionary) {
+ extension.addEntry(entry);
+ }
+ } else if (child instanceof PDFCollectionEntryElement) {
+ PDFCollectionEntryExtension entry = ((PDFCollectionEntryElement) child).getExtension();
+ extension.addEntry(entry);
+ }
+ }
+
+ @Override
+ public void endOfNode() throws FOPException {
+ super.endOfNode();
+ }
+
+ @Override
+ public String getLocalName() {
+ PDFDictionaryExtension extension = getDictionaryExtension();
+ return extension.getDictionaryType().elementName();
+ }
+
+ @Override
+ protected ExtensionAttachment instantiateExtensionAttachment() {
+ return new PDFDictionaryAttachment(getDictionaryExtension());
+ }
+
+}
diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryExtension.java b/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryExtension.java
new file mode 100644
index 000000000..50b6f3a83
--- /dev/null
+++ b/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryExtension.java
@@ -0,0 +1,121 @@
+/*
+ * 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.extensions;
+
+import java.util.List;
+import java.util.Map;
+
+// CSOFF: LineLengthCheck
+
+public class PDFDictionaryExtension extends PDFCollectionExtension {
+
+ public static final String PROPERTY_ID = "id";
+ public static final String PROPERTY_PAGE_NUMBERS = "page-numbers";
+
+ private static final long serialVersionUID = -1L;
+
+ private PDFDictionaryType dictionaryType;
+ private Map<String, String> properties;
+ private List<PDFCollectionEntryExtension> entries;
+
+ PDFDictionaryExtension() {
+ this(PDFDictionaryType.Dictionary);
+ }
+
+ PDFDictionaryExtension(PDFDictionaryType dictionaryType) {
+ super(PDFObjectType.Dictionary);
+ this.dictionaryType = dictionaryType;
+ this.properties = new java.util.HashMap<String, String>();
+ this.entries = new java.util.ArrayList<PDFCollectionEntryExtension>();
+ }
+
+ @Override
+ public void setValue(Object value) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Object getValue() {
+ return getEntries();
+ }
+
+ public PDFDictionaryType getDictionaryType() {
+ return dictionaryType;
+ }
+
+ public void setProperty(String name, String value) {
+ properties.put(name, value);
+ }
+
+ public String getProperty(String name) {
+ return properties.get(name);
+ }
+
+ @Override
+ public void addEntry(PDFCollectionEntryExtension entry) {
+ if ((entry.getKey() == null) || (entry.getKey().length() == 0)) {
+ throw new IllegalArgumentException();
+ } else {
+ entries.add(entry);
+ }
+ }
+
+ public List<PDFCollectionEntryExtension> getEntries() {
+ return entries;
+ }
+
+ public PDFCollectionEntryExtension findEntry(String key) {
+ for (PDFCollectionEntryExtension entry : entries) {
+ String entryKey = entry.getKey();
+ if ((entryKey != null) && entryKey.equals(key)) {
+ return entry;
+ }
+ }
+ return null;
+ }
+
+ public Object findEntryValue(String key) {
+ for (PDFCollectionEntryExtension entry : entries) {
+ String entryKey = entry.getKey();
+ if ((entryKey != null) && entryKey.equals(key)) {
+ return entry.getValue();
+ }
+ }
+ return null;
+ }
+
+ public PDFCollectionEntryExtension getLastEntry() {
+ if (entries.size() > 0) {
+ return entries.get(entries.size() - 1);
+ } else {
+ return null;
+ }
+ }
+
+ public boolean usesIDAttribute() {
+ return dictionaryType.usesIDAttribute();
+ }
+
+ @Override
+ public String getElementName() {
+ return dictionaryType.elementName();
+ }
+
+}
diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryType.java b/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryType.java
new file mode 100644
index 000000000..a49a5fc8c
--- /dev/null
+++ b/src/java/org/apache/fop/render/pdf/extensions/PDFDictionaryType.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$ */
+
+package org.apache.fop.render.pdf.extensions;
+
+// CSOFF: LineLengthCheck
+
+/**
+ * Enumeration type for PDF dictionary extension elements.
+ */
+public enum PDFDictionaryType {
+ Action("action", true), // action dictionary element
+ Catalog("catalog"), // catalog dictionary element
+ Dictionary("dictionary"), // generic (nested) dictionary element
+ Layer("layer", true), // optional content group dictionary element
+ Navigator("navigator", true), // navigation node dictionary element
+ Page("page"); // page dictionary element
+
+ private String elementName;
+ private boolean usesIDAttribute;
+ PDFDictionaryType(String elementName, boolean usesIDAttribute) {
+ this.elementName = elementName;
+ this.usesIDAttribute = usesIDAttribute;
+ }
+ PDFDictionaryType(String elementName) {
+ this(elementName, false);
+ }
+ public String elementName() {
+ return elementName;
+ }
+ public boolean usesIDAttribute() {
+ return usesIDAttribute;
+ }
+ static PDFDictionaryType valueOfElementName(String elementName) {
+ for (PDFDictionaryType type : values()) {
+ if (type.elementName.equals(elementName)) {
+ return type;
+ }
+ }
+ throw new IllegalArgumentException();
+ }
+ static boolean hasValueOfElementName(String elementName) {
+ try {
+ return valueOfElementName(elementName) != null;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+}
diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFElementMapping.java b/src/java/org/apache/fop/render/pdf/extensions/PDFElementMapping.java
index c70ed3635..1fba80796 100644
--- a/src/java/org/apache/fop/render/pdf/extensions/PDFElementMapping.java
+++ b/src/java/org/apache/fop/render/pdf/extensions/PDFElementMapping.java
@@ -22,6 +22,8 @@ package org.apache.fop.render.pdf.extensions;
import org.apache.fop.fo.ElementMapping;
import org.apache.fop.fo.FONode;
+// CSOFF: LineLengthCheck
+
/**
* This class provides the element mapping for the PDF-specific extensions.
*/
@@ -39,13 +41,97 @@ public class PDFElementMapping extends ElementMapping {
protected void initialize() {
if (foObjs == null) {
foObjs = new java.util.HashMap<String, Maker>();
- foObjs.put(PDFEmbeddedFileElement.ELEMENT, new PDFEmbeddedFileMaker());
+ // pdf:action
+ foObjs.put(PDFDictionaryType.Action.elementName(), new PDFActionElementMaker());
+ // pdf:array
+ foObjs.put(PDFObjectType.Array.elementName(), new PDFArrayElementMaker());
+ // pdf:boolean
+ foObjs.put(PDFObjectType.Boolean.elementName(), new PDFCollectionEntryElementMaker(PDFObjectType.Boolean));
+ // pdf:catalog
+ foObjs.put(PDFDictionaryType.Catalog.elementName(), new PDFCatalogElementMaker());
+ // pdf:dictionary
+ foObjs.put(PDFDictionaryType.Dictionary.elementName(), new PDFDictionaryElementMaker());
+ // pdf:embedded-file
+ foObjs.put(PDFEmbeddedFileElement.ELEMENT, new PDFEmbeddedFileElementMaker());
+ // pdf:name
+ foObjs.put(PDFObjectType.Name.elementName(), new PDFCollectionEntryElementMaker(PDFObjectType.Name));
+ // pdf:number
+ foObjs.put(PDFObjectType.Number.elementName(), new PDFCollectionEntryElementMaker(PDFObjectType.Number));
+ // pdf:navigator
+ foObjs.put(PDFDictionaryType.Navigator.elementName(), new PDFNavigatorElementMaker());
+ // pdf:layer
+ foObjs.put(PDFDictionaryType.Layer.elementName(), new PDFLayerElementMaker());
+ // pdf:page
+ foObjs.put(PDFDictionaryType.Page.elementName(), new PDFPageElementMaker());
+ // pdf:reference
+ foObjs.put(PDFObjectType.Reference.elementName(), new PDFReferenceElementMaker());
+ // pdf:string
+ foObjs.put(PDFObjectType.String.elementName(), new PDFCollectionEntryElementMaker(PDFObjectType.String));
+ }
+ }
+
+ static class PDFActionElementMaker extends ElementMapping.Maker {
+ public FONode make(FONode parent) {
+ return new PDFActionElement(parent);
+ }
+ }
+
+ static class PDFArrayElementMaker extends ElementMapping.Maker {
+ public FONode make(FONode parent) {
+ return new PDFArrayElement(parent);
+ }
+ }
+
+ static class PDFCatalogElementMaker extends ElementMapping.Maker {
+ public FONode make(FONode parent) {
+ return new PDFCatalogElement(parent);
+ }
+ }
+
+ static class PDFDictionaryElementMaker extends ElementMapping.Maker {
+ public FONode make(FONode parent) {
+ return new PDFDictionaryElement(parent, PDFDictionaryType.Dictionary);
}
}
- static class PDFEmbeddedFileMaker extends ElementMapping.Maker {
+ static class PDFEmbeddedFileElementMaker extends ElementMapping.Maker {
public FONode make(FONode parent) {
return new PDFEmbeddedFileElement(parent);
}
}
+
+ static class PDFLayerElementMaker extends ElementMapping.Maker {
+ public FONode make(FONode parent) {
+ return new PDFLayerElement(parent);
+ }
+ }
+
+ static class PDFNavigatorElementMaker extends ElementMapping.Maker {
+ public FONode make(FONode parent) {
+ return new PDFNavigatorElement(parent);
+ }
+ }
+
+ static class PDFPageElementMaker extends ElementMapping.Maker {
+ public FONode make(FONode parent) {
+ return new PDFPageElement(parent);
+ }
+ }
+
+ static class PDFCollectionEntryElementMaker extends ElementMapping.Maker {
+ private PDFObjectType entryType;
+ PDFCollectionEntryElementMaker(PDFObjectType entryType) {
+ this.entryType = entryType;
+ }
+ public FONode make(FONode parent) {
+ return new PDFCollectionEntryElement(parent, entryType);
+ }
+ }
+
+ static class PDFReferenceElementMaker extends ElementMapping.Maker {
+ public FONode make(FONode parent) {
+ return new PDFReferenceElement(parent);
+ }
+ }
+
}
diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFEmbeddedFileExtensionAttachment.java b/src/java/org/apache/fop/render/pdf/extensions/PDFEmbeddedFileAttachment.java
index 5f3f16dbb..a62b35a4b 100644
--- a/src/java/org/apache/fop/render/pdf/extensions/PDFEmbeddedFileExtensionAttachment.java
+++ b/src/java/org/apache/fop/render/pdf/extensions/PDFEmbeddedFileAttachment.java
@@ -26,7 +26,9 @@ import org.xml.sax.helpers.AttributesImpl;
/**
* This is the pass-through value object for the PDF extension.
*/
-public class PDFEmbeddedFileExtensionAttachment extends PDFExtensionAttachment {
+public class PDFEmbeddedFileAttachment extends PDFExtensionAttachment {
+
+ private static final long serialVersionUID = -1L;
/** element name */
protected static final String ELEMENT = "embedded-file";
@@ -52,7 +54,7 @@ public class PDFEmbeddedFileExtensionAttachment extends PDFExtensionAttachment {
/**
* No-argument contructor.
*/
- public PDFEmbeddedFileExtensionAttachment() {
+ public PDFEmbeddedFileAttachment() {
super();
}
@@ -62,7 +64,7 @@ public class PDFEmbeddedFileExtensionAttachment extends PDFExtensionAttachment {
* @param src the location of the file
* @param desc the description of the file
*/
- public PDFEmbeddedFileExtensionAttachment(String filename, String src, String desc) {
+ public PDFEmbeddedFileAttachment(String filename, String src, String desc) {
super();
this.filename = filename;
this.src = src;
diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFEmbeddedFileElement.java b/src/java/org/apache/fop/render/pdf/extensions/PDFEmbeddedFileElement.java
index 36d7c18d2..3fef5521e 100644
--- a/src/java/org/apache/fop/render/pdf/extensions/PDFEmbeddedFileElement.java
+++ b/src/java/org/apache/fop/render/pdf/extensions/PDFEmbeddedFileElement.java
@@ -30,7 +30,6 @@ import org.apache.fop.datatypes.URISpecification;
import org.apache.fop.fo.Constants;
import org.apache.fop.fo.FONode;
import org.apache.fop.fo.PropertyList;
-import org.apache.fop.fo.extensions.ExtensionAttachment;
/**
* Extension element for pdf:embedded-file.
@@ -44,11 +43,11 @@ public class PDFEmbeddedFileElement extends AbstractPDFExtensionElement {
* Main constructor
* @param parent parent FO node
*/
- protected PDFEmbeddedFileElement(FONode parent) {
+ PDFEmbeddedFileElement(FONode parent) {
super(parent);
}
- /** {@inheritDoc} */
+ @Override
public void startOfNode() throws FOPException {
super.startOfNode();
if (parent.getNameId() != Constants.FO_DECLARATIONS) {
@@ -57,12 +56,12 @@ public class PDFEmbeddedFileElement extends AbstractPDFExtensionElement {
}
}
- /** {@inheritDoc} */
+ @Override
public void processNode(String elementName, Locator locator,
Attributes attlist, PropertyList propertyList)
throws FOPException {
- PDFEmbeddedFileExtensionAttachment embeddedFile
- = (PDFEmbeddedFileExtensionAttachment)getExtensionAttachment();
+ PDFEmbeddedFileAttachment embeddedFile
+ = (PDFEmbeddedFileAttachment)getExtensionAttachment();
String desc = attlist.getValue("description");
if (desc != null && desc.length() > 0) {
embeddedFile.setDesc(desc);
@@ -94,13 +93,8 @@ public class PDFEmbeddedFileElement extends AbstractPDFExtensionElement {
embeddedFile.setFilename(filename);
}
- /** {@inheritDoc} */
+ @Override
public String getLocalName() {
return ELEMENT;
}
-
- /** {@inheritDoc} */
- protected ExtensionAttachment instantiateExtensionAttachment() {
- return new PDFEmbeddedFileExtensionAttachment();
- }
}
diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFExtensionAttachment.java b/src/java/org/apache/fop/render/pdf/extensions/PDFExtensionAttachment.java
index a4f5b47b7..9ac858309 100644
--- a/src/java/org/apache/fop/render/pdf/extensions/PDFExtensionAttachment.java
+++ b/src/java/org/apache/fop/render/pdf/extensions/PDFExtensionAttachment.java
@@ -23,43 +23,27 @@ import org.apache.xmlgraphics.util.XMLizable;
import org.apache.fop.fo.extensions.ExtensionAttachment;
-/**
- * This is the pass-through value object for the PDF extension.
- */
+// CSOFF: LineLengthCheck
+
public abstract class PDFExtensionAttachment implements ExtensionAttachment, XMLizable {
/** The category URI for this extension attachment. */
public static final String CATEGORY = "apache:fop:extensions:pdf";
+ /** The prefix to use with qualified names for this extension attachment. */
+ public static final String PREFIX = "pdf";
+
/**
* Default constructor.
*/
public PDFExtensionAttachment() {
- //nop
- }
-
- /**
- * @return the category URI
- * @see org.apache.fop.fo.extensions.ExtensionAttachment#getCategory()
- */
- public String getCategory() {
- return CATEGORY;
}
- /** @return type name */
- public String getType() {
- String className = getClass().getName();
- return className.substring(className.lastIndexOf('.') + 3);
+ public String getPrefix() {
+ return PREFIX;
}
- /**
- * @return a string representation of this object
- * @see java.lang.Object#toString()
- */
- public String toString() {
- return getType();
+ public String getCategory() {
+ return CATEGORY;
}
-
- /** @return element */
- protected abstract String getElement();
}
diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFExtensionHandler.java b/src/java/org/apache/fop/render/pdf/extensions/PDFExtensionHandler.java
index e7e863a23..2fd14058e 100644
--- a/src/java/org/apache/fop/render/pdf/extensions/PDFExtensionHandler.java
+++ b/src/java/org/apache/fop/render/pdf/extensions/PDFExtensionHandler.java
@@ -19,6 +19,8 @@
package org.apache.fop.render.pdf.extensions;
+import java.util.Stack;
+
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;
@@ -30,68 +32,189 @@ import org.apache.commons.logging.LogFactory;
import org.apache.fop.util.ContentHandlerFactory;
import org.apache.fop.util.ContentHandlerFactory.ObjectBuiltListener;
+// CSOFF: LineLengthCheck
+
/**
* ContentHandler (parser) for restoring PDF extension objects from XML.
*/
-public class PDFExtensionHandler extends DefaultHandler
- implements ContentHandlerFactory.ObjectSource {
+public class PDFExtensionHandler extends DefaultHandler implements ContentHandlerFactory.ObjectSource {
/** Logger instance */
protected static final Log log = LogFactory.getLog(PDFExtensionHandler.class);
- private Attributes lastAttributes;
-
private PDFExtensionAttachment returnedObject;
private ObjectBuiltListener listener;
- /** {@inheritDoc} */
- public void startElement(String uri, String localName, String qName, Attributes attributes)
- throws SAXException {
- boolean handled = false;
+ // PDFEmbeddedFileAttachment related state
+ private Attributes lastAttributes;
+
+ // PDFDictionaryAttachment related
+ private Stack<PDFCollectionExtension> collections = new Stack<PDFCollectionExtension>();
+ private boolean captureContent;
+ private StringBuffer characters;
+
+ @Override
+ public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
if (PDFExtensionAttachment.CATEGORY.equals(uri)) {
- lastAttributes = new AttributesImpl(attributes);
- handled = false;
- if (localName.equals(PDFEmbeddedFileExtensionAttachment.ELEMENT)) {
- //handled in endElement
- handled = true;
+ if (localName.equals(PDFEmbeddedFileAttachment.ELEMENT)) {
+ lastAttributes = new AttributesImpl(attributes);
+ } else if (PDFDictionaryType.Action.elementName().equals(localName)) {
+ PDFActionExtension action = new PDFActionExtension();
+ String id = attributes.getValue(PDFDictionaryElement.ATT_ID);
+ if (id != null) {
+ action.setProperty(PDFDictionaryExtension.PROPERTY_ID, id);
+ }
+ String type = attributes.getValue(PDFActionElement.ATT_TYPE);
+ if (type != null) {
+ action.setProperty(PDFActionExtension.PROPERTY_TYPE, type);
+ }
+ collections.push(action);
+ } else if (PDFObjectType.Array.elementName().equals(localName)) {
+ PDFArrayExtension array = new PDFArrayExtension();
+ String key = attributes.getValue(PDFCollectionEntryElement.ATT_KEY);
+ if (key != null) {
+ array.setKey(key);
+ }
+ collections.push(array);
+ } else if (PDFDictionaryType.Catalog.elementName().equals(localName)) {
+ PDFCatalogExtension catalog = new PDFCatalogExtension();
+ collections.push(catalog);
+ } else if (PDFDictionaryType.Dictionary.elementName().equals(localName)) {
+ PDFDictionaryExtension dictionary = new PDFDictionaryExtension();
+ String key = attributes.getValue(PDFCollectionEntryElement.ATT_KEY);
+ if (key != null) {
+ dictionary.setKey(key);
+ }
+ collections.push(dictionary);
+ } else if (PDFDictionaryType.Layer.elementName().equals(localName)) {
+ PDFLayerExtension layer = new PDFLayerExtension();
+ String id = attributes.getValue(PDFDictionaryElement.ATT_ID);
+ if (id != null) {
+ layer.setProperty(PDFDictionaryExtension.PROPERTY_ID, id);
+ }
+ collections.push(layer);
+ } else if (PDFDictionaryType.Navigator.elementName().equals(localName)) {
+ PDFNavigatorExtension navigator = new PDFNavigatorExtension();
+ String id = attributes.getValue(PDFDictionaryElement.ATT_ID);
+ if (id != null) {
+ navigator.setProperty(PDFDictionaryExtension.PROPERTY_ID, id);
+ }
+ collections.push(navigator);
+ } else if (PDFDictionaryType.Page.elementName().equals(localName)) {
+ PDFPageExtension page = new PDFPageExtension();
+ String pageNumbers = attributes.getValue(PDFPageElement.ATT_PAGE_NUMBERS);
+ if (pageNumbers != null) {
+ page.setProperty(PDFPageExtension.PROPERTY_PAGE_NUMBERS, pageNumbers);
+ }
+ collections.push(page);
+ } else if (PDFObjectType.hasValueOfElementName(localName)) {
+ PDFCollectionEntryExtension entry;
+ if (PDFObjectType.Reference.elementName().equals(localName)) {
+ entry = new PDFReferenceExtension();
+ } else {
+ entry = new PDFCollectionEntryExtension(PDFObjectType.valueOfElementName(localName));
+ }
+ String key = attributes.getValue(PDFCollectionEntryElement.ATT_KEY);
+ if (key != null) {
+ entry.setKey(key);
+ }
+ if (entry instanceof PDFReferenceExtension) {
+ String refid = attributes.getValue(PDFReferenceElement.ATT_REFID);
+ if (refid != null) {
+ ((PDFReferenceExtension) entry).setReferenceId(refid);
+ }
+ }
+ if (!collections.empty()) {
+ PDFCollectionExtension collection = collections.peek();
+ collection.addEntry(entry);
+ if (!(entry instanceof PDFReferenceExtension)) {
+ captureContent = true;
+ }
+ }
+ } else {
+ throw new SAXException("Unhandled element " + localName + " in namespace: " + uri);
}
+ } else {
+ log.warn("Unhandled element " + localName + " in namespace: " + uri);
}
- if (!handled) {
- if (PDFExtensionAttachment.CATEGORY.equals(uri)) {
- throw new SAXException("Unhandled element " + localName
- + " in namespace: " + uri);
- } else {
- log.warn("Unhandled element " + localName
- + " in namespace: " + uri);
+ }
+
+ @Override
+ public void characters(char[] data, int start, int length) throws SAXException {
+ if (captureContent) {
+ if (characters == null) {
+ characters = new StringBuffer((length < 16) ? 16 : length);
}
+ characters.append(data, start, length);
}
}
- /** {@inheritDoc} */
+ @Override
public void endElement(String uri, String localName, String qName) throws SAXException {
if (PDFExtensionAttachment.CATEGORY.equals(uri)) {
- if (PDFEmbeddedFileExtensionAttachment.ELEMENT.equals(localName)) {
+ if (PDFEmbeddedFileAttachment.ELEMENT.equals(localName)) {
String name = lastAttributes.getValue("name");
String src = lastAttributes.getValue("src");
String desc = lastAttributes.getValue("description");
- this.returnedObject = new PDFEmbeddedFileExtensionAttachment(name, src, desc);
+ this.lastAttributes = null;
+ this.returnedObject = new PDFEmbeddedFileAttachment(name, src, desc);
+ } else if (PDFDictionaryType.hasValueOfElementName(localName)) {
+ if (!collections.empty() && (collections.peek() instanceof PDFDictionaryExtension)) {
+ PDFDictionaryExtension dictionary = (PDFDictionaryExtension) collections.pop();
+ if (!collections.empty()) {
+ PDFCollectionExtension collectionOuter = collections.peek();
+ collectionOuter.addEntry(dictionary);
+ } else if (dictionary.getDictionaryType() != PDFDictionaryType.Dictionary) {
+ this.returnedObject = new PDFDictionaryAttachment(dictionary);
+ } else {
+ throw new SAXException(new IllegalStateException("generic dictionary not permitted at outer level"));
+ }
+ } else {
+ throw new SAXException(new IllegalStateException("collections stack is empty or not a dictionary"));
+ }
+ } else if (PDFObjectType.Array.elementName().equals(localName)) {
+ if (!collections.empty() && (collections.peek() instanceof PDFArrayExtension)) {
+ PDFArrayExtension array = (PDFArrayExtension) collections.pop();
+ if (!collections.empty()) {
+ PDFCollectionExtension collectionOuter = collections.peek();
+ collectionOuter.addEntry(array);
+ } else {
+ throw new SAXException(new IllegalStateException("array not permitted at outer level"));
+ }
+ } else {
+ throw new SAXException(new IllegalStateException("collections stack is empty or not an array"));
+ }
+ } else if (PDFObjectType.hasValueOfElementName(localName)) {
+ if (!collections.empty()) {
+ PDFCollectionExtension collection = collections.peek();
+ PDFCollectionEntryExtension entry = collection.getLastEntry();
+ if (entry != null) {
+ if (characters != null) {
+ entry.setValue(characters.toString());
+ characters = null;
+ }
+ } else {
+ throw new SAXException(new IllegalStateException("no current entry"));
+ }
+ } else {
+ throw new SAXException(new IllegalStateException("entry not permitted at outer level"));
+ }
}
}
+ captureContent = false;
}
- /** {@inheritDoc} */
+ @Override
public void endDocument() throws SAXException {
if (listener != null) {
listener.notifyObjectBuilt(getObject());
}
}
- /** {@inheritDoc} */
public Object getObject() {
return returnedObject;
}
- /** {@inheritDoc} */
public void setObjectBuiltListener(ObjectBuiltListener listener) {
this.listener = listener;
}
diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFExtensionHandlerFactory.java b/src/java/org/apache/fop/render/pdf/extensions/PDFExtensionHandlerFactory.java
new file mode 100644
index 000000000..6d83cd58f
--- /dev/null
+++ b/src/java/org/apache/fop/render/pdf/extensions/PDFExtensionHandlerFactory.java
@@ -0,0 +1,44 @@
+/*
+ * 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.extensions;
+
+import org.xml.sax.ContentHandler;
+
+import org.apache.fop.util.ContentHandlerFactory;
+
+// CSOFF: LineLengthCheck
+
+/**
+ * Factory for the ContentHandler that handles serialized PDFExtensionAttachment instances.
+ */
+public class PDFExtensionHandlerFactory implements ContentHandlerFactory {
+
+ private static final String[] NAMESPACES = new String[] {PDFExtensionAttachment.CATEGORY};
+
+ /** {@inheritDoc} */
+ public String[] getSupportedNamespaces() {
+ return NAMESPACES;
+ }
+
+ /** {@inheritDoc} */
+ public ContentHandler createContentHandler() {
+ return new PDFExtensionHandler();
+ }
+}
diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFLayerElement.java b/src/java/org/apache/fop/render/pdf/extensions/PDFLayerElement.java
new file mode 100644
index 000000000..dab0ecf78
--- /dev/null
+++ b/src/java/org/apache/fop/render/pdf/extensions/PDFLayerElement.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.pdf.extensions;
+
+import org.apache.fop.apps.FOPException;
+import org.apache.fop.fo.Constants;
+import org.apache.fop.fo.FONode;
+
+// CSOFF: LineLengthCheck
+
+/**
+ * Extension element for pdf:layer.
+ */
+public class PDFLayerElement extends PDFDictionaryElement {
+
+ PDFLayerElement(FONode parent) {
+ super(parent, PDFDictionaryType.Layer);
+ }
+
+ @Override
+ public void startOfNode() throws FOPException {
+ super.startOfNode();
+ if (parent.getNameId() != Constants.FO_DECLARATIONS) {
+ invalidChildError(getLocator(), parent.getName(), getNamespaceURI(), getName(), "rule.childOfDeclarations");
+ }
+ }
+
+}
diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFLayerExtension.java b/src/java/org/apache/fop/render/pdf/extensions/PDFLayerExtension.java
new file mode 100644
index 000000000..d6cc27ab8
--- /dev/null
+++ b/src/java/org/apache/fop/render/pdf/extensions/PDFLayerExtension.java
@@ -0,0 +1,29 @@
+/*
+ * 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.extensions;
+
+// CSOFF: LineLengthCheck
+
+public class PDFLayerExtension extends PDFDictionaryExtension {
+
+ PDFLayerExtension() {
+ super(PDFDictionaryType.Layer);
+ }
+}
diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFNavigatorElement.java b/src/java/org/apache/fop/render/pdf/extensions/PDFNavigatorElement.java
new file mode 100644
index 000000000..5c5b779b5
--- /dev/null
+++ b/src/java/org/apache/fop/render/pdf/extensions/PDFNavigatorElement.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.pdf.extensions;
+
+import org.apache.fop.apps.FOPException;
+import org.apache.fop.fo.Constants;
+import org.apache.fop.fo.FONode;
+
+// CSOFF: LineLengthCheck
+
+/**
+ * Extension element for pdf:navigator.
+ */
+public class PDFNavigatorElement extends PDFDictionaryElement {
+
+ PDFNavigatorElement(FONode parent) {
+ super(parent, PDFDictionaryType.Navigator);
+ }
+
+ @Override
+ public void startOfNode() throws FOPException {
+ super.startOfNode();
+ if (parent.getNameId() != Constants.FO_DECLARATIONS) {
+ invalidChildError(getLocator(), parent.getName(), getNamespaceURI(), getName(), "rule.childOfDeclarations");
+ }
+ }
+
+}
diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFNavigatorExtension.java b/src/java/org/apache/fop/render/pdf/extensions/PDFNavigatorExtension.java
new file mode 100644
index 000000000..ee9dfa7c8
--- /dev/null
+++ b/src/java/org/apache/fop/render/pdf/extensions/PDFNavigatorExtension.java
@@ -0,0 +1,30 @@
+/*
+ * 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.extensions;
+
+// CSOFF: LineLengthCheck
+
+public class PDFNavigatorExtension extends PDFDictionaryExtension {
+
+ PDFNavigatorExtension() {
+ super(PDFDictionaryType.Navigator);
+ }
+
+}
diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFObjectExtension.java b/src/java/org/apache/fop/render/pdf/extensions/PDFObjectExtension.java
new file mode 100644
index 000000000..5447d87f2
--- /dev/null
+++ b/src/java/org/apache/fop/render/pdf/extensions/PDFObjectExtension.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.pdf.extensions;
+
+import org.apache.fop.util.XMLUtil;
+
+// CSOFF: LineLengthCheck
+
+public class PDFObjectExtension {
+
+ private PDFObjectType type;
+ private Object value;
+
+ PDFObjectExtension(PDFObjectType type) {
+ this.type = type;
+ }
+
+ public PDFObjectType getType() {
+ return type;
+ }
+
+ public void setValue(Object value) {
+ this.value = value;
+ }
+
+ public Object getValue() {
+ return value;
+ }
+
+ /**
+ * Obtain entry value as Boolean.
+ * @return entry value
+ */
+ public Boolean getValueAsBoolean() {
+ Object value = getValue();
+ if (value instanceof Boolean) {
+ return (Boolean) value;
+ } else if (value instanceof String) {
+ return Boolean.valueOf((String)value);
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Obtain entry value as Number.
+ * @return entry value
+ */
+ public Number getValueAsNumber() {
+ Object value = getValue();
+ if (value instanceof Number) {
+ return (Number) value;
+ } else if (value instanceof String) {
+ double d = Double.parseDouble((String) value);
+ if (Math.abs(Math.floor(d) - d) < 1E-10) {
+ return Long.valueOf((long) d);
+ } else {
+ return Double.valueOf(d);
+ }
+ } else {
+ return Integer.valueOf(0);
+ }
+ }
+
+ public String getValueAsString() {
+ Object value = getValue();
+ if (value == null) {
+ return null;
+ } else if (value instanceof String) {
+ return (String) value;
+ } else {
+ return value.toString();
+ }
+ }
+
+ public String getValueAsXMLEscapedString() {
+ return XMLUtil.escape(getValueAsString());
+ }
+
+ public String getElementName() {
+ return type.elementName();
+ }
+
+}
diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFObjectType.java b/src/java/org/apache/fop/render/pdf/extensions/PDFObjectType.java
new file mode 100644
index 000000000..c193a3b7b
--- /dev/null
+++ b/src/java/org/apache/fop/render/pdf/extensions/PDFObjectType.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.render.pdf.extensions;
+
+// CSOFF: LineLengthCheck
+
+/**
+ * Enumeration type for leaf PDF object extension types used as singletons,
+ * dictionary entries, or array entries.
+ */
+public enum PDFObjectType {
+ Array("array"), // array valued entry
+ Boolean("boolean"), // boolean valued entry
+ Dictionary("dictionary"), // dictionary valued entry
+ Name("name"), // name valued entry
+ Number("number"), // number valued entry
+ Reference("reference"), // indirect object reference entry
+ String("string"); // string valued entry
+
+ private String elementName;
+ PDFObjectType(String elementName) {
+ this.elementName = elementName;
+ }
+ public String elementName() {
+ return elementName;
+ }
+ static PDFObjectType valueOfElementName(String elementName) {
+ for (PDFObjectType type : values()) {
+ if (type.elementName.equals(elementName)) {
+ return type;
+ }
+ }
+ throw new IllegalArgumentException();
+ }
+ static boolean hasValueOfElementName(String elementName) {
+ try {
+ valueOfElementName(elementName);
+ return true;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+}
diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFPageElement.java b/src/java/org/apache/fop/render/pdf/extensions/PDFPageElement.java
new file mode 100644
index 000000000..28a966ead
--- /dev/null
+++ b/src/java/org/apache/fop/render/pdf/extensions/PDFPageElement.java
@@ -0,0 +1,64 @@
+/*
+ * 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.extensions;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.Locator;
+
+import org.apache.fop.apps.FOPException;
+import org.apache.fop.fo.Constants;
+import org.apache.fop.fo.FONode;
+import org.apache.fop.fo.PropertyList;
+
+// CSOFF: LineLengthCheck
+
+/**
+ * Extension element for pdf:page.
+ */
+public class PDFPageElement extends PDFDictionaryElement {
+
+ public static final String ATT_PAGE_NUMBERS = PDFDictionaryExtension.PROPERTY_PAGE_NUMBERS;
+
+ /**
+ * Main constructor
+ * @param parent parent FO node
+ */
+ PDFPageElement(FONode parent) {
+ super(parent, PDFDictionaryType.Page);
+ }
+
+ @Override
+ public void processNode(String elementName, Locator locator, Attributes attlist, PropertyList propertyList) throws FOPException {
+ super.processNode(elementName, locator, attlist, propertyList);
+ String pageNumbers = attlist.getValue(ATT_PAGE_NUMBERS);
+ if (pageNumbers != null) {
+ getDictionaryExtension().setProperty(PDFDictionaryExtension.PROPERTY_PAGE_NUMBERS, pageNumbers);
+ }
+ }
+
+ @Override
+ public void startOfNode() throws FOPException {
+ super.startOfNode();
+ if (parent.getNameId() != Constants.FO_SIMPLE_PAGE_MASTER) {
+ invalidChildError(getLocator(), parent.getName(), getNamespaceURI(), getName(), "rule.childOfSPM");
+ }
+ }
+
+}
diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFPageExtension.java b/src/java/org/apache/fop/render/pdf/extensions/PDFPageExtension.java
new file mode 100644
index 000000000..0726a4e55
--- /dev/null
+++ b/src/java/org/apache/fop/render/pdf/extensions/PDFPageExtension.java
@@ -0,0 +1,73 @@
+/*
+ * 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.extensions;
+
+// CSOFF: LineLengthCheck
+
+public class PDFPageExtension extends PDFDictionaryExtension {
+
+ public static final String PROPERTY_PAGE_NUMBERS = "page-numbers";
+
+ PDFPageExtension() {
+ super(PDFDictionaryType.Page);
+ }
+
+ /**
+ * Determine if page dictionary and page number matches.
+ * @param pageNumber page number, where first page number is 1
+ * @return true if this dictionary is a page dictionary and specified page number matches specified page-number property
+ */
+ public boolean matchesPageNumber(int pageNumber) {
+ String pageNumbers = getProperty(PROPERTY_PAGE_NUMBERS);
+ if ((pageNumbers == null) || (pageNumbers.length() == 0)) {
+ return false;
+ } else if (pageNumbers.equals("*")) {
+ return true;
+ } else {
+ for (String interval : pageNumbers.split("\\s*,\\s*")) {
+ String[] components = interval.split("\\s*-\\s*");
+ if (components.length < 1) {
+ continue;
+ } else {
+ try {
+ int start = Integer.parseInt(components[0]);
+ int end = 0;
+ if (components.length > 1) {
+ if (!components[1].equals("LAST")) {
+ end = Integer.parseInt(components[1]);
+ }
+ }
+ if ((end == 0) && (pageNumber == start)) {
+ return true;
+ } else if ((end > start) && (pageNumber >= start) && (pageNumber < end)) {
+ return true;
+ } else {
+ continue;
+ }
+ } catch (NumberFormatException e) {
+ continue;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+}
diff --git a/src/java/org/apache/fop/fo/extensions/ExtensionObj.java b/src/java/org/apache/fop/render/pdf/extensions/PDFReferenceElement.java
index c35dcfc73..37aeeb890 100644
--- a/src/java/org/apache/fop/fo/extensions/ExtensionObj.java
+++ b/src/java/org/apache/fop/render/pdf/extensions/PDFReferenceElement.java
@@ -17,49 +17,42 @@
/* $Id$ */
-package org.apache.fop.fo.extensions;
+package org.apache.fop.render.pdf.extensions;
import org.xml.sax.Attributes;
import org.xml.sax.Locator;
import org.apache.fop.apps.FOPException;
-import org.apache.fop.fo.FOEventHandler;
import org.apache.fop.fo.FONode;
-import org.apache.fop.fo.FObj;
import org.apache.fop.fo.PropertyList;
+// CSOFF: LineLengthCheck
+
/**
- * Base class for pdf bookmark extension objects.
+ * Extension element for pdf:reference.
*/
-public abstract class ExtensionObj extends FObj {
+public class PDFReferenceElement extends PDFCollectionEntryElement {
- /**
- * Create a new extension object.
- *
- * @param parent the parent formatting object
- */
- public ExtensionObj(FONode parent) {
- super(parent);
- }
+ public static final String ATT_REFID = PDFReferenceExtension.PROPERTY_REFID;
/**
- * {@inheritDoc}
+ * Main constructor
+ * @param parent parent FO node
*/
- public void processNode(String elementName, Locator locator,
- Attributes attlist, PropertyList pList)
- throws FOPException {
+ PDFReferenceElement(FONode parent) {
+ super(parent, PDFObjectType.Reference);
}
- /**
- * Create a default property list for this element.
- * @param parent the parent property list
- * @param foEventHandler an event handler
- * @return property list
- * @throws FOPException in case of exception
- */
- protected PropertyList createPropertyList(PropertyList parent,
- FOEventHandler foEventHandler) throws FOPException {
- return null;
+ @Override
+ public void processNode(String elementName, Locator locator, Attributes attlist, PropertyList propertyList) throws FOPException {
+ super.processNode(elementName, locator, attlist, propertyList);
+ String refid = attlist.getValue(ATT_REFID);
+ if (refid == null) {
+ missingPropertyError(ATT_REFID);
+ } else if (refid.length() == 0) {
+ invalidPropertyValueError(ATT_REFID, refid, null);
+ } else {
+ ((PDFReferenceExtension) getExtension()).setReferenceId(refid);
+ }
}
}
-
diff --git a/src/java/org/apache/fop/render/pdf/extensions/PDFReferenceExtension.java b/src/java/org/apache/fop/render/pdf/extensions/PDFReferenceExtension.java
new file mode 100644
index 000000000..09621c01c
--- /dev/null
+++ b/src/java/org/apache/fop/render/pdf/extensions/PDFReferenceExtension.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.render.pdf.extensions;
+
+// CSOFF: LineLengthCheck
+
+public class PDFReferenceExtension extends PDFCollectionEntryExtension {
+
+ public static final String PROPERTY_REFID = "refid";
+
+ private String refid;
+ private Object resolvedReference;
+
+ PDFReferenceExtension() {
+ super(PDFObjectType.Reference);
+ }
+
+ @Override
+ public void setValue(Object value) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Object getValue() {
+ return this;
+ }
+
+ public String getReferenceId() {
+ return refid;
+ }
+
+ public void setReferenceId(String refid) {
+ this.refid = refid;
+ }
+
+ public Object getResolvedReference() {
+ return resolvedReference;
+ }
+
+ public void setResolvedReference(Object resolvedReference) {
+ this.resolvedReference = resolvedReference;
+ }
+
+}
diff --git a/src/java/org/apache/fop/render/ps/PSFontUtils.java b/src/java/org/apache/fop/render/ps/PSFontUtils.java
index e57567b88..06191f84d 100644
--- a/src/java/org/apache/fop/render/ps/PSFontUtils.java
+++ b/src/java/org/apache/fop/render/ps/PSFontUtils.java
@@ -29,6 +29,7 @@ import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.apache.fontbox.cff.CFFStandardString;
import org.apache.xmlgraphics.fonts.Glyphs;
import org.apache.xmlgraphics.ps.DSCConstants;
@@ -50,9 +51,14 @@ 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.cff.CFFDataReader;
+import org.apache.fop.fonts.cff.CFFDataReader.DICTEntry;
import org.apache.fop.fonts.truetype.FontFileReader;
+import org.apache.fop.fonts.truetype.OFFontLoader;
+import org.apache.fop.fonts.truetype.OTFFile;
+import org.apache.fop.fonts.truetype.OTFSubSetFile;
+import org.apache.fop.fonts.truetype.OpenFont.PostScriptVersion;
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;
@@ -169,6 +175,15 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils {
if (!tracker.isResourceSupplied(WINANSI_ENCODING_RESOURCE)) {
//Only out Base 14 fonts still use that
+ for (Typeface tf : fonts.values()) {
+ if (tf instanceof LazyFont) {
+ tf = ((LazyFont)tf).getRealFont();
+ if (tf instanceof SingleByteFont
+ && ((SingleByteFont) tf).getEncoding().getName().equals("custom")) {
+ defineEncoding(gen, ((SingleByteFont) tf).getEncoding());
+ }
+ }
+ }
defineWinAnsiEncoding(gen);
}
gen.commentln("%FOPBeginFontReencode");
@@ -221,6 +236,7 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils {
private static PSFontResource embedFont(PSGenerator gen, Typeface tf, PSResource fontRes,
PSEventProducer eventProducer) throws IOException {
+ boolean embeddedFont = false;
FontType fontType = tf.getFontType();
PSFontResource fontResource = null;
if (!(fontType == FontType.TYPE1 || fontType == FontType.TRUETYPE
@@ -232,52 +248,63 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils {
CustomFont cf = (CustomFont)tf;
if (isEmbeddable(cf)) {
InputStream in = getInputStreamOnFont(gen, cf);
- if (in == null) {
+ if (in != null) {
+ if (fontType == FontType.TYPE0) {
+ if (((MultiByteFont)tf).isOTFFile()) {
+ checkPostScriptLevel3(gen, eventProducer, "OpenType CFF");
+ embedType2CFF(gen, (MultiByteFont) tf, in);
+ } else {
+ if (gen.embedIdentityH()) {
+ checkPostScriptLevel3(gen, eventProducer, "TrueType");
+ /*
+ * First CID-keyed font to be embedded; add
+ * %%IncludeResource: comment for ProcSet CIDInit.
+ */
+ gen.includeProcsetCIDInitResource();
+ }
+ PSResource cidFontResource;
+ 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);
+ embeddedFont = true;
+ } else {
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);
+ }
+ if (!embeddedFont) {
+ gen.writeDSCComment(DSCConstants.INCLUDE_RESOURCE, fontRes);
+ fontResource = PSFontResource.createFontResource(fontRes);
+ return fontResource;
}
return fontResource;
}
- private static void checkPostScriptLevel3(PSGenerator gen, PSEventProducer eventProducer) {
+ private static void checkPostScriptLevel3(PSGenerator gen, PSEventProducer eventProducer,
+ String fontType) {
if (gen.getPSLevel() < 3) {
if (eventProducer != null) {
eventProducer.postscriptLevel3Needed(gen);
} else {
throw new IllegalStateException("PostScript Level 3 is"
- + " required to use TrueType fonts,"
+ + " required to use " + fontType + " fonts,"
+ " configured level is "
+ gen.getPSLevel());
}
@@ -415,6 +442,96 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils {
gen.writeln("] composefont pop");
}
+ private static void embedType2CFF(PSGenerator gen,
+ MultiByteFont font, InputStream fontStream) throws IOException {
+ FontFileReader reader = new FontFileReader(fontStream);
+ String header = OFFontLoader.readHeader(reader);
+ String psName;
+ CFFDataReader cffReader = new CFFDataReader(reader);
+ if (cffReader.getFDSelect() != null) {
+ throw new UnsupportedOperationException("CID-Keyed OTF CFF fonts are not supported"
+ + " for PostScript output.");
+ }
+
+ byte[] bytes;
+ if (font.getEmbeddingMode() == EmbeddingMode.FULL) {
+ font.setFontName(new String(cffReader.getNameIndex().getValue(0)));
+ psName = font.getEmbedFontName();
+ Map<String, DICTEntry> topDICT = cffReader.getTopDictEntries();
+ int charsetOffset = topDICT.get("charset").getOperands().get(0).intValue();
+ for (int gid = 0; gid < cffReader.getCharStringIndex().getNumObjects(); gid++) {
+ int sid = cffReader.getSIDFromGID(charsetOffset, gid);
+
+ //Check whether the SID falls into the standard string set
+ if (sid < 391) {
+ font.mapUsedGlyphName(gid,
+ CFFStandardString.getName(sid));
+ } else {
+ int index = sid - 391;
+ if (index < cffReader.getStringIndex().getNumObjects()) {
+ font.mapUsedGlyphName(gid,
+ new String(cffReader.getStringIndex().getValue(index)));
+ } else {
+ font.mapUsedGlyphName(gid, ".notdef");
+ }
+ }
+ }
+ bytes = OTFFile.getCFFData(reader);
+ } else {
+ psName = font.getEmbedFontName();
+ OTFSubSetFile otfFile = new OTFSubSetFile();
+ otfFile.readFont(reader, psName, header, font);
+ bytes = otfFile.getFontSubset();
+ }
+
+ gen.writeln("%!PS-Adobe-3.0 Resource-FontSet");
+ gen.writeln("%%DocumentNeedResources:ProcSet(FontSetInit)");
+ gen.writeln("%%Title:(FontSet/" + psName + ")");
+ gen.writeln("%%Version: 1.000");
+ gen.writeln("%%EndComments");
+ gen.writeln("%%IncludeResource:ProcSet(FontSetInit)");
+ gen.writeln("%%BeginResource: FontSet (" + psName + ")");
+ gen.writeln("/FontSetInit /ProcSet findresource begin");
+ //Next line + 1
+ String fontDeclaration = "/" + psName + " " + bytes.length + " StartData";
+ gen.writeln("%%BeginData: " + (fontDeclaration.length() + 1 + bytes.length) + " Binary Bytes");
+ gen.writeln(fontDeclaration);
+ gen.writeByteArr(bytes);
+ gen.writeln("%%EndData");
+ gen.writeln("%%EndResource");
+
+ gen.writeln("/" + psName + ".0.enc [ ");
+ int lengthCount = 0;
+ int charCount = 1;
+ int encodingCount = 0;
+ String line = "";
+ for (int gid : font.getUsedGlyphNames().keySet()) {
+ line += "/" + font.getUsedGlyphNames().get(gid) + " ";
+ lengthCount++;
+ charCount++;
+ if (lengthCount == 8) {
+ gen.writeln(line);
+ line = "";
+ lengthCount = 0;
+ }
+ if (charCount > 256) {
+ encodingCount++;
+ charCount = 1;
+ gen.writeln(line);
+ line = "";
+ lengthCount = 0;
+ gen.writeln("] def");
+ gen.writeln(String.format("/%s.%d %s.%d.enc /%s RE", psName,
+ encodingCount - 1, psName, encodingCount - 1, psName));
+ gen.writeln("/" + psName + "." + encodingCount + ".enc [ ");
+ }
+ }
+ gen.writeln(line);
+ gen.writeln("] def");
+ gen.writeln(String.format("/%s.%d %s.%d.enc /%s RE", psName, encodingCount,
+ psName, encodingCount, psName));
+ }
+
private static PSResource embedType2CIDFont(PSGenerator gen,
MultiByteFont font, InputStream fontStream) throws IOException {
assert font.getCIDType() == CIDFontType.CIDTYPE2;
@@ -502,17 +619,18 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils {
}
gen.writeln(">] def");
FontFileReader reader = new FontFileReader(fontStream);
+ String header = OFFontLoader.readHeader(reader);
TTFFile ttfFile;
if (font.getEmbeddingMode() != EmbeddingMode.FULL) {
ttfFile = new TTFSubSetFile();
- ttfFile.readFont(reader, font.getTTCName(), font.getUsedGlyphs());
+ //Change the TTFFile to have the abstract method for TTFSubSetFile
+ ((TTFSubSetFile)ttfFile).readFont(reader, font.getTTCName(), header, 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");
@@ -670,7 +788,7 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils {
private static PSResource defineDerivedTrueTypeFont(PSGenerator gen,
PSEventProducer eventProducer, String baseFontName, String fontName,
SingleByteEncoding encoding, CMapSegment[] cmap) throws IOException {
- checkPostScriptLevel3(gen, eventProducer);
+ checkPostScriptLevel3(gen, eventProducer, "TrueType");
PSResource res = new PSResource(PSResource.TYPE_FONT, fontName);
gen.writeDSCComment(DSCConstants.BEGIN_RESOURCE, res);
gen.commentln("%XGCDependencies: font " + baseFontName);
diff --git a/src/java/org/apache/fop/render/ps/PSImageHandlerSVG.java b/src/java/org/apache/fop/render/ps/PSImageHandlerSVG.java
index bd8e97063..4d215926b 100644
--- a/src/java/org/apache/fop/render/ps/PSImageHandlerSVG.java
+++ b/src/java/org/apache/fop/render/ps/PSImageHandlerSVG.java
@@ -19,26 +19,50 @@
package org.apache.fop.render.ps;
+import java.awt.Color;
+import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.geom.AffineTransform;
+import java.awt.geom.Rectangle2D;
+import java.awt.image.BufferedImage;
+import java.awt.image.ColorModel;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.imageio.ImageIO;
import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
import org.apache.batik.bridge.BridgeContext;
import org.apache.batik.bridge.GVTBuilder;
import org.apache.batik.gvt.GraphicsNode;
+import org.apache.batik.transcoder.SVGAbstractTranscoder;
+import org.apache.batik.transcoder.TranscoderException;
+import org.apache.batik.transcoder.TranscoderInput;
+import org.apache.batik.transcoder.TranscoderOutput;
+import org.apache.batik.transcoder.image.PNGTranscoder;
import org.apache.xmlgraphics.image.loader.Image;
import org.apache.xmlgraphics.image.loader.ImageFlavor;
import org.apache.xmlgraphics.image.loader.impl.ImageXMLDOM;
-import org.apache.xmlgraphics.java2d.ps.PSGraphics2D;
+import org.apache.xmlgraphics.ps.ImageEncoder;
+import org.apache.xmlgraphics.ps.ImageEncodingHelper;
import org.apache.xmlgraphics.ps.PSGenerator;
import org.apache.fop.image.loader.batik.BatikImageFlavors;
import org.apache.fop.image.loader.batik.BatikUtil;
import org.apache.fop.render.ImageHandler;
import org.apache.fop.render.RenderingContext;
+import org.apache.fop.render.ps.svg.PSSVGGraphics2D;
import org.apache.fop.svg.SVGEventProducer;
import org.apache.fop.svg.SVGUserAgent;
import org.apache.fop.svg.font.FOPFontFamilyResolverImpl;
@@ -48,6 +72,9 @@ import org.apache.fop.svg.font.FOPFontFamilyResolverImpl;
*/
public class PSImageHandlerSVG implements ImageHandler {
+ private static final Color FALLBACK_COLOR = new Color(255, 33, 117);
+ private HashMap<String, String> gradientsFound = new HashMap<String, String>();
+
private static final ImageFlavor[] FLAVORS = new ImageFlavor[] {
BatikImageFlavors.SVG_DOM
};
@@ -59,78 +86,262 @@ public class PSImageHandlerSVG implements ImageHandler {
PSGenerator gen = psContext.getGenerator();
ImageXMLDOM imageSVG = (ImageXMLDOM)image;
- //Controls whether text painted by Batik is generated using text or path operations
- boolean strokeText = false;
- //TODO Configure text stroking
+ if (shouldRaster(imageSVG)) {
+ InputStream is = renderSVGToInputStream(context, imageSVG);
+
+ 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);
+
+ MaskedImage mi = convertToRGB(ImageIO.read(is));
+ BufferedImage ri = mi.getImage();
+ 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, ri, mi.getMaskColor());
+ } else {
+ //Controls whether text painted by Batik is generated using text or path operations
+ boolean strokeText = false;
+ //TODO Configure text stroking
+
+ SVGUserAgent ua = new SVGUserAgent(context.getUserAgent(),
+ new FOPFontFamilyResolverImpl(psContext.getFontInfo()), new AffineTransform());
+
+ PSSVGGraphics2D graphics = new PSSVGGraphics2D(strokeText, gen);
+ graphics.setGraphicContext(new org.apache.xmlgraphics.java2d.GraphicContext());
+
+ BridgeContext ctx = new PSBridgeContext(ua,
+ (strokeText ? null : psContext.getFontInfo()),
+ context.getUserAgent().getImageManager(),
+ context.getUserAgent().getImageSessionContext());
+
+ //Cloning SVG DOM as Batik attaches non-thread-safe facilities (like the CSS engine)
+ //to it.
+ Document clonedDoc = BatikUtil.cloneSVGDocument(imageSVG.getDocument());
+
+ GraphicsNode root;
+ try {
+ GVTBuilder builder = new GVTBuilder();
+ root = builder.build(ctx, clonedDoc);
+ } catch (Exception e) {
+ SVGEventProducer eventProducer = SVGEventProducer.Provider.get(
+ context.getUserAgent().getEventBroadcaster());
+ eventProducer.svgNotBuilt(this, e, image.getInfo().getOriginalURI());
+ return;
+ }
+ // get the 'width' and 'height' attributes of the SVG document
+ float w = (float)ctx.getDocumentSize().getWidth() * 1000f;
+ float h = (float)ctx.getDocumentSize().getHeight() * 1000f;
- SVGUserAgent ua = new SVGUserAgent(context.getUserAgent(),
- new FOPFontFamilyResolverImpl(psContext.getFontInfo()), new AffineTransform());
+ float sx = pos.width / w;
+ float sy = pos.height / h;
- PSGraphics2D graphics = new PSGraphics2D(strokeText, gen);
- graphics.setGraphicContext(new org.apache.xmlgraphics.java2d.GraphicContext());
+ ctx = null;
- BridgeContext ctx = new PSBridgeContext(ua,
- (strokeText ? null : psContext.getFontInfo()),
- context.getUserAgent().getImageManager(),
- context.getUserAgent().getImageSessionContext());
+ gen.commentln("%FOPBeginSVG");
+ gen.saveGraphicsState();
+ final boolean clip = false;
+ if (clip) {
+ /*
+ * Clip to the svg area.
+ * Note: To have the svg overlay (under) a text area then use
+ * an fo:block-container
+ */
+ gen.writeln("newpath");
+ gen.defineRect(pos.getMinX() / 1000f, pos.getMinY() / 1000f,
+ pos.width / 1000f, pos.height / 1000f);
+ gen.writeln("clip");
+ }
- //Cloning SVG DOM as Batik attaches non-thread-safe facilities (like the CSS engine)
- //to it.
- Document clonedDoc = BatikUtil.cloneSVGDocument(imageSVG.getDocument());
+ // transform so that the coordinates (0,0) is from the top left
+ // and positive is down and to the right. (0,0) is where the
+ // viewBox puts it.
+ gen.concatMatrix(sx, 0, 0, sy, pos.getMinX() / 1000f, pos.getMinY() / 1000f);
- GraphicsNode root;
+ AffineTransform transform = new AffineTransform();
+ // scale to viewbox
+ transform.translate(pos.getMinX(), pos.getMinY());
+ gen.getCurrentState().concatMatrix(transform);
+ try {
+ root.paint(graphics);
+ } catch (Exception e) {
+ SVGEventProducer eventProducer = SVGEventProducer.Provider.get(
+ context.getUserAgent().getEventBroadcaster());
+ eventProducer.svgRenderingError(this, e, image.getInfo().getOriginalURI());
+ }
+
+ gen.restoreGraphicsState();
+ gen.commentln("%FOPEndSVG");
+ }
+ }
+
+ private InputStream renderSVGToInputStream(RenderingContext context, ImageXMLDOM imageSVG) throws IOException {
+ PNGTranscoder png = new PNGTranscoder();
+ Float width = getDimension(imageSVG.getDocument(), "width") * 8;
+ png.addTranscodingHint(SVGAbstractTranscoder.KEY_WIDTH, width);
+ Float height = getDimension(imageSVG.getDocument(), "height") * 8;
+ png.addTranscodingHint(SVGAbstractTranscoder.KEY_HEIGHT, height);
+ TranscoderInput input = new TranscoderInput(imageSVG.getDocument());
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+ TranscoderOutput output = new TranscoderOutput(os);
try {
- GVTBuilder builder = new GVTBuilder();
- root = builder.build(ctx, clonedDoc);
- } catch (Exception e) {
+ png.transcode(input, output);
+ } catch (TranscoderException ex) {
SVGEventProducer eventProducer = SVGEventProducer.Provider.get(
context.getUserAgent().getEventBroadcaster());
- eventProducer.svgNotBuilt(this, e, image.getInfo().getOriginalURI());
- return;
+ eventProducer.svgRenderingError(this, ex, imageSVG.getInfo().getOriginalURI());
+ } finally {
+ os.flush();
+ os.close();
+ }
+ return new ByteArrayInputStream(os.toByteArray());
+ }
+
+ private MaskedImage convertToRGB(BufferedImage alphaImage) {
+ int[] red = new int[256];
+ int[] green = new int[256];
+ int[] blue = new int[256];
+ BufferedImage rgbImage = new BufferedImage(alphaImage.getWidth(),
+ alphaImage.getHeight(), BufferedImage.TYPE_INT_RGB);
+ //Count occurances of each colour in image
+ for (int cx = 0; cx < alphaImage.getWidth(); cx++) {
+ for (int cy = 0; cy < alphaImage.getHeight(); cy++) {
+ int pixelValue = alphaImage.getRGB(cx, cy);
+ Color pixelColor = new Color(pixelValue);
+ red[pixelColor.getRed()]++;
+ green[pixelColor.getGreen()]++;
+ blue[pixelColor.getBlue()]++;
+ }
+ }
+ //Find colour not in image
+ Color alphaSwap = null;
+ for (int i = 0; i < 256; i++) {
+ if (red[i] == 0) {
+ alphaSwap = new Color(i, 0, 0);
+ break;
+ } else if (green[i] == 0) {
+ alphaSwap = new Color(0, i, 0);
+ break;
+ } else if (blue[i] == 0) {
+ alphaSwap = new Color(0, 0, i);
+ break;
+ }
}
- // get the 'width' and 'height' attributes of the SVG document
- float w = (float)ctx.getDocumentSize().getWidth() * 1000f;
- float h = (float)ctx.getDocumentSize().getHeight() * 1000f;
-
- float sx = pos.width / w;
- float sy = pos.height / h;
-
- ctx = null;
-
- gen.commentln("%FOPBeginSVG");
- gen.saveGraphicsState();
- final boolean clip = false;
- if (clip) {
- /*
- * Clip to the svg area.
- * Note: To have the svg overlay (under) a text area then use
- * an fo:block-container
- */
- gen.writeln("newpath");
- gen.defineRect(pos.getMinX() / 1000f, pos.getMinY() / 1000f,
- pos.width / 1000f, pos.height / 1000f);
- gen.writeln("clip");
+ //Check if all variations are used in all three colours
+ if (alphaSwap == null) {
+ //Fallback colour is no unique colour channel can be found
+ alphaSwap = FALLBACK_COLOR;
}
+ //Replace alpha channel with the new mask colour
+ for (int cx = 0; cx < alphaImage.getWidth(); cx++) {
+ for (int cy = 0; cy < alphaImage.getHeight(); cy++) {
+ int pixelValue = alphaImage.getRGB(cx, cy);
+ if (pixelValue == 0) {
+ rgbImage.setRGB(cx, cy, alphaSwap.getRGB());
+ } else {
+ rgbImage.setRGB(cx, cy, alphaImage.getRGB(cx, cy));
+ }
+ }
+ }
+ return new MaskedImage(rgbImage, alphaSwap);
+ }
+
+ private static class MaskedImage {
+ private Color maskColor = new Color(0, 0, 0);
+ private BufferedImage image;
+
+ public MaskedImage(BufferedImage image, Color maskColor) {
+ this.image = image;
+ this.maskColor = maskColor;
+ }
+
+ public Color getMaskColor() {
+ return maskColor;
+ }
+
+ public BufferedImage getImage() {
+ return image;
+ }
+ }
- // transform so that the coordinates (0,0) is from the top left
- // and positive is down and to the right. (0,0) is where the
- // viewBox puts it.
- gen.concatMatrix(sx, 0, 0, sy, pos.getMinX() / 1000f, pos.getMinY() / 1000f);
+ private Float getDimension(Document document, String dimension) {
+ if (document.getFirstChild().getAttributes().getNamedItem(dimension) != null) {
+ String width = document.getFirstChild().getAttributes().getNamedItem(dimension).getNodeValue();
+ width = width.replaceAll("[^\\d.]", "");
+ return Float.parseFloat(width);
+ }
+ return null;
+ }
- AffineTransform transform = new AffineTransform();
- // scale to viewbox
- transform.translate(pos.getMinX(), pos.getMinY());
- gen.getCurrentState().concatMatrix(transform);
+ private boolean shouldRaster(ImageXMLDOM image) {
+ //A list of objects on which to check opacity
try {
- root.paint(graphics);
- } catch (Exception e) {
- SVGEventProducer eventProducer = SVGEventProducer.Provider.get(
- context.getUserAgent().getEventBroadcaster());
- eventProducer.svgRenderingError(this, e, image.getInfo().getOriginalURI());
+ List<String> gradMatches = new ArrayList<String>();
+ gradMatches.add("radialGradient");
+ gradMatches.add("linearGradient");
+ return recurseSVGElements(image.getDocument().getChildNodes(), gradMatches, false);
+ } finally {
+ gradientsFound.clear();
}
+ }
- gen.restoreGraphicsState();
- gen.commentln("%FOPEndSVG");
+ private boolean recurseSVGElements(NodeList childNodes, List<String> gradMatches, boolean isMatched) {
+ boolean opacityFound = false;
+ for (int i = 0; i < childNodes.getLength(); i++) {
+ Node curNode = childNodes.item(i);
+ if (isMatched && curNode.getLocalName() != null && curNode.getLocalName().equals("stop")) {
+ if (curNode.getAttributes().getNamedItem("style") != null) {
+ String[] stylePairs = curNode.getAttributes().getNamedItem("style").getNodeValue()
+ .split(";");
+ for (int styleAtt = 0; styleAtt < stylePairs.length; styleAtt++) {
+ String[] style = stylePairs[styleAtt].split(":");
+ if (style[0].equalsIgnoreCase("stop-opacity")) {
+ if (Double.parseDouble(style[1]) < 1) {
+ return true;
+ }
+ }
+ }
+ }
+ if (curNode.getAttributes().getNamedItem("stop-opacity") != null) {
+ String opacityValue = curNode.getAttributes().getNamedItem("stop-opacity").getNodeValue();
+ if (Double.parseDouble(opacityValue) < 1) {
+ return true;
+ }
+ }
+ }
+ String nodeName = curNode.getLocalName();
+ //Special case where rasterization needed for radial gradients
+ if (nodeName != null && nodeName.equals("ellipse")) {
+ String found = "";
+ String ellipseFill = curNode.getAttributes().getNamedItem("fill").getNodeValue();
+ Pattern pattern = Pattern.compile("#(.*?)\\)");
+ Matcher matcher = pattern.matcher(ellipseFill);
+ if (matcher.find()) {
+ found = matcher.group(1);
+ }
+ if (gradientsFound.get(found) != null) {
+ return true;
+ }
+ }
+ boolean inMatch = false;
+ if (!isMatched) {
+ inMatch = nodeName != null && gradMatches.contains(nodeName);
+ if (inMatch) {
+ gradientsFound.put(curNode.getAttributes().getNamedItem("id").getNodeValue(), nodeName);
+ }
+ } else {
+ inMatch = true;
+ }
+ opacityFound = recurseSVGElements(curNode.getChildNodes(), gradMatches, inMatch);
+ if (opacityFound) {
+ return true;
+ }
+ }
+ return opacityFound;
}
/** {@inheritDoc} */
diff --git a/src/java/org/apache/fop/render/ps/PSPainter.java b/src/java/org/apache/fop/render/ps/PSPainter.java
index b0b370c79..547662c47 100644
--- a/src/java/org/apache/fop/render/ps/PSPainter.java
+++ b/src/java/org/apache/fop/render/ps/PSPainter.java
@@ -40,6 +40,7 @@ import org.apache.xmlgraphics.image.loader.ImageSessionContext;
import org.apache.xmlgraphics.ps.PSGenerator;
import org.apache.xmlgraphics.ps.PSResource;
+import org.apache.fop.fonts.EmbeddingMode;
import org.apache.fop.fonts.Font;
import org.apache.fop.fonts.FontTriplet;
import org.apache.fop.fonts.LazyFont;
@@ -115,7 +116,7 @@ public class PSPainter extends AbstractIFPainter<PSDocumentHandler> {
}
/** {@inheritDoc} */
- public void startGroup(AffineTransform transform) throws IFException {
+ public void startGroup(AffineTransform transform, String layer) throws IFException {
try {
PSGenerator generator = getGenerator();
saveGraphicsState();
@@ -375,7 +376,13 @@ public class PSPainter extends AbstractIFPainter<PSDocumentHandler> {
}
Font font = getFontInfo().getFontInstance(triplet, sizeMillipoints);
- useFont(fontKey, sizeMillipoints);
+ PSFontResource res = getDocumentHandler().getPSResourceForFontKey(fontKey);
+ if (tf instanceof MultiByteFont && ((MultiByteFont)tf).isOTFFile()) {
+ generator.writeln("/" + res.getName() + ".0 "
+ + generator.formatDouble(sizeMillipoints / 1000f) + " F");
+ } else {
+ useFont(fontKey, sizeMillipoints);
+ }
if (dp != null && dp[0] != null) {
x += dp[0][0];
@@ -408,7 +415,30 @@ public class PSPainter extends AbstractIFPainter<PSDocumentHandler> {
}
}
} else {
- useFont(fontKey, sizeMillipoints);
+ if (tf instanceof MultiByteFont && ((MultiByteFont)tf).isOTFFile()) {
+ //Analyze string and split up in order to paint in different sub-fonts/encodings
+ int curEncoding = 0;
+ for (int i = start; i < textLen; i++) {
+ char orgChar = text.charAt(i);
+
+ MultiByteFont mbFont = (MultiByteFont)tf;
+ int origGlyphIdx = mbFont.findGlyphIndex(orgChar);
+ int newGlyphIdx = mbFont.getUsedGlyphs().get(origGlyphIdx);
+ int encoding = newGlyphIdx / 256;
+ if (encoding != curEncoding) {
+ if (i != 0) {
+ writeText(text, start, i - start, letterSpacing, wordSpacing, dp, font, tf,
+ true);
+ start = i;
+ }
+ generator.writeln("/" + res.getName() + "." + encoding + " "
+ + generator.formatDouble(sizeMillipoints / 1000f) + " F");
+ curEncoding = encoding;
+ }
+ }
+ } else {
+ useFont(fontKey, sizeMillipoints);
+ }
}
writeText(text, start, textLen - start, letterSpacing, wordSpacing, dp, font, tf,
tf instanceof MultiByteFont);
@@ -431,12 +461,14 @@ public class PSPainter extends AbstractIFPainter<PSDocumentHandler> {
int lineStart = 0;
StringBuffer accText = new StringBuffer(initialSize);
StringBuffer sb = new StringBuffer(initialSize);
+ boolean isOTF = multiByte && ((MultiByteFont)tf).isOTFFile();
for (int i = start; i < end; i++) {
char orgChar = text.charAt(i);
char ch;
int cw;
int xGlyphAdjust = 0;
int yGlyphAdjust = 0;
+
if (CharUtilities.isFixedWidthSpace(orgChar)) {
//Fixed width space are rendered as spaces so copy/paste works in a reader
ch = font.mapChar(CharUtilities.SPACE);
@@ -460,11 +492,16 @@ public class PSPainter extends AbstractIFPainter<PSDocumentHandler> {
xGlyphAdjust -= dp[i + 1][0];
yGlyphAdjust += dp[i + 1][1];
}
- if (multiByte) {
- accText.append(HexEncoder.encode(ch));
- } else {
+ if (!multiByte || isOTF) {
char codepoint = (char)(ch % 256);
- PSGenerator.escapeChar(codepoint, accText); //add character to accumulated text
+ if (isOTF) {
+ codepoint -= (((MultiByteFont)tf).getEmbeddingMode() == EmbeddingMode.FULL) ? 0 : 1;
+ accText.append(HexEncoder.encode(codepoint, 2));
+ } else {
+ PSGenerator.escapeChar(codepoint, accText); //add character to accumulated text
+ }
+ } else {
+ accText.append(HexEncoder.encode(ch));
}
if (xGlyphAdjust != 0 || yGlyphAdjust != 0) {
needTJ = true;
diff --git a/src/java/org/apache/fop/render/ps/svg/PSFunction.java b/src/java/org/apache/fop/render/ps/svg/PSFunction.java
new file mode 100644
index 000000000..b03e0b590
--- /dev/null
+++ b/src/java/org/apache/fop/render/ps/svg/PSFunction.java
@@ -0,0 +1,143 @@
+/*
+ * 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.svg;
+
+import java.io.UnsupportedEncodingException;
+import java.util.List;
+
+import org.apache.fop.render.shading.Function;
+import org.apache.fop.render.shading.FunctionDelegate;
+import org.apache.fop.render.shading.FunctionPattern;
+
+public class PSFunction implements Function {
+
+ private FunctionDelegate delegate;
+
+ /**
+ * Creates a Postscript function dictionary
+ * @param theFunctionType The function type (0 = Sampled, 2 = Exponential
+ * Interpolation, 3 = Stitching)
+ * @param theDomain The function domain
+ * @param theRange Range used for clipping
+ * @param theFunctions An array of sub-functions such as determining the
+ * colour values used in a gradient.
+ * @param theBounds Bounds determines where each boundary exists for whatever
+ * the function is mean't. In a gradient case, it would be the point between
+ * colours.
+ * @param theEncode The function encoding
+ */
+ public PSFunction(int theFunctionType, List<Double> theDomain,
+ List<Double> theRange, List<Function> theFunctions,
+ List<Double> theBounds, List<Double> theEncode) {
+ delegate = new FunctionDelegate(this, theFunctionType, theDomain, theRange, theFunctions,
+ theBounds, theEncode);
+ }
+
+ /**
+ * Creates a Postscript function dictionary
+ * @param theFunctionType The function type (0 = Sampled, 2 = Exponential
+ * Interpolation, 3 = Stitching)
+ * @param theDomain The function domain
+ * @param theRange Range used for clipping
+ * @param theCZero In a gradient, this would be the first colour
+ * @param theCOne In a gradient, this would be the second colour
+ * @param theInterpolationExponentN Determines the number of values
+ * the function returns.
+ */
+ public PSFunction(int theFunctionType, List<Double> theDomain,
+ List<Double> theRange, List<Double> theCZero, List<Double> theCOne,
+ double theInterpolationExponentN) {
+ delegate = new FunctionDelegate(this, theFunctionType, theDomain, theRange, theCZero,
+ theCOne, theInterpolationExponentN);
+ }
+
+ /**
+ * Outputs the function to a byte array
+ */
+ public byte[] toByteString() {
+ FunctionPattern pattern = new FunctionPattern(this);
+ try {
+ return pattern.toWriteableString().getBytes("UTF-8");
+ } catch (UnsupportedEncodingException ex) {
+ //This should have been made an enum type to avoid throwing exceptions.
+ return new byte[0];
+ }
+ }
+
+ public int getFunctionType() {
+ return delegate.getFunctionType();
+ }
+
+ public List<Double> getBounds() {
+ return delegate.getBounds();
+ }
+
+ public List<Double> getDomain() {
+ return delegate.getDomain();
+ }
+
+ public List<Double> getSize() {
+ return delegate.getSize();
+ }
+
+ public List<String> getFilter() {
+ return delegate.getFilter();
+ }
+
+ public List<Double> getEncode() {
+ return delegate.getEncode();
+ }
+
+ public List<Function> getFunctions() {
+ return delegate.getFunctions();
+ }
+
+ public int getBitsPerSample() {
+ return delegate.getBitsPerSample();
+ }
+
+ public double getInterpolationExponentN() {
+ return delegate.getInterpolationExponentN();
+ }
+
+ public int getOrder() {
+ return delegate.getOrder();
+ }
+
+ public List<Double> getRange() {
+ return delegate.getRange();
+ }
+
+ public List<Double> getDecode() {
+ return delegate.getDecode();
+ }
+
+ public StringBuffer getDataStream() {
+ return delegate.getDataStream();
+ }
+
+ public List<Double> getCZero() {
+ return delegate.getCZero();
+ }
+
+ public List<Double> getCOne() {
+ return delegate.getCOne();
+ }
+}
diff --git a/src/java/org/apache/fop/render/ps/svg/PSPattern.java b/src/java/org/apache/fop/render/ps/svg/PSPattern.java
new file mode 100644
index 000000000..46f976672
--- /dev/null
+++ b/src/java/org/apache/fop/render/ps/svg/PSPattern.java
@@ -0,0 +1,103 @@
+/*
+ * 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.svg;
+
+import java.util.List;
+
+import org.apache.fop.render.shading.Pattern;
+import org.apache.fop.render.shading.Shading;
+
+public class PSPattern implements Pattern {
+
+ /**
+ * Either one (1) for tiling, or two (2) for shading.
+ */
+ protected int patternType = 2; // Default
+
+ /**
+ * The Shading object comprising the Type 2 pattern
+ */
+ protected PSShading shading = null;
+
+ /**
+ * List of Integers represetning the Extended unique Identifier
+ */
+ protected List xUID = null;
+
+ /**
+ * TODO use PDFGState
+ * String representing the extended Graphics state.
+ * Probably will never be used like this.
+ */
+ protected StringBuffer extGState = null;
+
+ /**
+ * Creates a radial or axial shading pattern
+ * @param thePatternType The pattern type which will be 3 for radial and 2 for axial
+ * @param theShading The shading object to determine how the gradient
+ * is drawn
+ * @param theXUID The XUID
+ * @param theExtGState The exit state
+ */
+ public PSPattern(int thePatternType, Shading theShading, List theXUID,
+ StringBuffer theExtGState) {
+ this.patternType = 2; // thePatternType;
+ assert theShading instanceof PSShading;
+ this.shading = (PSShading)theShading;
+ this.xUID = theXUID;
+ this.extGState = theExtGState; // always null
+ }
+
+ /**
+ * Outputs the radial or axial pattern as a string dictionary to insert
+ * into a postscript document.
+ */
+ public String toString() {
+ int vectorSize = 0;
+ int tempInt = 0;
+ StringBuffer p = new StringBuffer(64);
+ p.append("/Pattern setcolorspace\n");
+ p.append("<< \n/Type /Pattern \n");
+
+ p.append("/PatternType " + this.patternType + " \n");
+
+ if (this.shading != null) {
+ p.append("/Shading " + this.shading.toString() + " \n");
+ }
+
+ if (this.xUID != null) {
+ vectorSize = this.xUID.size();
+ p.append("/XUID [ ");
+ for (tempInt = 0; tempInt < vectorSize; tempInt++) {
+ p.append((this.xUID.get(tempInt)) + " ");
+ }
+ p.append("] \n");
+ }
+
+ if (this.extGState != null) {
+ p.append("/ExtGState " + this.extGState + " \n");
+ }
+
+ p.append(">> \n");
+ p.append("matrix makepattern setcolor\n");
+
+ return p.toString();
+ }
+}
diff --git a/src/java/org/apache/fop/render/ps/svg/PSSVGGraphics2D.java b/src/java/org/apache/fop/render/ps/svg/PSSVGGraphics2D.java
new file mode 100644
index 000000000..1c15e569b
--- /dev/null
+++ b/src/java/org/apache/fop/render/ps/svg/PSSVGGraphics2D.java
@@ -0,0 +1,294 @@
+/*
+ * 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.svg;
+
+import java.awt.Color;
+import java.awt.Graphics;
+import java.awt.Paint;
+import java.awt.geom.AffineTransform;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.batik.ext.awt.LinearGradientPaint;
+import org.apache.batik.ext.awt.MultipleGradientPaint;
+import org.apache.batik.ext.awt.RadialGradientPaint;
+
+import org.apache.xmlgraphics.java2d.ps.PSGraphics2D;
+import org.apache.xmlgraphics.ps.PSGenerator;
+
+import org.apache.fop.pdf.PDFDeviceColorSpace;
+import org.apache.fop.render.shading.Function;
+import org.apache.fop.render.shading.GradientFactory;
+import org.apache.fop.render.shading.GradientRegistrar;
+import org.apache.fop.render.shading.PSGradientFactory;
+import org.apache.fop.render.shading.Pattern;
+import org.apache.fop.render.shading.Shading;
+
+
+public class PSSVGGraphics2D extends PSGraphics2D implements GradientRegistrar {
+
+ private static final Log LOG = LogFactory.getLog(PSSVGGraphics2D.class);
+
+ /**
+ * Create a new Graphics2D that generates PostScript code.
+ * @param textAsShapes True if text should be rendered as graphics
+ * @see org.apache.xmlgraphics.java2d.AbstractGraphics2D#AbstractGraphics2D(boolean)
+ */
+ public PSSVGGraphics2D(boolean textAsShapes) {
+ super(textAsShapes);
+ }
+
+ /**
+ * Create a new Graphics2D that generates PostScript code.
+ * @param textAsShapes True if text should be rendered as graphics
+ * @param gen PostScript generator to use for output
+ * @see org.apache.xmlgraphics.java2d.AbstractGraphics2D#AbstractGraphics2D(boolean)
+ */
+ public PSSVGGraphics2D(boolean textAsShapes, PSGenerator gen) {
+ super(textAsShapes, gen);
+ }
+
+ /**
+ * Constructor for creating copies
+ * @param g parent PostScript Graphics2D
+ */
+ public PSSVGGraphics2D(PSGraphics2D g) {
+ super(g);
+ }
+
+ protected void applyPaint(Paint paint, boolean fill) {
+ super.applyPaint(paint, fill);
+ if (paint instanceof RadialGradientPaint) {
+ RadialGradientPaint rgp = (RadialGradientPaint)paint;
+ try {
+ handleRadialGradient(rgp, gen);
+ } catch (IOException ioe) {
+ handleIOException(ioe);
+ }
+ } else if (paint instanceof LinearGradientPaint) {
+ LinearGradientPaint lgp = (LinearGradientPaint)paint;
+ try {
+ handleLinearGradient(lgp, gen);
+ } catch (IOException ioe) {
+ handleIOException(ioe);
+ }
+ }
+ }
+
+ private void handleLinearGradient(LinearGradientPaint lgp, PSGenerator gen) throws IOException {
+ MultipleGradientPaint.CycleMethodEnum cycle = lgp.getCycleMethod();
+ if (cycle != MultipleGradientPaint.NO_CYCLE) {
+ return;
+ }
+ float[] fractions = lgp.getFractions();
+ Color[] cols = lgp.getColors();
+
+ AffineTransform transform = new AffineTransform(getBaseTransform());
+ transform.concatenate(getTransform());
+ transform.concatenate(lgp.getTransform());
+
+ List theMatrix = new ArrayList();
+ double [] mat = new double[6];
+ transform.getMatrix(mat);
+ for (int idx = 0; idx < mat.length; idx++) {
+ theMatrix.add(Double.valueOf(mat[idx]));
+ }
+
+
+ List<Double> theCoords = new java.util.ArrayList<Double>();
+
+
+ AffineTransform start = applyTransform(lgp.getTransform(),
+ lgp.getStartPoint().getX(), lgp.getStartPoint().getY());
+ AffineTransform end = applyTransform(lgp.getTransform(), lgp.getEndPoint().getX(), lgp.getEndPoint().getY());
+ double startX = start.getTranslateX();
+ double startY = start.getTranslateY();
+ double endX = end.getTranslateX();
+ double endY = end.getTranslateY();
+
+ double width = endX - startX;
+ double height = endY - startY;
+
+ startX = startX + width * fractions[0];
+ endX = endX - width * (1 - fractions[fractions.length - 1]);
+ startY = startY + (height * fractions[0]);
+ endY = endY - height * (1 - fractions[fractions.length - 1]);
+
+ theCoords.add(startX);
+ theCoords.add(startY);
+ theCoords.add(endX);
+ theCoords.add(endY);
+
+
+ List<Color> someColors = new java.util.ArrayList<Color>();
+ for (int count = 0; count < cols.length; count++) {
+ Color c1 = cols[count];
+ if (c1.getAlpha() != 255) {
+ LOG.warn("Opacity is not currently supported for Postscript output");
+ }
+ someColors.add(c1);
+ }
+ List<Double> theBounds = new java.util.ArrayList<Double>();
+ for (int count = 1; count < fractions.length - 1; count++) {
+ float offset = fractions[count];
+ theBounds.add(Double.valueOf(offset));
+ }
+ PDFDeviceColorSpace colSpace;
+ colSpace = new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_RGB);
+
+ PSGradientFactory gradientFactory = (PSGradientFactory)GradientFactory.newInstance(this);
+ PSPattern myPattern = gradientFactory.createGradient(false, colSpace,
+ someColors, theBounds, theCoords, theMatrix);
+
+ gen.write(myPattern.toString());
+
+ }
+
+
+
+ private void handleRadialGradient(RadialGradientPaint rgp, PSGenerator gen) throws IOException {
+ MultipleGradientPaint.CycleMethodEnum cycle = rgp.getCycleMethod();
+ if (cycle != MultipleGradientPaint.NO_CYCLE) {
+ return;
+ }
+
+ AffineTransform transform;
+ transform = new AffineTransform(getBaseTransform());
+ transform.concatenate(getTransform());
+ transform.concatenate(rgp.getTransform());
+
+ AffineTransform resultCentre = applyTransform(rgp.getTransform(),
+ rgp.getCenterPoint().getX(), rgp.getCenterPoint().getY());
+ AffineTransform resultFocus = applyTransform(rgp.getTransform(),
+ rgp.getFocusPoint().getX(), rgp.getFocusPoint().getY());
+ double scale = Math.sqrt(rgp.getTransform().getDeterminant());
+ double radius = rgp.getRadius() * scale;
+ double centreX = resultCentre.getTranslateX();
+ double centreY = resultCentre.getTranslateY();
+ double focusX = resultFocus.getTranslateX();
+ double focusY = resultFocus.getTranslateY();
+
+ List<Double> theMatrix = new java.util.ArrayList<Double>();
+ double [] mat = new double[6];
+ transform.getMatrix(mat);
+ for (int idx = 0; idx < mat.length; idx++) {
+ theMatrix.add(Double.valueOf(mat[idx]));
+ }
+
+ List<Double> theCoords = new java.util.ArrayList<Double>();
+ float[] fractions = rgp.getFractions();
+
+ theCoords.add(centreX);
+ theCoords.add(centreY);
+ theCoords.add(radius * rgp.getFractions()[0]);
+ theCoords.add(focusX);
+ theCoords.add(focusY);
+ theCoords.add(radius * fractions[fractions.length - 1]);
+
+ Color[] cols = rgp.getColors();
+ List<Color> someColors = new java.util.ArrayList<Color>();
+ for (int count = 0; count < cols.length; count++) {
+ Color cc = cols[count];
+ if (cc.getAlpha() != 255) {
+ /* This should never happen because radial gradients with opacity should now
+ * be rasterized in the PSImageHandlerSVG class. Please see the shouldRaster()
+ * method for more information. */
+ LOG.warn("Opacity is not currently supported for Postscript output");
+ }
+
+ someColors.add(cc);
+ }
+
+ List<Double> theBounds = new java.util.ArrayList<Double>();
+ for (int count = 1; count < fractions.length - 1; count++) {
+ float offset = fractions[count];
+ theBounds.add(Double.valueOf(offset));
+ }
+ PDFDeviceColorSpace colSpace;
+ colSpace = new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_RGB);
+
+ PSGradientFactory gradientFactory = (PSGradientFactory)GradientFactory.newInstance(this);
+ PSPattern myPattern = gradientFactory.createGradient(true, colSpace,
+ someColors, theBounds, theCoords, theMatrix);
+
+ gen.write(myPattern.toString());
+ }
+
+ private AffineTransform applyTransform(AffineTransform base, double posX, double posY) {
+ AffineTransform result = AffineTransform.getTranslateInstance(posX, posY);
+ AffineTransform orig = base;
+ orig.concatenate(result);
+ return orig;
+ }
+
+ protected AffineTransform getBaseTransform() {
+ AffineTransform at = new AffineTransform(this.getTransform());
+ return at;
+ }
+
+ /**
+ * Creates a new <code>Graphics</code> object that is
+ * a copy of this <code>Graphics</code> object.
+ * @return a new graphics context that is a copy of
+ * this graphics context.
+ */
+ @Override
+ public Graphics create() {
+ preparePainting();
+ return new PSSVGGraphics2D(this);
+ }
+
+ /**
+ * Registers a function object against the output format document
+ * @param function The function object to register
+ * @return Returns either the function which has already been registered
+ * or the current new registered object.
+ */
+ public Function registerFunction(Function function) {
+ //Objects aren't needed to be registered in Postscript
+ return function;
+ }
+
+ /**
+ * Registers a shading object against the otuput format document
+ * @param shading The shading object to register
+ * @return Returs either the shading which has already been registered
+ * or the current new registered object
+ */
+ public Shading registerShading(Shading shading) {
+ //Objects aren't needed to be registered in Postscript
+ return shading;
+ }
+
+ /**
+ * Registers a pattern object against the output format document
+ * @param pattern The pattern object to register
+ * @return Returns either the pattern which has already been registered
+ * or the current new registered object
+ */
+ public Pattern registerPattern(Pattern pattern) {
+ // TODO Auto-generated method stub
+ return pattern;
+ }
+}
diff --git a/src/java/org/apache/fop/render/ps/svg/PSShading.java b/src/java/org/apache/fop/render/ps/svg/PSShading.java
new file mode 100644
index 000000000..7465fcadb
--- /dev/null
+++ b/src/java/org/apache/fop/render/ps/svg/PSShading.java
@@ -0,0 +1,228 @@
+/*
+ * 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.svg;
+
+import java.io.UnsupportedEncodingException;
+import java.util.List;
+
+import org.apache.fop.pdf.PDFDeviceColorSpace;
+import org.apache.fop.pdf.PDFNumber;
+import org.apache.fop.render.shading.Function;
+import org.apache.fop.render.shading.Shading;
+import org.apache.fop.render.shading.ShadingPattern;
+
+public class PSShading implements Shading {
+
+ /**
+ * Required: The Type of shading (1,2,3,4,5,6,7)
+ */
+ protected int shadingType = 3; // Default
+
+ /**
+ * A ColorSpace representing the colorspace. "DeviceRGB" is an example.
+ */
+ protected PDFDeviceColorSpace colorSpace = null;
+
+ /**
+ * The background color. Since shading is opaque,
+ * this is very rarely used.
+ */
+ protected List background = null;
+
+ /**
+ * Optional: A List specifying the clipping rectangle
+ */
+ protected List bBox = null;
+
+ /**
+ * Optional: A flag whether or not to filter the shading function
+ * to prevent aliasing artifacts. Default is false.
+ */
+ protected boolean antiAlias = false;
+
+ /**
+ * Optional for Type 1: Array of four numbers, xmin, xmax, ymin, ymax.
+ * Default is [0 1 0 1]
+ * Optional for Type 2: An array of two numbers between which the blend
+ * varies between start and end points. Default is 0, 1.
+ * Optional for Type 3: An array of two numbers between which the blend
+ * varies between start and end points. Default is 0, 1.
+ */
+ protected List domain = null;
+
+ /**
+ * Required for Type 1, 2, and 3:
+ * The object of the color mapping function (usually type 2 or 3).
+ * Optional for Type 4,5,6, and 7: When it's nearly the same thing.
+ */
+ protected PSFunction function = null;
+
+ /**
+ * Required for Type 2: An Array of four numbers specifying
+ * the starting and ending coordinate pairs
+ * Required for Type 3: An Array of six numbers [x0,y0,r0,x1,y1,r1]
+ * specifying the centers and radii of
+ * the starting and ending circles.
+ */
+ protected List coords = null;
+
+ /**
+ * Required for Type 2+3: An Array of two boolean values specifying
+ * whether to extend the start and end colors past the start
+ * and end points, respectively.
+ * Default is false, false.
+ */
+ protected List extend = null;
+
+ /**
+ * Constructor for Type 2 and 3
+ *
+ * @param theShadingType 2 or 3 for axial or radial shading
+ * @param theColorSpace "DeviceRGB" or similar.
+ * @param theBackground theBackground An array of color components appropriate to the
+ * colorspace key specifying a single color value.
+ * This key is used by the f operator buy ignored by the sh operator.
+ * @param theBBox List of double's representing a rectangle
+ * in the coordinate space that is current at the
+ * time of shading is imaged. Temporary clipping
+ * boundary.
+ * @param theAntiAlias Default is false
+ * @param theCoords List of four (type 2) or 6 (type 3) Double
+ * @param theDomain List of Doubles specifying the domain
+ * @param theFunction the Stitching (PDFfunction type 3) function,
+ * even if it's stitching a single function
+ * @param theExtend List of Booleans of whether to extend the start
+ * and end colors past the start and end points
+ * The default is [false, false]
+ */
+ public PSShading(int theShadingType, PDFDeviceColorSpace theColorSpace,
+ List<Double> theBackground, List<Double> theBBox,
+ boolean theAntiAlias, List<Double> theCoords,
+ List<Double> theDomain, Function theFunction,
+ List<Integer> theExtend) {
+ this.shadingType = theShadingType; // 2 or 3
+ this.colorSpace = theColorSpace;
+ this.background = theBackground;
+ this.bBox = theBBox;
+ this.antiAlias = theAntiAlias;
+
+ this.coords = theCoords;
+ this.domain = theDomain;
+ assert theFunction instanceof PSFunction;
+ this.function = (PSFunction)theFunction;
+ this.extend = theExtend;
+ }
+
+ /**
+ * represent as PS. Whatever the shadingType is, the correct
+ * representation spits out. The sets of required and optional
+ * attributes are different for each type, but if a required
+ * attribute's object was constructed as null, then no error
+ * is raised. Instead, the malformed PS that was requested
+ * by the construction is dutifully output.
+ * This policy should be reviewed.
+ *
+ * @return the PDF string.
+ */
+ public String toString() {
+ ShadingPattern pattern = new ShadingPattern(this);
+ return pattern.toString(colorSpace, shadingType, background, bBox, antiAlias);
+ }
+
+ /**
+ * A method to write a type 2 or 3 shading object
+ * @param p The StringBuffer to write the shading object
+ * @return Returns the StringBuffer to which the shading object was written
+ */
+ public StringBuffer handleShadingType2or3(StringBuffer p) {
+ if (this.coords != null) {
+ p.append("\t/Coords [ ");
+ for (int coordIndex = 0; coordIndex < coords.size(); coordIndex++) {
+ p.append(PDFNumber.doubleOut((Double)this.coords.get(coordIndex))
+ + " ");
+ }
+ p.append("] \n");
+ }
+
+ // DOMAIN
+ if (this.domain != null) {
+ p.append("\t/Domain [ ");
+ for (int domainIndex = 0; domainIndex < domain.size(); domainIndex++) {
+ p.append(PDFNumber.doubleOut((Double)this.domain.get(domainIndex))
+ + " ");
+ }
+ p.append("] \n");
+ } else {
+ p.append("\t/Domain [ 0 1 ] \n");
+ }
+
+ if (this.extend != null) {
+ p.append("\t/Extend [ ");
+ for (int extendIndex = 0; extendIndex < extend.size(); extendIndex++) {
+ p.append((this.extend.get(extendIndex)) + " ");
+ }
+
+ p.append("] \n");
+ } else {
+ p.append("\t/Extend [ true true ] \n");
+ }
+
+
+ if (this.function != null) {
+ p.append("\t/Function ");
+ try {
+ p.append(new String(this.function.toByteString(), "UTF-8") + " \n");
+ } catch (UnsupportedEncodingException ex) {
+ //This should have been made an enum type to avoid throwing exceptions.
+ }
+ }
+ return p;
+ }
+
+ /**
+ * A method to write a type 1 shading object
+ * @param p The StringBuffer to write the shading object
+ * @return Returns the StringBuffer to which the shading object was written
+ */
+ public StringBuffer handleShadingType1(StringBuffer p) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ /**
+ * A method to write a type 4, 6 or 7 shading object
+ * @param p The StringBuffer to write the shading object
+ * @return Returns the StringBuffer to which the shading object was written
+ */
+ public StringBuffer handleShadingType4or6or7(StringBuffer p) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ /**
+ * A method to write a type 5 shading object
+ * @param p The StringBuffer to write the shading object
+ * @return Returns the StringBuffer to which the shading object was written
+ */
+ public StringBuffer handleShadingType5(StringBuffer p) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+}
diff --git a/src/java/org/apache/fop/render/rtf/RTFHandler.java b/src/java/org/apache/fop/render/rtf/RTFHandler.java
index dfeff65f1..73fa3504c 100644
--- a/src/java/org/apache/fop/render/rtf/RTFHandler.java
+++ b/src/java/org/apache/fop/render/rtf/RTFHandler.java
@@ -157,6 +157,7 @@ public class RTFHandler extends FOEventHandler {
private PercentContext percentManager = new PercentContext();
+
/**
* Creates a new RTF structure handler.
* @param userAgent the FOUserAgent for this process
@@ -270,7 +271,7 @@ public class RTFHandler extends FOEventHandler {
return;
} else {
- builderContext.popContainer();
+ builderContext.popContainer(RtfSection.class, this);
this.pagemaster = null;
}
}
@@ -377,10 +378,10 @@ public class RTFHandler extends FOEventHandler {
//just do nothing
} else if (regionBefore != null
&& fl.getFlowName().equals(regionBefore.getRegionName())) {
- builderContext.popContainer();
+ builderContext.popContainer(RtfBefore.class, this);
} else if (regionAfter != null
&& fl.getFlowName().equals(regionAfter.getRegionName())) {
- builderContext.popContainer();
+ builderContext.popContainer(RtfAfter.class, this);
}
} catch (Exception e) {
log.error("endFlow: " + e.getMessage());
@@ -571,7 +572,7 @@ public class RTFHandler extends FOEventHandler {
nestedTableDepth--;
builderContext.popTableContext();
- builderContext.popContainer();
+ builderContext.popContainer(RtfTable.class, this);
}
/** {@inheritDoc} */
@@ -605,21 +606,25 @@ public class RTFHandler extends FOEventHandler {
/** {@inheritDoc} */
public void startHeader(TableHeader header) {
+ builderContext.pushPart(header);
startPart(header);
}
/** {@inheritDoc} */
public void endHeader(TableHeader header) {
+ builderContext.popPart(header.getClass(), this);
endPart(header);
}
/** {@inheritDoc} */
public void startFooter(TableFooter footer) {
+ builderContext.pushPart(footer);
startPart(footer);
}
/** {@inheritDoc} */
public void endFooter(TableFooter footer) {
+ builderContext.popPart(footer.getClass(), this);
endPart(footer);
}
@@ -711,11 +716,13 @@ public class RTFHandler extends FOEventHandler {
* {@inheritDoc}
*/
public void startBody(TableBody body) {
+ builderContext.pushPart(body);
startPart(body);
}
/** {@inheritDoc} */
public void endBody(TableBody body) {
+ builderContext.popPart(TableBody.class, this);
endPart(body);
}
@@ -784,7 +791,7 @@ public class RTFHandler extends FOEventHandler {
}
- builderContext.popContainer();
+ builderContext.popContainer(RtfTableRow.class, this);
builderContext.getTableContext().decreaseRowSpannings();
}
@@ -893,7 +900,7 @@ public class RTFHandler extends FOEventHandler {
throw new RuntimeException(e.getMessage());
}
- builderContext.popContainer();
+ builderContext.popContainer(RtfTableCell.class, this);
builderContext.getTableContext().selectNextColumn();
}
@@ -929,7 +936,7 @@ public class RTFHandler extends FOEventHandler {
return;
}
- builderContext.popContainer();
+ builderContext.popContainer(RtfList.class, this);
}
/** {@inheritDoc} */
@@ -976,7 +983,7 @@ public class RTFHandler extends FOEventHandler {
return;
}
- builderContext.popContainer();
+ builderContext.popContainer(RtfListItem.class, this);
}
/** {@inheritDoc} */
@@ -1005,7 +1012,7 @@ public class RTFHandler extends FOEventHandler {
return;
}
- builderContext.popContainer();
+ builderContext.popContainer(RtfListItemLabel.class, this);
}
/** {@inheritDoc} */
@@ -1070,7 +1077,7 @@ public class RTFHandler extends FOEventHandler {
return;
}
- builderContext.popContainer();
+ builderContext.popContainer(RtfHyperLink.class, this);
}
/** {@inheritDoc} */
@@ -1306,7 +1313,7 @@ public class RTFHandler extends FOEventHandler {
return;
}
- builderContext.popContainer();
+ builderContext.popContainer(RtfFootnote.class, this);
}
/** {@inheritDoc} */
@@ -1688,6 +1695,19 @@ public class RTFHandler extends FOEventHandler {
}
/**
+ * Closes any mismatched tags that are detected in the RTF structure.
+ * @param containerClass The class representing the tag to close.
+ * @return Determines whether the tag mismatch has been handled.
+ */
+ public boolean endContainer(Class containerClass) {
+ if (containerClass == RtfTableRow.class) {
+ endRow(null);
+ return true;
+ }
+ return false;
+ }
+
+ /**
* Calls the event handlers for the passed FONode and all its elements.
*
* @param foNode FONode object which shall be recursed
diff --git a/src/java/org/apache/fop/render/rtf/RTFPlaceHolderHelper.java b/src/java/org/apache/fop/render/rtf/RTFPlaceHolderHelper.java
new file mode 100644
index 000000000..e3278ad95
--- /dev/null
+++ b/src/java/org/apache/fop/render/rtf/RTFPlaceHolderHelper.java
@@ -0,0 +1,74 @@
+/*
+ * 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.rtf;
+
+import org.apache.fop.render.rtf.rtflib.exceptions.RtfException;
+import org.apache.fop.render.rtf.rtflib.rtfdoc.RtfAttributes;
+import org.apache.fop.render.rtf.rtflib.rtfdoc.RtfContainer;
+import org.apache.fop.render.rtf.rtflib.rtfdoc.RtfTable;
+import org.apache.fop.render.rtf.rtflib.rtfdoc.RtfTableRow;
+import org.apache.fop.render.rtf.rtflib.tools.BuilderContext;
+
+/**
+ * This class creates objects which are missing from the XSL:FO but are required
+ * by the RTF format.
+ */
+public class RTFPlaceHolderHelper {
+ /** The context object for building the RTF */
+ private BuilderContext builderContext;
+
+ /**
+ * Creates a new instance for the RTF place holder which attempts to resolve
+ * mismatches in structure between XSL:FO and RTF.
+ * @param builderContext The builder context
+ */
+ public RTFPlaceHolderHelper(BuilderContext builderContext) {
+ this.builderContext = builderContext;
+ }
+
+ /**
+ * A method to create an object which is missing and required from the
+ * RTF structure.
+ * @param containerClass The class which is missing
+ * @throws Exception
+ */
+ public void createRTFPlaceholder(Class containerClass) throws RtfException {
+ if (containerClass == RtfTableRow.class) {
+ createRtfTableRow();
+ }
+ }
+
+ private void createRtfTableRow() throws RtfException {
+ try {
+ RtfContainer element = builderContext.getContainer(RtfTable.class, true, null);
+ if (element != null && element instanceof RtfTable) {
+ RtfTable table = (RtfTable)element;
+ RtfAttributes attribs = new RtfAttributes();
+ RtfTableRow newRow = table.newTableRow(attribs);
+ builderContext.pushContainer(newRow);
+ builderContext.getTableContext().selectFirstColumn();
+ }
+ } catch (org.apache.fop.apps.FOPException e) {
+ throw new RtfException(e.getMessage());
+ } catch (java.io.IOException e) {
+ throw new RtfException(e.getMessage());
+ }
+ }
+}
diff --git a/src/java/org/apache/fop/render/rtf/rtflib/tools/BuilderContext.java b/src/java/org/apache/fop/render/rtf/rtflib/tools/BuilderContext.java
index f0a29a0ab..4b3f8bd4a 100644
--- a/src/java/org/apache/fop/render/rtf/rtflib/tools/BuilderContext.java
+++ b/src/java/org/apache/fop/render/rtf/rtflib/tools/BuilderContext.java
@@ -21,6 +21,12 @@ package org.apache.fop.render.rtf.rtflib.tools;
import java.util.Stack;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.fop.fo.FObj;
+import org.apache.fop.render.rtf.RTFHandler;
+import org.apache.fop.render.rtf.RTFPlaceHolderHelper;
import org.apache.fop.render.rtf.rtflib.exceptions.RtfException;
import org.apache.fop.render.rtf.rtflib.rtfdoc.IRtfOptions;
import org.apache.fop.render.rtf.rtflib.rtfdoc.RtfContainer;
@@ -38,6 +44,10 @@ import org.apache.fop.render.rtf.rtflib.rtfdoc.RtfContainer;
*/
public class BuilderContext {
+
+ /** Static logging instance */
+ protected static final Log LOG = LogFactory.getLog(BuilderContext.class.getName());
+
/** stack of RtfContainers */
private final Stack containers = new Stack();
@@ -96,17 +106,22 @@ public class BuilderContext {
* @throws RtfException if not caught
*/
public RtfContainer getContainer(Class containerClass, boolean required,
- Object /*IBuilder*/ forWhichBuilder) throws RtfException {
+ Object forWhichBuilder) throws RtfException {
// TODO what to do if the desired container is not at the top of the stack?
// close top-of-stack container?
- final RtfContainer result = (RtfContainer)getObjectFromStack(containers,
+ RtfContainer result = (RtfContainer)getObjectFromStack(containers,
containerClass);
if (result == null && required) {
- throw new RtfException(
- "No RtfContainer of class '" + containerClass.getName()
- + "' available for '" + forWhichBuilder.getClass().getName() + "' builder"
- );
+ RTFPlaceHolderHelper placeHolderHelper = new RTFPlaceHolderHelper(this);
+ placeHolderHelper.createRTFPlaceholder(containerClass);
+ result = getContainer(containerClass, required, forWhichBuilder);
+ if (result == null) {
+ throw new RtfException(
+ "No RtfContainer of class '" + containerClass.getName()
+ + "' available for '" + forWhichBuilder.getClass().getName() + "' builder"
+ );
+ }
}
return result;
@@ -121,6 +136,14 @@ public class BuilderContext {
}
/**
+ * Push a Class representing a non-writeable section of the FO tree
+ * @param part the part
+ */
+ public void pushPart(FObj part) {
+ containers.push(part);
+ }
+
+ /**
* In some cases an RtfContainer must be replaced by another one on the
* stack. This happens when handling nested fo:blocks for example: after
* handling a nested block the enclosing block must switch to a new
@@ -142,8 +165,40 @@ public class BuilderContext {
}
/** pop the topmost RtfContainer from our stack */
- public void popContainer() {
- containers.pop();
+ public void popContainer(Class containerClass, RTFHandler handler) {
+ handlePop(containerClass, handler);
+ }
+
+ /** pop the topmost part class from our stack */
+ public void popPart(Class part, RTFHandler handler) {
+ handlePop(part, handler);
+ }
+
+ /**
+ * This method checks for any tag mismatches between what is being closed
+ * and what exists on the stack. If a mismatch is found, then it will push
+ * the object back onto the stack and attempt to close the given section
+ * before retrying with the current pop task.
+ * @param aClass The class to be popped from the stack
+ * @param handler The RTF handler class to fix any mismatches
+ */
+ private void handlePop(Class aClass, RTFHandler handler) {
+ Object object = containers.pop();
+ if (object.getClass() != aClass) {
+ pushAndClose(aClass, object, handler);
+ }
+ }
+
+ private void pushAndClose(Class aClass, Object object, RTFHandler handler) {
+ containers.push(object);
+ if (handler.endContainer(object.getClass())) {
+ popContainer(aClass, handler);
+ } else {
+ /* This should never happen unless a placeholder is not catered for
+ * in the RTFHandler.endContainer method. */
+ LOG.warn("Unhandled RTF structure tag mismatch detected between "
+ + aClass.getSimpleName() + " and " + object.getClass().getSimpleName());
+ }
}
/* push an IBuilder to our stack /
diff --git a/src/java/org/apache/fop/render/shading/Function.java b/src/java/org/apache/fop/render/shading/Function.java
new file mode 100644
index 000000000..5bd44087e
--- /dev/null
+++ b/src/java/org/apache/fop/render/shading/Function.java
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+package org.apache.fop.render.shading;
+
+import java.util.List;
+
+public interface Function {
+ int getFunctionType();
+ List<Double> getBounds();
+ List<Double> getDomain();
+ List<Double> getSize();
+ List<String> getFilter();
+ List<Double> getEncode();
+ List<Function> getFunctions();
+ int getBitsPerSample();
+ double getInterpolationExponentN();
+ int getOrder();
+ List<Double> getRange();
+ List<Double> getDecode();
+ StringBuffer getDataStream();
+ List<Double> getCZero();
+ List<Double> getCOne();
+ byte[] toByteString();
+}
diff --git a/src/java/org/apache/fop/render/shading/FunctionDelegate.java b/src/java/org/apache/fop/render/shading/FunctionDelegate.java
new file mode 100644
index 000000000..eea24e0db
--- /dev/null
+++ b/src/java/org/apache/fop/render/shading/FunctionDelegate.java
@@ -0,0 +1,451 @@
+/*
+ * 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.
+ */
+
+package org.apache.fop.render.shading;
+
+import java.util.List;
+
+public class FunctionDelegate implements Function {
+
+ private Function parentFunction;
+
+ /**
+ * Required: The Type of function (0,2,3,4) default is 0.
+ */
+ protected int functionType = 0; // Default
+
+ /**
+ * Required: 2 * m Array of Double numbers which are possible inputs to the function
+ */
+ protected List<Double> domain = null;
+
+ /**
+ * Required: 2 * n Array of Double numbers which are possible outputs to the function
+ */
+ protected List<Double> range = null;
+
+ /* ********************TYPE 0***************************** */
+ // FunctionType 0 specific function guts
+
+ /**
+ * Required: Array containing the Integer size of the Domain and Range, respectively.
+ * Note: This is really more like two seperate integers, sizeDomain, and sizeRange,
+ * but since they're expressed as an array in PDF, my implementation reflects that.
+ */
+ protected List<Double> size = null;
+
+ /**
+ * Required for Type 0: Number of Bits used to represent each sample value.
+ * Limited to 1,2,4,8,12,16,24, or 32
+ */
+ protected int bitsPerSample = 1;
+
+ /**
+ * Optional for Type 0: order of interpolation between samples.
+ * Limited to linear (1) or cubic (3). Default is 1
+ */
+ protected int order = 1;
+
+ /**
+ * Optional for Type 0: A 2 * m array of Doubles which provides a
+ * linear mapping of input values to the domain.
+ *
+ * Required for Type 3: A 2 * k array of Doubles that, taken
+ * in pairs, map each subset of the domain defined by Domain
+ * and the Bounds array to the domain of the corresponding function.
+ * Should be two values per function, usually (0,1),
+ * as in [0 1 0 1] for 2 functions.
+ */
+ protected List<Double> encode = null;
+
+ /**
+ * Optional for Type 0: A 2 * n array of Doubles which provides
+ * a linear mapping of sample values to the range. Defaults to Range.
+ */
+ protected List<Double> decode = null;
+
+ /**
+ * Optional For Type 0: A stream of sample values
+ */
+
+ /**
+ * Required For Type 4: Postscript Calculator function
+ * composed of arithmetic, boolean, and stack operators + boolean constants
+ */
+ protected StringBuffer functionDataStream = null;
+
+ /**
+ * Required (possibly) For Type 0: A vector of Strings for the
+ * various filters to be used to decode the stream.
+ * These are how the string is compressed. Flate, LZW, etc.
+ */
+ protected List<String> filter = null;
+ /* *************************TYPE 2************************** */
+
+ /**
+ * Required For Type 2: An Array of n Doubles defining
+ * the function result when x=0. Default is [0].
+ */
+ protected List<Double> cZero = null;
+
+ /**
+ * Required For Type 2: An Array of n Doubles defining
+ * the function result when x=1. Default is [1].
+ */
+ protected List<Double> cOne = null;
+
+ /**
+ * Required for Type 2: The interpolation exponent.
+ * Each value x will return n results.
+ * Must be greater than 0.
+ */
+ protected double interpolationExponentN = 1;
+
+ /* *************************TYPE 3************************** */
+
+ /**
+ * Required for Type 3: An vector of PDFFunctions which
+ * form an array of k single input functions making up
+ * the stitching function.
+ */
+ protected List<Function> functions = null;
+
+ /**
+ * Optional for Type 3: An array of (k-1) Doubles that,
+ * in combination with Domain, define the intervals to which
+ * each function from the Functions array apply. Bounds
+ * elements must be in order of increasing magnitude,
+ * and each value must be within the value of Domain.
+ * k is the number of functions.
+ * If you pass null, it will output (1/k) in an array of k-1 elements.
+ * This makes each function responsible for an equal amount of the stitching function.
+ * It makes the gradient even.
+ */
+ protected List<Double> bounds = null;
+
+ /**
+ * create an complete Function object of Type 0, A Sampled function.
+ *
+ * Use null for an optional object parameter if you choose not to use it.
+ * For optional int parameters, pass the default.
+ *
+ * @param theDomain List objects of Double objects.
+ * This is the domain of the function.
+ * See page 264 of the PDF 1.3 Spec.
+ * @param theRange List objects of Double objects.
+ * This is the Range of the function.
+ * See page 264 of the PDF 1.3 Spec.
+ * @param theSize A List object of Integer objects.
+ * This is the number of samples in each input dimension.
+ * I can't imagine there being more or less than two input dimensions,
+ * so maybe this should be an array of length 2.
+ *
+ * See page 265 of the PDF 1.3 Spec.
+ * @param theBitsPerSample An int specifying the number of bits
+ used to represent each sample value.
+ * Limited to 1,2,4,8,12,16,24 or 32.
+ * See page 265 of the 1.3 PDF Spec.
+ * @param theOrder The order of interpolation between samples. Default is 1 (one). Limited
+ * to 1 (one) or 3, which means linear or cubic-spline interpolation.
+ *
+ * This attribute is optional.
+ *
+ * See page 265 in the PDF 1.3 spec.
+ * @param theEncode List objects of Double objects.
+ * This is the linear mapping of input values intop the domain
+ * of the function's sample table. Default is hard to represent in
+ * ascii, but basically [0 (Size0 1) 0 (Size1 1)...].
+ * This attribute is optional.
+ *
+ * See page 265 in the PDF 1.3 spec.
+ * @param theDecode List objects of Double objects.
+ * This is a linear mapping of sample values into the range.
+ * The default is just the range.
+ *
+ * This attribute is optional.
+ * Read about it on page 265 of the PDF 1.3 spec.
+ * @param theFunctionDataStream The sample values that specify
+ * the function are provided in a stream.
+ *
+ * This is optional, but is almost always used.
+ *
+ * Page 265 of the PDF 1.3 spec has more.
+ * @param theFilter This is a vector of String objects which are the various filters that
+ * have are to be applied to the stream to make sense of it. Order matters,
+ * so watch out.
+ *
+ * This is not documented in the Function section of the PDF 1.3 spec,
+ * it was deduced from samples that this is sometimes used, even if we may never
+ * use it in FOP. It is added for completeness sake.
+ * @param theFunctionType This is the type of function (0,2,3, or 4).
+ * It should be 0 as this is the constructor for sampled functions.
+ */
+ public FunctionDelegate(Function parentFunction, int theFunctionType, List<Double> theDomain,
+ List<Double> theRange, List<Double> theSize, int theBitsPerSample,
+ int theOrder, List<Double> theEncode, List<Double> theDecode,
+ StringBuffer theFunctionDataStream, List<String> theFilter) {
+ this.parentFunction = parentFunction;
+ this.functionType = 0; // dang well better be 0;
+ this.size = theSize;
+ this.bitsPerSample = theBitsPerSample;
+ this.order = theOrder; // int
+ this.encode = theEncode; // vector of int
+ this.decode = theDecode; // vector of int
+ this.functionDataStream = theFunctionDataStream;
+ this.filter = theFilter; // vector of Strings
+
+ // the domain and range are actually two dimensional arrays.
+ // so if there's not an even number of items, bad stuff
+ // happens.
+ this.domain = theDomain;
+ this.range = theRange;
+ }
+
+ /**
+ * create an complete Function object of Type 2, an Exponential Interpolation function.
+ *
+ * Use null for an optional object parameter if you choose not to use it.
+ * For optional int parameters, pass the default.
+ *
+ * @param theDomain List objects of Double objects.
+ * This is the domain of the function.
+ * See page 264 of the PDF 1.3 Spec.
+ * @param theRange List of Doubles that is the Range of the function.
+ * See page 264 of the PDF 1.3 Spec.
+ * @param theCZero This is a vector of Double objects which defines the function result
+ * when x=0.
+ *
+ * This attribute is optional.
+ * It's described on page 268 of the PDF 1.3 spec.
+ * @param theCOne This is a vector of Double objects which defines the function result
+ * when x=1.
+ *
+ * This attribute is optional.
+ * It's described on page 268 of the PDF 1.3 spec.
+ * @param theInterpolationExponentN This is the inerpolation exponent.
+ *
+ * This attribute is required.
+ * PDF Spec page 268
+ * @param theFunctionType The type of the function, which should be 2.
+ */
+ public FunctionDelegate(Function parentFunction, int theFunctionType, List<Double> theDomain,
+ List<Double> theRange, List<Double> theCZero, List<Double> theCOne,
+ double theInterpolationExponentN) {
+ this.parentFunction = parentFunction;
+ this.functionType = 2; // dang well better be 2;
+
+ this.cZero = theCZero;
+ this.cOne = theCOne;
+ this.interpolationExponentN = theInterpolationExponentN;
+
+ this.domain = theDomain;
+ this.range = theRange;
+
+ }
+
+ /**
+ * create an complete Function object of Type 3, a Stitching function.
+ *
+ * Use null for an optional object parameter if you choose not to use it.
+ * For optional int parameters, pass the default.
+ *
+ * @param theDomain List objects of Double objects.
+ * This is the domain of the function.
+ * See page 264 of the PDF 1.3 Spec.
+ * @param theRange List objects of Double objects.
+ * This is the Range of the function.
+ * See page 264 of the PDF 1.3 Spec.
+ * @param theFunctions A List of the PDFFunction objects that the stitching function stitches.
+ *
+ * This attributed is required.
+ * It is described on page 269 of the PDF spec.
+ * @param theBounds This is a vector of Doubles representing the numbers that,
+ * in conjunction with Domain define the intervals to which each function from
+ * the 'functions' object applies. It must be in order of increasing magnitude,
+ * and each must be within Domain.
+ *
+ * It basically sets how much of the gradient each function handles.
+ *
+ * This attributed is required.
+ * It's described on page 269 of the PDF 1.3 spec.
+ * @param theEncode List objects of Double objects.
+ * This is the linear mapping of input values intop the domain
+ * of the function's sample table. Default is hard to represent in
+ * ascii, but basically [0 (Size0 1) 0 (Size1 1)...].
+ * This attribute is required.
+ *
+ * See page 270 in the PDF 1.3 spec.
+ * @param theFunctionType This is the function type. It should be 3,
+ * for a stitching function.
+ */
+ public FunctionDelegate(Function parentFunction, int theFunctionType, List<Double> theDomain,
+ List<Double> theRange, List<Function> theFunctions,
+ List<Double> theBounds, List<Double> theEncode) {
+ this.parentFunction = parentFunction;
+ this.functionType = 3; // dang well better be 3;
+
+ this.functions = theFunctions;
+ this.bounds = theBounds;
+ this.encode = theEncode;
+ this.domain = theDomain;
+ this.range = theRange;
+
+ }
+
+ /**
+ * create an complete Function object of Type 4, a postscript calculator function.
+ *
+ * Use null for an optional object parameter if you choose not to use it.
+ * For optional int parameters, pass the default.
+ *
+ * @param theDomain List object of Double objects.
+ * This is the domain of the function.
+ * See page 264 of the PDF 1.3 Spec.
+ * @param theRange List object of Double objects.
+ * This is the Range of the function.
+ * See page 264 of the PDF 1.3 Spec.
+ * @param theFunctionDataStream This is a stream of arithmetic,
+ * boolean, and stack operators and boolean constants.
+ * I end up enclosing it in the '{' and '}' braces for you, so don't do it
+ * yourself.
+ *
+ * This attribute is required.
+ * It's described on page 269 of the PDF 1.3 spec.
+ * @param theFunctionType The type of function which should be 4, as this is
+ * a Postscript calculator function
+ */
+ public FunctionDelegate(Function parentFunction, int theFunctionType, List<Double> theDomain,
+ List<Double> theRange, StringBuffer theFunctionDataStream) {
+ this.parentFunction = parentFunction;
+ this.functionType = 4; // dang well better be 4;
+ this.functionDataStream = theFunctionDataStream;
+
+ this.domain = theDomain;
+
+ this.range = theRange;
+
+ }
+
+ /**
+ * Gets the function type
+ */
+ public int getFunctionType() {
+ return functionType;
+ }
+
+ /**
+ * Gets the function bounds
+ */
+ public List<Double> getBounds() {
+ return bounds;
+ }
+
+ /**
+ * The function domain
+ */
+ public List<Double> getDomain() {
+ return domain;
+ }
+
+ /**
+ * The function size
+ */
+ public List<Double> getSize() {
+ return size;
+ }
+
+ /**
+ * Gets the function encoding
+ */
+ public List<Double> getEncode() {
+ return encode;
+ }
+
+ /**
+ * Gets the sub-functions
+ */
+ public List<Function> getFunctions() {
+ return functions;
+ }
+
+ /**
+ * Gets the function filter
+ */
+ public List<String> getFilter() {
+ return filter;
+ }
+
+ /**
+ * Gets the bits per sample of the function
+ */
+ public int getBitsPerSample() {
+ return bitsPerSample;
+ }
+
+ /**
+ * Gets the interpolation exponent of the function
+ */
+ public double getInterpolationExponentN() {
+ return interpolationExponentN;
+ }
+
+ /**
+ * Gets the function order
+ */
+ public int getOrder() {
+ return order;
+ }
+
+ /**
+ * Gets the function range
+ */
+ public List<Double> getRange() {
+ return range;
+ }
+
+ /**
+ * Gets the function decoding
+ */
+ public List<Double> getDecode() {
+ return decode;
+ }
+
+ /**
+ * Gets the function data stream
+ */
+ public StringBuffer getDataStream() {
+ return functionDataStream;
+ }
+
+ /**
+ * Gets the function C0 value (color for gradient)
+ */
+ public List<Double> getCZero() {
+ return cZero;
+ }
+
+ /**
+ * Gets the function C1 value (color for gradient)
+ */
+ public List<Double> getCOne() {
+ return cOne;
+ }
+
+ public byte[] toByteString() {
+ return parentFunction.toByteString();
+ }
+}
diff --git a/src/java/org/apache/fop/render/shading/FunctionPattern.java b/src/java/org/apache/fop/render/shading/FunctionPattern.java
new file mode 100644
index 000000000..044053a8b
--- /dev/null
+++ b/src/java/org/apache/fop/render/shading/FunctionPattern.java
@@ -0,0 +1,363 @@
+/*
+ * 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.shading;
+
+import java.io.UnsupportedEncodingException;
+
+import org.apache.fop.pdf.PDFFunction;
+import org.apache.fop.pdf.PDFNumber;
+import org.apache.fop.render.ps.svg.PSFunction;
+
+/**
+ * A class for writing function objects for different output formats
+ */
+public class FunctionPattern {
+
+ private Function function;
+
+ /**
+ * Constructor
+ * @param function The function from which to write the output
+ */
+ public FunctionPattern(Function function) {
+ this.function = function;
+ }
+
+ /**
+ * Outputs the function to a byte array
+ */
+ public String toWriteableString() {
+ int vectorSize = 0;
+ int numberOfFunctions = 0;
+ int tempInt = 0;
+ StringBuffer p = new StringBuffer(256);
+ p.append("<< \n/FunctionType " + function.getFunctionType() + " \n");
+
+ // FunctionType 0
+ if (this.function.getFunctionType() == 0) {
+ if (function.getDomain() != null) {
+ // DOMAIN
+ p.append("/Domain [ ");
+ vectorSize = function.getDomain().size();
+ for (tempInt = 0; tempInt < vectorSize; tempInt++) {
+ p.append(PDFNumber.doubleOut(function.getDomain().get(tempInt))
+ + " ");
+ }
+
+ p.append("] \n");
+ } else {
+ p.append("/Domain [ 0 1 ] \n");
+ }
+
+ // SIZE
+ if (function.getSize() != null) {
+ p.append("/Size [ ");
+ vectorSize = function.getSize().size();
+ for (tempInt = 0; tempInt < vectorSize; tempInt++) {
+ p.append(PDFNumber.doubleOut(function.getSize().get(tempInt))
+ + " ");
+ }
+ p.append("] \n");
+ }
+ // ENCODE
+ if (function.getEncode() != null) {
+ p.append("/Encode [ ");
+ vectorSize = function.getEncode().size();
+ for (tempInt = 0; tempInt < vectorSize; tempInt++) {
+ p.append(PDFNumber.doubleOut(function.getEncode().get(tempInt))
+ + " ");
+ }
+ p.append("] \n");
+ } else {
+ p.append("/Encode [ ");
+ vectorSize = function.getFunctions().size();
+ for (tempInt = 0; tempInt < vectorSize; tempInt++) {
+ p.append("0 1 ");
+ }
+ p.append("] \n");
+
+ }
+
+ // BITSPERSAMPLE
+ p.append("/BitsPerSample " + function.getBitsPerSample());
+
+ // ORDER (optional)
+ if (function.getOrder() == 1 || function.getOrder() == 3) {
+ p.append(" \n/Order " + function.getOrder() + " \n");
+ }
+
+ // RANGE
+ if (function.getRange() != null) {
+ p.append("/Range [ ");
+ vectorSize = function.getRange().size();
+ for (tempInt = 0; tempInt < vectorSize; tempInt++) {
+ p.append(PDFNumber.doubleOut(function.getRange().get(tempInt))
+ + " ");
+ }
+
+ p.append("] \n");
+ }
+
+ // DECODE
+ if (function.getDecode() != null) {
+ p.append("/Decode [ ");
+ vectorSize = function.getDecode().size();
+ for (tempInt = 0; tempInt < vectorSize; tempInt++) {
+ p.append(PDFNumber.doubleOut(function.getDecode().get(tempInt))
+ + " ");
+ }
+
+ p.append("] \n");
+ }
+
+ // LENGTH
+ if (function.getDataStream() != null) {
+ p.append("/Length " + (function.getDataStream().length() + 1)
+ + " \n");
+ }
+
+ // FILTER?
+ if (function.getFilter() != null) { // if there's a filter
+ vectorSize = function.getFilter().size();
+ p.append("/Filter ");
+ if (vectorSize == 1) {
+ p.append("/" + (function.getFilter().get(0))
+ + " \n");
+ } else {
+ p.append("[ ");
+ for (tempInt = 0; tempInt < vectorSize; tempInt++) {
+ p.append("/" + (function.getFilter().get(0))
+ + " ");
+ }
+ p.append("] \n");
+ }
+ }
+ p.append(">>");
+
+ // stream representing the function
+ if (function.getDataStream() != null) {
+ p.append("\nstream\n" + function.getDataStream()
+ + "\nendstream");
+ }
+
+ // end of if FunctionType 0
+
+ } else if (function.getFunctionType() == 2) {
+ // DOMAIN
+ if (function.getDomain() != null) {
+ p.append("/Domain [ ");
+ vectorSize = function.getDomain().size();
+ for (tempInt = 0; tempInt < vectorSize; tempInt++) {
+ p.append(PDFNumber.doubleOut(function.getDomain().get(tempInt))
+ + " ");
+ }
+
+ p.append("] \n");
+ } else {
+ p.append("/Domain [ 0 1 ] \n");
+ }
+
+
+ // RANGE
+ if (function.getRange() != null) {
+ p.append("/Range [ ");
+ vectorSize = function.getRange().size();
+ for (tempInt = 0; tempInt < vectorSize; tempInt++) {
+ p.append(PDFNumber.doubleOut(function.getRange().get(tempInt))
+ + " ");
+ }
+
+ p.append("] \n");
+ }
+
+ // FunctionType, C0, C1, N are required in PDF
+
+ // C0
+ if (function.getCZero() != null) {
+ p.append("/C0 [ ");
+ vectorSize = function.getCZero().size();
+ for (tempInt = 0; tempInt < vectorSize; tempInt++) {
+ p.append(PDFNumber.doubleOut(function.getCZero().get(tempInt))
+ + " ");
+ }
+ p.append("] \n");
+ }
+
+ // C1
+ if (function.getCOne() != null) {
+ p.append("/C1 [ ");
+ vectorSize = function.getCOne().size();
+ for (tempInt = 0; tempInt < vectorSize; tempInt++) {
+ p.append(PDFNumber.doubleOut(function.getCOne().get(tempInt))
+ + " ");
+ }
+ p.append("] \n");
+ }
+
+ // N: The interpolation Exponent
+ p.append("/N "
+ + PDFNumber.doubleOut(Double.valueOf(function.getInterpolationExponentN()))
+ + " \n");
+
+ p.append(">>");
+
+ } else if (function.getFunctionType()
+ == 3) { // fix this up when my eyes uncross
+ // DOMAIN
+ if (function.getDomain() != null) {
+ p.append("/Domain [ ");
+ vectorSize = function.getDomain().size();
+ for (tempInt = 0; tempInt < vectorSize; tempInt++) {
+ p.append(PDFNumber.doubleOut(function.getDomain().get(tempInt))
+ + " ");
+ }
+ p.append("] \n");
+ } else {
+ p.append("/Domain [ 0 1 ] \n");
+ }
+
+ // RANGE
+ if (function.getRange() != null) {
+ p.append("/Range [ ");
+ vectorSize = function.getRange().size();
+ for (tempInt = 0; tempInt < vectorSize; tempInt++) {
+ p.append(PDFNumber.doubleOut(function.getRange().get(tempInt))
+ + " ");
+ }
+
+ p.append("] \n");
+ }
+
+ // FUNCTIONS
+ if (function.getFunctions() != null) {
+ p.append("/Functions [ ");
+ numberOfFunctions = function.getFunctions().size();
+ for (tempInt = 0; tempInt < numberOfFunctions; tempInt++) {
+ try {
+ if (function instanceof PSFunction) {
+ p.append(new String(function.getFunctions().get(tempInt).toByteString(), "UTF-8")
+ + " ");
+ } else {
+ p.append(((PDFFunction)function.getFunctions().get(tempInt)).referencePDF()
+ + " ");
+ }
+ } catch (UnsupportedEncodingException ex) {
+ //This should have been made an enum type to avoid throwing exceptions.
+ }
+ }
+ p.append("] \n");
+ }
+
+
+ // ENCODE
+ if (function.getEncode() != null) {
+ p.append("/Encode [ ");
+ vectorSize = function.getEncode().size();
+ for (tempInt = 0; tempInt < vectorSize; tempInt++) {
+ p.append(PDFNumber.doubleOut(function.getEncode().get(tempInt))
+ + " ");
+ }
+
+ p.append("] \n");
+ } else {
+ p.append("/Encode [ ");
+ vectorSize = function.getFunctions().size();
+ for (tempInt = 0; tempInt < vectorSize; tempInt++) {
+ p.append("0 1 ");
+ }
+ p.append("] \n");
+
+ }
+
+
+ // BOUNDS, required, but can be empty
+ p.append("/Bounds [ ");
+ if (function.getBounds() != null) {
+
+ vectorSize = function.getBounds().size();
+ for (tempInt = 0; tempInt < vectorSize; tempInt++) {
+ p.append(PDFNumber.doubleOut(function.getBounds().get(tempInt))
+ + " ");
+ }
+
+ } else {
+ if (function.getFunctions() != null) {
+ // if there are n functions,
+ // there must be n-1 bounds.
+ // so let each function handle an equal portion
+ // of the whole. e.g. if there are 4, then [ 0.25 0.25 0.25 ]
+
+ String functionsFraction = PDFNumber.doubleOut(Double.valueOf(1.0
+ / (numberOfFunctions)));
+
+ for (tempInt = 0; tempInt + 1 < numberOfFunctions;
+ tempInt++) {
+
+ p.append(functionsFraction + " ");
+ }
+ }
+
+ }
+ p.append("]\n>>");
+ } else if (function.getFunctionType()
+ == 4) { // fix this up when my eyes uncross
+ // DOMAIN
+ if (function.getDomain() != null) {
+ p.append("/Domain [ ");
+ vectorSize = function.getDomain().size();
+ for (tempInt = 0; tempInt < vectorSize; tempInt++) {
+ p.append(PDFNumber.doubleOut(function.getDomain().get(tempInt))
+ + " ");
+ }
+
+ p.append("] \n");
+ } else {
+ p.append("/Domain [ 0 1 ] \n");
+ }
+
+ // RANGE
+ if (function.getRange() != null) {
+ p.append("/Range [ ");
+ vectorSize = function.getRange().size();
+ for (tempInt = 0; tempInt < vectorSize; tempInt++) {
+ p.append(PDFNumber.doubleOut(function.getRange().get(tempInt))
+ + " ");
+ }
+
+ p.append("] \n");
+ }
+
+ // LENGTH
+ if (function.getDataStream() != null) {
+ p.append("/Length " + (function.getDataStream().length() + 1)
+ + " \n");
+ }
+
+ p.append(">>");
+
+ // stream representing the function
+ if (function.getDataStream() != null) {
+ p.append("\nstream\n{ " + function.getDataStream()
+ + " }\nendstream");
+ }
+ }
+ return p.toString();
+ }
+}
diff --git a/src/java/org/apache/fop/render/shading/GradientFactory.java b/src/java/org/apache/fop/render/shading/GradientFactory.java
new file mode 100644
index 000000000..87ac11c83
--- /dev/null
+++ b/src/java/org/apache/fop/render/shading/GradientFactory.java
@@ -0,0 +1,162 @@
+/*
+ * 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.shading;
+
+import java.awt.Color;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.xmlgraphics.java2d.color.ColorUtil;
+
+import org.apache.fop.pdf.PDFDeviceColorSpace;
+import org.apache.fop.render.ps.svg.PSSVGGraphics2D;
+
+public abstract class GradientFactory {
+
+ static GradientRegistrar registrar;
+
+ /**
+ * Constructor
+ * @param registrar The object used to register new embedded objects in the
+ * output format.
+ */
+ public static GradientFactory newInstance(GradientRegistrar theRegistrar) {
+ registrar = theRegistrar;
+ if (registrar instanceof PSSVGGraphics2D) {
+ return new PSGradientFactory();
+ } else {
+ return new PDFGradientFactory();
+ }
+ }
+
+ /**
+ * Creates a new gradient
+ * @param radial Determines whether the gradient is radial
+ * @param theColorspace The colorspace used in PDF and Postscript
+ * @param theColors The colors to be used in the gradient
+ * @param theBounds The bounds of each color
+ * @param theCoords The co-ordinates of the gradient
+ * @param theMatrix The matrix for any transformations
+ * @return Returns the Pattern object of the gradient
+ */
+ public abstract Pattern createGradient(boolean radial,
+ PDFDeviceColorSpace theColorspace, List<Color> theColors, List<Double> theBounds,
+ List<Double> theCoords, List<Double> theMatrix);
+
+ protected Pattern makeGradient(boolean radial, PDFDeviceColorSpace theColorspace,
+ List<Color> theColors, List<Double> theBounds,
+ List<Double> theCoords, List<Double> theMatrix) {
+ Shading myShad;
+ Function myfunky;
+ Function myfunc;
+ List<Double> theCzero;
+ List<Double> theCone;
+ double interpolation = 1.000;
+ List<Function> theFunctions = new ArrayList<Function>();
+
+ int currentPosition;
+ int lastPosition = theColors.size() - 1;
+
+
+ // if 5 elements, the penultimate element is 3.
+ // do not go beyond that, because you always need
+ // to have a next color when creating the function.
+
+ for (currentPosition = 0; currentPosition < lastPosition;
+ currentPosition++) { // for every consecutive color pair
+ Color currentColor = theColors.get(currentPosition);
+ Color nextColor = theColors.get(currentPosition + 1);
+
+ // colorspace must be consistent, so we simply convert to sRGB where necessary
+ if (!currentColor.getColorSpace().isCS_sRGB()) {
+ //Convert to sRGB
+ currentColor = ColorUtil.toSRGBColor(currentColor);
+ theColors.set(currentPosition, currentColor);
+ }
+ if (!nextColor.getColorSpace().isCS_sRGB()) {
+ //Convert to sRGB
+ nextColor = ColorUtil.toSRGBColor(nextColor);
+ theColors.set(currentPosition + 1, nextColor);
+ }
+
+ theCzero = toColorVector(currentColor);
+ theCone = toColorVector(nextColor);
+
+ myfunc = makeFunction(2, null, null, theCzero, theCone,
+ interpolation);
+
+ theFunctions.add(myfunc);
+
+ } // end of for every consecutive color pair
+
+ myfunky = makeFunction(3, null, null, theFunctions, theBounds,
+ null);
+
+ if (radial) {
+ if (theCoords.size() == 6) {
+ // make Shading of Type 2 or 3
+ myShad = makeShading(3, theColorspace, null, null, false, theCoords,
+ null, myfunky, null);
+ } else { // if the center x, center y, and radius specifiy
+ // the gradient, then assume the same center x, center y,
+ // and radius of zero for the other necessary component
+ List<Double> newCoords = new ArrayList<Double>();
+ newCoords.add(theCoords.get(0));
+ newCoords.add(theCoords.get(1));
+ newCoords.add(theCoords.get(2));
+ newCoords.add(theCoords.get(0));
+ newCoords.add(theCoords.get(1));
+ newCoords.add(Double.valueOf(0.0));
+
+ myShad = makeShading(3, theColorspace, null, null, false, newCoords,
+ null, myfunky, null);
+ }
+ } else {
+ myShad = makeShading(2, theColorspace, null, null, false, theCoords,
+ null, myfunky, null);
+ }
+ return makePattern(2, myShad, null, null, theMatrix);
+ }
+
+ public abstract Function makeFunction(int functionType, List<Double> theDomain,
+ List<Double> theRange, List<Function> theFunctions,
+ List<Double> theBounds, List<Double> theEncode);
+
+ public abstract Function makeFunction(int functionType, List<Double> theDomain,
+ List<Double> theRange, List<Double> theCZero, List<Double> theCOne,
+ double theInterpolationExponentN);
+
+ public abstract Shading makeShading(int theShadingType,
+ PDFDeviceColorSpace theColorSpace, List<Double> theBackground, List<Double> theBBox,
+ boolean theAntiAlias, List<Double> theCoords, List<Double> theDomain,
+ Function theFunction, List<Integer> theExtend);
+
+ public abstract Pattern makePattern(int thePatternType, Shading theShading, List theXUID,
+ StringBuffer theExtGState, List<Double> theMatrix);
+
+ private List<Double> toColorVector(Color nextColor) {
+ List<Double> vector = new java.util.ArrayList<Double>();
+ float[] comps = nextColor.getColorComponents(null);
+ for (int i = 0, c = comps.length; i < c; i++) {
+ vector.add(Double.valueOf(comps[i]));
+ }
+ return vector;
+ }
+}
diff --git a/src/java/org/apache/fop/render/shading/GradientRegistrar.java b/src/java/org/apache/fop/render/shading/GradientRegistrar.java
new file mode 100644
index 000000000..617fcd4fb
--- /dev/null
+++ b/src/java/org/apache/fop/render/shading/GradientRegistrar.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.
+ */
+
+package org.apache.fop.render.shading;
+
+public interface GradientRegistrar {
+
+ /**
+ * Registers a function object against the output format document
+ * @param function The function object to register
+ * @return Returns either the function which has already been registered
+ * or the current new registered object.
+ */
+ Function registerFunction(Function function);
+
+ /**
+ * Registers a shading object against the output format document
+ * @param shading The shading object to register
+ * @return Returns either the shading which has already been registered
+ * or the current new registered object
+ */
+ Shading registerShading(Shading shading);
+
+ /**
+ * Registers a pattern object against the output format document
+ * @param pattern The pattern object to register
+ * @return Returns either the pattern which has already been registered
+ * or the current new registered object
+ */
+ Pattern registerPattern(Pattern pattern);
+}
diff --git a/src/java/org/apache/fop/render/shading/PDFGradientFactory.java b/src/java/org/apache/fop/render/shading/PDFGradientFactory.java
new file mode 100644
index 000000000..3b3dcab75
--- /dev/null
+++ b/src/java/org/apache/fop/render/shading/PDFGradientFactory.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.
+ */
+
+package org.apache.fop.render.shading;
+
+import java.awt.Color;
+import java.util.List;
+
+import org.apache.fop.pdf.PDFDeviceColorSpace;
+import org.apache.fop.pdf.PDFFunction;
+import org.apache.fop.pdf.PDFPattern;
+import org.apache.fop.pdf.PDFShading;
+
+public class PDFGradientFactory extends GradientFactory {
+
+ @Override
+ public PDFPattern createGradient(boolean radial, PDFDeviceColorSpace theColorspace, List<Color> theColors,
+ List<Double> theBounds, List<Double> theCoords, List<Double> theMatrix) {
+ return (PDFPattern)makeGradient(radial, theColorspace, theColors, theBounds,
+ theCoords, theMatrix);
+ }
+
+ @Override
+ public Function makeFunction(int functionType, List<Double> theDomain,
+ List<Double> theRange, List<Function> theFunctions,
+ List<Double> theBounds, List<Double> theEncode) {
+ Function newFunction = new PDFFunction(functionType, theDomain, theRange, theFunctions,
+ theBounds, theEncode);
+ newFunction = registrar.registerFunction(newFunction);
+ return newFunction;
+ }
+
+ public Function makeFunction(int functionType, List<Double> theDomain,
+ List<Double> theRange, List<Double> theCZero, List<Double> theCOne,
+ double theInterpolationExponentN) {
+ Function newFunction = new PDFFunction(functionType, theDomain, theRange, theCZero,
+ theCOne, theInterpolationExponentN);
+ newFunction = registrar.registerFunction(newFunction);
+ return newFunction;
+ }
+
+ @Override
+ public Shading makeShading(int theShadingType,
+ PDFDeviceColorSpace theColorSpace, List<Double> theBackground, List<Double> theBBox,
+ boolean theAntiAlias, List<Double> theCoords, List<Double> theDomain,
+ Function theFunction, List<Integer> theExtend) {
+ Shading newShading = new PDFShading(theShadingType, theColorSpace, theBackground,
+ theBBox, theAntiAlias, theCoords, theDomain, theFunction, theExtend);
+ newShading = registrar.registerShading(newShading);
+ return newShading;
+ }
+
+ @Override
+ public Pattern makePattern(int thePatternType, Shading theShading, List theXUID,
+ StringBuffer theExtGState, List<Double> theMatrix) {
+ Pattern newPattern = new PDFPattern(thePatternType, theShading, theXUID, theExtGState,
+ theMatrix);
+ newPattern = registrar.registerPattern(newPattern);
+ return newPattern;
+ }
+
+}
diff --git a/src/java/org/apache/fop/render/shading/PSGradientFactory.java b/src/java/org/apache/fop/render/shading/PSGradientFactory.java
new file mode 100644
index 000000000..cd47de93a
--- /dev/null
+++ b/src/java/org/apache/fop/render/shading/PSGradientFactory.java
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ */
+
+package org.apache.fop.render.shading;
+
+import java.awt.Color;
+import java.util.List;
+
+import org.apache.fop.pdf.PDFDeviceColorSpace;
+import org.apache.fop.render.ps.svg.PSFunction;
+import org.apache.fop.render.ps.svg.PSPattern;
+import org.apache.fop.render.ps.svg.PSShading;
+
+public class PSGradientFactory extends GradientFactory {
+
+ @Override
+ public PSPattern createGradient(boolean radial, PDFDeviceColorSpace theColorspace,
+ List<Color> theColors, List<Double> theBounds, List<Double> theCoords,
+ List<Double> theMatrix) {
+ return (PSPattern)makeGradient(radial, theColorspace, theColors, theBounds,
+ theCoords, theMatrix);
+ }
+
+ public Function makeFunction(int functionType, List<Double> theDomain,
+ List<Double> theRange, List<Function> theFunctions,
+ List<Double> theBounds, List<Double> theEncode) {
+ Function newFunction = new PSFunction(functionType, theDomain, theRange, theFunctions,
+ theBounds, theEncode);
+ return newFunction;
+ }
+
+ @Override
+ public Function makeFunction(int functionType, List<Double> theDomain,
+ List<Double> theRange, List<Double> theCZero, List<Double> theCOne,
+ double theInterpolationExponentN) {
+ Function newFunction = new PSFunction(functionType, theDomain, theRange, theCZero,
+ theCOne, theInterpolationExponentN);
+ return newFunction;
+ }
+
+ @Override
+ public Shading makeShading(int theShadingType,
+ PDFDeviceColorSpace theColorSpace, List<Double> theBackground, List<Double> theBBox,
+ boolean theAntiAlias, List<Double> theCoords, List<Double> theDomain,
+ Function theFunction, List<Integer> theExtend) {
+ Shading newShading = new PSShading(theShadingType, theColorSpace, theBackground, theBBox,
+ theAntiAlias, theCoords, theDomain, theFunction, theExtend);
+ return newShading;
+ }
+
+ @Override
+ public Pattern makePattern(int thePatternType, Shading theShading, List theXUID,
+ StringBuffer theExtGState, List<Double> theMatrix) {
+ return new PSPattern(thePatternType, theShading, theXUID, theExtGState);
+ }
+}
diff --git a/src/java/org/apache/fop/render/shading/Pattern.java b/src/java/org/apache/fop/render/shading/Pattern.java
new file mode 100644
index 000000000..b66926e53
--- /dev/null
+++ b/src/java/org/apache/fop/render/shading/Pattern.java
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+
+package org.apache.fop.render.shading;
+
+public interface Pattern {
+
+}
diff --git a/src/java/org/apache/fop/render/shading/Shading.java b/src/java/org/apache/fop/render/shading/Shading.java
new file mode 100644
index 000000000..385cb112b
--- /dev/null
+++ b/src/java/org/apache/fop/render/shading/Shading.java
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+
+package org.apache.fop.render.shading;
+
+
+public interface Shading {
+ StringBuffer handleShadingType1(StringBuffer p);
+ StringBuffer handleShadingType2or3(StringBuffer p);
+ StringBuffer handleShadingType4or6or7(StringBuffer p);
+ StringBuffer handleShadingType5(StringBuffer p);
+}
diff --git a/src/java/org/apache/fop/render/shading/ShadingPattern.java b/src/java/org/apache/fop/render/shading/ShadingPattern.java
new file mode 100644
index 000000000..6dac65f55
--- /dev/null
+++ b/src/java/org/apache/fop/render/shading/ShadingPattern.java
@@ -0,0 +1,105 @@
+/*
+ * 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.
+ */
+
+package org.apache.fop.render.shading;
+
+import java.util.List;
+
+import org.apache.fop.pdf.PDFDeviceColorSpace;
+import org.apache.fop.pdf.PDFNumber;
+
+/**
+ * A class for writing shading objects for different output formats
+ */
+public class ShadingPattern {
+
+ private Shading shading;
+
+ /**
+ * Constructor
+ * @param shading The shading object from which to write the output
+ */
+ public ShadingPattern(Shading shading) {
+ this.shading = shading;
+ }
+
+ /**
+ * Outputs the given shading object to a String
+ * @param colorSpace The Colospace (PDF and Postscript)
+ * @param shadingType The shading type
+ * @param background The background
+ * @param bBox The bounding box
+ * @param antiAlias Anti-aliasing
+ * @return Returns the output string
+ */
+ public String toString(PDFDeviceColorSpace colorSpace, int shadingType, List background,
+ List bBox, boolean antiAlias) {
+ StringBuffer p = new StringBuffer(128);
+ p.append("<<\n/ShadingType " + shadingType + " \n");
+ if (colorSpace != null) {
+ p.append("/ColorSpace /"
+ + colorSpace.getName() + " \n");
+ }
+
+ if (background != null) {
+ p.append("/Background [ ");
+ for (int bgIndex = 0; bgIndex < background.size(); bgIndex++) {
+ p.append(PDFNumber.doubleOut((Double)background.get(bgIndex))
+ + " ");
+ }
+ p.append("] \n");
+ }
+
+ if (bBox
+ != null) { // I've never seen an example, so I guess this is right.
+ p.append("/BBox [ ");
+ for (int bboxIndex = 0; bboxIndex < bBox.size(); bboxIndex++) {
+ p.append(PDFNumber.doubleOut((Double)bBox.get(bboxIndex))
+ + " ");
+ }
+ p.append("] \n");
+ }
+
+ if (antiAlias) {
+ p.append("/AntiAlias " + antiAlias + " \n");
+ }
+
+ // Here's where we differentiate based on what type it is.
+ switch (shadingType) {
+ //Function based shading
+ case 1: p = shading.handleShadingType1(p); break;
+ //Axial shading
+ case 2:
+ //Radial shading
+ case 3: p = shading.handleShadingType2or3(p); break;
+ //Free-form Gouraud-shaded triangle meshes
+ case 4:
+ //Coons patch meshes
+ case 6:
+ //Tensor product patch meshes
+ case 7: p = shading.handleShadingType4or6or7(p); break;
+ //Lattice Free form gouraud-shaded triangle mesh
+ case 5: p = shading.handleShadingType5(p); break;
+ default: //Shading pattern outside expecting values
+ break;
+ }
+
+ p.append(">>");
+
+ return (p.toString());
+ }
+}
diff --git a/src/java/org/apache/fop/render/txt/TXTRenderer.java b/src/java/org/apache/fop/render/txt/TXTRenderer.java
index bf4a46e19..5b398711f 100644
--- a/src/java/org/apache/fop/render/txt/TXTRenderer.java
+++ b/src/java/org/apache/fop/render/txt/TXTRenderer.java
@@ -583,6 +583,14 @@ public class TXTRenderer extends AbstractPathOrientedRenderer {
}
/** {@inheritDoc} */
+ protected void startLayer(String layer) {
+ }
+
+ /** {@inheritDoc} */
+ protected void endLayer() {
+ }
+
+ /** {@inheritDoc} */
protected void concatenateTransformationMatrix(AffineTransform at) {
currentState.push(new CTM(UnitConv.ptToMpt(at)));
}
diff --git a/src/java/org/apache/fop/render/xml/XMLRenderer.java b/src/java/org/apache/fop/render/xml/XMLRenderer.java
index 2212da434..68e2e3c62 100644
--- a/src/java/org/apache/fop/render/xml/XMLRenderer.java
+++ b/src/java/org/apache/fop/render/xml/XMLRenderer.java
@@ -545,6 +545,16 @@ public class XMLRenderer extends AbstractXMLRenderer {
//only necessary for graphical output
}
+ /** {@inheritDoc} */
+ protected void startLayer(String layer) {
+ //only necessary for graphical output
+ }
+
+ /** {@inheritDoc} */
+ protected void endLayer() {
+ //only necessary for graphical output
+ }
+
/**
* {@inheritDoc}
* org.apache.fop.area.inline.InlineArea)
diff --git a/src/java/org/apache/fop/svg/PDFDocumentGraphics2D.java b/src/java/org/apache/fop/svg/PDFDocumentGraphics2D.java
index e91e2231c..cb2b58c40 100644
--- a/src/java/org/apache/fop/svg/PDFDocumentGraphics2D.java
+++ b/src/java/org/apache/fop/svg/PDFDocumentGraphics2D.java
@@ -278,7 +278,6 @@ public class PDFDocumentGraphics2D extends PDFGraphics2D {
PDFStream pdfStream = this.pdfDoc.getFactory().makeStream(
PDFFilterList.CONTENT_FILTER, false);
pdfStream.add(getString());
- currentStream = null;
this.pdfDoc.registerObject(pdfStream);
pdfContext.getCurrentPage().setContents(pdfStream);
PDFAnnotList annots = pdfContext.getCurrentPage().getAnnotations();
diff --git a/src/java/org/apache/fop/svg/PDFDocumentGraphics2DConfigurator.java b/src/java/org/apache/fop/svg/PDFDocumentGraphics2DConfigurator.java
index 0c0335a8b..6f2d4429d 100644
--- a/src/java/org/apache/fop/svg/PDFDocumentGraphics2DConfigurator.java
+++ b/src/java/org/apache/fop/svg/PDFDocumentGraphics2DConfigurator.java
@@ -88,7 +88,8 @@ public class PDFDocumentGraphics2DConfigurator {
final boolean strict = false;
if (cfg != null) {
URI thisUri = new File(".").getAbsoluteFile().toURI();
- InternalResourceResolver resourceResolver = ResourceResolverFactory.createDefaultInternalResourceResolver(thisUri);
+ InternalResourceResolver resourceResolver
+ = ResourceResolverFactory.createDefaultInternalResourceResolver(thisUri);
//TODO The following could be optimized by retaining the FontManager somewhere
FontManager fontManager = new FontManager(resourceResolver, FontDetectorFactory.createDefault(),
FontCacheManagerFactory.createDefault());
diff --git a/src/java/org/apache/fop/svg/PDFGraphics2D.java b/src/java/org/apache/fop/svg/PDFGraphics2D.java
index 526420d9c..1fcf9f870 100644
--- a/src/java/org/apache/fop/svg/PDFGraphics2D.java
+++ b/src/java/org/apache/fop/svg/PDFGraphics2D.java
@@ -80,6 +80,7 @@ import org.apache.fop.pdf.PDFColorHandler;
import org.apache.fop.pdf.PDFConformanceException;
import org.apache.fop.pdf.PDFDeviceColorSpace;
import org.apache.fop.pdf.PDFDocument;
+import org.apache.fop.pdf.PDFFunction;
import org.apache.fop.pdf.PDFGState;
import org.apache.fop.pdf.PDFImage;
import org.apache.fop.pdf.PDFImageXObject;
@@ -89,11 +90,18 @@ import org.apache.fop.pdf.PDFPaintingState;
import org.apache.fop.pdf.PDFPattern;
import org.apache.fop.pdf.PDFResourceContext;
import org.apache.fop.pdf.PDFResources;
+import org.apache.fop.pdf.PDFShading;
import org.apache.fop.pdf.PDFText;
import org.apache.fop.pdf.PDFXObject;
import org.apache.fop.render.pdf.ImageRawCCITTFaxAdapter;
import org.apache.fop.render.pdf.ImageRawJPEGAdapter;
import org.apache.fop.render.pdf.ImageRenderedAdapter;
+import org.apache.fop.render.shading.Function;
+import org.apache.fop.render.shading.GradientFactory;
+import org.apache.fop.render.shading.GradientRegistrar;
+import org.apache.fop.render.shading.PDFGradientFactory;
+import org.apache.fop.render.shading.Pattern;
+import org.apache.fop.render.shading.Shading;
/**
* <p>PDF Graphics 2D.
@@ -104,7 +112,7 @@ import org.apache.fop.render.pdf.ImageRenderedAdapter;
*
* @see org.apache.batik.ext.awt.g2d.AbstractGraphics2D
*/
-public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHandler {
+public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHandler, GradientRegistrar {
private static final AffineTransform IDENTITY_TRANSFORM = new AffineTransform();
/** The number of decimal places. */
@@ -868,11 +876,10 @@ public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHand
}
//Gradients are currently restricted to sRGB
- PDFDeviceColorSpace aColorSpace;
- aColorSpace = new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_RGB);
- PDFPattern myPat = pdfDoc.getFactory().makeGradient(
- resourceContext, false, aColorSpace,
- someColors, theBounds, theCoords, theMatrix);
+ PDFDeviceColorSpace colSpace = new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_RGB);
+ PDFGradientFactory gradientFactory = (PDFGradientFactory)GradientFactory.newInstance(this);
+ PDFPattern myPat = gradientFactory.createGradient(false, colSpace, someColors, theBounds,
+ theCoords, theMatrix);
currentStream.write(myPat.getColorSpaceOut(fill));
return true;
@@ -944,13 +951,10 @@ public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHand
float offset = fractions[count];
theBounds.add(new Double(offset));
}
- PDFDeviceColorSpace colSpace;
- colSpace = new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_RGB);
-
- PDFPattern myPat = pdfDoc.getFactory().makeGradient(
- resourceContext, true, colSpace,
- someColors, theBounds, theCoords, theMatrix);
-
+ PDFDeviceColorSpace colSpace = new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_RGB);
+ PDFGradientFactory gradientFactory = (PDFGradientFactory) GradientFactory.newInstance(this);
+ PDFPattern myPat = gradientFactory.createGradient(true, colSpace, someColors, theBounds,
+ theCoords, theMatrix);
currentStream.write(myPat.getColorSpaceOut(fill));
return true;
@@ -1691,11 +1695,15 @@ public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHand
* @param iter PathIterator to process
*/
public void processPathIterator(PathIterator iter) {
+ double lastX = 0.0;
+ double lastY = 0.0;
while (!iter.isDone()) {
double[] vals = new double[6];
int type = iter.currentSegment(vals);
switch (type) {
case PathIterator.SEG_CUBICTO:
+ lastX = vals[4];
+ lastY = vals[5];
currentStream.write(PDFNumber.doubleOut(vals[0], DEC) + " "
+ PDFNumber.doubleOut(vals[1], DEC) + " "
+ PDFNumber.doubleOut(vals[2], DEC) + " "
@@ -1704,18 +1712,30 @@ public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHand
+ PDFNumber.doubleOut(vals[5], DEC) + " c\n");
break;
case PathIterator.SEG_LINETO:
+ lastX = vals[0];
+ lastY = vals[1];
currentStream.write(PDFNumber.doubleOut(vals[0], DEC) + " "
+ PDFNumber.doubleOut(vals[1], DEC) + " l\n");
break;
case PathIterator.SEG_MOVETO:
+ lastX = vals[0];
+ lastY = vals[1];
currentStream.write(PDFNumber.doubleOut(vals[0], DEC) + " "
+ PDFNumber.doubleOut(vals[1], DEC) + " m\n");
break;
case PathIterator.SEG_QUADTO:
- currentStream.write(PDFNumber.doubleOut(vals[0], DEC) + " "
- + PDFNumber.doubleOut(vals[1], DEC) + " "
- + PDFNumber.doubleOut(vals[2], DEC) + " "
- + PDFNumber.doubleOut(vals[3], DEC) + " y\n");
+ double controlPointAX = lastX + ((2.0 / 3.0) * (vals[0] - lastX));
+ double controlPointAY = lastY + ((2.0 / 3.0) * (vals[1] - lastY));
+ double controlPointBX = vals[2] + ((2.0 / 3.0) * (vals[0] - vals[2]));
+ double controlPointBY = vals[3] + ((2.0 / 3.0) * (vals[1] - vals[3]));
+ currentStream.write(PDFNumber.doubleOut(controlPointAX, DEC) + " "
+ + PDFNumber.doubleOut(controlPointAY, DEC) + " "
+ + PDFNumber.doubleOut(controlPointBX, DEC) + " "
+ + PDFNumber.doubleOut(controlPointBY, DEC) + " "
+ + PDFNumber.doubleOut(vals[2], DEC) + " "
+ + PDFNumber.doubleOut(vals[3], DEC) + " c\n");
+ lastX = vals[2];
+ lastY = vals[3];
break;
case PathIterator.SEG_CLOSE:
currentStream.write("h\n");
@@ -1840,4 +1860,36 @@ public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHand
//NYI
}
+ /**
+ * Registers a function object against the output format document
+ * @param function The function object to register
+ * @return Returns either the function which has already been registered
+ * or the current new registered object.
+ */
+ public Function registerFunction(Function function) {
+ return pdfDoc.getFactory().registerFunction((PDFFunction)function);
+ }
+
+ /**
+ * Registers a shading object against the otuput format document
+ * @param shading The shading object to register
+ * @return Returs either the shading which has already been registered
+ * or the current new registered object
+ */
+ public Shading registerShading(Shading shading) {
+ assert shading instanceof PDFShading;
+ return pdfDoc.getFactory().registerShading(resourceContext, (PDFShading)shading);
+ }
+
+ /**
+ * Registers a pattern object against the output format document
+ * @param pattern The pattern object to register
+ * @return Returns either the pattern which has already been registered
+ * or the current new registered object
+ */
+ public Pattern registerPattern(Pattern pattern) {
+ assert pattern instanceof PDFPattern;
+ return pdfDoc.getFactory().registerPattern(resourceContext, (PDFPattern)pattern);
+ }
+
}
diff --git a/src/java/org/apache/fop/svg/SimpleSVGUserAgent.java b/src/java/org/apache/fop/svg/SimpleSVGUserAgent.java
index 42a18b17c..60a6020fc 100644
--- a/src/java/org/apache/fop/svg/SimpleSVGUserAgent.java
+++ b/src/java/org/apache/fop/svg/SimpleSVGUserAgent.java
@@ -23,8 +23,11 @@ import java.awt.Dimension;
import java.awt.geom.AffineTransform;
import java.awt.geom.Dimension2D;
+import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParserFactory;
+import org.xml.sax.SAXException;
+
import org.apache.batik.bridge.UserAgentAdapter;
import org.apache.batik.gvt.font.FontFamilyResolver;
@@ -84,17 +87,28 @@ public class SimpleSVGUserAgent extends UserAgentAdapter {
return null; // userStyleSheetURI;
}
+
+ private static final String XML_PARSER_CLASS_NAME;
+
+ static {
+ String result;
+ try {
+ SAXParserFactory factory = SAXParserFactory.newInstance();
+ result = factory.newSAXParser().getXMLReader().getClass().getName();
+ } catch (SAXException e) {
+ result = null;
+ } catch (ParserConfigurationException e) {
+ result = null;
+ }
+ XML_PARSER_CLASS_NAME = result;
+ }
+
/**
* Returns the class name of the XML parser.
* @return the XML parser class name
*/
public String getXMLParserClassName() {
- try {
- SAXParserFactory factory = SAXParserFactory.newInstance();
- return factory.newSAXParser().getXMLReader().getClass().getName();
- } catch (Exception e) {
- return null;
- }
+ return XML_PARSER_CLASS_NAME;
}
/**
diff --git a/src/java/org/apache/fop/text/linebreak/LineBreakUtils.java b/src/java/org/apache/fop/text/linebreak/LineBreakUtils.java
index d6e19a97a..6598d1b85 100644
--- a/src/java/org/apache/fop/text/linebreak/LineBreakUtils.java
+++ b/src/java/org/apache/fop/text/linebreak/LineBreakUtils.java
@@ -29,7 +29,6 @@ package org.apache.fop.text.linebreak;
* - commit BOTH changed files
*/
-// CSOFF: WhitespaceAfterCheck
// CSOFF: LineLengthCheck
/** Line breaking utilities. */
@@ -694,11 +693,11 @@ public final class LineBreakUtils {
"CP", "CR", "EX", "GL", "H2", "H3", "HY", "ID", "IN", "IS", "JL", "JT", "JV", "LF", "NL", "NS", "NU", "OP",
"PO", "PR", "QU", "SA", "SG", "SP", "SY", "WJ", "XX", "ZW"};
- private static String[] lineBreakPropertyLongNames = {"Ambiguous","Alphabetic","Break_Both","Break_After","Break_Before",
- "Mandatory_Break","Contingent_Break","Close_Punctuation","Combining_Mark","Close_Parenthesis","Carriage_Return",
- "Exclamation","Glue","H2","H3","Hyphen","Ideographic","Inseparable","Infix_Numeric","JL","JT","JV","Line_Feed",
- "Next_Line","Nonstarter","Numeric","Open_Punctuation","Postfix_Numeric","Prefix_Numeric","Quotation","Complex_Context",
- "Surrogate","Space","Break_Symbols","Word_Joiner","Unknown","ZWSpace"};
+ private static String[] lineBreakPropertyLongNames = {"Ambiguous", "Alphabetic", "Break_Both", "Break_After", "Break_Before",
+ "Mandatory_Break", "Contingent_Break", "Close_Punctuation", "Combining_Mark", "Close_Parenthesis", "Carriage_Return",
+ "Exclamation", "Glue", "H2", "H3", "Hyphen", "Ideographic", "Inseparable", "Infix_Numeric", "JL", "JT", "JV", "Line_Feed",
+ "Next_Line", "Nonstarter", "Numeric", "Open_Punctuation", "Postfix_Numeric", "Prefix_Numeric", "Quotation", "Complex_Context",
+ "Surrogate", "Space", "Break_Symbols", "Word_Joiner", "Unknown", "ZWSpace"};
/**
* Return the short name for the linebreak property corresponding
diff --git a/src/java/org/apache/fop/tools/fontlist/FontListMain.java b/src/java/org/apache/fop/tools/fontlist/FontListMain.java
index 659f7ccac..e2f5a651e 100644
--- a/src/java/org/apache/fop/tools/fontlist/FontListMain.java
+++ b/src/java/org/apache/fop/tools/fontlist/FontListMain.java
@@ -117,7 +117,7 @@ public final class FontListMain {
}
try {
GenerationHelperContentHandler helper = new GenerationHelperContentHandler(
- handler, null);
+ handler, null, null);
FontListSerializer serializer = new FontListSerializer();
serializer.generateSAX(fontFamilies, singleFamily, helper);
} finally {
diff --git a/src/java/org/apache/fop/traits/WritingMode.java b/src/java/org/apache/fop/traits/WritingMode.java
index 8718118ef..f8c784941 100644
--- a/src/java/org/apache/fop/traits/WritingMode.java
+++ b/src/java/org/apache/fop/traits/WritingMode.java
@@ -54,8 +54,9 @@ public final class WritingMode extends TraitEnum {
* Assign writing mode traits from this trait to the specified
* writing mode traits setter.
* @param wms a writing mode traits setter
+ * @param explicit true if writing mode property explicitly specified
*/
- public void assignWritingModeTraits(WritingModeTraitsSetter wms) {
+ public void assignWritingModeTraits(WritingModeTraitsSetter wms, boolean explicit) {
Direction inlineProgressionDirection;
Direction blockProgressionDirection;
Direction columnProgressionDirection;
@@ -97,7 +98,7 @@ public final class WritingMode extends TraitEnum {
wms.setColumnProgressionDirection(columnProgressionDirection);
wms.setRowProgressionDirection(rowProgressionDirection);
wms.setShiftDirection(shiftDirection);
- wms.setWritingMode(this);
+ wms.setWritingMode(this, explicit);
}
/**
diff --git a/src/java/org/apache/fop/traits/WritingModeTraits.java b/src/java/org/apache/fop/traits/WritingModeTraits.java
index 279326afa..fbd150ca9 100644
--- a/src/java/org/apache/fop/traits/WritingModeTraits.java
+++ b/src/java/org/apache/fop/traits/WritingModeTraits.java
@@ -31,20 +31,21 @@ public class WritingModeTraits implements WritingModeTraitsSetter {
private Direction rowProgressionDirection;
private Direction shiftDirection;
private WritingMode writingMode;
+ private boolean explicit;
/**
* Default writing mode traits constructor.
*/
public WritingModeTraits() {
- this (WritingMode.LR_TB);
+ this (WritingMode.LR_TB, false);
}
/**
* Construct writing mode traits using the specified writing mode.
* @param writingMode a writing mode traits object
*/
- public WritingModeTraits(WritingMode writingMode) {
- assignWritingModeTraits(writingMode);
+ public WritingModeTraits(WritingMode writingMode, boolean explicit) {
+ assignWritingModeTraits(writingMode, explicit);
}
/**
@@ -125,17 +126,25 @@ public class WritingModeTraits implements WritingModeTraitsSetter {
}
/**
+ * @return the "explicit-writing-mode" trait.
+ */
+ public boolean getExplicitWritingMode() {
+ return explicit;
+ }
+
+ /**
* @param writingMode the "writing-mode" trait.
*/
- public void setWritingMode(WritingMode writingMode) {
+ public void setWritingMode(WritingMode writingMode, boolean explicit) {
this.writingMode = writingMode;
+ this.explicit = explicit;
}
/**
* @param writingMode the "writing-mode" trait.
*/
- public void assignWritingModeTraits(WritingMode writingMode) {
- writingMode.assignWritingModeTraits(this);
+ public void assignWritingModeTraits(WritingMode writingMode, boolean explicit) {
+ writingMode.assignWritingModeTraits(this, explicit);
}
/**
diff --git a/src/java/org/apache/fop/traits/WritingModeTraitsGetter.java b/src/java/org/apache/fop/traits/WritingModeTraitsGetter.java
index a67e437c9..7340b3e8c 100644
--- a/src/java/org/apache/fop/traits/WritingModeTraitsGetter.java
+++ b/src/java/org/apache/fop/traits/WritingModeTraitsGetter.java
@@ -55,4 +55,9 @@ public interface WritingModeTraitsGetter {
*/
WritingMode getWritingMode();
+ /**
+ * @return the "explicit-writing-mode" trait
+ */
+ boolean getExplicitWritingMode();
+
}
diff --git a/src/java/org/apache/fop/traits/WritingModeTraitsSetter.java b/src/java/org/apache/fop/traits/WritingModeTraitsSetter.java
index 6dcf0fb12..b70f73954 100644
--- a/src/java/org/apache/fop/traits/WritingModeTraitsSetter.java
+++ b/src/java/org/apache/fop/traits/WritingModeTraitsSetter.java
@@ -58,13 +58,14 @@ public interface WritingModeTraitsSetter extends WritingModeTraitsGetter {
* Set value of writing-mode trait.
* @param writingMode the "writing-mode" trait
*/
- void setWritingMode(WritingMode writingMode);
+ void setWritingMode(WritingMode writingMode, boolean explicit);
/**
* Collectivelly assign values to all writing mode traits based upon a specific
* writing mode.
* @param writingMode the "writing-mode" trait
+ * @param explicit true if writing mode explicitly specified
*/
- void assignWritingModeTraits(WritingMode writingMode);
+ void assignWritingModeTraits(WritingMode writingMode, boolean explicit);
}
diff --git a/src/java/org/apache/fop/util/AbstractPaintingState.java b/src/java/org/apache/fop/util/AbstractPaintingState.java
index 96c3633e6..3e966d202 100644
--- a/src/java/org/apache/fop/util/AbstractPaintingState.java
+++ b/src/java/org/apache/fop/util/AbstractPaintingState.java
@@ -24,7 +24,6 @@ import java.awt.geom.AffineTransform;
import java.io.Serializable;
import java.util.Arrays;
import java.util.Collection;
-import java.util.Iterator;
import java.util.List;
import java.util.Stack;
@@ -36,10 +35,10 @@ public abstract class AbstractPaintingState implements Cloneable, Serializable {
private static final long serialVersionUID = 5998356138437094188L;
/** current state data */
- private AbstractData data = null;
+ private AbstractData data;
/** the state stack */
- private StateStack/*<AbstractData>*/ stateStack = new StateStack/*<AbstractData>*/();
+ private StateStack<AbstractData> stateStack = new StateStack<AbstractData>();
/**
* Instantiates a new state data object
@@ -216,8 +215,7 @@ public abstract class AbstractPaintingState implements Cloneable, Serializable {
*/
public AffineTransform getTransform() {
AffineTransform at = new AffineTransform();
- for (Iterator iter = stateStack.iterator(); iter.hasNext();) {
- AbstractData data = (AbstractData)iter.next();
+ for (AbstractData data : stateStack) {
AffineTransform stackTrans = data.getTransform();
at.concatenate(stackTrans);
}
@@ -249,7 +247,7 @@ public abstract class AbstractPaintingState implements Cloneable, Serializable {
if (stateStack.isEmpty()) {
return null;
} else {
- AbstractData baseData = (AbstractData)stateStack.get(0);
+ AbstractData baseData = stateStack.get(0);
return (AffineTransform) baseData.getTransform().clone();
}
}
@@ -297,7 +295,7 @@ public abstract class AbstractPaintingState implements Cloneable, Serializable {
*/
public AbstractData restore() {
if (!stateStack.isEmpty()) {
- setData((AbstractData)stateStack.pop());
+ setData(stateStack.pop());
return this.data;
} else {
return null;
@@ -310,12 +308,11 @@ public abstract class AbstractPaintingState implements Cloneable, Serializable {
*
* @param dataList a state data list
*/
- public void saveAll(List/*<AbstractData>*/ dataList) {
- Iterator it = dataList.iterator();
- while (it.hasNext()) {
+ public void saveAll(List<AbstractData> dataList) {
+ for (AbstractData data : dataList) {
// save current data on stack
save();
- setData((AbstractData)it.next());
+ setData(data);
}
}
@@ -325,8 +322,8 @@ public abstract class AbstractPaintingState implements Cloneable, Serializable {
*
* @return a list of state data popped from the stack
*/
- public List/*<AbstractData>*/ restoreAll() {
- List/*<AbstractData>*/ dataList = new java.util.ArrayList/*<AbstractData>*/();
+ public List<AbstractData> restoreAll() {
+ List<AbstractData> dataList = new java.util.ArrayList<AbstractData>();
AbstractData data;
while (true) {
data = getData();
@@ -361,7 +358,7 @@ public abstract class AbstractPaintingState implements Cloneable, Serializable {
*
* @return the state stack
*/
- protected Stack/*<AbstractData>*/ getStateStack() {
+ protected Stack<AbstractData> getStateStack() {
return this.stateStack;
}
@@ -369,8 +366,10 @@ public abstract class AbstractPaintingState implements Cloneable, Serializable {
@Override
public Object clone() {
AbstractPaintingState state = instantiate();
- state.stateStack = new StateStack(this.stateStack);
- state.data = (AbstractData)this.data.clone();
+ state.stateStack = new StateStack<AbstractData>(this.stateStack);
+ if (this.data != null) {
+ state.data = (AbstractData)this.data.clone();
+ }
return state;
}
@@ -385,7 +384,7 @@ public abstract class AbstractPaintingState implements Cloneable, Serializable {
/**
* A stack implementation which holds state objects
*/
- public class StateStack extends java.util.Stack {
+ public class StateStack<E> extends java.util.Stack<E> {
private static final long serialVersionUID = 4897178211223823041L;
@@ -393,7 +392,6 @@ public abstract class AbstractPaintingState implements Cloneable, Serializable {
* Default constructor
*/
public StateStack() {
- super();
}
/**
@@ -419,25 +417,28 @@ public abstract class AbstractPaintingState implements Cloneable, Serializable {
private static final long serialVersionUID = 5208418041189828624L;
/** The current color */
- protected Color color = null;
+ protected Color color;
/** The current background color */
- protected Color backColor = null;
+ protected Color backColor;
/** The current font name */
- protected String fontName = null;
+ protected String fontName;
/** The current font size */
- protected int fontSize = 0;
+ protected int fontSize;
/** The current line width */
- protected float lineWidth = 0;
+ protected float lineWidth;
/** The dash array for the current basic stroke (line type) */
- protected float[] dashArray = null;
+ protected float[] dashArray;
/** The current transform */
- protected AffineTransform transform = null;
+ protected AffineTransform transform;
+
+ /** The current (optional content group) layer. */
+ protected String layer;
/**
* Returns a newly create data object
@@ -485,6 +486,18 @@ public abstract class AbstractPaintingState implements Cloneable, Serializable {
transform = new AffineTransform();
}
+ public void setLayer(String layer) {
+ if (layer != null) {
+ this.layer = layer;
+ } else {
+ throw new IllegalArgumentException();
+ }
+ }
+
+ public String getLayer() {
+ return this.layer;
+ }
+
/**
* Returns the derived rotation from the current transform
*
@@ -523,6 +536,7 @@ public abstract class AbstractPaintingState implements Cloneable, Serializable {
this.transform = new AffineTransform();
}
data.transform = new AffineTransform(this.transform);
+ data.layer = this.layer;
return data;
}
@@ -535,7 +549,8 @@ public abstract class AbstractPaintingState implements Cloneable, Serializable {
+ ", fontSize=" + fontSize
+ ", lineWidth=" + lineWidth
+ ", dashArray=" + dashArray
- + ", transform=" + transform;
+ + ", transform=" + transform
+ + ", layer=" + layer;
}
}
}
diff --git a/src/java/org/apache/fop/util/CommandLineLogger.java b/src/java/org/apache/fop/util/CommandLineLogger.java
deleted file mode 100644
index a9e59158f..000000000
--- a/src/java/org/apache/fop/util/CommandLineLogger.java
+++ /dev/null
@@ -1,263 +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.util;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
-/**
- * This is a commons-logging logger for command line use.
- */
-public class CommandLineLogger implements Log {
- /** "Trace" level logging. */
- public static final int LOG_LEVEL_TRACE = 1;
- /** "Debug" level logging. */
- public static final int LOG_LEVEL_DEBUG = 2;
- /** "Info" level logging. */
- public static final int LOG_LEVEL_INFO = 3;
- /** "Warn" level logging. */
- public static final int LOG_LEVEL_WARN = 4;
- /** "Error" level logging. */
- public static final int LOG_LEVEL_ERROR = 5;
- /** "Fatal" level logging. */
- public static final int LOG_LEVEL_FATAL = 6;
-
- private int logLevel;
- private String logName;
-
- /**
- * Construct the logger with a default log level taken from the LogFactory
- * attribute "level".
- * @param logName the logger name.
- */
- public CommandLineLogger(String logName) {
- this.logName = logName;
- setLogLevel((String) LogFactory.getFactory().getAttribute("level"));
- }
-
- /**
- * Set a log level for the logger.
- * @param level the log level
- */
- public void setLogLevel(String level) {
- if ("fatal".equals(level)) {
- logLevel = LOG_LEVEL_FATAL;
- } else if ("error".equals(level)) {
- logLevel = LOG_LEVEL_ERROR;
- } else if ("warn".equals(level)) {
- logLevel = LOG_LEVEL_WARN;
- } else if ("info".equals(level)) {
- logLevel = LOG_LEVEL_INFO;
- } else if ("debug".equals(level)) {
- logLevel = LOG_LEVEL_DEBUG;
- } else if ("trace".equals(level)) {
- logLevel = LOG_LEVEL_TRACE;
- } else {
- logLevel = LOG_LEVEL_INFO;
- }
- }
-
- /**
- * {@inheritDoc}
- */
- public final boolean isTraceEnabled() {
- return logLevel <= LOG_LEVEL_TRACE;
- }
-
- /**
- * {@inheritDoc}
- */
- public final boolean isDebugEnabled() {
- return logLevel <= LOG_LEVEL_DEBUG;
- }
-
- /**
- * {@inheritDoc}
- */
- public final boolean isInfoEnabled() {
- return logLevel <= LOG_LEVEL_INFO;
- }
-
- /**
- * {@inheritDoc}
- */
- public final boolean isWarnEnabled() {
- return logLevel <= LOG_LEVEL_WARN;
- }
-
- /**
- * {@inheritDoc}
- */
- public final boolean isErrorEnabled() {
- return logLevel <= LOG_LEVEL_ERROR;
- }
-
- /**
- * {@inheritDoc}
- */
- public final boolean isFatalEnabled() {
- return logLevel <= LOG_LEVEL_FATAL;
- }
-
- /**
- * {@inheritDoc}
- */
- public final void trace(Object message) {
- if (isTraceEnabled()) {
- log(LOG_LEVEL_TRACE, message, null);
- }
- }
-
- /**
- * {@inheritDoc}
- */
- public final void trace(Object message, Throwable t) {
- if (isTraceEnabled()) {
- log(LOG_LEVEL_TRACE, message, t);
- }
- }
-
- /**
- * {@inheritDoc}
- */
- public final void debug(Object message) {
- if (isDebugEnabled()) {
- log(LOG_LEVEL_DEBUG, message, null);
- }
- }
-
- /**
- * {@inheritDoc}
- */
- public final void debug(Object message, Throwable t) {
- if (isDebugEnabled()) {
- log(LOG_LEVEL_DEBUG, message, t);
- }
- }
-
- /**
- * {@inheritDoc}
- */
- public final void info(Object message) {
- if (isInfoEnabled()) {
- log(LOG_LEVEL_INFO, message, null);
- }
- }
-
- /**
- * {@inheritDoc}
- */
- public final void info(Object message, Throwable t) {
- if (isInfoEnabled()) {
- log(LOG_LEVEL_INFO, message, t);
- }
- }
-
- /**
- * {@inheritDoc}
- */
- public final void warn(Object message) {
- if (isWarnEnabled()) {
- log(LOG_LEVEL_WARN, message, null);
- }
- }
-
- /**
- * {@inheritDoc}
- */
- public final void warn(Object message, Throwable t) {
- if (isWarnEnabled()) {
- log(LOG_LEVEL_WARN, message, t);
- }
- }
-
- /**
- * {@inheritDoc}
- */
- public final void error(Object message) {
- if (isErrorEnabled()) {
- log(LOG_LEVEL_ERROR, message, null);
- }
- }
-
- /**
- * {@inheritDoc}
- */
- public final void error(Object message, Throwable t) {
- if (isErrorEnabled()) {
- log(LOG_LEVEL_ERROR, message, t);
- }
- }
-
- /**
- * {@inheritDoc}
- */
- public final void fatal(Object message) {
- if (isFatalEnabled()) {
- log(LOG_LEVEL_FATAL, message, null);
- }
- }
-
- /**
- * {@inheritDoc}
- */
- public final void fatal(Object message, Throwable t) {
- if (isFatalEnabled()) {
- log(LOG_LEVEL_FATAL, message, t);
- }
- }
-
- /**
- * Do the actual logging.
- * This method assembles the message and prints it to
- * and then calls <code>write()</code> to cause it to be written.</p>
- *
- * @param type One of the LOG_LEVEL_XXX constants defining the log level
- * @param message The message itself (typically a String)
- * @param t The exception whose stack trace should be logged
- */
- protected void log(int type, Object message, Throwable t) {
- StringBuffer buf = new StringBuffer();
- // Append the message
- buf.append(String.valueOf(message));
- if (t != null) {
- buf.append("\n");
- // Append a stack trace or just the stack trace message.
- if (!isDebugEnabled()) {
- buf.append(t.toString());
- buf.append("\n");
- } else {
- java.io.StringWriter sw = new java.io.StringWriter(1024);
- java.io.PrintWriter pw = new java.io.PrintWriter(sw);
- t.printStackTrace(pw);
- pw.close();
- buf.append(sw.toString());
- }
- }
-
- // Print to the appropriate destination
- if (type >= LOG_LEVEL_WARN) {
- System.err.println(buf);
- } else {
- System.out.println(buf);
- }
-
- }
-}
diff --git a/src/java/org/apache/fop/util/GenerationHelperContentHandler.java b/src/java/org/apache/fop/util/GenerationHelperContentHandler.java
index 64fabbc8a..68970a7f2 100644
--- a/src/java/org/apache/fop/util/GenerationHelperContentHandler.java
+++ b/src/java/org/apache/fop/util/GenerationHelperContentHandler.java
@@ -35,6 +35,7 @@ public class GenerationHelperContentHandler extends DelegatingContentHandler {
private static final Attributes EMPTY_ATTS = new AttributesImpl();
private String mainNamespace;
+ private Object contentHandlerContext;
/**
* Main constructor. If the given handler also implements any of the EntityResolver,
@@ -42,10 +43,12 @@ public class GenerationHelperContentHandler extends DelegatingContentHandler {
* @param handler the SAX content handler to delegate all calls to
* @param mainNamespace the main namespace used for generated XML content when abbreviated
* ContentHandler calls are used.
+ * @param contentHandlerContext additional content handler context state
*/
- public GenerationHelperContentHandler(ContentHandler handler, String mainNamespace) {
+ public GenerationHelperContentHandler(ContentHandler handler, String mainNamespace, Object contentHandlerContext) {
super(handler);
this.mainNamespace = mainNamespace;
+ this.contentHandlerContext = contentHandlerContext;
}
/**
@@ -66,6 +69,14 @@ public class GenerationHelperContentHandler extends DelegatingContentHandler {
}
/**
+ * Returns the context object (may be null).
+ * @return the context object
+ */
+ public Object getContentHandlerContext() {
+ return this.contentHandlerContext;
+ }
+
+ /**
* Convenience method to generate a startElement SAX event.
* @param localName the local name of the element
* @param atts the attributes
diff --git a/src/java/org/apache/fop/util/XMLUtil.java b/src/java/org/apache/fop/util/XMLUtil.java
index 24c75922c..3f815e59e 100644
--- a/src/java/org/apache/fop/util/XMLUtil.java
+++ b/src/java/org/apache/fop/util/XMLUtil.java
@@ -300,4 +300,35 @@ public final class XMLUtil implements XMLConstants {
}
}
+ /**
+ * Escape '<', '>' and '&' using NCRs.
+ * @param unescaped string
+ * @return escaped string
+ */
+ public static String escape(String unescaped) {
+ int needsEscape = 0;
+ for (int i = 0, n = unescaped.length(); i < n; ++i) {
+ char c = unescaped.charAt(i);
+ if ((c == '<') || (c == '>') || (c == '&')) {
+ ++needsEscape;
+ }
+ }
+ if (needsEscape > 0) {
+ StringBuffer sb = new StringBuffer(unescaped.length() + 6 * needsEscape);
+ for (int i = 0, n = unescaped.length(); i < n; ++i) {
+ char c = unescaped.charAt(i);
+ if ((c == '<') || (c == '>') || (c == '&')) {
+ sb.append("&#x");
+ sb.append(Integer.toString(c, 16));
+ sb.append(';');
+ } else {
+ sb.append(c);
+ }
+ }
+ return sb.toString();
+ } else {
+ return unescaped;
+ }
+ }
+
}