Browse Source

Merge changes from trunk through revision 1364036.

git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/fop-1_1@1400689 13f79535-47bb-0310-9956-ffa450edef68
fop-1_1
Glenn Adams 11 years ago
parent
commit
bdcb38fb7d
100 changed files with 4021 additions and 3303 deletions
  1. 2
    1
      build.xml
  2. 5
    8
      examples/embedding/java/embedding/ExampleAWTViewer.java
  3. 5
    8
      examples/embedding/java/embedding/ExampleDOM2PDF.java
  4. 1
    1
      examples/embedding/java/embedding/ExampleFO2JPSPrint.java
  5. 1
    1
      examples/embedding/java/embedding/ExampleFO2OldStylePrint.java
  6. 5
    8
      examples/embedding/java/embedding/ExampleFO2PDF.java
  7. 4
    7
      examples/embedding/java/embedding/ExampleFO2PDFUsingSAXParser.java
  8. 5
    7
      examples/embedding/java/embedding/ExampleFO2RTF.java
  9. 7
    9
      examples/embedding/java/embedding/ExampleObj2PDF.java
  10. 4
    6
      examples/embedding/java/embedding/ExampleXML2PDF.java
  11. 6
    8
      examples/embedding/java/embedding/MultipleFO2PDF.java
  12. 1
    1
      examples/embedding/java/embedding/atxml/ExampleConcat.java
  13. 1
    1
      examples/embedding/java/embedding/atxml/ExampleStamp.java
  14. 1
    1
      examples/embedding/java/embedding/events/ExampleEvents.java
  15. 2
    3
      examples/embedding/java/embedding/intermediate/ExampleConcat.java
  16. 1
    1
      examples/embedding/java/embedding/intermediate/ExampleStamp.java
  17. 11
    6
      src/documentation/content/xdocs/team.xml
  18. 55
    72
      src/documentation/content/xdocs/trunk/embedding.xml
  19. 0
    2
      src/documentation/content/xdocs/trunk/output.xml
  20. 2
    0
      src/java/org/apache/fop/afp/AFPDataObjectInfo.java
  21. 2
    1
      src/java/org/apache/fop/afp/AFPDitheredRectanglePainter.java
  22. 3
    2
      src/java/org/apache/fop/afp/AFPGraphics2D.java
  23. 2
    1
      src/java/org/apache/fop/afp/AFPResourceInfo.java
  24. 67
    77
      src/java/org/apache/fop/afp/AFPResourceLevel.java
  25. 2
    1
      src/java/org/apache/fop/afp/AFPResourceLevelDefaults.java
  26. 15
    13
      src/java/org/apache/fop/afp/AFPResourceManager.java
  27. 41
    71
      src/java/org/apache/fop/afp/AFPStreamer.java
  28. 2
    3
      src/java/org/apache/fop/afp/fonts/AFPBase12FontCollection.java
  29. 6
    12
      src/java/org/apache/fop/afp/fonts/AFPFont.java
  30. 5
    6
      src/java/org/apache/fop/afp/fonts/AbstractOutlineFont.java
  31. 5
    5
      src/java/org/apache/fop/afp/fonts/CharacterSet.java
  32. 35
    40
      src/java/org/apache/fop/afp/fonts/CharacterSetBuilder.java
  33. 29
    3
      src/java/org/apache/fop/afp/fonts/CharacterSetType.java
  34. 5
    23
      src/java/org/apache/fop/afp/fonts/CharactersetEncoder.java
  35. 6
    3
      src/java/org/apache/fop/afp/fonts/DoubleByteFont.java
  36. 2
    2
      src/java/org/apache/fop/afp/fonts/FopCharacterSet.java
  37. 3
    2
      src/java/org/apache/fop/afp/fonts/OutlineFont.java
  38. 7
    7
      src/java/org/apache/fop/afp/fonts/RasterFont.java
  39. 3
    4
      src/java/org/apache/fop/afp/modca/IncludedResourceObject.java
  40. 0
    152
      src/java/org/apache/fop/afp/svg/AFPGraphicsConfiguration.java
  41. 0
    80
      src/java/org/apache/fop/afp/svg/AFPGraphicsDevice.java
  42. 82
    0
      src/java/org/apache/fop/afp/util/AFPResourceAccessor.java
  43. 43
    10
      src/java/org/apache/fop/afp/util/AFPResourceUtil.java
  44. 0
    87
      src/java/org/apache/fop/afp/util/DefaultFOPResourceAccessor.java
  45. 0
    76
      src/java/org/apache/fop/afp/util/SimpleResourceAccessor.java
  46. 55
    0
      src/java/org/apache/fop/apps/EnvironmentProfile.java
  47. 116
    0
      src/java/org/apache/fop/apps/EnvironmentalProfileFactory.java
  48. 0
    376
      src/java/org/apache/fop/apps/FOURIResolver.java
  49. 204
    123
      src/java/org/apache/fop/apps/FOUserAgent.java
  50. 3
    3
      src/java/org/apache/fop/apps/Fop.java
  51. 356
    0
      src/java/org/apache/fop/apps/FopConfParser.java
  52. 118
    481
      src/java/org/apache/fop/apps/FopFactory.java
  53. 657
    0
      src/java/org/apache/fop/apps/FopFactoryBuilder.java
  54. 134
    0
      src/java/org/apache/fop/apps/FopFactoryConfig.java
  55. 0
    406
      src/java/org/apache/fop/apps/FopFactoryConfigurator.java
  56. 2
    0
      src/java/org/apache/fop/apps/MimeConstants.java
  57. 153
    0
      src/java/org/apache/fop/apps/io/InternalResourceResolver.java
  58. 25
    18
      src/java/org/apache/fop/apps/io/Resource.java
  59. 51
    0
      src/java/org/apache/fop/apps/io/ResourceResolver.java
  60. 276
    0
      src/java/org/apache/fop/apps/io/ResourceResolverFactory.java
  61. 48
    0
      src/java/org/apache/fop/apps/io/TempResourceResolver.java
  62. 57
    0
      src/java/org/apache/fop/apps/io/TempResourceURIGenerator.java
  63. 6
    0
      src/java/org/apache/fop/apps/io/package.html
  64. 1
    1
      src/java/org/apache/fop/area/AreaTreeHandler.java
  65. 3
    3
      src/java/org/apache/fop/area/AreaTreeParser.java
  66. 19
    28
      src/java/org/apache/fop/area/CachedRenderPagesModel.java
  67. 44
    28
      src/java/org/apache/fop/cli/CommandLineOptions.java
  68. 1
    1
      src/java/org/apache/fop/cli/IFInputHandler.java
  69. 2
    17
      src/java/org/apache/fop/cli/InputHandler.java
  70. 1
    1
      src/java/org/apache/fop/fo/FOTreeBuilder.java
  71. 4
    4
      src/java/org/apache/fop/fo/PropertyList.java
  72. 4
    6
      src/java/org/apache/fop/fo/extensions/svg/SVGElement.java
  73. 1
    1
      src/java/org/apache/fop/fo/flow/ExternalGraphic.java
  74. 1
    1
      src/java/org/apache/fop/fo/properties/CommonBorderPaddingBackground.java
  75. 9
    0
      src/java/org/apache/fop/fonts/CIDFont.java
  76. 37
    43
      src/java/org/apache/fop/fonts/CustomFont.java
  77. 9
    17
      src/java/org/apache/fop/fonts/CustomFontCollection.java
  78. 343
    0
      src/java/org/apache/fop/fonts/DefaultFontConfig.java
  79. 186
    0
      src/java/org/apache/fop/fonts/DefaultFontConfigurator.java
  80. 33
    50
      src/java/org/apache/fop/fonts/EmbedFontInfo.java
  81. 11
    6
      src/java/org/apache/fop/fonts/FontAdder.java
  82. 11
    9
      src/java/org/apache/fop/fonts/FontCache.java
  83. 21
    13
      src/java/org/apache/fop/fonts/FontCacheManager.java
  84. 92
    0
      src/java/org/apache/fop/fonts/FontCacheManagerFactory.java
  85. 50
    0
      src/java/org/apache/fop/fonts/FontConfig.java
  86. 39
    0
      src/java/org/apache/fop/fonts/FontConfigurator.java
  87. 5
    77
      src/java/org/apache/fop/fonts/FontDetector.java
  88. 119
    0
      src/java/org/apache/fop/fonts/FontDetectorFactory.java
  89. 0
    328
      src/java/org/apache/fop/fonts/FontInfoConfigurator.java
  90. 17
    95
      src/java/org/apache/fop/fonts/FontLoader.java
  91. 51
    100
      src/java/org/apache/fop/fonts/FontManager.java
  92. 48
    32
      src/java/org/apache/fop/fonts/FontManagerConfigurator.java
  93. 49
    63
      src/java/org/apache/fop/fonts/FontReader.java
  94. 10
    45
      src/java/org/apache/fop/fonts/FontSetup.java
  95. 3
    10
      src/java/org/apache/fop/fonts/FontTriplet.java
  96. 38
    76
      src/java/org/apache/fop/fonts/LazyFont.java
  97. 5
    3
      src/java/org/apache/fop/fonts/MultiByteFont.java
  98. 4
    3
      src/java/org/apache/fop/fonts/MutableFont.java
  99. 5
    3
      src/java/org/apache/fop/fonts/SingleByteFont.java
  100. 0
    0
      src/java/org/apache/fop/fonts/apps/TTFReader.java

+ 2
- 1
build.xml View File

@@ -562,6 +562,7 @@ list of possible build targets.
<include name="org/apache/fop/accessibility/StructureTreeElement.class"/>
<include name="org/apache/fop/apps/Fop.class"/>
<include name="org/apache/fop/apps/FOPException.class"/>
<include name="org/apache/fop/apps/io/**"/>
<include name="org/apache/fop/complexscripts/fonts/*.class"/>
<include name="org/apache/fop/fo/Constants.class"/>
<include name="org/apache/fop/fo/FOTreeBuilder.class"/>
@@ -584,7 +585,7 @@ list of possible build targets.
<exclude name="org/apache/fop/render/pdf/PDFRenderer.class"/>
<exclude name="org/apache/fop/render/pdf/PDFXMLHandler*"/>
<include name="org/apache/fop/render/intermediate/IFDocumentHandlerConfigurator.class"/>
<include name="org/apache/fop/render/**Configurator**"/>
<include name="org/apache/fop/render/**Config**"/>
<include name="org/apache/fop/util/AbstractPaintingState**"/>
<include name="org/apache/fop/pdf/**"/>
</patternset>

+ 5
- 8
examples/embedding/java/embedding/ExampleAWTViewer.java View File

@@ -23,19 +23,16 @@ package embedding;
import java.io.File;
import java.io.IOException;

//JAXP
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerException;
import javax.xml.transform.Source;
import javax.xml.transform.Result;
import javax.xml.transform.stream.StreamSource;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.sax.SAXResult;
import javax.xml.transform.stream.StreamSource;

//Avalon
import org.apache.avalon.framework.ExceptionUtil;

//FOP
import org.apache.fop.apps.FOPException;
import org.apache.fop.apps.Fop;
import org.apache.fop.apps.FopFactory;
@@ -47,7 +44,7 @@ import org.apache.fop.apps.MimeConstants;
public class ExampleAWTViewer {

// configure fopFactory as desired
private FopFactory fopFactory = FopFactory.newInstance();
private final FopFactory fopFactory = FopFactory.newInstance(new File(".").toURI());

/**
* Display an FO file in the AWT Preview.

+ 5
- 8
examples/embedding/java/embedding/ExampleDOM2PDF.java View File

@@ -22,25 +22,22 @@ package embedding;
// Java
import java.io.File;
import java.io.OutputStream;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
//JAXP
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.Source;
import javax.xml.transform.Result;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.sax.SAXResult;

// DOM
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.Text;

// FOP
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.apps.Fop;
import org.apache.fop.apps.FopFactory;
@@ -54,7 +51,7 @@ import org.apache.fop.apps.MimeConstants;
public class ExampleDOM2PDF {

// configure fopFactory as desired
private FopFactory fopFactory = FopFactory.newInstance();
private final FopFactory fopFactory = FopFactory.newInstance(new File(".").toURI());

/** xsl-fo namespace URI */
protected static String foNS = "http://www.w3.org/1999/XSL/Format";

+ 1
- 1
examples/embedding/java/embedding/ExampleFO2JPSPrint.java View File

@@ -53,7 +53,7 @@ import org.apache.fop.render.print.PageableRenderer;
public class ExampleFO2JPSPrint {

// configure fopFactory as desired
private FopFactory fopFactory = FopFactory.newInstance();
private final FopFactory fopFactory = FopFactory.newInstance(new File(".").toURI());

private DocPrintJob createDocPrintJob() {
PrintService[] services = PrintServiceLookup.lookupPrintServices(

+ 1
- 1
examples/embedding/java/embedding/ExampleFO2OldStylePrint.java View File

@@ -43,7 +43,7 @@ import org.apache.fop.apps.MimeConstants;
public class ExampleFO2OldStylePrint {

// configure fopFactory as desired
private FopFactory fopFactory = FopFactory.newInstance();
private final FopFactory fopFactory = FopFactory.newInstance(new File(".").toURI());

/**
* Prints an FO file using an old-style PrinterJob.

+ 5
- 8
examples/embedding/java/embedding/ExampleFO2PDF.java View File

@@ -26,19 +26,16 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

//JAXP
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.Source;
import javax.xml.transform.Result;
import javax.xml.transform.stream.StreamSource;
import javax.xml.transform.sax.SAXResult;
import javax.xml.transform.stream.StreamSource;


// FOP
import org.apache.fop.apps.FOPException;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.apps.Fop;
import org.apache.fop.apps.FOPException;
import org.apache.fop.apps.FopFactory;
import org.apache.fop.apps.FormattingResults;
import org.apache.fop.apps.MimeConstants;
@@ -50,7 +47,7 @@ import org.apache.fop.apps.PageSequenceResults;
public class ExampleFO2PDF {

// configure fopFactory as desired
private FopFactory fopFactory = FopFactory.newInstance();
private final FopFactory fopFactory = FopFactory.newInstance(new File(".").toURI());

/**
* Converts an FO file to a PDF file using FOP

+ 4
- 7
examples/embedding/java/embedding/ExampleFO2PDFUsingSAXParser.java View File

@@ -26,17 +26,14 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

//JAXP
import javax.xml.parsers.SAXParserFactory;
import javax.xml.parsers.FactoryConfigurationError;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

//SAX
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

// FOP
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.apps.Fop;
import org.apache.fop.apps.FopFactory;
@@ -49,7 +46,7 @@ import org.apache.fop.apps.MimeConstants;
public class ExampleFO2PDFUsingSAXParser {

// configure fopFactory as desired
private FopFactory fopFactory = FopFactory.newInstance();
private final FopFactory fopFactory = FopFactory.newInstance(new File(".").toURI());

/**
* Converts an FO file to a PDF file using FOP

+ 5
- 7
examples/embedding/java/embedding/ExampleFO2RTF.java View File

@@ -26,18 +26,16 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

//JAXP
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.Source;
import javax.xml.transform.Result;
import javax.xml.transform.stream.StreamSource;
import javax.xml.transform.sax.SAXResult;
import javax.xml.transform.stream.StreamSource;

// FOP
import org.apache.fop.apps.FOPException;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.apps.Fop;
import org.apache.fop.apps.FOPException;
import org.apache.fop.apps.FopFactory;
import org.apache.fop.apps.MimeConstants;

@@ -50,7 +48,7 @@ import org.apache.fop.apps.MimeConstants;
public class ExampleFO2RTF {

// configure fopFactory as desired
private FopFactory fopFactory = FopFactory.newInstance();
private final FopFactory fopFactory = FopFactory.newInstance(new File(".").toURI());

/**
* Converts an FO file to a RTF file using FOP

+ 7
- 9
examples/embedding/java/embedding/ExampleObj2PDF.java View File

@@ -21,22 +21,20 @@ package embedding;

// Java
import java.io.File;
import java.io.OutputStream;
import java.io.IOException;
import java.io.OutputStream;

// JAXP
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerException;
import javax.xml.transform.Source;
import javax.xml.transform.Result;
import javax.xml.transform.stream.StreamSource;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.sax.SAXResult;
import javax.xml.transform.stream.StreamSource;

// FOP
import org.apache.fop.apps.FOPException;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.apps.Fop;
import org.apache.fop.apps.FOPException;
import org.apache.fop.apps.FopFactory;
import org.apache.fop.apps.MimeConstants;

@@ -49,7 +47,7 @@ import embedding.model.ProjectTeam;
public class ExampleObj2PDF {

// configure fopFactory as desired
private FopFactory fopFactory = FopFactory.newInstance();
private final FopFactory fopFactory = FopFactory.newInstance(new File(".").toURI());

/**
* Converts a ProjectTeam object to a PDF file.

+ 4
- 6
examples/embedding/java/embedding/ExampleXML2PDF.java View File

@@ -23,15 +23,13 @@ package embedding;
import java.io.File;
import java.io.OutputStream;

//JAXP
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.Source;
import javax.xml.transform.Result;
import javax.xml.transform.stream.StreamSource;
import javax.xml.transform.sax.SAXResult;
import javax.xml.transform.stream.StreamSource;

//FOP
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.apps.Fop;
import org.apache.fop.apps.FopFactory;
@@ -69,7 +67,7 @@ public class ExampleXML2PDF {
System.out.println("Transforming...");

// configure fopFactory as desired
FopFactory fopFactory = FopFactory.newInstance();
final FopFactory fopFactory = FopFactory.newInstance(new File(".").toURI());

FOUserAgent foUserAgent = fopFactory.newFOUserAgent();
// configure foUserAgent as desired

+ 6
- 8
examples/embedding/java/embedding/MultipleFO2PDF.java View File

@@ -27,20 +27,19 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

//JAXP
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.Source;
import javax.xml.transform.Result;
import javax.xml.transform.stream.StreamSource;
import javax.xml.transform.sax.SAXResult;
import javax.xml.transform.stream.StreamSource;

// FOP
import org.apache.commons.io.IOUtils;

import org.apache.fop.apps.FOPException;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.apps.Fop;
import org.apache.fop.apps.FOPException;
import org.apache.fop.apps.FopFactory;
import org.apache.fop.apps.FormattingResults;
import org.apache.fop.apps.MimeConstants;
@@ -55,8 +54,6 @@ import org.apache.fop.apps.PageSequenceResults;
public class MultipleFO2PDF {

// configure fopFactory as desired
private FopFactory fopFactory = FopFactory.newInstance();

// JAXP TransformerFactory can be reused, too
private TransformerFactory factory = TransformerFactory.newInstance();

@@ -71,6 +68,7 @@ public class MultipleFO2PDF {
*/
public FormattingResults convertFO2PDF(File fo, File pdf)
throws TransformerException, IOException, FOPException {
FopFactory fopFactory = FopFactory.newInstance(new File(".").toURI());

OutputStream out = null;
Fop fop;

+ 1
- 1
examples/embedding/java/embedding/atxml/ExampleConcat.java View File

@@ -57,7 +57,7 @@ import embedding.model.ProjectTeam;
public class ExampleConcat {

// configure fopFactory as desired
private FopFactory fopFactory = FopFactory.newInstance();
private final FopFactory fopFactory = FopFactory.newInstance(new File(".").toURI());

/**
* Creates a sample ProjectTeam instance for this demo.

+ 1
- 1
examples/embedding/java/embedding/atxml/ExampleStamp.java View File

@@ -50,7 +50,7 @@ import embedding.model.ProjectTeam;
public class ExampleStamp {

// configure fopFactory as desired
private FopFactory fopFactory = FopFactory.newInstance();
private final FopFactory fopFactory = FopFactory.newInstance(new File(".").toURI());

/**
* Stamps an area tree XML file and renders it to a PDF file.

+ 1
- 1
examples/embedding/java/embedding/events/ExampleEvents.java View File

@@ -56,7 +56,7 @@ import org.apache.fop.events.model.EventSeverity;
public class ExampleEvents {

// configure fopFactory as desired
private FopFactory fopFactory = FopFactory.newInstance();
private final FopFactory fopFactory = FopFactory.newInstance(new File(".").toURI());

/**
* Converts an FO file to a PDF file using FOP

+ 2
- 3
examples/embedding/java/embedding/intermediate/ExampleConcat.java View File

@@ -58,7 +58,7 @@ import embedding.model.ProjectTeam;
public class ExampleConcat {

// configure fopFactory as desired
private FopFactory fopFactory = FopFactory.newInstance();
private final FopFactory fopFactory = FopFactory.newInstance(new File(".").toURI());

/**
* Creates a sample ProjectTeam instance for this demo.
@@ -95,8 +95,7 @@ public class ExampleConcat {
userAgent, MimeConstants.MIME_PDF);

//Create the IFSerializer to write the intermediate format
IFSerializer ifSerializer = new IFSerializer();
ifSerializer.setContext(new IFContext(userAgent));
IFSerializer ifSerializer = new IFSerializer(new IFContext(userAgent));

//Tell the IFSerializer to mimic the target format
ifSerializer.mimicDocumentHandler(targetHandler);

+ 1
- 1
examples/embedding/java/embedding/intermediate/ExampleStamp.java View File

@@ -51,7 +51,7 @@ import embedding.model.ProjectTeam;
public class ExampleStamp {

// configure fopFactory as desired
private FopFactory fopFactory = FopFactory.newInstance();
private final FopFactory fopFactory = FopFactory.newInstance(new File(".").toURI());

/**
* Stamps an intermediate file and renders it to a PDF file.

+ 11
- 6
src/documentation/content/xdocs/team.xml View File

@@ -58,7 +58,7 @@
recent XML/XSL-FO convert, he has been nit-picking FAQs &amp; assorted web
pages since his first webmaster position @brain.com in 1996. Most
important creation? He's got a couple of cool kids.</li>
<li id="jm"><link href="mailto:jeremias@apache.org">Jeremias Mrki</link> (JM)
<li id="jm"><link href="mailto:jeremias@apache.org">Jeremias Märki</link> (JM)
is an independent software engineer and consultant from Lucerne, Switzerland. He's also
the creator of <fork href="http://barcode4j.sourceforge.net">Barcode4J</fork>.
See his <fork href="http://www.jeremias-maerki.ch">home page</fork> for more information
@@ -67,20 +67,25 @@
<li id="sp"><link href="mailto:spepping@apache.org">Simon Pepping</link> (SP) came to FOP
from the TeX/LaTeX world. See his <fork href="http://www.leverkruid.eu">home
page</fork> for some of his private projects.</li>
<li id="jp"><link href="mailto:pietsch@apache.org">Jrg Pietschmann</link> (JP)</li>
<li id="jp"><link href="mailto:pietsch@apache.org">Jörg Pietschmann</link> (JP)</li>
<li id="ps"><link href="mailto:tcho@club-internet.fr">Pascal Sancho</link> (PS)
is an XML developper from Aix-en-Provence (France). He works on software solutions for
rendering various kind of documents on various supports, more specifically taking care
of PDF generation with FOP.</li>
is an XML developper from Aix-en-Provence (France). He works on software solutions for
rendering various kind of documents on various supports, more specifically taking care
of PDF generation with FOP.</li>
</ul>
</section>
<section id="contribute-active">
<title>Active Contributors</title>
<ul>
<li id="ag">Alexios Giotis holds a Ph.D. in the optimization of turbomachinery cascades
using evolutionary algorithms, neural networks and parallel processing. He is one of
the founding members of i-docs (software for enterprises) and he has been leading its
technical design &amp; implementation based on open source libraries since its inception.
He is relying on Apache FOP for generating high volumes of documents on major banks and
telecom operators since FOP's 1.0 release. He lives in Athens, Greece.</li>
<li id="gd">Georg Datterl is a software developer from Austria, currently working for
Geneon media solutions gmbh in Nuremberg, Germany. He needs FOP to wrestle gigabytes of
electronic data into thousands of printed pages.</li>
<li id="lm">Louis Masters</li>
<li id="lb">Luis Bernardo</li>
</ul>
</section>

+ 55
- 72
src/documentation/content/xdocs/trunk/embedding.xml View File

@@ -41,8 +41,10 @@
OutputStream, which OutputStream to use for the results of the rendering. You can
customize FOP's behaviour in a rendering run by supplying your own FOUserAgent
instance. The FOUserAgent can, for example, be used to set your own document handler
instance (details below). Finally, you retrieve a SAX DefaultHandler instance from
the Fop object and use that as the SAXResult of your transformation.
instance (details below). Because the FOUserAgent holds FOP-run-specific configuration
data, it should only be used for a single run and not over multiple FOP invocations.
Finally, you retrieve a SAX DefaultHandler instance from the Fop object and use that
as the SAXResult of your transformation.
</p>
</section>
<section id="API">
@@ -54,6 +56,7 @@
clearly defined, the list of classes below are the generally agreed public API:
<source><![CDATA[
org.apache.fop.apps.*
org.apache.fop.apps.io.*
org.apache.fop.fo.FOEventHandler
org.apache.fop.fo.ElementMappingRegistry
org.apache.fop.fonts.FontManager
@@ -89,7 +92,8 @@ import org.apache.fop.apps.MimeConstants;

// Step 1: Construct a FopFactory
// (reuse if you plan to render multiple documents!)
FopFactory fopFactory = FopFactory.newInstance();
// Supply FOP with the base URI from which to resolve other URIs from
FopFactory fopFactory = FopFactory.newInstance(new File(".").toURI());

// Step 2: Set up output stream.
// Note: Using BufferedOutputStream for performance reasons (helpful with FileOutputStreams).
@@ -123,8 +127,10 @@ try {
<ul>
<li>
<strong>Step 1:</strong> You create a new FopFactory instance. The FopFactory instance holds
references to configuration information and cached data. It's important to reuse this
instance if you plan to render multiple documents during a JVM's lifetime.
references to configuration information and cached data. It is important to reuse this
instance if you plan to render multiple documents during a JVM's lifetime. URIs used within
FOP runs (images in the FO, fonts in the fop conf etc...) will be resolved against the base
URI given here.
</li>
<li>
<strong>Step 2:</strong> You set up an OutputStream that the generated document
@@ -302,38 +308,33 @@ try {
<title>Configuring Apache FOP Programmatically</title>
<p>
Apache FOP provides two levels on which you can customize FOP's
behaviour: the FopFactory and the user agent.
behaviour: the FopFactoryBuilder and the user agent.
</p>
<section id="fop-factory">
<title>Customizing the FopFactory</title>
<p>
The FopFactory holds configuration data and references to objects which are reusable over
multiple rendering runs. It's important to instantiate it only once (except in special
environments) and reuse it every time to create new FOUserAgent and Fop instances.
multiple rendering runs. The FopFactoryBuilder allows users to set configuration and then
build the FopFactory so that the FopFactory doesn't change between runs. The FopFactory performs
some performance expensive operations (i.e. detecting system fonts), as such it only needs to be
built once and cane be reused every time to create new FOUserAgent and Fop instances.
</p>
<p>
You can set all sorts of things on the FopFactory:
The FopFactoryBuilder can be instantiated with three objects; the base URI, the ResourceResolver
and the EnvironmentProfile. The base URI and the ResourceResolver are used for resolving resource
URIs throughout the FOP invocation. The EnvironmentProfile will be discussed further below but, in
short, it gives users more control over FOPs system dependent services.
</p>
<p>
You can set all sorts of things on the FopFactoryBuilder:
</p>
<ul>
<li>
<p>
The <strong>font base URL</strong> to use when resolving relative URLs for fonts. Example:
</p>
<source>fopFactory.getFontManager().setFontBaseURL("file:///C:/Temp/fonts");</source>
</li>
<li>
<p>
The <strong>hyphenation base URL</strong> to use when resolving relative URLs for
hyphenation patterns. Example:
</p>
<source>fopFactory.setHyphenBaseURL("file:///C:/Temp/hyph");</source>
</li>
<li>
<p>
Disable <strong>strict validation</strong>. When disabled FOP is less strict about the rules
established by the XSL-FO specification. Example:
</p>
<source>fopFactory.setStrictValidation(false);</source>
<source>fopFactoryBuilder.setStrictFOValidation(false);</source>
</li>
<li>
<p>
@@ -342,14 +343,14 @@ try {
'false', which causes Apache FOP to behave exactly as described in the specification. To enable the
alternative behaviour, call:
</p>
<source>fopFactory.setBreakIndentInheritanceOnReferenceAreaBoundary(true);</source>
<source>fopFactoryBuilder.setBreakIndentInheritanceOnReferenceAreaBoundary(true);</source>
</li>
<li>
<p>
Set the <strong>source resolution</strong> for the document. This is used internally to determine the pixel
size for SVG images and bitmap images without resolution information. Default: 72 dpi. Example:
</p>
<source>fopFactory.setSourceResolution(96); // =96dpi (dots/pixels per Inch)</source>
<source>fopFactoryBuilder.setSourceResolution(96); // =96dpi (dots/pixels per Inch)</source>
</li>
<li>
<p>
@@ -357,22 +358,21 @@ try {
you can give the instance to the FOUserAgent. Normally, the FOP extensions can be automatically detected
(see the documentation on extension for more info). Example:
</p>
<source>fopFactory.addElementMapping(myElementMapping); // myElementMapping is a org.apache.fop.fo.ElementMapping</source>
</li>
<li>
<p>
Set a <strong>URIResolver</strong> for custom URI resolution. By supplying a JAXP URIResolver you can add
custom URI resolution functionality to FOP. For example, you can use
<a href="ext:xml.apache.org/commons/resolver">Apache XML Commons Resolver</a> to make use of XCatalogs. Example:
</p>
<source>fopFactory.setURIResolver(myResolver); // myResolver is a javax.xml.transform.URIResolver</source>
<note>
Both the FopFactory and the FOUserAgent have a method to set a URIResolver. The URIResolver on the FopFactory
is primarily used to resolve URIs on factory-level (hyphenation patterns, for example) and it is always used
if no other URIResolver (for example on the FOUserAgent) resolved the URI first.
</note>
<source>fopFactoryBuilder.addElementMapping(myElementMapping); // myElementMapping is a org.apache.fop.fo.ElementMapping</source>
</li>
</ul>
<p>
Once the settings on the FopFactoryBuilder had been set, you can create a FopFactory by invoking the build method:
</p>
<source>FopFactory factory = fopFactoryBuilder.build();</source>
</section>
<section id="environment-profile">
<title>Environment Profile</title>
<p>
The EnvironmentProfile can be used to define the limitations and restrictions of FOPs access to system resources. FOP, for example,
can auto-detect system fonts which users may want control over. This environment profile also holds the base URI and the
ResourceResolver discussed previously, since they are intrinsically bound to the environment inwhich FOP is invoked.
</p>
</section>
<section id="user-agent">
<title>Customizing the User Agent</title>
@@ -383,7 +383,7 @@ try {
to the factory method that will create a new Fop instance:
</p>
<source><![CDATA[
FopFactory fopFactory = FopFactory.newInstance(); // Reuse the FopFactory if possible!
FopFactory fopFactory = FopFactory.newInstance(...); // Reuse the FopFactory if possible!
// do the following for each new rendering run
FOUserAgent userAgent = fopFactory.newFOUserAgent();
// customize userAgent
@@ -392,12 +392,6 @@ try {
You can do all sorts of things on the user agent:
</p>
<ul>
<li>
<p>
The <strong>base URL</strong> to use when resolving relative URLs. Example:
</p>
<source>userAgent.setBaseURL("file:///C:/Temp/");</source>
</li>
<li>
<p>
Set the <strong>producer</strong> of the document. This is metadata information that can be used for certain output formats such as PDF. The default producer is "Apache FOP". Example:
@@ -459,19 +453,6 @@ try {
</p>
<source>userAgent.setFOEventHandlerOverride(myFOEventHandler); // myFOEventHandler is an org.apache.fop.fo.FOEventHandler</source>
</li>
<li>
<p>
Set a <strong>URIResolver</strong> for custom URI resolution. By supplying a JAXP URIResolver you can add
custom URI resolution functionality to FOP. For example, you can use
<a href="ext:xml.apache.org/commons/resolver">Apache XML Commons Resolver</a> to make use of XCatalogs. Example:
</p>
<source>userAgent.setURIResolver(myResolver); // myResolver is a javax.xml.transform.URIResolver</source>
<note>
Both the FopFactory and the FOUserAgent have a method to set a URIResolver. The URIResolver on the FOUserAgent is
used for resolving URIs which are document-related. If it's not set or cannot resolve a URI, the URIResolver
from the FopFactory is used.
</note>
</li>
</ul>
<note>
You should not reuse an FOUserAgent instance between FOP rendering runs although you can. Especially
@@ -486,18 +467,20 @@ try {
many values from an XML configuration file:
</p>
<source><![CDATA[
import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.DefaultConfigurationBuilder;

/*..*/

DefaultConfigurationBuilder cfgBuilder = new DefaultConfigurationBuilder();
Configuration cfg = cfgBuilder.buildFromFile(new File("C:/Temp/mycfg.xml"));
fopFactory.setUserConfig(cfg);

/* ..or.. */

fopFactory.setUserConfig(new File("C:/Temp/mycfg.xml"));]]></source>
FopFactory fopFactory = FopFactory.newInstance(new File("C:/Temp/mycfg.xml"));]]></source>
<p>
If however, you wish to override some of the configuration settings within the fop conf programmatically
then you can do so by using the FopConfParser. This allows the FopFactory to remain immutable and consistent
across multiple threads/invocations while still keeping the API flexible for the user.
</p>
<source><![CDATA[
// Create an instance of the FopFactoryBuilder populated with config from the fop conf
FopFactoryBuilder fopFactoryBuilder = new FopConfParser(new File("fop.xconf")).getFopFactoryBuilder();
// Override the configuration programmatically
fopFactoryBuilderuilder.setAccessibility(true);
...
// Build the FopFactory
FopFactory factory = fopFactoryBuilder.build();]]></source>
<p>
The layout of the configuration file is described on the <a href="configuration.html">Configuration page</a>.
</p>
@@ -749,4 +732,4 @@ mailing list.
</section>
</section>
</body>
</document>
</document>

+ 0
- 2
src/documentation/content/xdocs/trunk/output.xml View File

@@ -331,8 +331,6 @@ out = proc.getOutputStream();]]></source>
<title>Limitations</title>
<ul>
<li>Images and SVG may not be displayed correctly. SVG support is far from being complete. No image transparency is available.</li>
<li>Only Type 1 fonts are supported.</li>
<li>Multibyte characters are not supported.</li>
<li>PPD support is still missing.</li>
</ul>
</section>

+ 2
- 0
src/java/org/apache/fop/afp/AFPDataObjectInfo.java View File

@@ -57,6 +57,8 @@ public class AFPDataObjectInfo {
/** controls the mapping of the image data into the image area */
private byte mappingOption = MappingOptionTriplet.SCALE_TO_FILL;

public static final byte DEFAULT_MAPPING_OPTION = 0x00;

/**
* Default constructor
*/

+ 2
- 1
src/java/org/apache/fop/afp/AFPDitheredRectanglePainter.java View File

@@ -28,6 +28,7 @@ import java.io.IOException;
import org.apache.xmlgraphics.image.loader.ImageSize;
import org.apache.xmlgraphics.util.MimeConstants;

import org.apache.fop.afp.AFPResourceLevel.ResourceType;
import org.apache.fop.afp.modca.triplets.MappingOptionTriplet;
import org.apache.fop.util.bitmap.DitherUtil;

@@ -66,7 +67,7 @@ public class AFPDitheredRectanglePainter extends AbstractAFPPainter {
AFPImageObjectInfo imageObjectInfo = new AFPImageObjectInfo();
imageObjectInfo.setMimeType(MimeConstants.MIME_AFP_IOCA_FS10);
//imageObjectInfo.setCreatePageSegment(true);
imageObjectInfo.getResourceInfo().setLevel(new AFPResourceLevel(AFPResourceLevel.INLINE));
imageObjectInfo.getResourceInfo().setLevel(new AFPResourceLevel(ResourceType.INLINE));
imageObjectInfo.getResourceInfo().setImageDimension(ditherSize);
imageObjectInfo.setBitsPerPixel(1);
imageObjectInfo.setColor(false);

+ 3
- 2
src/java/org/apache/fop/afp/AFPGraphics2D.java View File

@@ -26,6 +26,7 @@ import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.Image;
import java.awt.Paint;
@@ -52,13 +53,13 @@ import org.apache.xmlgraphics.image.loader.ImageSize;
import org.apache.xmlgraphics.image.loader.impl.ImageRendered;
import org.apache.xmlgraphics.java2d.AbstractGraphics2D;
import org.apache.xmlgraphics.java2d.GraphicContext;
import org.apache.xmlgraphics.java2d.GraphicsConfigurationWithTransparency;
import org.apache.xmlgraphics.java2d.StrokingTextHandler;
import org.apache.xmlgraphics.java2d.TextHandler;
import org.apache.xmlgraphics.util.UnitConv;

import org.apache.fop.afp.goca.GraphicsSetLineType;
import org.apache.fop.afp.modca.GraphicsObject;
import org.apache.fop.afp.svg.AFPGraphicsConfiguration;
import org.apache.fop.afp.util.CubicBezierApproximator;
import org.apache.fop.fonts.FontInfo;
import org.apache.fop.render.afp.AFPImageHandlerRenderedImage;
@@ -111,7 +112,7 @@ public class AFPGraphics2D extends AbstractGraphics2D implements NativeImageHand
private AFPPaintingState paintingState = null;

/** AFP graphics configuration */
private final AFPGraphicsConfiguration graphicsConfig = new AFPGraphicsConfiguration();
private final GraphicsConfigurationWithTransparency graphicsConfig = new GraphicsConfigurationWithTransparency();

/** The AFP FontInfo */
private FontInfo fontInfo;

+ 2
- 1
src/java/org/apache/fop/afp/AFPResourceInfo.java View File

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

import java.awt.Dimension;

import org.apache.fop.afp.AFPResourceLevel.ResourceType;

/**
* The level at which a resource is to reside in the AFP output
@@ -29,7 +30,7 @@ public class AFPResourceInfo {

/** the general default resource level */
public static final AFPResourceLevel DEFAULT_LEVEL
= new AFPResourceLevel(AFPResourceLevel.PRINT_FILE);
= new AFPResourceLevel(ResourceType.PRINT_FILE);

/** the URI of this resource */
private String uri = null;

+ 67
- 77
src/java/org/apache/fop/afp/AFPResourceLevel.java View File

@@ -19,46 +19,55 @@

package org.apache.fop.afp;

import java.net.URI;

import static org.apache.fop.afp.AFPResourceLevel.ResourceType.DOCUMENT;
import static org.apache.fop.afp.AFPResourceLevel.ResourceType.EXTERNAL;
import static org.apache.fop.afp.AFPResourceLevel.ResourceType.INLINE;
import static org.apache.fop.afp.AFPResourceLevel.ResourceType.PAGE;
import static org.apache.fop.afp.AFPResourceLevel.ResourceType.PAGE_GROUP;
import static org.apache.fop.afp.AFPResourceLevel.ResourceType.PRINT_FILE;

/**
* A resource level
*/
public class AFPResourceLevel {
public enum ResourceType {
/** directly in page **/
INLINE("inline"),
/** page level **/
PAGE("page"),
/** page group level **/
PAGE_GROUP("page-group"),
/** document level **/
DOCUMENT("document"),
/** print file level **/
PRINT_FILE("print-file"),
/** external level **/
EXTERNAL("external");

private final String name;

private ResourceType(String name) {
this.name = name;
}

/** directly in page **/
public static final int INLINE = 0;

/** page level **/
public static final int PAGE = 1;

/** page group level **/
public static final int PAGE_GROUP = 2;

/** document level **/
public static final int DOCUMENT = 3;

/** print file level **/
public static final int PRINT_FILE = 4;

/** external level **/
public static final int EXTERNAL = 5;

private static final String NAME_INLINE = "inline";
private static final String NAME_PAGE = "page";
private static final String NAME_PAGE_GROUP = "page-group";
private static final String NAME_DOCUMENT = "document";
private static final String NAME_PRINT_FILE = "print-file";
private static final String NAME_EXTERNAL = "external";

private static final String[] NAMES = new String[] {
NAME_INLINE, NAME_PAGE, NAME_PAGE_GROUP, NAME_DOCUMENT, NAME_PRINT_FILE, NAME_EXTERNAL
};

public static ResourceType getValueOf(String levelString) {
for (ResourceType resType : ResourceType.values()) {
if (resType.name.equalsIgnoreCase(levelString)) {
return resType;
}
}
return null;
}

/** where the resource will reside in the AFP output */
private int level = PRINT_FILE; // default is print-file level (images)
public String getName() {
return name;
}
}

/** the external resource group file path */
private String extFilePath = null;
private URI extUri = null;
private final ResourceType resourceType;

/**
* Sets the resource placement level within the AFP output
@@ -67,36 +76,17 @@ public class AFPResourceLevel {
* @return true if the resource level was successfully set
*/
public static AFPResourceLevel valueOf(String levelString) {
if (levelString != null) {
levelString = levelString.toLowerCase();
AFPResourceLevel resourceLevel = null;
for (int i = 0; i < NAMES.length; i++) {
if (NAMES[i].equals(levelString)) {
resourceLevel = new AFPResourceLevel(i);
break;
}
}
return resourceLevel;
}
return null;
ResourceType resType = ResourceType.getValueOf(levelString);
return resType != null ? new AFPResourceLevel(resType) : null;
}

/**
* Main constructor
*
* @param level the resource level
*/
public AFPResourceLevel(int level) {
setLevel(level);
}

/**
* Sets the resource level
*
* @param level the resource level
* @param resourceType the resource type
*/
public void setLevel(int level) {
this.level = level;
public AFPResourceLevel(ResourceType resourceType) {
this.resourceType = resourceType;
}

/**
@@ -105,7 +95,7 @@ public class AFPResourceLevel {
* @return true if this is at page level
*/
public boolean isPage() {
return level == PAGE;
return resourceType == PAGE;
}

/**
@@ -114,7 +104,7 @@ public class AFPResourceLevel {
* @return true if this is at page group level
*/
public boolean isPageGroup() {
return level == PAGE_GROUP;
return resourceType == PAGE_GROUP;
}

/**
@@ -123,7 +113,7 @@ public class AFPResourceLevel {
* @return true if this is at document level
*/
public boolean isDocument() {
return level == DOCUMENT;
return resourceType == DOCUMENT;
}

/**
@@ -132,7 +122,7 @@ public class AFPResourceLevel {
* @return true if this is at external level
*/
public boolean isExternal() {
return level == EXTERNAL;
return resourceType == EXTERNAL;
}

/**
@@ -141,7 +131,7 @@ public class AFPResourceLevel {
* @return true if this is at print-file level
*/
public boolean isPrintFile() {
return level == PRINT_FILE;
return resourceType == PRINT_FILE;
}

/**
@@ -150,30 +140,30 @@ public class AFPResourceLevel {
* @return true if this resource level is inline
*/
public boolean isInline() {
return level == INLINE;
return resourceType == INLINE;
}

/**
* Returns the destination file path of the external resource group file
* Returns the URI of the external resource group.
*
* @return the destination file path of the external resource group file
* @return the destination URI of the external resource group
*/
public String getExternalFilePath() {
return this.extFilePath;
public URI getExternalURI() {
return this.extUri;
}

/**
* Sets the external destination of the resource
* Sets the URI of the external resource group.
*
* @param filePath the external resource group file
* @param uri the URI of the external resource group
*/
public void setExternalFilePath(String filePath) {
this.extFilePath = filePath;
public void setExternalUri(URI uri) {
this.extUri = uri;
}

/** {@inheritDoc} */
public String toString() {
return NAMES[level] + (isExternal() ? ", file=" + extFilePath : "");
return resourceType + (isExternal() ? ", uri=" + extUri : "");
}

/** {@inheritDoc} */
@@ -186,16 +176,16 @@ public class AFPResourceLevel {
}

AFPResourceLevel rl = (AFPResourceLevel)obj;
return (level == rl.level)
&& (extFilePath == rl.extFilePath
|| extFilePath != null && extFilePath.equals(rl.extFilePath));
return (resourceType == rl.resourceType)
&& (extUri == rl.extUri
|| extUri != null && extUri.equals(rl.extUri));
}

/** {@inheritDoc} */
public int hashCode() {
int hash = 7;
hash = 31 * hash + level;
hash = 31 * hash + (null == extFilePath ? 0 : extFilePath.hashCode());
hash = 31 * hash + resourceType.hashCode();
hash = 31 * hash + (null == extUri ? 0 : extUri.hashCode());
return hash;
}
}

+ 2
- 1
src/java/org/apache/fop/afp/AFPResourceLevelDefaults.java View File

@@ -22,6 +22,7 @@ package org.apache.fop.afp;
import java.util.Iterator;
import java.util.Map;

import org.apache.fop.afp.AFPResourceLevel.ResourceType;
import org.apache.fop.afp.modca.ResourceObject;

/**
@@ -58,7 +59,7 @@ public class AFPResourceLevelDefaults {
// level not explicitly set/changed so default to inline for GOCA graphic objects
// (due to a bug in the IBM AFP Workbench Viewer (2.04.01.07), hard copy works just fine)
setDefaultResourceLevel(ResourceObject.TYPE_GRAPHIC,
new AFPResourceLevel(AFPResourceLevel.INLINE));
new AFPResourceLevel(ResourceType.INLINE));
}

/**

+ 15
- 13
src/java/org/apache/fop/afp/AFPResourceManager.java View File

@@ -31,6 +31,7 @@ import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.apache.fop.afp.AFPResourceLevel.ResourceType;
import org.apache.fop.afp.fonts.AFPFont;
import org.apache.fop.afp.fonts.CharacterSet;
import org.apache.fop.afp.modca.AbstractNamedAFPObject;
@@ -41,8 +42,9 @@ import org.apache.fop.afp.modca.PageSegment;
import org.apache.fop.afp.modca.Registry;
import org.apache.fop.afp.modca.ResourceGroup;
import org.apache.fop.afp.modca.ResourceObject;
import org.apache.fop.afp.util.AFPResourceAccessor;
import org.apache.fop.afp.util.AFPResourceUtil;
import org.apache.fop.afp.util.ResourceAccessor;
import org.apache.fop.apps.io.InternalResourceResolver;

/**
* Manages the creation and storage of document resources
@@ -78,9 +80,9 @@ public class AFPResourceManager {
/**
* Main constructor
*/
public AFPResourceManager() {
public AFPResourceManager(InternalResourceResolver resourceResolver) {
this.factory = new Factory();
this.streamer = new AFPStreamer(factory);
this.streamer = new AFPStreamer(factory, resourceResolver);
this.dataObjectFactory = new AFPDataObjectFactory(factory);
}

@@ -118,13 +120,13 @@ public class AFPResourceManager {
}

/**
* Sets the default resource group file path
* Sets the default resource group URI.
*
* @param filePath the default resource group file path
* @param uri the default resource group URI
*/

public void setDefaultResourceGroupFilePath(String filePath) {
streamer.setDefaultResourceGroupFilePath(filePath);
public void setDefaultResourceGroupUri(URI uri) {
streamer.setDefaultResourceGroupUri(uri);
}

/**
@@ -257,7 +259,7 @@ public class AFPResourceManager {
if (afpFont.isEmbeddable()) {
//Embed fonts (char sets and code pages)
if (charSet.getResourceAccessor() != null) {
ResourceAccessor accessor = charSet.getResourceAccessor();
AFPResourceAccessor accessor = charSet.getResourceAccessor();
createIncludedResource(
charSet.getName(), accessor,
ResourceObject.TYPE_FONT_CHARACTER_SET);
@@ -284,7 +286,7 @@ public class AFPResourceManager {
* @param resourceObjectType the resource object type ({@link ResourceObject}.*)
* @throws IOException if an I/O error occurs while loading the resource
*/
public void createIncludedResource(String resourceName, ResourceAccessor accessor,
public void createIncludedResource(String resourceName, AFPResourceAccessor accessor,
byte resourceObjectType) throws IOException {
URI uri;
try {
@@ -305,9 +307,9 @@ public class AFPResourceManager {
* @param resourceObjectType the resource object type ({@link ResourceObject}.*)
* @throws IOException if an I/O error occurs while loading the resource
*/
public void createIncludedResource(String resourceName, URI uri, ResourceAccessor accessor,
public void createIncludedResource(String resourceName, URI uri, AFPResourceAccessor accessor,
byte resourceObjectType) throws IOException {
AFPResourceLevel resourceLevel = new AFPResourceLevel(AFPResourceLevel.PRINT_FILE);
AFPResourceLevel resourceLevel = new AFPResourceLevel(ResourceType.PRINT_FILE);

AFPResourceInfo resourceInfo = new AFPResourceInfo();
resourceInfo.setLevel(resourceLevel);
@@ -343,9 +345,9 @@ public class AFPResourceManager {
* @throws IOException if an I/O error occurs while loading the resource
*/
public void createIncludedResourceFromExternal(final String resourceName,
final URI uri, final ResourceAccessor accessor) throws IOException {
final URI uri, final AFPResourceAccessor accessor) throws IOException {

AFPResourceLevel resourceLevel = new AFPResourceLevel(AFPResourceLevel.PRINT_FILE);
AFPResourceLevel resourceLevel = new AFPResourceLevel(ResourceType.PRINT_FILE);

AFPResourceInfo resourceInfo = new AFPResourceInfo();
resourceInfo.setLevel(resourceLevel);

+ 41
- 71
src/java/org/apache/fop/afp/AFPStreamer.java View File

@@ -20,20 +20,22 @@
package org.apache.fop.afp;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.net.URI;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

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

import org.apache.fop.afp.modca.ResourceGroup;
import org.apache.fop.afp.modca.StreamedResourceGroup;
import org.apache.fop.apps.io.InternalResourceResolver;
import org.apache.fop.apps.io.TempResourceURIGenerator;

/**
* Manages the streaming of the AFP output
@@ -42,43 +44,45 @@ public class AFPStreamer implements Streamable {
/** Static logging instance */
private static final Log LOG = LogFactory.getLog(AFPStreamer.class);

private static final String AFPDATASTREAM_TEMP_FILE_PREFIX = "AFPDataStream_";

private static final int BUFFER_SIZE = 4096; // 4k writing buffer

private static final String DEFAULT_EXTERNAL_RESOURCE_FILENAME = "resources.afp";

private static final TempResourceURIGenerator TEMP_URI_GENERATOR
= new TempResourceURIGenerator("AFPDataStream_");

private final Factory factory;

private final InternalResourceResolver resourceResolver;

/** A mapping of external resource destinations to resource groups */
private final Map/*<String,AFPExternalResourceGroup>*/pathResourceGroupMap
= new java.util.HashMap/*<String,AFPExternalResourceGroup>*/();
private final Map<URI, ResourceGroup> pathResourceGroupMap = new HashMap<URI, ResourceGroup>();

private StreamedResourceGroup printFileResourceGroup;

/** Sets the default resource group file path */
private String defaultResourceGroupFilePath = DEFAULT_EXTERNAL_RESOURCE_FILENAME;
private URI defaultResourceGroupUri;

private File tempFile;
private final URI tempUri;

/** temporary document outputstream */
private OutputStream documentOutputStream;
private OutputStream tempOutputStream;

/** the final outputstream */
private OutputStream outputStream;

private RandomAccessFile documentFile;

private DataStream dataStream;

/**
* Main constructor
*
* @param factory a factory
* @param resourceResolver resource resolver
*/
public AFPStreamer(Factory factory) {
public AFPStreamer(Factory factory, InternalResourceResolver resourceResolver) {
this.factory = factory;
this.resourceResolver = resourceResolver;
this.tempUri = TEMP_URI_GENERATOR.generate();
defaultResourceGroupUri = URI.create(DEFAULT_EXTERNAL_RESOURCE_FILENAME);

}

/**
@@ -89,21 +93,18 @@ public class AFPStreamer implements Streamable {
* @throws IOException thrown if an I/O exception of some sort has occurred
*/
public DataStream createDataStream(AFPPaintingState paintingState) throws IOException {
this.tempFile = File.createTempFile(AFPDATASTREAM_TEMP_FILE_PREFIX, null);
this.documentFile = new RandomAccessFile(tempFile, "rw");
this.documentOutputStream = new BufferedOutputStream(
new FileOutputStream(documentFile.getFD()));
this.dataStream = factory.createDataStream(paintingState, documentOutputStream);
this.tempOutputStream = new BufferedOutputStream(resourceResolver.getOutputStream(tempUri));
this.dataStream = factory.createDataStream(paintingState, tempOutputStream);
return dataStream;
}

/**
* Sets the default resource group file path
* Sets the default resource group URI.
*
* @param filePath the default resource group file path
* @param uri the default resource group URI
*/
public void setDefaultResourceGroupFilePath(String filePath) {
this.defaultResourceGroupFilePath = filePath;
public void setDefaultResourceGroupUri(URI uri) {
this.defaultResourceGroupUri = uri;
}

/**
@@ -118,23 +119,23 @@ public class AFPStreamer implements Streamable {
return null;
}
if (level.isExternal()) {
String filePath = level.getExternalFilePath();
if (filePath == null) {
URI uri = level.getExternalURI();
if (uri == null) {
LOG.warn("No file path provided for external resource, using default.");
filePath = defaultResourceGroupFilePath;
uri = defaultResourceGroupUri;
}
resourceGroup = (ResourceGroup)pathResourceGroupMap.get(filePath);
resourceGroup = pathResourceGroupMap.get(uri);
if (resourceGroup == null) {
OutputStream os = null;
try {
os = new BufferedOutputStream(new FileOutputStream(filePath));
} catch (FileNotFoundException fnfe) {
LOG.error("Failed to create/open external resource group file '"
+ filePath + "'");
os = new BufferedOutputStream(resourceResolver.getOutputStream(uri));
} catch (IOException ioe) {
LOG.error("Failed to create/open external resource group for uri '"
+ uri + "'");
} finally {
if (os != null) {
resourceGroup = factory.createStreamedResourceGroup(os);
pathResourceGroupMap.put(filePath, resourceGroup);
pathResourceGroupMap.put(uri, resourceGroup);
}
}
}
@@ -156,34 +157,20 @@ public class AFPStreamer implements Streamable {
*
* @throws IOException if an an I/O exception of some sort has occurred
*/
// write out any external resource groups
// write out any external resource groups
public void close() throws IOException {
Iterator it = pathResourceGroupMap.values().iterator();
while (it.hasNext()) {
StreamedResourceGroup resourceGroup = (StreamedResourceGroup)it.next();
resourceGroup.close();
}

// close any open print-file resource group
if (printFileResourceGroup != null) {
printFileResourceGroup.close();
}

// write out document
writeToStream(outputStream);

outputStream.close();


if (documentOutputStream != null) {
documentOutputStream.close();
}

if (documentFile != null) {
documentFile.close();
}
// delete temporary file
tempFile.delete();
}

/**
@@ -197,28 +184,11 @@ public class AFPStreamer implements Streamable {

/** {@inheritDoc} */
public void writeToStream(OutputStream os) throws IOException {
// long start = System.currentTimeMillis();
int len = (int)documentFile.length();
int numChunks = len / BUFFER_SIZE;
int remainingChunkSize = len % BUFFER_SIZE;
byte[] buffer;

documentFile.seek(0);
if (numChunks > 0) {
buffer = new byte[BUFFER_SIZE];
for (int i = 0; i < numChunks; i++) {
documentFile.read(buffer, 0, BUFFER_SIZE);
os.write(buffer, 0, BUFFER_SIZE);
}
} else {
buffer = new byte[remainingChunkSize];
}
if (remainingChunkSize > 0) {
documentFile.read(buffer, 0, remainingChunkSize);
os.write(buffer, 0, remainingChunkSize);
}
tempOutputStream.close();
InputStream tempInputStream = resourceResolver.getResource(tempUri);
IOUtils.copy(tempInputStream, os);
//TODO this should notify the stream provider that it is safe to delete the temp data
tempInputStream.close();
os.flush();
// long end = System.currentTimeMillis();
// log.debug("writing time " + (end - start) + "ms");
}
}

+ 2
- 3
src/java/org/apache/fop/afp/fonts/AFPBase12FontCollection.java View File

@@ -165,9 +165,8 @@ public class AFPBase12FontCollection implements FontCollection {
}

private RasterFont createReferencedRasterFont(String fontFamily) {
RasterFont font = new RasterFont(fontFamily);
font.setEmbeddable(false); //Font is assumed to be available on the target platform
return font;
boolean embeddable = false; //Font is assumed to be available on the target platform
return new RasterFont(fontFamily, embeddable);
}

}

+ 6
- 12
src/java/org/apache/fop/afp/fonts/AFPFont.java View File

@@ -35,16 +35,18 @@ import org.apache.fop.fonts.Typeface;
public abstract class AFPFont extends Typeface {

/** The font name */
protected String name;
protected final String name;

private boolean embeddable = true;
private final boolean embeddable;

/**
* Constructor for the base font requires the name.
* @param name the name of the font
* @param embeddable whether this font is to be embedded
*/
public AFPFont(String name) {
public AFPFont(String name, boolean embeddable) {
this.name = name;
this.embeddable = embeddable;
}

/** {@inheritDoc} */
@@ -89,7 +91,7 @@ public abstract class AFPFont extends Typeface {
* Returns the kerning map for the font.
* @return the kerning map
*/
public Map getKerningInfo() {
public Map<Integer, Map<Integer, Integer>> getKerningInfo() {
return null;
}

@@ -100,14 +102,6 @@ public abstract class AFPFont extends Typeface {
*/
public abstract CharacterSet getCharacterSet(int size);

/**
* Controls whether this font is embeddable or not.
* @param value true to enable embedding, false otherwise.
*/
public void setEmbeddable(boolean value) {
this.embeddable = value;
}

/**
* Indicates if this font may be embedded.
* @return True, if embedding is possible/permitted

+ 5
- 6
src/java/org/apache/fop/afp/fonts/AbstractOutlineFont.java View File

@@ -32,13 +32,12 @@ public abstract class AbstractOutlineFont extends AFPFont {
/**
* Constructor for an outline font.
*
* @param name
* the name of the font
* @param charSet
* the chracter set
* @param name the name of the font
* @param embeddable sets whether or not this font is to be embedded
* @param charSet the chracter set
*/
public AbstractOutlineFont(String name, CharacterSet charSet) {
super(name);
public AbstractOutlineFont(String name, boolean embeddable, CharacterSet charSet) {
super(name, embeddable);
this.charSet = charSet;
}


+ 5
- 5
src/java/org/apache/fop/afp/fonts/CharacterSet.java View File

@@ -30,7 +30,7 @@ import org.apache.commons.logging.LogFactory;
import org.apache.fop.afp.AFPConstants;
import org.apache.fop.afp.AFPEventProducer;
import org.apache.fop.afp.fonts.CharactersetEncoder.EncodedChars;
import org.apache.fop.afp.util.ResourceAccessor;
import org.apache.fop.afp.util.AFPResourceAccessor;
import org.apache.fop.afp.util.StringUtils;

/**
@@ -77,7 +77,7 @@ public class CharacterSet {
protected final String name;

/** The path to the installed fonts */
private final ResourceAccessor accessor;
private final AFPResourceAccessor accessor;

/** The current orientation (currently only 0 is supported by FOP) */
private final String currentOrientation = "0";
@@ -100,7 +100,7 @@ public class CharacterSet {
* @param eventProducer for handling AFP related events
*/
CharacterSet(String codePage, String encoding, CharacterSetType charsetType, String name,
ResourceAccessor accessor, AFPEventProducer eventProducer) {
AFPResourceAccessor accessor, AFPEventProducer eventProducer) {
if (name.length() > MAX_NAME_LEN) {
String msg = "Character set name '" + name + "' must be a maximum of "
+ MAX_NAME_LEN + " characters";
@@ -115,7 +115,7 @@ public class CharacterSet {
}
this.codePage = codePage;
this.encoding = encoding;
this.encoder = CharactersetEncoder.newInstance(encoding, charsetType);
this.encoder = charsetType.getEncoder(encoding);
this.accessor = accessor;

this.characterSetOrientations = new HashMap<String, CharacterSetOrientation>(4);
@@ -211,7 +211,7 @@ public class CharacterSet {
* Returns the resource accessor to load the font resources with.
* @return the resource accessor to load the font resources with
*/
public ResourceAccessor getResourceAccessor() {
public AFPResourceAccessor getResourceAccessor() {
return this.accessor;
}


+ 35
- 40
src/java/org/apache/fop/afp/fonts/CharacterSetBuilder.java View File

@@ -19,9 +19,9 @@

package org.apache.fop.afp.fonts;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
@@ -38,8 +38,9 @@ import org.apache.xmlgraphics.image.loader.util.SoftMapCache;

import org.apache.fop.afp.AFPConstants;
import org.apache.fop.afp.AFPEventProducer;
import org.apache.fop.afp.util.ResourceAccessor;
import org.apache.fop.afp.util.AFPResourceAccessor;
import org.apache.fop.afp.util.StructuredFieldReader;
import org.apache.fop.apps.io.InternalResourceResolver;
import org.apache.fop.fonts.Typeface;

/**
@@ -138,28 +139,24 @@ public abstract class CharacterSetBuilder {
* Returns an InputStream to a given file path and filename
*
* * @param accessor the resource accessor
* @param filename the file name
* @param uriStr the URI
* @param eventProducer for handling AFP related events
* @return an inputStream
*
* @throws IOException in the event that an I/O exception of some sort has occurred
*/
protected InputStream openInputStream(ResourceAccessor accessor, String filename,
private InputStream openInputStream(AFPResourceAccessor accessor, String uriStr,
AFPEventProducer eventProducer)
throws IOException {
URI uri;
try {
uri = new URI(filename.trim());
uri = InternalResourceResolver.cleanURI(uriStr.trim());
} catch (URISyntaxException e) {
throw new FileNotFoundException("Invalid filename: "
+ filename + " (" + e.getMessage() + ")");
throw new MalformedURLException("Invalid uri: " + uriStr + " (" + e.getMessage() + ")");
}

if (LOG.isDebugEnabled()) {
LOG.debug("Opening " + uri);
}
InputStream inputStream = accessor.createInputStream(uri);
return inputStream;
return accessor.createInputStream(uri);
}

/**
@@ -167,7 +164,7 @@ public abstract class CharacterSetBuilder {
*
* @param inputStream the inputstream to close
*/
protected void closeInputStream(InputStream inputStream) {
private void closeInputStream(InputStream inputStream) {
try {
if (inputStream != null) {
inputStream.close();
@@ -191,7 +188,7 @@ public abstract class CharacterSetBuilder {
* @throws IOException if an I/O error occurs
*/
public CharacterSet buildSBCS(String characterSetName, String codePageName, String encoding,
ResourceAccessor accessor, AFPEventProducer eventProducer) throws IOException {
AFPResourceAccessor accessor, AFPEventProducer eventProducer) throws IOException {
return processFont(characterSetName, codePageName, encoding, CharacterSetType.SINGLE_BYTE,
accessor, eventProducer);
}
@@ -211,7 +208,7 @@ public abstract class CharacterSetBuilder {
* @throws IOException if an I/O error occurs
*/
public CharacterSet buildDBCS(String characterSetName, String codePageName, String encoding,
CharacterSetType charsetType, ResourceAccessor accessor, AFPEventProducer eventProducer)
CharacterSetType charsetType, AFPResourceAccessor accessor, AFPEventProducer eventProducer)
throws IOException {
return processFont(characterSetName, codePageName, encoding, charsetType, accessor,
eventProducer);
@@ -236,7 +233,7 @@ public abstract class CharacterSetBuilder {
}

private CharacterSet processFont(String characterSetName, String codePageName, String encoding,
CharacterSetType charsetType, ResourceAccessor accessor, AFPEventProducer eventProducer)
CharacterSetType charsetType, AFPResourceAccessor accessor, AFPEventProducer eventProducer)
throws IOException {
// check for cached version of the characterset
String descriptor = characterSetName + "_" + encoding + "_" + codePageName;
@@ -329,7 +326,7 @@ public abstract class CharacterSetBuilder {
* @throws IOException if an I/O exception of some sort has occurred.
*/
protected Map<String, String> loadCodePage(String codePage, String encoding,
ResourceAccessor accessor, AFPEventProducer eventProducer) throws IOException {
AFPResourceAccessor accessor, AFPEventProducer eventProducer) throws IOException {

// Create the HashMap to store code page information
Map<String, String> codePages = new HashMap<String, String>();
@@ -337,7 +334,11 @@ public abstract class CharacterSetBuilder {
InputStream inputStream = null;
try {
inputStream = openInputStream(accessor, codePage.trim(), eventProducer);

} catch (IOException e) {
eventProducer.codePageNotFound(this, e);
throw e;
}
try {
StructuredFieldReader structuredFieldReader = new StructuredFieldReader(inputStream);
byte[] data = structuredFieldReader.getNext(CHARACTER_TABLE_SF);

@@ -367,8 +368,6 @@ public abstract class CharacterSetBuilder {
position++;
}
}
} catch (FileNotFoundException e) {
eventProducer.codePageNotFound(this, e);
} finally {
closeInputStream(inputStream);
}
@@ -383,9 +382,8 @@ public abstract class CharacterSetBuilder {
* @return a class representing the font descriptor
* @throws IOException if an I/O exception of some sort has occurred.
*/
protected static FontDescriptor processFontDescriptor(
StructuredFieldReader structuredFieldReader)
throws IOException {
private static FontDescriptor processFontDescriptor(
StructuredFieldReader structuredFieldReader) throws IOException {

byte[] fndData = structuredFieldReader.getNext(FONT_DESCRIPTOR_SF);
return new FontDescriptor(fndData);
@@ -399,8 +397,8 @@ public abstract class CharacterSetBuilder {
* @return the FontControl
* @throws IOException if an I/O exception of some sort has occurred.
*/
protected FontControl processFontControl(StructuredFieldReader structuredFieldReader)
throws IOException {
private FontControl processFontControl(StructuredFieldReader structuredFieldReader)
throws IOException {

byte[] fncData = structuredFieldReader.getNext(FONT_CONTROL_SF);

@@ -431,7 +429,7 @@ public abstract class CharacterSetBuilder {
* @return CharacterSetOrientation array
* @throws IOException if an I/O exception of some sort has occurred.
*/
protected CharacterSetOrientation[] processFontOrientation(
private CharacterSetOrientation[] processFontOrientation(
StructuredFieldReader structuredFieldReader) throws IOException {

byte[] data = structuredFieldReader.getNext(FONT_ORIENTATION_SF);
@@ -464,7 +462,6 @@ public abstract class CharacterSetBuilder {

}
}

return orientations.toArray(EMPTY_CSO_ARRAY);
}

@@ -480,7 +477,7 @@ public abstract class CharacterSetBuilder {
* font metric values
* @throws IOException if an I/O exception of some sort has occurred.
*/
protected void processFontPosition(StructuredFieldReader structuredFieldReader,
private void processFontPosition(StructuredFieldReader structuredFieldReader,
CharacterSetOrientation[] characterSetOrientations, double metricNormalizationFactor)
throws IOException {

@@ -537,7 +534,7 @@ public abstract class CharacterSetBuilder {
* font metric values
* @throws IOException if an I/O exception of some sort has occurred.
*/
protected void processFontIndex(StructuredFieldReader structuredFieldReader,
private void processFontIndex(StructuredFieldReader structuredFieldReader,
CharacterSetOrientation cso, Map<String, String> codepage,
double metricNormalizationFactor)
throws IOException {
@@ -709,22 +706,23 @@ public abstract class CharacterSetBuilder {
return INSTANCE;
}

@Override
protected Map<String, String> loadCodePage(String codePage, String encoding,
ResourceAccessor accessor, AFPEventProducer eventProducer) throws IOException {

AFPResourceAccessor accessor, AFPEventProducer eventProducer) throws IOException {
// Create the HashMap to store code page information
Map<String, String> codePages = new HashMap<String, String>();

InputStream inputStream = null;
try {
inputStream = openInputStream(accessor, codePage.trim(), eventProducer);

StructuredFieldReader structuredFieldReader
= new StructuredFieldReader(inputStream);
inputStream = super.openInputStream(accessor, codePage.trim(), eventProducer);
} catch (IOException e) {
eventProducer.codePageNotFound(this, e);
throw e;
}
try {
StructuredFieldReader structuredFieldReader = new StructuredFieldReader(inputStream);
byte[] data;
while ((data = structuredFieldReader.getNext(CHARACTER_TABLE_SF)) != null) {
int position = 0;

byte[] gcgiBytes = new byte[8];
byte[] charBytes = new byte[2];
// Read data, ignoring bytes 0 - 2
@@ -752,12 +750,9 @@ public abstract class CharacterSetBuilder {
}
}
}
} catch (FileNotFoundException e) {
eventProducer.codePageNotFound(this, e);
} finally {
closeInputStream(inputStream);
super.closeInputStream(inputStream);
}

return codePages;
}


+ 29
- 3
src/java/org/apache/fop/afp/fonts/CharacterSetType.java View File

@@ -19,13 +19,39 @@

package org.apache.fop.afp.fonts;

import org.apache.fop.afp.fonts.CharactersetEncoder.DefaultEncoder;
import org.apache.fop.afp.fonts.CharactersetEncoder.EbcdicDoubleByteLineDataEncoder;

/**
* An enumeration of AFP characterset types.
*/
public enum CharacterSetType {
/** Double byte character sets; these do NOT have the shift-in;shift-out operators */
DOUBLE_BYTE,
DOUBLE_BYTE {
@Override
CharactersetEncoder getEncoder(String encoding) {
return new DefaultEncoder(encoding, true);
}
},
/** Double byte character sets; these can have the shift-in;shift-out operators */
DOUBLE_BYTE_LINE_DATA,
SINGLE_BYTE;
DOUBLE_BYTE_LINE_DATA {
@Override
CharactersetEncoder getEncoder(String encoding) {
return new EbcdicDoubleByteLineDataEncoder(encoding);
}
},
SINGLE_BYTE {
@Override
CharactersetEncoder getEncoder(String encoding) {
return new DefaultEncoder(encoding, false);
}
};

/**
* Returns the character-set encoder
*
* @param encoding
* @return
*/
abstract CharactersetEncoder getEncoder(String encoding);
}

+ 5
- 23
src/java/org/apache/fop/afp/fonts/CharactersetEncoder.java View File

@@ -87,7 +87,7 @@ public abstract class CharactersetEncoder {
*/
public static EncodedChars encodeSBCS(CharSequence chars, String encoding)
throws CharacterCodingException {
CharactersetEncoder encoder = newInstance(encoding, CharacterSetType.SINGLE_BYTE);
CharactersetEncoder encoder = CharacterSetType.SINGLE_BYTE.getEncoder(encoding);
return encoder.encode(chars);
}

@@ -97,8 +97,8 @@ public abstract class CharactersetEncoder {
* sequence it will return its EBCDIC code-point, however, the "Shift In - Shift Out" operators
* are removed from the sequence of bytes. These are only used in Line Data.
*/
private static final class EbcdicDoubleByteLineDataEncoder extends CharactersetEncoder {
private EbcdicDoubleByteLineDataEncoder(String encoding) {
static final class EbcdicDoubleByteLineDataEncoder extends CharactersetEncoder {
EbcdicDoubleByteLineDataEncoder(String encoding) {
super(encoding);
}
@Override
@@ -115,10 +115,10 @@ public abstract class CharactersetEncoder {
* the primary format for most Latin character sets. This can also be used for Unicode double-
* byte character sets (DBCS).
*/
private static final class DefaultEncoder extends CharactersetEncoder {
static final class DefaultEncoder extends CharactersetEncoder {
private final boolean isDBCS;

private DefaultEncoder(String encoding, boolean isDBCS) {
DefaultEncoder(String encoding, boolean isDBCS) {
super(encoding);
this.isDBCS = isDBCS;
}
@@ -129,24 +129,6 @@ public abstract class CharactersetEncoder {
}
}

/**
* Returns an new instance of a {@link CharactersetEncoder}.
*
* @param encoding the encoding for the underlying character encoder
* @param isEbcdicDBCS whether or not this wraps a double-byte EBCDIC code page.
* @return the CharactersetEncoder
*/
static CharactersetEncoder newInstance(String encoding, CharacterSetType charsetType) {
switch (charsetType) {
case DOUBLE_BYTE_LINE_DATA:
return new EbcdicDoubleByteLineDataEncoder(encoding);
case DOUBLE_BYTE:
return new DefaultEncoder(encoding, true);
default:
return new DefaultEncoder(encoding, false);
}
}

/**
* A container for encoded character bytes
*/

+ 6
- 3
src/java/org/apache/fop/afp/fonts/DoubleByteFont.java View File

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

package org.apache.fop.afp.fonts;

import java.lang.Character.UnicodeBlock;
import java.util.HashSet;
import java.util.Set;

/**
@@ -33,7 +35,7 @@ public class DoubleByteFont extends AbstractOutlineFont {

//See also http://unicode.org/reports/tr11/ which we've not closely looked at, yet
//TODO the Unicode block listed here is probably not complete (ex. Hiragana, Katakana etc.)
private static final Set IDEOGRAPHIC = new java.util.HashSet();
private static final Set<UnicodeBlock> IDEOGRAPHIC = new HashSet<UnicodeBlock>();
static {
IDEOGRAPHIC.add(Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS);
//IDEOGRAPHIC.add(Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS_SUPPLEMENT);//Java 1.5
@@ -45,10 +47,11 @@ public class DoubleByteFont extends AbstractOutlineFont {
/**
* Constructor for an double-byte outline font.
* @param name the name of the font
* @param embeddable whether or not this font is embeddable
* @param charSet the character set
*/
public DoubleByteFont(String name, CharacterSet charSet) {
super(name, charSet);
public DoubleByteFont(String name, boolean embeddable, CharacterSet charSet) {
super(name, embeddable, charSet);
}

/** {@inheritDoc} */

+ 2
- 2
src/java/org/apache/fop/afp/fonts/FopCharacterSet.java View File

@@ -20,7 +20,7 @@
package org.apache.fop.afp.fonts;

import org.apache.fop.afp.AFPEventProducer;
import org.apache.fop.afp.util.ResourceAccessor;
import org.apache.fop.afp.util.AFPResourceAccessor;
import org.apache.fop.fonts.Typeface;

/**
@@ -42,7 +42,7 @@ public class FopCharacterSet extends CharacterSet {
*/
public FopCharacterSet(String codePage, String encoding, String name, Typeface charSet,
AFPEventProducer eventProducer) {
super(codePage, encoding, CharacterSetType.SINGLE_BYTE, name, (ResourceAccessor) null,
super(codePage, encoding, CharacterSetType.SINGLE_BYTE, name, (AFPResourceAccessor) null,
eventProducer);
this.charSet = charSet;
}

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

@@ -27,10 +27,11 @@ public class OutlineFont extends AbstractOutlineFont {
/**
* Construct outline font with specified name and character set.
* @param name font's name
* @param embeddable whether or not this font is embeddable
* @param charSet font's character set
*/
public OutlineFont(String name, CharacterSet charSet) {
super(name, charSet);
public OutlineFont(String name, boolean embeddable, CharacterSet charSet) {
super(name, embeddable, charSet);
}

}

+ 7
- 7
src/java/org/apache/fop/afp/fonts/RasterFont.java View File

@@ -52,8 +52,8 @@ public class RasterFont extends AFPFont {
* @param name
* the name of the font
*/
public RasterFont(String name) {
super(name);
public RasterFont(String name, boolean embeddable) {
super(name, embeddable);
}

/**
@@ -76,7 +76,7 @@ public class RasterFont extends AFPFont {
public CharacterSet getCharacterSet(int sizeInMpt) {

Integer requestedSize = Integer.valueOf(sizeInMpt);
CharacterSet csm = (CharacterSet) charSets.get(requestedSize);
CharacterSet csm = charSets.get(requestedSize);
double sizeInPt = sizeInMpt / 1000.0;

if (csm != null) {
@@ -85,7 +85,7 @@ public class RasterFont extends AFPFont {

if (substitutionCharSets != null) {
//Check first if a substitution has already been added
csm = (CharacterSet) substitutionCharSets.get(requestedSize);
csm = substitutionCharSets.get(requestedSize);
}

if (csm == null && !charSets.isEmpty()) {
@@ -95,9 +95,9 @@ public class RasterFont extends AFPFont {
SortedMap<Integer, CharacterSet> smallerSizes = charSets.headMap(requestedSize);
SortedMap<Integer, CharacterSet> largerSizes = charSets.tailMap(requestedSize);
int smallerSize = smallerSizes.isEmpty() ? 0
: ((Integer)smallerSizes.lastKey()).intValue();
: smallerSizes.lastKey().intValue();
int largerSize = largerSizes.isEmpty() ? Integer.MAX_VALUE
: ((Integer)largerSizes.firstKey()).intValue();
: largerSizes.firstKey().intValue();

Integer fontSize;
if (!smallerSizes.isEmpty()
@@ -106,7 +106,7 @@ public class RasterFont extends AFPFont {
} else {
fontSize = Integer.valueOf(largerSize);
}
csm = (CharacterSet) charSets.get(fontSize);
csm = charSets.get(fontSize);

if (csm != null) {
// Add the substitute mapping, so subsequent calls will

+ 3
- 4
src/java/org/apache/fop/afp/modca/IncludedResourceObject.java View File

@@ -26,16 +26,15 @@ import java.net.URI;

import org.apache.commons.io.IOUtils;

import org.apache.fop.afp.util.AFPResourceAccessor;
import org.apache.fop.afp.util.AFPResourceUtil;
import org.apache.fop.afp.util.ResourceAccessor;


/**
* Encapsulates an included resource object that is loaded from an external file.
*/
public class IncludedResourceObject extends AbstractNamedAFPObject {

private ResourceAccessor resourceAccessor;
private AFPResourceAccessor resourceAccessor;
private URI uri;

/**
@@ -45,7 +44,7 @@ public class IncludedResourceObject extends AbstractNamedAFPObject {
* @param uri the URI of the external file
*/
public IncludedResourceObject(String name,
ResourceAccessor resourceAccessor, URI uri) {
AFPResourceAccessor resourceAccessor, URI uri) {
super(name);
this.resourceAccessor = resourceAccessor;
this.uri = uri;

+ 0
- 152
src/java/org/apache/fop/afp/svg/AFPGraphicsConfiguration.java View File

@@ -1,152 +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.afp.svg;

import java.awt.GraphicsDevice;
import java.awt.Rectangle;
import java.awt.Transparency;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;

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

import org.apache.fop.svg.GraphicsConfiguration;

/**
* Our implementation of the class that returns information about
* roughly what we can handle and want to see (alpha for example).
*/
public class AFPGraphicsConfiguration extends GraphicsConfiguration {
// We use this to get a good colormodel..
private static final BufferedImage BI_WITH_ALPHA
= new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
// We use this to get a good colormodel..
private static final BufferedImage BI_WITHOUT_ALPHA
= new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB);

/**
* Construct a buffered image with an alpha channel, unless
* transparencty is OPAQUE (no alpha at all).
*
* @param width the width of the image
* @param height the height of the image
* @param transparency the alpha value of the image
* @return the new buffered image
*/
public BufferedImage createCompatibleImage(int width, int height,
int transparency) {
if (transparency == Transparency.OPAQUE) {
return new BufferedImage(width, height,
BufferedImage.TYPE_INT_RGB);
} else {
return new BufferedImage(width, height,
BufferedImage.TYPE_INT_ARGB);
}
}

/**
* Construct a buffered image with an alpha channel.
*
* @param width the width of the image
* @param height the height of the image
* @return the new buffered image
*/
public BufferedImage createCompatibleImage(int width, int height) {
return new BufferedImage(width, height,
BufferedImage.TYPE_INT_ARGB);
}

/**
* TODO: This should return the page bounds in Pts,
* I couldn't figure out how to get this for the current
* page (this still works for now,
* but it should be fixed...).
*
* @return the bounds of the page
*/
public Rectangle getBounds() {
return null;
}

/**
* Return a good default color model for this 'device'.
* @return the colour model for the configuration
*/
public ColorModel getColorModel() {
return BI_WITH_ALPHA.getColorModel();
}

/**
* Return a good color model given <code>transparency</code>
*
* @param transparency the alpha value for the colour model
* @return the colour model for the configuration
*/
public ColorModel getColorModel(int transparency) {
if (transparency == Transparency.OPAQUE) {
return BI_WITHOUT_ALPHA.getColorModel();
} else {
return BI_WITH_ALPHA.getColorModel();
}
}

private static final Log LOG
= LogFactory.getLog(AFPGraphicsConfiguration.class);

private AffineTransform defaultTransform = null;
private AffineTransform normalizingTransform = null;
private final GraphicsDevice graphicsDevice = new AFPGraphicsDevice(this);;

/**
* The default transform (1:1).
*
* @return the default transform for the configuration
*/
public AffineTransform getDefaultTransform() {
LOG.debug("getDefaultTransform()");
if (defaultTransform == null) {
defaultTransform = new AffineTransform();
}
return defaultTransform;
}

/**
* The normalizing transform (1:1) (since we currently
* render images at 72dpi, which we might want to change
* in the future).
*
* @return the normalizing transform for the configuration
*/
public AffineTransform getNormalizingTransform() {
LOG.debug("getNormalizingTransform()");
if (normalizingTransform == null) {
normalizingTransform = new AffineTransform(2, 0, 0, 2, 0, 0);
}
return normalizingTransform;
}

/** {@inheritDoc} */
public GraphicsDevice getDevice() {
LOG.debug("getDevice()");
return graphicsDevice;
}
}

+ 0
- 80
src/java/org/apache/fop/afp/svg/AFPGraphicsDevice.java View File

@@ -1,80 +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.afp.svg;

import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;

/**
* This implements the GraphicsDevice interface as appropriate for
* an AFPGraphics2D.
*/
public class AFPGraphicsDevice extends GraphicsDevice {

/**
* The Graphics Config that created us...
*/
protected GraphicsConfiguration gc;

/**
* Create a new AF{ graphics device.
*
* @param gc The graphics configuration we should reference
*/
public AFPGraphicsDevice(AFPGraphicsConfiguration gc) {
this.gc = gc;
}

/**
* Return an array of our one GraphicsConfig
*
* @return an array containing the one graphics configuration
*/
public GraphicsConfiguration[] getConfigurations() {
return new GraphicsConfiguration[] {gc};
}

/**
* Return out sole GraphicsConfig.
*
* @return the graphics configuration that created this object
*/
public GraphicsConfiguration getDefaultConfiguration() {
return this.gc;
}

/**
* Generate an IdString..
*
* @return the ID string for this device, uses toString
*/
public String getIDstring() {
return toString();
}

/**
* Let the caller know that we are "a printer"
*
* @return the type which is always printer
*/
public int getType() {
return GraphicsDevice.TYPE_PRINTER;
}
}

+ 82
- 0
src/java/org/apache/fop/afp/util/AFPResourceAccessor.java View File

@@ -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.afp.util;

import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;

import org.apache.fop.apps.io.InternalResourceResolver;

/**
* Defines an interface through which external resource objects can be accessed.
*/
public final class AFPResourceAccessor {

private final InternalResourceResolver resourceResolver;
private final String baseURI;

/**
* Constructor for resource to be accessed via the {@link org.apache.fop.apps.FOUserAgent}. This
* contructor takes a base URI for resolving font resource URIs. So, if fonts need to be
* accessed, you can set the {@link org.apache.fop.fonts.FontManager}'s base URI instead of the
* one on the {@link org.apache.fop.apps.FopFactory}.
*
* @param resourceResolver the resolver of resources
* @param baseURI the custom base URI to resolve relative URIs against (may be null)
*/
public AFPResourceAccessor(InternalResourceResolver resourceResolver, String baseURI) {
this.resourceResolver = resourceResolver;
this.baseURI = baseURI;
}

/**
* Constructor for resource to be accessed via the {@link org.apache.fop.apps.FOUserAgent}.
*
* @param resourceResolver the resolver of resources
*/
public AFPResourceAccessor(InternalResourceResolver resourceResolver) {
this(resourceResolver, null);
}

private URI getResourceURI(URI uri) {
if (baseURI == null) {
return uri;
}
try {
URI baseURI = InternalResourceResolver.getBaseURI(this.baseURI);
return baseURI.resolve(uri);
} catch (URISyntaxException use) {
return uri;
}
}

/**
* Creates an {@link InputStream} given a URI.
*
* @param uri the URI of the InputStream
* @return an InputStream
* @throws IOException if an I/O error occurs while creating the InputStream.
*/
public InputStream createInputStream(URI uri) throws IOException {
return resourceResolver.getResource(getResourceURI(uri));
}
}

+ 43
- 10
src/java/org/apache/fop/afp/util/AFPResourceUtil.java View File

@@ -56,8 +56,11 @@ import org.apache.fop.afp.parser.UnparsedStructuredField;
*/
public final class AFPResourceUtil {

private static final byte TYPE_CODE_BEGIN = (byte)(0xA8 & 0xFF);
private static final byte TYPE_CODE_END = (byte)(0xA9 & 0xFF);
private static final byte TYPE_CODE_BEGIN = (byte) (0xA8 & 0xFF);

private static final byte TYPE_CODE_END = (byte) (0xA9 & 0xFF);

private static final byte END_FIELD_ANY_NAME = (byte) (0xFF & 0xFF);

private static final Log LOG = LogFactory.getLog(AFPResourceUtil.class);

@@ -186,12 +189,9 @@ public final class AFPResourceUtil {
}
}

private static void copyNamedStructuredFields(final String name,
UnparsedStructuredField fieldBegin, MODCAParser parser,
OutputStream out) throws IOException {

private static void copyNamedStructuredFields(final String name, UnparsedStructuredField fieldBegin,
MODCAParser parser, OutputStream out) throws IOException {
UnparsedStructuredField field = fieldBegin;

while (true) {
if (field == null) {
throw new IOException("Ending structured field not found for resource " + name);
@@ -199,12 +199,45 @@ public final class AFPResourceUtil {
out.write(MODCAParser.CARRIAGE_CONTROL_CHAR);
field.writeTo(out);

if (field.getSfTypeCode() == TYPE_CODE_END
&& fieldBegin.getSfCategoryCode() == field.getSfCategoryCode()
&& name.equals(getResourceName(field))) {
if (isEndOfStructuredField(field, fieldBegin, name)) {
break;
}
field = parser.readNextStructuredField();
}
}

private static boolean isEndOfStructuredField(UnparsedStructuredField field,
UnparsedStructuredField fieldBegin, String name) throws UnsupportedEncodingException {
return fieldMatchesEndTagType(field)
&& fieldMatchesBeginCategoryCode(field, fieldBegin)
&& fieldHasValidName(field, name);
}

private static boolean fieldMatchesEndTagType(UnparsedStructuredField field) {
return field.getSfTypeCode() == TYPE_CODE_END;
}

private static boolean fieldMatchesBeginCategoryCode(UnparsedStructuredField field,
UnparsedStructuredField fieldBegin) {
return fieldBegin.getSfCategoryCode() == field.getSfCategoryCode();
}

/**
* The AFP specification states that it is valid for the end structured field to have:
* - No tag name specified, which will cause it to match any existing tag type match.
* - The name has FFFF as its first two bytes
* - The given name matches the previous structured field name
*/
private static boolean fieldHasValidName(UnparsedStructuredField field, String name)
throws UnsupportedEncodingException {
if (field.getData().length > 0) {
if (field.getData()[0] == field.getData()[1]
&& field.getData()[0] == END_FIELD_ANY_NAME) {
return true;
} else {
return name.equals(getResourceName(field));
}
}
return true;
}
}

+ 0
- 87
src/java/org/apache/fop/afp/util/DefaultFOPResourceAccessor.java View File

@@ -1,87 +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.afp.util;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URL;

import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;

import org.apache.commons.io.IOUtils;

import org.apache.fop.apps.FOUserAgent;

/**
* Default implementation of the {@link ResourceAccessor} interface for use inside FOP.
*/
public class DefaultFOPResourceAccessor extends SimpleResourceAccessor {

private FOUserAgent userAgent;
private String categoryBaseURI;

/**
* Constructor for resource to be accessed via the {@link FOUserAgent}. This contructor
* can take two base URIs: the category base URI is the one to use when differentiating between
* normal resources (ex. images) and font resources. So, if fonts need to be accessed, you can
* set the {@link org.apache.fop.fonts.FontManager}'s base URI instead of the one on the
* {@link org.apache.fop.apps.FopFactory}.
* @param userAgent the FO user agent
* @param categoryBaseURI the category base URI (may be null)
* @param baseURI the custom base URI to resolve relative URIs against (may be null)
*/
public DefaultFOPResourceAccessor(FOUserAgent userAgent, String categoryBaseURI, URI baseURI) {
super(baseURI);
this.userAgent = userAgent;
this.categoryBaseURI = categoryBaseURI;
}

/** {@inheritDoc} */
public InputStream createInputStream(URI uri) throws IOException {
//Step 1: resolve against local base URI --> URI
URI resolved = resolveAgainstBase(uri);

//Step 2: resolve against the user agent --> stream
String base = (this.categoryBaseURI != null
? this.categoryBaseURI
: this.userAgent.getBaseURL());
Source src = userAgent.resolveURI(resolved.toASCIIString(), base);

if (src == null) {
throw new FileNotFoundException("Resource not found: " + uri.toASCIIString());
} else if (src instanceof StreamSource) {
StreamSource ss = (StreamSource)src;
InputStream in = ss.getInputStream();
if (in != null) {
return in;
}
if (ss.getReader() != null) {
//Don't support reader, retry using system ID below
IOUtils.closeQuietly(ss.getReader());
}
}
URL url = new URL(src.getSystemId());
return url.openStream();
}

}

+ 0
- 76
src/java/org/apache/fop/afp/util/SimpleResourceAccessor.java View File

@@ -1,76 +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.afp.util;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URL;

/**
* Simple implementation of the {@link ResourceAccessor} interface for access relative to a
* base URI.
*/
public class SimpleResourceAccessor implements ResourceAccessor {

private URI baseURI;

/**
* Creates a new simple resource accessor.
* @param baseURI the base URI to resolve relative URIs against (may be null)
*/
public SimpleResourceAccessor(URI baseURI) {
this.baseURI = baseURI;
}

/**
* Creates a new simple resource accessor.
* @param baseDir the base directory to resolve relative filenames against (may be null)
*/
public SimpleResourceAccessor(File baseDir) {
this(baseDir != null ? baseDir.toURI() : null);
}

/**
* Returns the base URI.
* @return the base URI (or null if no base URI was set)
*/
public URI getBaseURI() {
return this.baseURI;
}

/**
* Resolve the given URI against the baseURI.
* @param uri the URI to resolve
* @return the resolved URI
*/
protected URI resolveAgainstBase(URI uri) {
return (getBaseURI() != null ? getBaseURI().resolve(uri) : uri);
}

/** {@inheritDoc} */
public InputStream createInputStream(URI uri) throws IOException {
URI resolved = resolveAgainstBase(uri);
URL url = resolved.toURL();
return url.openStream();
}

}

+ 55
- 0
src/java/org/apache/fop/apps/EnvironmentProfile.java View File

@@ -0,0 +1,55 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/* $Id$ */

package org.apache.fop.apps;

import java.net.URI;

import org.apache.fop.apps.io.ResourceResolver;
import org.apache.fop.fonts.FontManager;

/**
* The environment profile represents the system in which FOP is invoked. Some of FOPs services rely
* upon features within the system, as such, the client may want to restrict access to these
* services. This object allows clients to control those restrictions by implementing the interfaces
* that the environment profile holds.
*/
public interface EnvironmentProfile {

/**
* Returns resource resolver for this environment.
*
* @return the resource resolver
*/
ResourceResolver getResourceResolver();

/**
* Returns the font manager with restrictions/allowances set for this environment.
*
* @return the font manager
*/
FontManager getFontManager();

/**
* The default base URI used for resolving URIs.
*
* @return the default base URI
*/
URI getDefaultBaseURI();
}

+ 116
- 0
src/java/org/apache/fop/apps/EnvironmentalProfileFactory.java View File

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

import java.net.URI;

import org.apache.fop.apps.io.InternalResourceResolver;
import org.apache.fop.apps.io.ResourceResolver;
import org.apache.fop.apps.io.ResourceResolverFactory;
import org.apache.fop.fonts.FontCacheManager;
import org.apache.fop.fonts.FontCacheManagerFactory;
import org.apache.fop.fonts.FontDetector;
import org.apache.fop.fonts.FontDetectorFactory;
import org.apache.fop.fonts.FontManager;

/**
* Creates an {@link EnvironmentProfile} that sets the environment in which a FOP instance is run.
*/
public final class EnvironmentalProfileFactory {

private EnvironmentalProfileFactory() {
};

/**
* Creates the default environment that FOP is invoked in. This default profile has no
* operational restrictions for FOP.
*
* @param defaultBaseUri the default base URI for resolving resource URIs
* @param resourceResolver the resource resolver
* @return the environment profile
*/
public static EnvironmentProfile createDefault(URI defaultBaseUri,
ResourceResolver resourceResolver) {
return new Profile(defaultBaseUri, resourceResolver,
createFontManager(defaultBaseUri, resourceResolver,
FontDetectorFactory.createDefault(),
FontCacheManagerFactory.createDefault()));
}

/**
* Creates an IO-restricted environment for FOP by disabling some of the environment-specific
* functionality within FOP.
*
* @param defaultBaseUri the default base URI for resolving resource URIs
* @param resourceResolver the resource resolver
* @return the environment profile
*/
public static EnvironmentProfile createRestrictedIO(URI defaultBaseUri,
ResourceResolver resourceResolver) {
return new Profile(defaultBaseUri, resourceResolver,
createFontManager(defaultBaseUri, resourceResolver,
FontDetectorFactory.createDisabled(),
FontCacheManagerFactory.createDisabled()));
}

private static final class Profile implements EnvironmentProfile {

private final ResourceResolver resourceResolver;

private final FontManager fontManager;

private final URI defaultBaseURI;

private Profile(URI defaultBaseURI, ResourceResolver resourceResolver,
FontManager fontManager) {
if (defaultBaseURI == null) {
throw new IllegalArgumentException("Default base URI must not be null");
}
if (resourceResolver == null) {
throw new IllegalArgumentException("ResourceResolver must not be null");
}
if (fontManager == null) {
throw new IllegalArgumentException("The FontManager must not be null");
}
this.defaultBaseURI = defaultBaseURI;
this.resourceResolver = resourceResolver;
this.fontManager = fontManager;
}

public ResourceResolver getResourceResolver() {
return resourceResolver;
}

public FontManager getFontManager() {
return fontManager;
}

public URI getDefaultBaseURI() {
return defaultBaseURI;
}
}

private static FontManager createFontManager(URI defaultBaseUri, ResourceResolver resourceResolver,
FontDetector fontDetector, FontCacheManager fontCacheManager) {
InternalResourceResolver internalResolver = ResourceResolverFactory.createInternalResourceResolver(
defaultBaseUri, resourceResolver);
return new FontManager(internalResolver, fontDetector, fontCacheManager);
}
}

+ 0
- 376
src/java/org/apache/fop/apps/FOURIResolver.java View File

@@ -1,376 +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.apps;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLConnection;

import javax.xml.transform.Source;
import javax.xml.transform.TransformerException;
import javax.xml.transform.URIResolver;
import javax.xml.transform.stream.StreamSource;

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

import org.apache.xmlgraphics.util.io.Base64EncodeStream;
import org.apache.xmlgraphics.util.uri.CommonURIResolver;

/**
* Provides FOP specific URI resolution. This is the default URIResolver
* {@link FOUserAgent} will use unless overridden.
*
* @see javax.xml.transform.URIResolver
*/
public class FOURIResolver implements javax.xml.transform.URIResolver {

// log
private Log log = LogFactory.getLog("FOP");

/** Common URIResolver */
private CommonURIResolver commonURIResolver = new CommonURIResolver();

/** A user settable URI Resolver */
private URIResolver uriResolver = null;

/** true if exceptions are to be thrown if the URIs cannot be resolved. */
private boolean throwExceptions = false;

/**
* Checks if the given base URL is acceptable. It also normalizes the URL.
* @param base the base URL to check
* @return the normalized URL
* @throws MalformedURLException if there's a problem with a file URL
*/
public String checkBaseURL(String base) throws MalformedURLException {
// replace back slash with forward slash to ensure windows file:/// URLS are supported
base = base.replace('\\', '/');
if (!base.endsWith("/")) {
// The behavior described by RFC 3986 regarding resolution of relative
// references may be misleading for normal users:
// file://path/to/resources + myResource.res -> file://path/to/myResource.res
// file://path/to/resources/ + myResource.res -> file://path/to/resources/myResource.res
// We assume that even when the ending slash is missing, users have the second
// example in mind
base += "/";
}
File dir = new File(base);
if (dir.isDirectory()) {
return dir.toURI().toASCIIString();
} else {
URI baseURI;
try {
baseURI = new URI(base);
String scheme = baseURI.getScheme();
boolean directoryExists = true;
if ("file".equals(scheme)) {
dir = FileUtils.toFile(baseURI.toURL());
directoryExists = dir.isDirectory();
}
if (scheme == null || !directoryExists) {
String message = "base " + base + " is not a valid directory";
if (throwExceptions) {
throw new MalformedURLException(message);
}
log.error(message);
}
return baseURI.toASCIIString();
} catch (URISyntaxException e) {
//TODO not ideal: our base URLs are actually base URIs.
throw new MalformedURLException(e.getMessage());
}
}
}

/**
* Default constructor
*/
public FOURIResolver() {
this(false);
}

/**
* Additional constructor
*
* @param throwExceptions
* true if exceptions are to be thrown if the URIs cannot be
* resolved.
*/
public FOURIResolver(boolean throwExceptions) {
this.throwExceptions = throwExceptions;
}

/**
* Handles resolve exceptions appropriately.
*
* @param e
* the exception
* @param errorStr
* error string
* @param strict
* strict user config
*/
private void handleException(Exception e, String errorStr, boolean strict)
throws TransformerException {
if (strict) {
throw new TransformerException(errorStr, e);
}
log.error(e.getMessage());
}

/**
* Called by the processor through {@link FOUserAgent} when it encounters an
* uri in an external-graphic element. (see also
* {@link javax.xml.transform.URIResolver#resolve(String, String)} This
* resolver will allow URLs without a scheme, i.e. it assumes 'file:' as the
* default scheme. It also allows relative URLs with scheme, e.g.
* file:../../abc.jpg which is not strictly RFC compliant as long as the
* scheme is the same as the scheme of the base URL. If the base URL is null
* a 'file:' URL referencing the current directory is used as the base URL.
* If the method is successful it will return a Source of type
* {@link javax.xml.transform.stream.StreamSource} with its SystemID set to
* the resolved URL used to open the underlying InputStream.
*
* @param href
* An href attribute, which may be relative or absolute.
* @param base
* The base URI against which the first argument will be made
* absolute if the absolute URI is required.
* @return A {@link javax.xml.transform.Source} object, or null if the href
* cannot be resolved.
* @throws javax.xml.transform.TransformerException
* Never thrown by this implementation.
* @see javax.xml.transform.URIResolver#resolve(String, String)
*/
public Source resolve(String href, String base) throws TransformerException {
Source source = null;

// data URLs can be quite long so evaluate early and don't try to build a File
// (can lead to problems)
source = commonURIResolver.resolve(href, base);

// Custom uri resolution
if (source == null && uriResolver != null) {
source = uriResolver.resolve(href, base);
}

// Fallback to default resolution mechanism
if (source == null) {
URL absoluteURL = null;
int hashPos = href.indexOf('#');
String fileURL;
String fragment;
if (hashPos >= 0) {
fileURL = href.substring(0, hashPos);
fragment = href.substring(hashPos);
} else {
fileURL = href;
fragment = null;
}
File file = new File(fileURL);
if (file.canRead() && file.isFile()) {
try {
if (fragment != null) {
absoluteURL = new URL(file.toURI().toURL().toExternalForm() + fragment);
} else {
absoluteURL = file.toURI().toURL();
}
} catch (MalformedURLException mfue) {
handleException(mfue, "Could not convert filename '" + href
+ "' to URL", throwExceptions);
}
} else {
// no base provided
if (base == null) {
// We don't have a valid file protocol based URL
try {
absoluteURL = new URL(href);
} catch (MalformedURLException mue) {
try {
// the above failed, we give it another go in case
// the href contains only a path then file: is
// assumed
absoluteURL = new URL("file:" + href);
} catch (MalformedURLException mfue) {
handleException(mfue, "Error with URL '" + href
+ "'", throwExceptions);
}
}

// try and resolve from context of base
} else {
URL baseURL = null;
try {
baseURL = new URL(base);
} catch (MalformedURLException mfue) {
handleException(mfue, "Error with base URL '" + base
+ "'", throwExceptions);
}

/*
* This piece of code is based on the following statement in
* RFC2396 section 5.2:
*
* 3) If the scheme component is defined, indicating that
* the reference starts with a scheme name, then the
* reference is interpreted as an absolute URI and we are
* done. Otherwise, the reference URI's scheme is inherited
* from the base URI's scheme component.
*
* Due to a loophole in prior specifications [RFC1630], some
* parsers allow the scheme name to be present in a relative
* URI if it is the same as the base URI scheme.
* Unfortunately, this can conflict with the correct parsing
* of non-hierarchical URI. For backwards compatibility, an
* implementation may work around such references by
* removing the scheme if it matches that of the base URI
* and the scheme is known to always use the <hier_part>
* syntax.
*
* The URL class does not implement this work around, so we
* do.
*/
assert (baseURL != null);
String scheme = baseURL.getProtocol() + ":";
if (href.startsWith(scheme) && "file:".equals(scheme)) {
href = href.substring(scheme.length());
int colonPos = href.indexOf(':');
int slashPos = href.indexOf('/');
if (slashPos >= 0 && colonPos >= 0
&& colonPos < slashPos) {
href = "/" + href; // Absolute file URL doesn't
// have a leading slash
}
}
try {
absoluteURL = new URL(baseURL, href);
} catch (MalformedURLException mfue) {
handleException(mfue, "Error with URL; base '" + base
+ "' " + "href '" + href + "'", throwExceptions);
}
}
}

if (absoluteURL != null) {
String effURL = absoluteURL.toExternalForm();
try {
URLConnection connection = absoluteURL.openConnection();
connection.setAllowUserInteraction(false);
connection.setDoInput(true);
updateURLConnection(connection, href);
connection.connect();
return new StreamSource(connection.getInputStream(), effURL);
} catch (FileNotFoundException fnfe) {
// Note: This is on "debug" level since the caller is
// supposed to handle this
log.debug("File not found: " + effURL);
} catch (java.io.IOException ioe) {
log.error("Error with opening URL '" + effURL + "': "
+ ioe.getMessage());
}
}
}
return source;
}

/**
* This method allows you to set special values on a URLConnection just
* before the connect() method is called. Subclass FOURIResolver and
* override this method to do things like adding the user name and password
* for HTTP basic authentication.
*
* @param connection
* the URLConnection instance
* @param href
* the original URI
*/
protected void updateURLConnection(URLConnection connection, String href) {
// nop
}

/**
* This is a convenience method for users who want to override
* updateURLConnection for HTTP basic authentication. Simply call it using
* the right username and password.
*
* @param connection
* the URLConnection to set up for HTTP basic authentication
* @param username
* the username
* @param password
* the password
*/
protected void applyHttpBasicAuthentication(URLConnection connection,
String username, String password) {
String combined = username + ":" + password;
try {
ByteArrayOutputStream baout = new ByteArrayOutputStream(combined
.length() * 2);
Base64EncodeStream base64 = new Base64EncodeStream(baout);
// TODO Not sure what charset/encoding can be used with basic
// authentication
base64.write(combined.getBytes("UTF-8"));
base64.close();
connection.setRequestProperty("Authorization", "Basic "
+ new String(baout.toByteArray(), "UTF-8"));
} catch (IOException e) {
// won't happen. We're operating in-memory.
throw new RuntimeException(
"Error during base64 encodation of username/password");
}
}

/**
* Sets the custom URI Resolver. It is used for resolving factory-level URIs like
* hyphenation patterns and as backup for URI resolution performed during a
* rendering run.
*
* @param resolver
* the new URI resolver
*/
public void setCustomURIResolver(URIResolver resolver) {
this.uriResolver = resolver;
}

/**
* Returns the custom URI Resolver.
*
* @return the URI Resolver or null, if none is set
*/
public URIResolver getCustomURIResolver() {
return this.uriResolver;
}

/**
* @param throwExceptions
* Whether or not to throw exceptions on resolution error
*/
public void setThrowExceptions(boolean throwExceptions) {
this.throwExceptions = throwExceptions;
}
}

+ 204
- 123
src/java/org/apache/fop/apps/FOUserAgent.java View File

@@ -21,18 +21,22 @@ package org.apache.fop.apps;

// Java
import java.io.File;
import java.net.MalformedURLException;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URISyntaxException;
import java.util.Date;
import java.util.Map;

import javax.xml.transform.Source;
import javax.xml.transform.TransformerException;
import javax.xml.transform.URIResolver;
import javax.xml.transform.stream.StreamSource;

import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.ConfigurationException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.apache.xmlgraphics.image.loader.ImageContext;
import org.apache.xmlgraphics.image.loader.ImageManager;
import org.apache.xmlgraphics.image.loader.ImageSessionContext;
import org.apache.xmlgraphics.image.loader.impl.AbstractImageSessionContext;
import org.apache.xmlgraphics.util.UnitConv;
@@ -41,17 +45,27 @@ import org.apache.fop.Version;
import org.apache.fop.accessibility.Accessibility;
import org.apache.fop.accessibility.DummyStructureTreeEventHandler;
import org.apache.fop.accessibility.StructureTreeEventHandler;
import org.apache.fop.apps.io.InternalResourceResolver;
import org.apache.fop.events.DefaultEventBroadcaster;
import org.apache.fop.events.Event;
import org.apache.fop.events.EventBroadcaster;
import org.apache.fop.events.EventListener;
import org.apache.fop.events.FOPEventListenerProxy;
import org.apache.fop.events.LoggingEventListener;
import org.apache.fop.fo.ElementMappingRegistry;
import org.apache.fop.fo.FOEventHandler;
import org.apache.fop.fonts.FontManager;
import org.apache.fop.layoutmgr.LayoutManagerMaker;
import org.apache.fop.render.ImageHandlerRegistry;
import org.apache.fop.render.Renderer;
import org.apache.fop.render.RendererConfig;
import org.apache.fop.render.RendererConfig.RendererConfigParser;
import org.apache.fop.render.RendererConfigOption;
import org.apache.fop.render.RendererFactory;
import org.apache.fop.render.XMLHandlerRegistry;
import org.apache.fop.render.intermediate.IFDocumentHandler;
import org.apache.fop.util.ColorSpaceCache;
import org.apache.fop.util.ContentHandlerFactoryRegistry;

/**
* This is the user agent for FOP.
@@ -75,24 +89,13 @@ import org.apache.fop.render.intermediate.IFDocumentHandler;
*/
public class FOUserAgent {

/** Defines the default target resolution (72dpi) for FOP */
public static final float DEFAULT_TARGET_RESOLUTION
= FopFactoryConfigurator.DEFAULT_TARGET_RESOLUTION;

private static Log log = LogFactory.getLog("FOP");

private FopFactory factory;

/**
* The base URL for all URL resolutions, especially for
* external-graphics.
*/
private String base = null;
private final FopFactory factory;

/** A user settable URI Resolver */
private URIResolver uriResolver = null;
private final InternalResourceResolver resourceResolver;

private float targetResolution = FopFactoryConfigurator.DEFAULT_TARGET_RESOLUTION;
private float targetResolution = FopFactoryConfig.DEFAULT_TARGET_RESOLUTION;
private Map rendererOptions = new java.util.HashMap();
private File outputFile = null;
private IFDocumentHandler documentHandlerOverride = null;
@@ -131,7 +134,7 @@ public class FOUserAgent {
private ImageSessionContext imageSessionContext = new AbstractImageSessionContext() {

public ImageContext getParentContext() {
return getFactory();
return factory;
}

public float getTargetResolution() {
@@ -148,21 +151,59 @@ public class FOUserAgent {
* Main constructor. <b>This constructor should not be called directly. Please use the
* methods from FopFactory to construct FOUserAgent instances!</b>
* @param factory the factory that provides environment-level information
* @param resourceResolver the resolver used to acquire resources
* @see org.apache.fop.apps.FopFactory
*/
public FOUserAgent(FopFactory factory) {
if (factory == null) {
throw new NullPointerException("The factory parameter must not be null");
}
FOUserAgent(FopFactory factory, InternalResourceResolver resourceResolver) {
this.factory = factory;
setBaseURL(factory.getBaseURL());
this.resourceResolver = resourceResolver;
setTargetResolution(factory.getTargetResolution());
setAccessibility(factory.isAccessibilityEnabled());
}

/** @return the associated FopFactory instance */
public FopFactory getFactory() {
return this.factory;
/**
* Returns a new {@link Fop} instance. Use this factory method if your output type
* requires an output stream and you want to configure this very rendering run,
* i.e. if you want to set some metadata like the title and author of the document
* you want to render. In that case, create a new {@link FOUserAgent} instance
* using {@link #newFOUserAgent()}.
* <p>
* MIME types are used to select the output format (ex. "application/pdf" for PDF). You can
* use the constants defined in {@link MimeConstants}.
* @param outputFormat the MIME type of the output format to use (ex. "application/pdf").
* @param stream the output stream
* @return the new Fop instance
* @throws FOPException when the constructor fails
*/
public Fop newFop(String outputFormat, OutputStream stream) throws FOPException {
return new Fop(outputFormat, this, stream);
}


/**
* Returns a new {@link Fop} instance. Use this factory method if you want to configure this
* very rendering run, i.e. if you want to set some metadata like the title and author of the
* document you want to render. In that case, create a new {@link FOUserAgent}
* instance using {@link #newFOUserAgent()}.
* <p>
* MIME types are used to select the output format (ex. "application/pdf" for PDF). You can
* use the constants defined in {@link MimeConstants}.
* @param outputFormat the MIME type of the output format to use (ex. "application/pdf").
* @return the new Fop instance
* @throws FOPException when the constructor fails
*/
public Fop newFop(String outputFormat) throws FOPException {
return newFop(outputFormat, null);
}


/**
* Returns the resource resolver.
*
* @return the resource resolver
*/
public InternalResourceResolver getResourceResolver() {
return resourceResolver;
}

// ---------------------------------------------- rendering-run dependent stuff
@@ -177,7 +218,6 @@ public class FOUserAgent {
setStructureTreeEventHandler(documentHandler.getStructureTreeEventHandler());
}
this.documentHandlerOverride = documentHandler;

}

/**
@@ -345,48 +385,13 @@ public class FOUserAgent {
}

/**
* Sets the base URL.
* @param baseUrl base URL
*/
public void setBaseURL(String baseUrl) {
this.base = baseUrl;
}

/**
* Sets font base URL.
* @param fontBaseUrl font base URL
* @deprecated Use {@link org.apache.fop.fonts.FontManager#setFontBaseURL(String)} instead.
*/
public void setFontBaseURL(String fontBaseUrl) {
try {
getFactory().getFontManager().setFontBaseURL(fontBaseUrl);
} catch (MalformedURLException e) {
throw new IllegalArgumentException(e.getMessage());
}
}

/**
* Returns the base URL.
* @return the base URL
*/
public String getBaseURL() {
return this.base;
}

/**
* Sets the URI Resolver.
* @param resolver the new URI resolver
*/
public void setURIResolver(URIResolver resolver) {
this.uriResolver = resolver;
}

/**
* Returns the URI Resolver.
* @return the URI Resolver
*/
public URIResolver getURIResolver() {
return this.uriResolver;
* Gets the renderer options given an interface representing renderer configuration options.
*
* @param option the renderer option
* @return the value
*/
public Object getRendererOption(RendererConfigOption option) {
return rendererOptions.get(option.getName());
}

/**
@@ -396,39 +401,20 @@ public class FOUserAgent {
* @param uri URI to access
* @return A {@link javax.xml.transform.Source} object, or null if the URI
* cannot be resolved.
* @see org.apache.fop.apps.FOURIResolver
*/
public Source resolveURI(String uri) {
return resolveURI(uri, getBaseURL());
}

/**
* Attempts to resolve the given URI.
* Will use the configured resolver and if not successful fall back
* to the default resolver.
* @param href URI to access
* @param base the base URI to resolve against
* @return A {@link javax.xml.transform.Source} object, or null if the URI
* cannot be resolved.
* @see org.apache.fop.apps.FOURIResolver
*/
public Source resolveURI(String href, String base) {
Source source = null;
//RFC 2397 data URLs don't need to be resolved, just decode them through FOP's default
//URIResolver.
boolean bypassURIResolution = href.startsWith("data:");
if (!bypassURIResolution && uriResolver != null) {
try {
source = uriResolver.resolve(href, base);
} catch (TransformerException te) {
log.error("Attempt to resolve URI '" + href + "' failed: ", te);
}
}
if (source == null) {
// URI Resolver not configured or returned null, use default resolver from the factory
source = getFactory().resolveURI(href, base);
public StreamSource resolveURI(String uri) {
// TODO: What do we want to do when resources aren't found??? We also need to remove this
// method entirely
try {
// Have to do this so we can resolve data URIs
StreamSource src = new StreamSource(resourceResolver.getResource(uri));
src.setSystemId(uri);
return src;
} catch (URISyntaxException use) {
return null;
} catch (IOException ioe) {
return null;
}
return source;
}

/**
@@ -497,17 +483,6 @@ public class FOUserAgent {
// ---------------------------------------------- environment-level stuff
// (convenience access to FopFactory methods)

/**
* Returns the font base URL.
* @return the font base URL
* @deprecated Use {@link org.apache.fop.fonts.FontManager#getFontBaseURL()} instead.
* This method is not used by FOP.
*/
public String getFontBaseURL() {
String fontBase = getFactory().getFontManager().getFontBaseURL();
return fontBase != null ? fontBase : getBaseURL();
}

/**
* Returns the conversion factor from pixel units to millimeters. This
* depends on the desired source resolution.
@@ -515,12 +490,12 @@ public class FOUserAgent {
* @see #getSourceResolution()
*/
public float getSourcePixelUnitToMillimeter() {
return getFactory().getSourcePixelUnitToMillimeter();
return factory.getSourcePixelUnitToMillimeter();
}

/** @return the resolution for resolution-dependant input */
public float getSourceResolution() {
return getFactory().getSourceResolution();
return factory.getSourceResolution();
}

/**
@@ -531,7 +506,7 @@ public class FOUserAgent {
* @see FopFactory#getPageHeight()
*/
public String getPageHeight() {
return getFactory().getPageHeight();
return factory.getPageHeight();
}

/**
@@ -542,7 +517,7 @@ public class FOUserAgent {
* @see FopFactory#getPageWidth()
*/
public String getPageWidth() {
return getFactory().getPageWidth();
return factory.getPageWidth();
}

/**
@@ -551,7 +526,7 @@ public class FOUserAgent {
* @see FopFactory#validateStrictly()
*/
public boolean validateStrictly() {
return getFactory().validateStrictly();
return factory.validateStrictly();
}

/**
@@ -560,21 +535,21 @@ public class FOUserAgent {
* @see FopFactory#isBreakIndentInheritanceOnReferenceAreaBoundary()
*/
public boolean isBreakIndentInheritanceOnReferenceAreaBoundary() {
return getFactory().isBreakIndentInheritanceOnReferenceAreaBoundary();
return factory.isBreakIndentInheritanceOnReferenceAreaBoundary();
}

/**
* @return the RendererFactory
*/
public RendererFactory getRendererFactory() {
return getFactory().getRendererFactory();
return factory.getRendererFactory();
}

/**
* @return the XML handler registry
*/
public XMLHandlerRegistry getXMLHandlerRegistry() {
return getFactory().getXMLHandlerRegistry();
return factory.getXMLHandlerRegistry();
}

/**
@@ -663,12 +638,53 @@ public class FOUserAgent {
}

/**
* Control whether complex script features should be enabled
* Returns the renderer configuration object for a particular MIME type.
*
* @param useComplexScriptFeatures true if FOP is to use complex script features
* @param mimeType the config MIME type
* @param configCreator the parser for creating the config for the first run of parsing.
* @return the renderer configuration object
* @throws FOPException if an error occurs when creating the config object
*/
public void setComplexScriptFeaturesEnabled(boolean useComplexScriptFeatures) {
factory.setComplexScriptFeaturesEnabled ( useComplexScriptFeatures );
public RendererConfig getRendererConfig(String mimeType, RendererConfigParser configCreator)
throws FOPException {
return factory.getRendererConfig(this, getRendererConfiguration(mimeType), configCreator);
}

/**
* Returns a {@link Configuration} object for which contains renderer configuration for a given
* MIME type.
*
* @param mimeType the renderer configuration MIME type
* @return the configuration object
*/
public Configuration getRendererConfiguration(String mimeType) {
Configuration cfg = getUserConfig();
String type = "renderer";
String mime = "mime";
if (cfg == null) {
if (log.isDebugEnabled()) {
log.debug("userconfig is null");
}
return null;
}

Configuration userConfig = null;

Configuration[] cfgs = cfg.getChild(type + "s").getChildren(type);
for (int i = 0; i < cfgs.length; ++i) {
Configuration child = cfgs[i];
try {
if (child.getAttribute(mime).equals(mimeType)) {
userConfig = child;
break;
}
} catch (ConfigurationException e) {
// silently pass over configurations without mime type
}
}
log.debug((userConfig == null ? "No u" : "U")
+ "ser configuration found for MIME type " + mimeType);
return userConfig;
}

/**
@@ -714,5 +730,70 @@ public class FOUserAgent {
public StructureTreeEventHandler getStructureTreeEventHandler() {
return this.structureTreeEventHandler;
}

/** @see FopFactory#getLayoutManagerMakerOverride() */
public LayoutManagerMaker getLayoutManagerMakerOverride() {
return factory.getLayoutManagerMakerOverride();
}

/** @see FopFactory#getContentHandlerFactoryRegistry() */
public ContentHandlerFactoryRegistry getContentHandlerFactoryRegistry() {
return factory.getContentHandlerFactoryRegistry();
}

/** @see FopFactory#getImageManager() */
public ImageManager getImageManager() {
return factory.getImageManager();
}

/** @see FopFactory#getElementMappingRegistry() */
public ElementMappingRegistry getElementMappingRegistry() {
return factory.getElementMappingRegistry();
}

/** @see FopFactory#getFontManager() */
public FontManager getFontManager() {
return factory.getFontManager();
}

/**
* Indicates whether a namespace URI is on the ignored list.
* @param namespaceURI the namespace URI
* @return true if the namespace is ignored by FOP
*/
public boolean isNamespaceIgnored(String namespaceURI) {
return factory.isNamespaceIgnored(namespaceURI);
}

/**
* Is the user configuration to be validated?
* @return if the user configuration should be validated
*/
public boolean validateUserConfigStrictly() {
return factory.validateUserConfigStrictly();
}

/**
* Get the user configuration.
* @return the user configuration
*/
public Configuration getUserConfig() {
return factory.getUserConfig();
}

/** @return the image handler registry */
public ImageHandlerRegistry getImageHandlerRegistry() {
return factory.getImageHandlerRegistry();
}

/** @return the color space cache */
public ColorSpaceCache getColorSpaceCache() {
return factory.getColorSpaceCache();
}

/** @see {@link FopFactory#getHyphenationPatternNames()} */
public Map<String, String> getHyphenationPatternNames() {
return factory.getHyphenationPatternNames();
}
}


+ 3
- 3
src/java/org/apache/fop/apps/Fop.java View File

@@ -66,12 +66,12 @@ public class Fop {
* @throws FOPException if setting up the DefaultHandler fails
*/
Fop(String outputFormat, FOUserAgent ua, OutputStream stream) throws FOPException {
if (ua == null) {
throw new FOPException("Cannot create a new Fop instance without a User Agent.");
}
this.outputFormat = outputFormat;

foUserAgent = ua;
if (foUserAgent == null) {
foUserAgent = FopFactory.newInstance().newFOUserAgent();
}

this.stream = stream;


+ 356
- 0
src/java/org/apache/fop/apps/FopConfParser.java View File

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

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

import org.xml.sax.SAXException;

import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.ConfigurationException;
import org.apache.avalon.framework.configuration.DefaultConfigurationBuilder;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.apache.xmlgraphics.image.loader.spi.ImageImplRegistry;
import org.apache.xmlgraphics.image.loader.util.Penalty;

import org.apache.fop.apps.io.InternalResourceResolver;
import org.apache.fop.apps.io.ResourceResolver;
import org.apache.fop.apps.io.ResourceResolverFactory;
import org.apache.fop.fonts.FontManagerConfigurator;
import org.apache.fop.hyphenation.HyphenationTreeCache;
import org.apache.fop.util.LogUtil;

/**
* Parses the FOP configuration file and returns a {@link FopFactoryBuilder} which builds a
* {@link FopFactory}.
*/
public class FopConfParser {

private static final String PREFER_RENDERER = "prefer-renderer";

private final Log log = LogFactory.getLog(FopConfParser.class);

private final FopFactoryBuilder fopFactoryBuilder;

/**
* Constructor that takes the FOP conf in the form of an {@link InputStream}. A default base URI
* must be given as a fall-back mechanism for URI resolution.
*
* @param fopConfStream the fop conf input stream
* @param enviro the profile of the FOP deployment environment
* @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(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);
}

/**
* Constructor that takes the FOP conf in the form of an {@link InputStream}. A default base URI
* must be given as a fall-back mechanism for URI resolution.
*
* @param fopConfStream the fop conf input stream
* @param defaultBaseURI the default base URI
* @param resourceResolver the URI resolver
* @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(InputStream fopConfStream, URI defaultBaseURI,
ResourceResolver resourceResolver) throws SAXException, IOException {
this(fopConfStream, EnvironmentalProfileFactory.createDefault(defaultBaseURI, resourceResolver));
}

/**
* Constructor that takes the FOP conf in the form of an {@link InputStream}. A default base URI
* must be given as a fall-back mechanism for URI resolution. The default URI resolvers is used.
*
* @param fopConfStream the fop conf input stream
* @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(InputStream fopConfStream, URI defaultBaseURI) throws SAXException,
IOException {
this(fopConfStream, defaultBaseURI, ResourceResolverFactory.createDefaultResourceResolver());
}

/**
* Constructor that takes the FOP conf and uses the default URI resolver.
*
* @param fopConfFile the FOP conf file
* @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) throws SAXException, IOException {
this(fopConfFile, ResourceResolverFactory.createDefaultResourceResolver());
}

/**
* Constructor that parses the FOP conf and uses the URI resolver given.
*
* @param fopConfFile the FOP conf file
* @param resourceResolver the URI resolver
* @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, ResourceResolver resourceResolver)
throws SAXException, IOException {
this(new FileInputStream(fopConfFile),
fopConfFile.getAbsoluteFile().getParentFile().toURI(), resourceResolver);
}

private void configure(final URI defaultBaseURI, final ResourceResolver resourceResolver,
Configuration cfg) throws FOPException {
if (log.isDebugEnabled()) {
log.debug("Initializing FopFactory Configuration");
}

// strict fo validation
if (cfg.getChild("strict-validation", false) != null) {
try {
boolean strict = cfg.getChild("strict-validation").getValueAsBoolean();
fopFactoryBuilder.setStrictFOValidation(strict);
} catch (ConfigurationException e) {
LogUtil.handleException(log, e, false);
}
}

boolean strict = false;
if (cfg.getChild("strict-configuration", false) != null) {
try {
strict = cfg.getChild("strict-configuration").getValueAsBoolean();
fopFactoryBuilder.setStrictUserConfigValidation(strict);
} catch (ConfigurationException e) {
LogUtil.handleException(log, e, false);
}
}

if (cfg.getChild("accessibility", false) != null) {
try {
fopFactoryBuilder.setAccessibility(cfg.getChild("accessibility").getValueAsBoolean());
} catch (ConfigurationException e) {
LogUtil.handleException(log, e, false);
}
}

// base definitions for relative path resolution
if (cfg.getChild("base", false) != null) {
try {
URI confUri = InternalResourceResolver.getBaseURI(cfg.getChild("base").getValue(null));
fopFactoryBuilder.setBaseURI(defaultBaseURI.resolve(confUri));
} catch (URISyntaxException use) {
LogUtil.handleException(log, use, strict);
}
}

// renderer options
if (cfg.getChild("source-resolution", false) != null) {
float srcRes = cfg.getChild("source-resolution").getValueAsFloat(
FopFactoryConfig.DEFAULT_SOURCE_RESOLUTION);
fopFactoryBuilder.setSourceResolution(srcRes);
if (log.isDebugEnabled()) {
log.debug("source-resolution set to: " + srcRes + "dpi");
}
}
if (cfg.getChild("target-resolution", false) != null) {
float targetRes = cfg.getChild("target-resolution").getValueAsFloat(
FopFactoryConfig.DEFAULT_TARGET_RESOLUTION);
fopFactoryBuilder.setTargetResolution(targetRes);
if (log.isDebugEnabled()) {
log.debug("target-resolution set to: " + targetRes + "dpi");
}
}
if (cfg.getChild("break-indent-inheritance", false) != null) {
try {
fopFactoryBuilder.setBreakIndentInheritanceOnReferenceAreaBoundary(
cfg.getChild("break-indent-inheritance").getValueAsBoolean());
} catch (ConfigurationException e) {
LogUtil.handleException(log, e, strict);
}
}
Configuration pageConfig = cfg.getChild("default-page-settings");
if (pageConfig.getAttribute("height", null) != null) {
String pageHeight = pageConfig.getAttribute("height",
FopFactoryConfig.DEFAULT_PAGE_HEIGHT);
fopFactoryBuilder.setPageHeight(pageHeight);
if (log.isInfoEnabled()) {
log.info("Default page-height set to: " + pageHeight);
}
}
if (pageConfig.getAttribute("width", null) != null) {
String pageWidth = pageConfig.getAttribute("width",
FopFactoryConfig.DEFAULT_PAGE_WIDTH);
fopFactoryBuilder.setPageWidth(pageWidth);
if (log.isInfoEnabled()) {
log.info("Default page-width set to: " + pageWidth);
}
}

if (cfg.getChild("complex-scripts") != null) {
Configuration csConfig = cfg.getChild("complex-scripts");
fopFactoryBuilder.setComplexScriptFeatures(!csConfig.getAttributeAsBoolean("disabled",
false));
}

setHyphPatNames(cfg, fopFactoryBuilder, strict);

// prefer Renderer over IFDocumentHandler
if (cfg.getChild(PREFER_RENDERER, false) != null) {
try {
fopFactoryBuilder.setPreferRenderer(
cfg.getChild(PREFER_RENDERER).getValueAsBoolean());
} catch (ConfigurationException e) {
LogUtil.handleException(log, e, strict);
}
}

// configure font manager
new FontManagerConfigurator(cfg, fopFactoryBuilder.getBaseURI(), resourceResolver).configure(
fopFactoryBuilder.getFontManager(), strict);

// configure image loader framework
configureImageLoading(cfg.getChild("image-loading", false), strict);
}

private void setHyphPatNames(Configuration cfg, FopFactoryBuilder builder, boolean strict)
throws FOPException {
Configuration[] hyphPatConfig = cfg.getChildren("hyphenation-pattern");
if (hyphPatConfig.length != 0) {
Map<String, String> hyphPatNames = new HashMap<String, String>();
for (int i = 0; i < hyphPatConfig.length; ++i) {
String lang;
String country;
String filename;
StringBuffer error = new StringBuffer();
String location = hyphPatConfig[i].getLocation();

lang = hyphPatConfig[i].getAttribute("lang", null);
if (lang == null) {
addError("The lang attribute of a hyphenation-pattern configuration"
+ " element must exist (" + location + ")", error);
} else if (!lang.matches("[a-zA-Z]{2}")) {
addError("The lang attribute of a hyphenation-pattern configuration"
+ " element must consist of exactly two letters ("
+ location + ")", error);
}
lang = lang.toLowerCase(Locale.getDefault());

country = hyphPatConfig[i].getAttribute("country", null);
if ("".equals(country)) {
country = null;
}
if (country != null) {
if (!country.matches("[a-zA-Z]{2}")) {
addError("The country attribute of a hyphenation-pattern configuration"
+ " element must consist of exactly two letters ("
+ location + ")", error);
}
country = country.toUpperCase(Locale.getDefault());
}

filename = hyphPatConfig[i].getValue(null);
if (filename == null) {
addError("The value of a hyphenation-pattern configuration"
+ " element may not be empty (" + location + ")", error);
}

if (error.length() != 0) {
LogUtil.handleError(log, error.toString(), strict);
continue;
}

String llccKey = HyphenationTreeCache.constructLlccKey(lang, country);
hyphPatNames.put(llccKey, filename);
if (log.isDebugEnabled()) {
log.debug("Using hyphenation pattern filename " + filename
+ " for lang=\"" + lang + "\""
+ (country != null ? ", country=\"" + country + "\"" : ""));
}
}
builder.setHyphPatNames(hyphPatNames);
}
}

private static void addError(String message, StringBuffer error) {
if (error.length() != 0) {
error.append(". ");
}
error.append(message);
}

private void configureImageLoading(Configuration parent, boolean strict) throws FOPException {
if (parent == null) {
return;
}
ImageImplRegistry registry = fopFactoryBuilder.getImageManager().getRegistry();
Configuration[] penalties = parent.getChildren("penalty");
try {
for (int i = 0, c = penalties.length; i < c; i++) {
Configuration penaltyCfg = penalties[i];
String className = penaltyCfg.getAttribute("class");
String value = penaltyCfg.getAttribute("value");
Penalty p = null;
if (value.toUpperCase(Locale.getDefault()).startsWith("INF")) {
p = Penalty.INFINITE_PENALTY;
} else {
try {
p = Penalty.toPenalty(Integer.parseInt(value));
} catch (NumberFormatException nfe) {
LogUtil.handleException(log, nfe, strict);
}
}
if (p != null) {
registry.setAdditionalPenalty(className, p);
}
}
} catch (ConfigurationException e) {
LogUtil.handleException(log, e, strict);
}
}

/**
* Returns the {@link FopFactoryBuilder}.
*
* @return the object for configuring the {@link FopFactory}
*/
public FopFactoryBuilder getFopFactoryBuilder() {
return fopFactoryBuilder;
}
}

+ 118
- 481
src/java/org/apache/fop/apps/FopFactory.java View File

@@ -21,19 +21,13 @@ package org.apache.fop.apps;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URI;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import javax.xml.transform.Source;
import javax.xml.transform.TransformerException;
import javax.xml.transform.URIResolver;

import org.xml.sax.SAXException;

import org.apache.avalon.framework.configuration.Configuration;
@@ -44,13 +38,15 @@ import org.apache.xmlgraphics.image.loader.ImageContext;
import org.apache.xmlgraphics.image.loader.ImageManager;
import org.apache.xmlgraphics.util.UnitConv;

import org.apache.fop.apps.io.InternalResourceResolver;
import org.apache.fop.apps.io.ResourceResolverFactory;
import org.apache.fop.fo.ElementMapping;
import org.apache.fop.fo.ElementMappingRegistry;
import org.apache.fop.fonts.FontCache;
import org.apache.fop.fonts.FontManager;
import org.apache.fop.hyphenation.HyphenationTreeResolver;
import org.apache.fop.layoutmgr.LayoutManagerMaker;
import org.apache.fop.render.ImageHandlerRegistry;
import org.apache.fop.render.RendererConfig;
import org.apache.fop.render.RendererConfig.RendererConfigParser;
import org.apache.fop.render.RendererFactory;
import org.apache.fop.render.XMLHandlerRegistry;
import org.apache.fop.util.ColorSpaceCache;
@@ -59,57 +55,49 @@ import org.apache.fop.util.ContentHandlerFactoryRegistry;
/**
* Factory class which instantiates new Fop and FOUserAgent instances. This
* class also holds environmental information and configuration used by FOP.
* Information that may potentially be different for each rendering run can be
* Information that may potentially be different for each renderingq run can be
* found and managed in the FOUserAgent.
*/
public class FopFactory implements ImageContext {
public final class FopFactory implements ImageContext {

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

/** Factory for Renderers and FOEventHandlers */
private RendererFactory rendererFactory;
private final RendererFactory rendererFactory;

/** Registry for XML handlers */
private XMLHandlerRegistry xmlHandlers;
private final XMLHandlerRegistry xmlHandlers;

/** Registry for image handlers */
private ImageHandlerRegistry imageHandlers;
private final ImageHandlerRegistry imageHandlers;

/** The registry for ElementMapping instances */
private ElementMappingRegistry elementMappingRegistry;
private final ElementMappingRegistry elementMappingRegistry;

/** The registry for ContentHandlerFactory instance */
private ContentHandlerFactoryRegistry contentHandlerFactoryRegistry
= new ContentHandlerFactoryRegistry();

/** The resolver for user-supplied hyphenation patterns */
private HyphenationTreeResolver hyphResolver = null;
private final ContentHandlerFactoryRegistry contentHandlerFactoryRegistry
= new ContentHandlerFactoryRegistry();

private ColorSpaceCache colorSpaceCache = null;
private final ColorSpaceCache colorSpaceCache;

/** Image manager for loading and caching image objects */
private ImageManager imageManager;
private final FopFactoryConfig config;

/** Font manager for font substitution, autodetection and caching **/
private FontManager fontManager;
private final InternalResourceResolver resolver;

/** Configuration layer used to configure fop */
private FopFactoryConfigurator config = null;
private final Map<String, RendererConfig> rendererConfig;

/**
* The base URL for all URL resolutions, especially for
* external-graphics.
*/
private String base = null;

/**
* Controls if accessibility is turned on or off
*/
private boolean accessibility = false;

/** The base URL for all hyphen URL resolutions. */
private String hyphenBase = null;
private FopFactory(FopFactoryConfig config) {
this.config = config;
this.resolver = ResourceResolverFactory.createInternalResourceResolver(config.getBaseURI(),
config.getResourceResolver());
this.elementMappingRegistry = new ElementMappingRegistry(this);
this.colorSpaceCache = new ColorSpaceCache(resolver);
this.rendererFactory = new RendererFactory(config.preferRenderer());
this.xmlHandlers = new XMLHandlerRegistry();
this.imageHandlers = new ImageHandlerRegistry();
rendererConfig = new HashMap<String, RendererConfig>();
}

/**
* Map of configured names of hyphenation pattern file names: ll_CC => name
@@ -121,73 +109,52 @@ public class FopFactory implements ImageContext {
* input XSL violates that FO's content model. This is the default
* behavior for FOP. However, this flag, if set, provides the user the
* ability for FOP to halt on all content model violations if desired.
* Returns a new FopFactory instance that is configured using the {@link FopFactoryConfig} object.
*
* @param config the fop configuration
* @return the requested FopFactory instance.
*/
private boolean strictFOValidation = FopFactoryConfigurator.DEFAULT_STRICT_FO_VALIDATION;
public static FopFactory newInstance(FopFactoryConfig config) {
return new FopFactory(config);
}

/**
* FOP will validate the contents of the user configuration strictly
* (e.g. base-urls and font urls/paths).
* Returns a new FopFactory instance that is configured using the {@link FopFactoryConfig} object that
* is created when the fopConf is parsed.
*
* @param fopConf the fop conf configuration file to parse
* @return the requested FopFactory instance.
* @throws IOException
* @throws SAXException
*/
private boolean strictUserConfigValidation
= FopFactoryConfigurator.DEFAULT_STRICT_USERCONFIG_VALIDATION;

/** Source resolution in dpi */
private float sourceResolution = FopFactoryConfigurator.DEFAULT_SOURCE_RESOLUTION;

/** Target resolution in dpi */
private float targetResolution = FopFactoryConfigurator.DEFAULT_TARGET_RESOLUTION;

/** Page height */
private String pageHeight = FopFactoryConfigurator.DEFAULT_PAGE_HEIGHT;

/** Page width */
private String pageWidth = FopFactoryConfigurator.DEFAULT_PAGE_WIDTH;

/** Complex scripts support enabled */
private boolean useComplexScriptFeatures
= FopFactoryConfigurator.DEFAULT_COMPLEX_SCRIPT_FEATURES;

/** @see #setBreakIndentInheritanceOnReferenceAreaBoundary(boolean) */
private boolean breakIndentInheritanceOnReferenceAreaBoundary
= FopFactoryConfigurator.DEFAULT_BREAK_INDENT_INHERITANCE;

/** Optional overriding LayoutManagerMaker */
private LayoutManagerMaker lmMakerOverride = null;

private Set<String> ignoredNamespaces;

private FOURIResolver foURIResolver;
public static FopFactory newInstance(File fopConf) throws SAXException, IOException {
return new FopConfParser(fopConf).getFopFactoryBuilder().build();
}

/**
* Main constructor.
* Returns a new FopFactory instance that is configured only by the default configuration
* parameters.
*
* @param baseURI the base URI to resolve resource URIs against
* @return the requested FopFactory instance.
*/
protected FopFactory() {
this.config = new FopFactoryConfigurator(this);
this.elementMappingRegistry = new ElementMappingRegistry(this);
this.foURIResolver = new FOURIResolver(validateUserConfigStrictly());
this.fontManager = new FontManager() {

/** {@inheritDoc} */
@Override
public void setFontBaseURL(String fontBase) throws MalformedURLException {
super.setFontBaseURL(getFOURIResolver().checkBaseURL(fontBase));
}

};
this.colorSpaceCache = new ColorSpaceCache(foURIResolver);
this.imageManager = new ImageManager(this);
this.rendererFactory = new RendererFactory();
this.xmlHandlers = new XMLHandlerRegistry();
this.imageHandlers = new ImageHandlerRegistry();
this.ignoredNamespaces = new java.util.HashSet<String>();
public static FopFactory newInstance(URI baseURI) {
return new FopFactoryBuilder(baseURI).build();
}

/**
* Returns a new FopFactory instance.
* Returns a new FopFactory instance that is configured using the {@link FopFactoryConfig} object that
* is created when the fopConf is parsed.
*
* @param baseURI the base URI to resolve resource URIs against
* @param confStream the fop conf configuration stream to parse
* @return the requested FopFactory instance.
* @throws SAXException
* @throws IOException
*/
public static FopFactory newInstance() {
return new FopFactory();
public static FopFactory newInstance(URI baseURI, InputStream confStream) throws SAXException,
IOException {
return new FopConfParser(confStream, baseURI).getFopFactoryBuilder().build();
}

/**
@@ -198,34 +165,12 @@ public class FopFactory implements ImageContext {
* @throws FOPException
*/
public FOUserAgent newFOUserAgent() {
FOUserAgent userAgent = new FOUserAgent(this);
FOUserAgent userAgent = new FOUserAgent(this, resolver);
return userAgent;
}

/**
* Sets accessibility support.
*
* @param value <code>true</code> to enable accessibility, <code>false</code> otherwise
*/
void setAccessibility(boolean value) {
this.accessibility = value;
}

boolean isAccessibilityEnabled() {
return accessibility;
}

/**
* Sets complex script support.
* @param value <code>true</code> to enable complex script features,
* <code>false</code> otherwise
*/
void setComplexScriptFeaturesEnabled(boolean value) {
this.useComplexScriptFeatures = value;
}

boolean isComplexScriptFeaturesEnabled() {
return useComplexScriptFeatures;
return config.isComplexScriptFeaturesEnabled();
}

/**
@@ -239,7 +184,7 @@ public class FopFactory implements ImageContext {
* @throws FOPException when the constructor fails
*/
public Fop newFop(String outputFormat) throws FOPException {
return newFop(outputFormat, newFOUserAgent());
return newFOUserAgent().newFop(outputFormat);
}

/**
@@ -256,7 +201,7 @@ public class FopFactory implements ImageContext {
* @throws FOPException when the constructor fails
*/
public Fop newFop(String outputFormat, FOUserAgent userAgent) throws FOPException {
return newFop(outputFormat, userAgent, null);
return userAgent.newFop(outputFormat, null);
}

/**
@@ -271,7 +216,7 @@ public class FopFactory implements ImageContext {
* @throws FOPException when the constructor fails
*/
public Fop newFop(String outputFormat, OutputStream stream) throws FOPException {
return newFop(outputFormat, newFOUserAgent(), stream);
return newFOUserAgent().newFop(outputFormat, stream);
}

/**
@@ -290,11 +235,8 @@ public class FopFactory implements ImageContext {
* @throws FOPException when the constructor fails
*/
public Fop newFop(String outputFormat, FOUserAgent userAgent, OutputStream stream)
throws FOPException {
if (userAgent == null) {
throw new NullPointerException("The userAgent parameter must not be null!");
}
return new Fop(outputFormat, userAgent, stream);
throws FOPException {
return userAgent.newFop(outputFormat, stream);
}

/**
@@ -343,11 +285,28 @@ public class FopFactory implements ImageContext {
}

/**
* Returns the image manager.
* @return the image manager
*/
public ImageManager getImageManager() {
return this.imageManager;
* Returns the renderer configuration object for a specific renderer given the parser and
* configuration to read. The renderer config is cached such that the {@link Configuration} is
* only parsed once per renderer, per FopFactory instance.
*
* @param userAgent the user agent
* @param cfg the configuration to be parsed
* @param configCreator the parser that creates the config object
* @return the config object
* @throws FOPException when an error occurs while creating the configuration object
*/
synchronized RendererConfig getRendererConfig(FOUserAgent userAgent, Configuration cfg,
RendererConfigParser configCreator) throws FOPException {
RendererConfig config = rendererConfig.get(configCreator.getMimeType());
if (config == null) {
try {
config = configCreator.build(userAgent, cfg);
rendererConfig.put(configCreator.getMimeType(), config);
} catch (Exception e) {
throw new FOPException(e);
}
}
return config;
}

/**
@@ -359,143 +318,32 @@ public class FopFactory implements ImageContext {
}

/**
* Sets an explicit LayoutManagerMaker instance which overrides the one
* defined by the AreaTreeHandler.
* @param lmMaker the LayoutManagerMaker instance
*/
public void setLayoutManagerMakerOverride(LayoutManagerMaker lmMaker) {
this.lmMakerOverride = lmMaker;
}

/**
* Returns the overriding LayoutManagerMaker instance, if any.
* @return the overriding LayoutManagerMaker or null
*/
public LayoutManagerMaker getLayoutManagerMakerOverride() {
return this.lmMakerOverride;
}

/**
* Sets the base URL.
* @param base the base URL
* @throws MalformedURLException if there's a problem with a file URL
*/
public void setBaseURL(String base) throws MalformedURLException {
this.base = foURIResolver.checkBaseURL(base);
}

/**
* Returns the base URL.
* @return the base URL
*/
public String getBaseURL() {
return this.base;
}

/**
* Sets the font base URL.
* @param fontBase font base URL
* @throws MalformedURLException if there's a problem with a file URL
* @deprecated use getFontManager().setFontBaseURL(fontBase) instead
* Returns whether accessibility is enabled.
* @return true if accessibility is enabled
*/
@Deprecated
public void setFontBaseURL(String fontBase) throws MalformedURLException {
getFontManager().setFontBaseURL(fontBase);
}

/**
* @return the font base URL
* @deprecated use getFontManager().setFontBaseURL(fontBase) instead
*/
@Deprecated
public String getFontBaseURL() {
return getFontManager().getFontBaseURL();
}

/** @return the hyphen base URL */
public String getHyphenBaseURL() {
return this.hyphenBase;
}

/**
* Sets the hyphen base URL.
* @param hyphenBase hythen base URL
* @throws MalformedURLException if there's a problem with a file URL
* */
public void setHyphenBaseURL(final String hyphenBase) throws MalformedURLException {
if (hyphenBase != null) {
setHyphenationTreeResolver(
new HyphenationTreeResolver() {
public Source resolve(String href) {
return resolveURI(href, hyphenBase);
}
});
}
this.hyphenBase = foURIResolver.checkBaseURL(hyphenBase);
}

/**
* @return the hyphPatNames
*/
public Map getHyphPatNames() {
return hyphPatNames;
}

/**
* @param hyphPatNames the hyphPatNames to set
*/
public void setHyphPatNames(Map hyphPatNames) {
if (hyphPatNames == null) {
hyphPatNames = new HashMap();
}
this.hyphPatNames = hyphPatNames;
}

/**
* Sets the URI Resolver. It is used for resolving factory-level URIs like hyphenation
* patterns and as backup for URI resolution performed during a rendering run.
* @param uriResolver the new URI resolver
*/
public void setURIResolver(URIResolver uriResolver) {
foURIResolver.setCustomURIResolver(uriResolver);
}

/**
* Returns the URI Resolver.
* @return the URI Resolver
*/
public URIResolver getURIResolver() {
return foURIResolver;
boolean isAccessibilityEnabled() {
return config.isAccessibilityEnabled();
}

/**
* Returns the FO URI Resolver.
* @return the FO URI Resolver
* Returns the image manager.
* @return the image manager
*/
public FOURIResolver getFOURIResolver() {
return foURIResolver;
}

/** @return the HyphenationTreeResolver for resolving user-supplied hyphenation patterns. */
public HyphenationTreeResolver getHyphenationTreeResolver() {
return this.hyphResolver;
public ImageManager getImageManager() {
return config.getImageManager();
}

/**
* Sets the HyphenationTreeResolver to be used for resolving user-supplied hyphenation files.
* @param hyphResolver the HyphenationTreeResolver instance
* Returns the overriding LayoutManagerMaker instance, if any.
* @return the overriding LayoutManagerMaker or null
*/
public void setHyphenationTreeResolver(HyphenationTreeResolver hyphResolver) {
this.hyphResolver = hyphResolver;
public LayoutManagerMaker getLayoutManagerMakerOverride() {
return config.getLayoutManagerMakerOverride();
}

/**
* Activates strict XSL content model validation for FOP
* Default is false (FOP will continue processing where it can)
* @param validateStrictly true to turn on strict validation
*/
public void setStrictValidation(boolean validateStrictly) {
this.strictFOValidation = validateStrictly;
/** @return the hyphenation pattern names */
public Map<String, String> getHyphenationPatternNames() {
return config.getHyphenationPatternNames();
}

/**
@@ -503,7 +351,7 @@ public class FopFactory implements ImageContext {
* @return true of strict validation turned on, false otherwise
*/
public boolean validateStrictly() {
return strictFOValidation;
return config.validateStrictly();
}

/**
@@ -511,48 +359,12 @@ public class FopFactory implements ImageContext {
* boundaries (for more info, see the javadoc for the relative member variable)
*/
public boolean isBreakIndentInheritanceOnReferenceAreaBoundary() {
return breakIndentInheritanceOnReferenceAreaBoundary;
}

/**
* Controls whether to enable a feature that breaks indent inheritance when crossing
* reference area boundaries.
* <p>
* This flag controls whether FOP will enable special code that breaks property
* inheritance for start-indent and end-indent when the evaluation of the inherited
* value would cross a reference area. This is described under
* http://wiki.apache.org/xmlgraphics-fop/IndentInheritance as is intended to
* improve interoperability with commercial FO implementations and to produce
* results that are more in line with the expectation of unexperienced FO users.
* Note: Enabling this features violates the XSL specification!
* @param value true to enable the feature
*/
public void setBreakIndentInheritanceOnReferenceAreaBoundary(boolean value) {
this.breakIndentInheritanceOnReferenceAreaBoundary = value;
}

/**
* @return true if kerning on base 14 fonts is enabled
* @deprecated use getFontManager().isBase14KerningEnabled() instead
*/
@Deprecated
public boolean isBase14KerningEnabled() {
return getFontManager().isBase14KerningEnabled();
}

/**
* Controls whether kerning is activated on base 14 fonts.
* @param value true if kerning should be activated
* @deprecated use getFontManager().setBase14KerningEnabled(boolean) instead
*/
@Deprecated
public void setBase14KerningEnabled(boolean value) {
getFontManager().setBase14KerningEnabled(value);
return config.isBreakIndentInheritanceOnReferenceAreaBoundary();
}

/** @return the resolution for resolution-dependant input */
/** @return the resolution for resolution-dependent input */
public float getSourceResolution() {
return this.sourceResolution;
return config.getSourceResolution();
}

/**
@@ -565,22 +377,9 @@ public class FopFactory implements ImageContext {
return UnitConv.IN2MM / getSourceResolution();
}

/**
* Sets the source resolution in dpi. This value is used to interpret the pixel size
* of source documents like SVG images and bitmap images without resolution information.
* @param dpi resolution in dpi
*/
public void setSourceResolution(float dpi) {
this.sourceResolution = dpi;
if (log.isDebugEnabled()) {
log.debug("source-resolution set to: " + sourceResolution
+ "dpi (px2mm=" + getSourcePixelUnitToMillimeter() + ")");
}
}

/** @return the resolution for resolution-dependant output */
public float getTargetResolution() {
return this.targetResolution;
return config.getTargetResolution();
}

/**
@@ -590,25 +389,7 @@ public class FopFactory implements ImageContext {
* @see #getTargetResolution()
*/
public float getTargetPixelUnitToMillimeter() {
return UnitConv.IN2MM / this.targetResolution;
}

/**
* Sets the source resolution in dpi. This value is used to interpret the pixel size
* of source documents like SVG images and bitmap images without resolution information.
* @param dpi resolution in dpi
*/
public void setTargetResolution(float dpi) {
this.targetResolution = dpi;
}

/**
* Sets the source resolution in dpi. This value is used to interpret the pixel size
* of source documents like SVG images and bitmap images without resolution information.
* @param dpi resolution in dpi
*/
public void setSourceResolution(int dpi) {
setSourceResolution((float)dpi);
return 25.4f / getTargetResolution();
}

/**
@@ -618,20 +399,7 @@ public class FopFactory implements ImageContext {
* @return the page-height, as a String
*/
public String getPageHeight() {
return this.pageHeight;
}

/**
* Sets the page-height to use as fallback, in case
* page-height="auto"
*
* @param pageHeight page-height as a String
*/
public void setPageHeight(String pageHeight) {
this.pageHeight = pageHeight;
if (log.isDebugEnabled()) {
log.debug("Default page-height set to: " + pageHeight);
}
return config.getPageHeight();
}

/**
@@ -641,40 +409,7 @@ public class FopFactory implements ImageContext {
* @return the page-width, as a String
*/
public String getPageWidth() {
return this.pageWidth;
}

/**
* Sets the page-width to use as fallback, in case
* page-width="auto"
*
* @param pageWidth page-width as a String
*/
public void setPageWidth(String pageWidth) {
this.pageWidth = pageWidth;
if (log.isDebugEnabled()) {
log.debug("Default page-width set to: " + pageWidth);
}
}

/**
* Adds a namespace to the set of ignored namespaces.
* If FOP encounters a namespace which it cannot handle, it issues a warning except if this
* namespace is in the ignored set.
* @param namespaceURI the namespace URI
*/
public void ignoreNamespace(String namespaceURI) {
this.ignoredNamespaces.add(namespaceURI);
}

/**
* Adds a collection of namespaces to the set of ignored namespaces.
* If FOP encounters a namespace which it cannot handle, it issues a warning except if this
* namespace is in the ignored set.
* @param namespaceURIs the namespace URIs
*/
public void ignoreNamespaces(Collection<String> namespaceURIs) {
this.ignoredNamespaces.addAll(namespaceURIs);
return config.getPageWidth();
}

/**
@@ -683,54 +418,16 @@ public class FopFactory implements ImageContext {
* @return true if the namespace is ignored by FOP
*/
public boolean isNamespaceIgnored(String namespaceURI) {
return this.ignoredNamespaces.contains(namespaceURI);
return config.isNamespaceIgnored(namespaceURI);
}

/** @return the set of namespaces that are ignored by FOP */
public Set<String> getIgnoredNamespace() {
return Collections.unmodifiableSet(this.ignoredNamespaces);
return config.getIgnoredNamespaces();
}

//------------------------------------------- Configuration stuff

/**
* Set the user configuration.
* @param userConfigFile the configuration file
* @throws IOException if an I/O error occurs
* @throws SAXException if a parsing error occurs
*/
public void setUserConfig(File userConfigFile) throws SAXException, IOException {
config.setUserConfig(userConfigFile);
}

/**
* Set the user configuration from an URI.
* @param uri the URI to the configuration file
* @throws IOException if an I/O error occurs
* @throws SAXException if a parsing error occurs
*/
public void setUserConfig(String uri) throws SAXException, IOException {
config.setUserConfig(uri);
}

/**
* Set the user configuration.
* @param userConfig configuration
* @throws FOPException if a configuration problem occurs
*/
public void setUserConfig(Configuration userConfig) throws FOPException {
config.setUserConfig(userConfig);
}

/**
* Set the base URI for the user configuration
* Useful for programmatic configurations
* @param baseURI the base URI
*/
public void setUserConfigBaseURI(URI baseURI) {
config.setBaseURI(baseURI);
}

/**
* Get the user configuration.
* @return the user configuration
@@ -739,81 +436,22 @@ public class FopFactory implements ImageContext {
return config.getUserConfig();
}

/**
* Is the user configuration to be validated?
* @param strictUserConfigValidation strict user config validation
*/
public void setStrictUserConfigValidation(boolean strictUserConfigValidation) {
this.strictUserConfigValidation = strictUserConfigValidation;
this.foURIResolver.setThrowExceptions(strictUserConfigValidation);
}

/**
* Is the user configuration to be validated?
* @return if the user configuration should be validated
*/
public boolean validateUserConfigStrictly() {
return this.strictUserConfigValidation;
return config.validateUserConfigStrictly();
}

//------------------------------------------- Font related stuff

/**
* Whether or not to cache results of font triplet detection/auto-config
* @param useCache use cache or not
* @deprecated use getFontManager().setUseCache(boolean) instead
*/
@Deprecated
public void setUseCache(boolean useCache) {
getFontManager().setUseCache(useCache);
}

/**
* Cache results of font triplet detection/auto-config?
* @return whether this factory is uses the cache
* @deprecated use getFontManager().useCache() instead
*/
@Deprecated
public boolean useCache() {
return getFontManager().useCache();
}

/**
* Returns the font cache instance used by this factory.
* @return the font cache
* @deprecated use getFontManager().getFontCache() instead
*/
@Deprecated
public FontCache getFontCache() {
return getFontManager().getFontCache();
}

/**
* Returns the font manager.
* @return the font manager
*/
public FontManager getFontManager() {
return this.fontManager;
}

/**
* Attempts to resolve the given URI.
* Will use the configured resolver and if not successful fall back
* to the default resolver.
* @param href URI to access
* @param baseUri the base URI to resolve against
* @return A {@link javax.xml.transform.Source} object, or null if the URI
* cannot be resolved.
* @see org.apache.fop.apps.FOURIResolver
*/
public Source resolveURI(String href, String baseUri) {
Source source = null;
try {
source = foURIResolver.resolve(href, baseUri);
} catch (TransformerException e) {
log.error("Attempt to resolve URI '" + href + "' failed: ", e);
}
return source;
return config.getFontManager();
}

/**
@@ -825,5 +463,4 @@ public class FopFactory implements ImageContext {
public ColorSpaceCache getColorSpaceCache() {
return this.colorSpaceCache;
}

}

+ 657
- 0
src/java/org/apache/fop/apps/FopFactoryBuilder.java View File

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

import java.net.URI;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.apache.avalon.framework.configuration.Configuration;

import org.apache.xmlgraphics.image.loader.ImageContext;
import org.apache.xmlgraphics.image.loader.ImageManager;

import org.apache.fop.apps.io.ResourceResolver;
import org.apache.fop.apps.io.ResourceResolverFactory;
import org.apache.fop.fonts.FontManager;
import org.apache.fop.layoutmgr.LayoutManagerMaker;

/**
* This is the builder class for {@link FopFactory}. Setters can be chained to
* make building a {@link FopFactory} object more concise and intuitive e.g.
*
* <pre>
* {@code
* FopFactoryBuilder fopFactoryBuilder = new FopFactoryBuilder(<URI>)
* .setURIResolver(<URIResolver>)
* .setPageHeight(<String>)
* .setPageWidth(<String>)
* .setStrictUserConfigValidation(<boolean>)
* ... etc ...
* FopFactory fopFactory = fopFactoryBuilder.build();
* }
* </pre>
*/
public final class FopFactoryBuilder {

private final FopFactoryConfig config;

private FopFactoryConfigBuilder fopFactoryConfigBuilder;

/**
* A builder class for {@link FopFactory} which can be used for setting configuration. This is
* a helper constructor that uses the default URI resolver implementation that FOP packages
* provide ({@link org.apache.fop.apps.io.ResourceResolverFactory.DefaultResourceResolver}).
*
* @param defaultBaseURI the default base URI for resolving URIs against
*/
public FopFactoryBuilder(URI defaultBaseURI) {
this(defaultBaseURI, ResourceResolverFactory.createDefaultResourceResolver());
}

/**
* A builder class for {@link FopFactory} which can be used for setting configuration.
*
* @param defaultBaseURI the default base URI for resolving URIs against
* @param resourceResolver the URI resolver
*/
public FopFactoryBuilder(URI defaultBaseURI, ResourceResolver resourceResolver) {
this(EnvironmentalProfileFactory.createDefault(defaultBaseURI, resourceResolver));
}

/**
* A builder class for {@link FopFactory} which can be used for setting configuration.
*
* @param enviro the profile of the FOP deployment environment
*/
public FopFactoryBuilder(EnvironmentProfile enviro) {
config = new FopFactoryConfigImpl(enviro);
fopFactoryConfigBuilder = new ActiveFopFactoryConfigBuilder((FopFactoryConfigImpl) config);
}

/**
* Returns the {@link FopFactoryConfig} which is needed to get an instance of
* {@link FopFactory}.
*
* @return build the {@link FopFactoryConfig}
* @deprecated Exposing the {@link FopFactoryConfig} is only to maintain backwards compatibility
*/
public FopFactoryConfig buildConfig() {
return buildConfiguration();
}

/**
* Builds the configuration object used by the FopFactory.
*
* @return the config for the {@link FopFactory}
*/
// The {@link FopFactoryConfig} doesn't need to be exposed in the "public" API, this method
// should remain package private.
FopFactoryConfig buildConfiguration() {
fopFactoryConfigBuilder = CompletedFopFactoryConfigBuilder.INSTANCE;
return config;
}

/**
* Builds an instance of the the {@link FopFactory}.
*
* @return the FopFactory instance
*/
public FopFactory build() {
return FopFactory.newInstance(buildConfiguration());
}

/**
* Gets the base URI used to resolve all URIs within FOP.
*
* @return the base URI
*/
URI getBaseURI() {
return config.getBaseURI();
}

/**
* Returns the {@link FontManager} used for managing the fonts within FOP.
*
* @return the font managing object
*/
public FontManager getFontManager() {
return config.getFontManager();
}

/**
* Return the {@link ImageManager} used for handling images through out FOP.
*
* @return the image manager
*/
public ImageManager getImageManager() {
return config.getImageManager();
}

/**
* Sets whether to include accessibility features in document creation.
*
* @param enableAccessibility true to set accessibility on
* @return <code>this</code>
*/
public FopFactoryBuilder setAccessibility(boolean enableAccessibility) {
fopFactoryConfigBuilder.setAccessibility(enableAccessibility);
return this;
}

/**
* Sets the {@link LayoutManagerMaker} so that users can configure how FOP creates
* {@link org.apache.fop.layoutmgr.LayoutManager}s.
*
* @param lmMaker he layout manager maker
* @return <code>this</code>
*/
public FopFactoryBuilder setLayoutManagerMakerOverride(LayoutManagerMaker lmMaker) {
fopFactoryConfigBuilder.setLayoutManagerMakerOverride(lmMaker);
return this;
}

/**
* Sets the base URI, this will be used for resolving all URIs given to FOP.
*
* @param baseURI the base URI
* @return <code>this</code>
*/
public FopFactoryBuilder setBaseURI(URI baseURI) {
fopFactoryConfigBuilder.setBaseURI(baseURI);
return this;
}

/**
* Sets whether to perform strict validation on the FO used.
*
* @param validateStrictly true if the FO is to be strictly validated
* @return <code>this</code>
*/
public FopFactoryBuilder setStrictFOValidation(boolean validateStrictly) {
fopFactoryConfigBuilder.setStrictFOValidation(validateStrictly);
return this;
}

/**
* Sets whether to perform strict alidation on the user-configuration.
*
* @param validateStrictly true if the fop conf is to be strictly validated
* @return <code>this</code>
*/
public FopFactoryBuilder setStrictUserConfigValidation(
boolean validateStrictly) {
fopFactoryConfigBuilder.setStrictUserConfigValidation(validateStrictly);
return this;
}

/**
* Sets whether the indent inheritance should be broken when crossing reference area boundaries.
*
* @param value true to break inheritance when crossing reference area boundaries
* @return <code>this</code>
*/
public FopFactoryBuilder setBreakIndentInheritanceOnReferenceAreaBoundary(
boolean value) {
fopFactoryConfigBuilder.setBreakIndentInheritanceOnReferenceAreaBoundary(value);
return this;
}

/**
* Sets the resolution of resolution-dependent input.
*
* @param dpi the source resolution
* @return <code>this</code>
*/
public FopFactoryBuilder setSourceResolution(float dpi) {
fopFactoryConfigBuilder.setSourceResolution(dpi);
return this;
}

/**
* Sets the resolution of resolution-dependent output.
*
* @param dpi the target resolution
* @return <code>this</code>
*/
public FopFactoryBuilder setTargetResolution(float dpi) {
fopFactoryConfigBuilder.setTargetResolution(dpi);
return this;
}

/**
* Sets the page height of the paginated output.
*
* @param pageHeight the page height
* @return <code>this</code>
*/
public FopFactoryBuilder setPageHeight(String pageHeight) {
fopFactoryConfigBuilder.setPageHeight(pageHeight);
return this;
}

/**
* Sets the page width of the paginated output.
*
* @param pageWidth the page width
* @return <code>this</code>
*/
public FopFactoryBuilder setPageWidth(String pageWidth) {
fopFactoryConfigBuilder.setPageWidth(pageWidth);
return this;
}

/**
* FOP will ignore the specified XML element namespace.
*
* @param namespaceURI the namespace URI to ignore
* @return <code>this</code>
*/
public FopFactoryBuilder ignoreNamespace(String namespaceURI) {
fopFactoryConfigBuilder.ignoreNamespace(namespaceURI);
return this;
}

/**
* FOP will ignore the colletion of XML element namespaces.
*
* @param namespaceURIs a collection of namespace URIs to ignore
* @return <code>this</code>
*/
public FopFactoryBuilder ignoreNamespaces(Collection<String> namespaceURIs) {
fopFactoryConfigBuilder.ignoreNamespaces(namespaceURIs);
return this;
}

/**
* Sets the Avalon configuration if a FOP conf is used.
*
* @param cfg the fop conf configuration
* @return <code>this</code>
*/
public FopFactoryBuilder setConfiguration(Configuration cfg) {
fopFactoryConfigBuilder.setConfiguration(cfg);
return this;
}

/**
* Sets whether to chose a {@link org.apache.fop.render.Renderer} in preference to an
* {@link org.apache.fop.render.intermediate.IFDocumentHandler}.
*
* @param preferRenderer true to prefer {@link org.apache.fop.render.Renderer}
* @return <code>this</code>
*/
public FopFactoryBuilder setPreferRenderer(boolean preferRenderer) {
fopFactoryConfigBuilder.setPreferRenderer(preferRenderer);
return this;
}

public FopFactoryBuilder setComplexScriptFeatures(boolean csf) {
fopFactoryConfigBuilder.setComplexScriptFeaturesEnabled(csf);
return this;
}

public FopFactoryBuilder setHyphPatNames(Map<String, String> hyphPatNames) {
fopFactoryConfigBuilder.setHyphPatNames(hyphPatNames);
return this;
}

public static class FopFactoryConfigImpl implements FopFactoryConfig {

private final EnvironmentProfile enviro;

private final ImageManager imageManager;

private boolean accessibility;

private LayoutManagerMaker layoutManagerMaker;

private URI baseURI;

private boolean hasStrictFOValidation = true;

private boolean hasStrictUserValidation = FopFactoryConfig.DEFAULT_STRICT_USERCONFIG_VALIDATION;

private boolean breakIndentInheritanceOnReferenceBoundary
= FopFactoryConfig.DEFAULT_BREAK_INDENT_INHERITANCE;

private float sourceResolution = FopFactoryConfig.DEFAULT_SOURCE_RESOLUTION;

private float targetResolution = FopFactoryConfig.DEFAULT_TARGET_RESOLUTION;

private String pageHeight = FopFactoryConfig.DEFAULT_PAGE_HEIGHT;

private String pageWidth = FopFactoryConfig.DEFAULT_PAGE_WIDTH;

private Set<String> ignoredNamespaces = new HashSet<String>();

private Configuration cfg;

private boolean preferRenderer;

private boolean isComplexScript = true;

private Map<String, String> hyphPatNames;

private static final class ImageContextImpl implements ImageContext {

private final FopFactoryConfig config;

ImageContextImpl(FopFactoryConfig config) {
this.config = config;
}

public float getSourceResolution() {
return config.getSourceResolution();
}
}

FopFactoryConfigImpl(EnvironmentProfile enviro) {
this.enviro = enviro;
this.baseURI = enviro.getDefaultBaseURI();
this.imageManager = new ImageManager(new ImageContextImpl(this));
}

/** {@inheritDoc} */
public boolean isAccessibilityEnabled() {
return accessibility;
}

/** {@inheritDoc} */
public LayoutManagerMaker getLayoutManagerMakerOverride() {
return layoutManagerMaker;
}

/** {@inheritDoc} */
public ResourceResolver getResourceResolver() {
return enviro.getResourceResolver();
}

/** {@inheritDoc} */
public URI getBaseURI() {
return baseURI;
}

/** {@inheritDoc} */
public boolean validateStrictly() {
return hasStrictFOValidation;
}

/** {@inheritDoc} */
public boolean validateUserConfigStrictly() {
return hasStrictUserValidation;
}

/** {@inheritDoc} */
public boolean isBreakIndentInheritanceOnReferenceAreaBoundary() {
return breakIndentInheritanceOnReferenceBoundary;
}

/** {@inheritDoc} */
public float getSourceResolution() {
return sourceResolution;
}

/** {@inheritDoc} */
public float getTargetResolution() {
return targetResolution;
}

/** {@inheritDoc} */
public String getPageHeight() {
return pageHeight;
}

/** {@inheritDoc} */
public String getPageWidth() {
return pageWidth;
}

/** {@inheritDoc} */
public Set<String> getIgnoredNamespaces() {
return Collections.unmodifiableSet(ignoredNamespaces);
}

/** {@inheritDoc} */
public boolean isNamespaceIgnored(String namespace) {
return ignoredNamespaces.contains(namespace);
}

/** {@inheritDoc} */
public Configuration getUserConfig() {
return cfg;
}

/** {@inheritDoc} */
public boolean preferRenderer() {
return preferRenderer;
}

/** {@inheritDoc} */
public FontManager getFontManager() {
return enviro.getFontManager();
}

/** {@inheritDoc} */
public ImageManager getImageManager() {
return imageManager;
}

public boolean isComplexScriptFeaturesEnabled() {
return isComplexScript;
}

public Map<String, String> getHyphenationPatternNames() {
return hyphPatNames;
}
}

private interface FopFactoryConfigBuilder {

void setAccessibility(boolean enableAccessibility);

void setLayoutManagerMakerOverride(LayoutManagerMaker lmMaker);

void setBaseURI(URI baseURI);

void setStrictFOValidation(boolean validateStrictly);

void setStrictUserConfigValidation(boolean validateStrictly);

void setBreakIndentInheritanceOnReferenceAreaBoundary(boolean value);

void setSourceResolution(float dpi);

void setTargetResolution(float dpi);

void setPageHeight(String pageHeight);

void setPageWidth(String pageWidth);

void ignoreNamespace(String namespaceURI);

void ignoreNamespaces(Collection<String> namespaceURIs);

void setConfiguration(Configuration cfg);

void setPreferRenderer(boolean preferRenderer);

void setComplexScriptFeaturesEnabled(boolean csf);

void setHyphPatNames(Map<String, String> hyphPatNames);
}

private static final class CompletedFopFactoryConfigBuilder implements FopFactoryConfigBuilder {

private static final CompletedFopFactoryConfigBuilder INSTANCE
= new CompletedFopFactoryConfigBuilder();

private void throwIllegalStateException() {
throw new IllegalStateException("The final FOP Factory configuration has already been built");
}

public void setAccessibility(boolean enableAccessibility) {
throwIllegalStateException();
}

public void setLayoutManagerMakerOverride(LayoutManagerMaker lmMaker) {
throwIllegalStateException();

}

public void setBaseURI(URI baseURI) {
throwIllegalStateException();
}

public void setStrictFOValidation(boolean validateStrictly) {
throwIllegalStateException();
}

public void setStrictUserConfigValidation(boolean validateStrictly) {
throwIllegalStateException();
}

public void setBreakIndentInheritanceOnReferenceAreaBoundary(
boolean value) {
throwIllegalStateException();
}

public void setSourceResolution(float dpi) {
throwIllegalStateException();
}

public void setTargetResolution(float dpi) {
throwIllegalStateException();
}

public void setPageHeight(String pageHeight) {
throwIllegalStateException();
}

public void setPageWidth(String pageWidth) {
throwIllegalStateException();
}

public void ignoreNamespace(String namespaceURI) {
throwIllegalStateException();
}

public void ignoreNamespaces(Collection<String> namespaceURIs) {
throwIllegalStateException();
}

public void setConfiguration(Configuration cfg) {
throwIllegalStateException();
}

public void setPreferRenderer(boolean preferRenderer) {
throwIllegalStateException();
}

public void setComplexScriptFeaturesEnabled(boolean csf) {
throwIllegalStateException();
}

public void setHyphPatNames(Map<String, String> hyphPatNames) {
throwIllegalStateException();
}

}

private static final class ActiveFopFactoryConfigBuilder implements FopFactoryConfigBuilder {

private final FopFactoryConfigImpl config;

private ActiveFopFactoryConfigBuilder(FopFactoryConfigImpl config) {
this.config = config;
}

public void setAccessibility(boolean enableAccessibility) {
config.accessibility = enableAccessibility;
}

public void setLayoutManagerMakerOverride(LayoutManagerMaker lmMaker) {
config.layoutManagerMaker = lmMaker;
}

public void setBaseURI(URI baseURI) {
config.baseURI = baseURI;
}

public void setStrictFOValidation(boolean validateStrictly) {
config.hasStrictFOValidation = validateStrictly;
}

public void setStrictUserConfigValidation(
boolean validateStrictly) {
config.hasStrictUserValidation = validateStrictly;
}

public void setBreakIndentInheritanceOnReferenceAreaBoundary(
boolean value) {
config.breakIndentInheritanceOnReferenceBoundary = value;
}

public void setSourceResolution(float dpi) {
config.sourceResolution = dpi;
}

public void setTargetResolution(float dpi) {
config.targetResolution = dpi;
}

public void setPageHeight(String pageHeight) {
config.pageHeight = pageHeight;
}

public void setPageWidth(String pageWidth) {
config.pageWidth = pageWidth;
}

public void ignoreNamespace(String namespaceURI) {
config.ignoredNamespaces.add(namespaceURI);
}

public void ignoreNamespaces(
Collection<String> namespaceURIs) {
config.ignoredNamespaces.addAll(namespaceURIs);
}

public void setConfiguration(Configuration cfg) {
config.cfg = cfg;
}

public void setPreferRenderer(boolean preferRenderer) {
config.preferRenderer = preferRenderer;
}

public void setComplexScriptFeaturesEnabled(boolean csf) {
config.isComplexScript = csf;
}

public void setHyphPatNames(Map<String, String> hyphPatNames) {
config.hyphPatNames = hyphPatNames;
}
}

}

+ 134
- 0
src/java/org/apache/fop/apps/FopFactoryConfig.java View File

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

import java.net.URI;
import java.util.Map;
import java.util.Set;

import org.apache.avalon.framework.configuration.Configuration;

import org.apache.xmlgraphics.image.loader.ImageManager;

import org.apache.fop.apps.io.ResourceResolver;
import org.apache.fop.fonts.FontManager;
import org.apache.fop.layoutmgr.LayoutManagerMaker;

/**
* The configuration data for a {@link FopFactory} instance.
*/
// TODO: Make this interface and any implementations of this interface package private. Though
// they are used by classes that are considered the public API, this object doesn't need to be a
// part of the API. Why would a user care how the internal objects are passed around? They shouldn't.
public interface FopFactoryConfig {

/** Defines if FOP should use an alternative rule to determine text indents */
boolean DEFAULT_BREAK_INDENT_INHERITANCE = false;

/** Defines if FOP should validate the user config strictly */
boolean DEFAULT_STRICT_USERCONFIG_VALIDATION = true;

/** Defines if FOP should use strict validation for FO and user config */
boolean DEFAULT_STRICT_FO_VALIDATION = true;

/** Defines the default page-width */
String DEFAULT_PAGE_WIDTH = "8.26in";

/** Defines the default page-height */
String DEFAULT_PAGE_HEIGHT = "11in";

/** Defines the default source resolution (72dpi) for FOP */
float DEFAULT_SOURCE_RESOLUTION = 72.0f; //dpi

/** Defines the default target resolution (72dpi) for FOP */
float DEFAULT_TARGET_RESOLUTION = 72.0f; //dpi

/**
* Whether accessibility features are switched on.
*
* @return true if accessibility features have been requested
*/
boolean isAccessibilityEnabled();

/** @see {@link FopFactory#getLayoutManagerMakerOverride()} */
LayoutManagerMaker getLayoutManagerMakerOverride();

/**
* The URI resolver used through-out FOP for controlling all file access.
*
* @return the URI resolver
*/
ResourceResolver getResourceResolver();

/**
* The base URI from which URIs are resolved against.
*
* @return the base URI
*/
URI getBaseURI();

/** @see {@link FopFactory#validateStrictly()} */
boolean validateStrictly();

/** @see {@link FopFactory#validateUserConfigStrictly()} */
boolean validateUserConfigStrictly();

/** @see {@link FopFactory#isBreakIndentInheritanceOnReferenceAreaBoundary()} */
boolean isBreakIndentInheritanceOnReferenceAreaBoundary();

/** @see {@link FopFactory#getSourceResolution()} */
float getSourceResolution();

/** @see {@link FopFactory#getTargetResolution()} */
float getTargetResolution();

/** @see {@link FopFactory#getPageHeight()} */
String getPageHeight();

/** @see {@link FopFactory#getPageWidth()} */
String getPageWidth();

/** @see {@link FopFactory#getIgnoredNamespace()} */
Set<String> getIgnoredNamespaces();

/** @see {@link FopFactory#isNamespaceIgnored(String)} */
boolean isNamespaceIgnored(String namespace);

/**
* Returns the Avalon {@link Configuration} object.
*
* @return the Avalon config object
*/
Configuration getUserConfig();

/** @see {@link org.apache.fop.render.RendererFactory#isRendererPreferred()} */
boolean preferRenderer();

/** @see {@link FopFactory#getFontManager()} */
FontManager getFontManager();

/** @see {@link FopFactory#getImageManager()} */
ImageManager getImageManager();

boolean isComplexScriptFeaturesEnabled();

/** @see {@link FopFactory#getHyphenationPatternNames()} */
Map<String, String> getHyphenationPatternNames();
}

+ 0
- 406
src/java/org/apache/fop/apps/FopFactoryConfigurator.java View File

@@ -1,406 +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.apps;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Map;

import org.xml.sax.SAXException;

import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.ConfigurationException;
import org.apache.avalon.framework.configuration.DefaultConfigurationBuilder;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.apache.xmlgraphics.image.GraphicsConstants;
import org.apache.xmlgraphics.image.loader.spi.ImageImplRegistry;
import org.apache.xmlgraphics.image.loader.util.Penalty;

import org.apache.fop.fonts.FontManagerConfigurator;
import org.apache.fop.hyphenation.HyphenationTreeCache;
import org.apache.fop.util.LogUtil;

/**
* FopFactory configurator
*/
public class FopFactoryConfigurator {

/** Defines if FOP should use an alternative rule to determine text indents */
public static final boolean DEFAULT_BREAK_INDENT_INHERITANCE = false;

/** Defines if FOP should validate the user config strictly */
public static final boolean DEFAULT_STRICT_USERCONFIG_VALIDATION = true;

/** Defines if FOP should use strict validation for FO and user config */
public static final boolean DEFAULT_STRICT_FO_VALIDATION = true;

/** Defines the default page-width */
public static final String DEFAULT_PAGE_WIDTH = "8.26in";

/** Defines the default page-height */
public static final String DEFAULT_PAGE_HEIGHT = "11in";

/** Defines the default source resolution (72dpi) for FOP */
public static final float DEFAULT_SOURCE_RESOLUTION = GraphicsConstants.DEFAULT_DPI; //dpi

/** Defines the default target resolution (72dpi) for FOP */
public static final float DEFAULT_TARGET_RESOLUTION = GraphicsConstants.DEFAULT_DPI; //dpi

/** Defines the default complex script support */
public static final boolean DEFAULT_COMPLEX_SCRIPT_FEATURES = true;

private static final String PREFER_RENDERER = "prefer-renderer";

/** logger instance */
private final Log log = LogFactory.getLog(FopFactoryConfigurator.class);

/** Fop factory */
private FopFactory factory = null;

/** Fop factory configuration */
private Configuration cfg = null;

/** The base URI of the configuration file **/
private URI baseURI = null;

/**
* Default constructor
* @param factory fop factory
*/
public FopFactoryConfigurator(FopFactory factory) {
super();
this.factory = factory;
}

/**
* Initializes user agent settings from the user configuration
* file, if present: baseURL, resolution, default page size,...
* @param factory fop factory
* @throws FOPException fop exception
*/
public void configure(FopFactory factory) throws FOPException { // CSOK: MethodLength
// strict configuration
if (cfg.getChild("strict-configuration", false) != null) {
try {
factory.setStrictUserConfigValidation(
cfg.getChild("strict-configuration").getValueAsBoolean());
} catch (ConfigurationException e) {
LogUtil.handleException(log, e, false);
}
}
boolean strict = factory.validateUserConfigStrictly();
if (log.isDebugEnabled()) {
log.debug("Initializing FopFactory Configuration"
+ "with " + (strict ? "strict" : "permissive") + " validation");
}

if (cfg.getChild("accessibility", false) != null) {
try {
this.factory.setAccessibility(
cfg.getChild("accessibility").getValueAsBoolean());
} catch (ConfigurationException e) {
LogUtil.handleException(log, e, strict);
}
}

// strict fo validation
if (cfg.getChild("strict-validation", false) != null) {
try {
factory.setStrictValidation(
cfg.getChild("strict-validation").getValueAsBoolean());
} catch (ConfigurationException e) {
LogUtil.handleException(log, e, strict);
}
}

// base definitions for relative path resolution
if (cfg.getChild("base", false) != null) {
String path = cfg.getChild("base").getValue(null);
if (baseURI != null) {
path = baseURI.resolve(path).normalize().toString();
}
try {
factory.setBaseURL(path);
} catch (MalformedURLException mfue) {
LogUtil.handleException(log, mfue, strict);
}
}
if (cfg.getChild("hyphenation-base", false) != null) {
String path = cfg.getChild("hyphenation-base").getValue(null);
if (baseURI != null) {
path = baseURI.resolve(path).normalize().toString();
}
try {
factory.setHyphenBaseURL(path);
} catch (MalformedURLException mfue) {
LogUtil.handleException(log, mfue, strict);
}
}

/**
* Read configuration elements hyphenation-pattern,
* construct a map ll_CC => filename, and set it on the factory
*/
Configuration[] hyphPatConfig = cfg.getChildren("hyphenation-pattern");
if (hyphPatConfig.length != 0) {
Map/*<String,String>*/ hyphPatNames = new HashMap/*<String,String>*/();
for (int i = 0; i < hyphPatConfig.length; ++i) {
String lang;
String country;
String filename;
StringBuffer error = new StringBuffer();
String location = hyphPatConfig[i].getLocation();

lang = hyphPatConfig[i].getAttribute("lang", null);
if (lang == null) {
addError("The lang attribute of a hyphenation-pattern configuration"
+ " element must exist (" + location + ")", error);
} else if (!lang.matches("[a-zA-Z]{2}")) {
addError("The lang attribute of a hyphenation-pattern configuration"
+ " element must consist of exactly two letters ("
+ location + ")", error);
}
lang = lang.toLowerCase();

country = hyphPatConfig[i].getAttribute("country", null);
if ("".equals(country)) {
country = null;
}
if (country != null) {
if (!country.matches("[a-zA-Z]{2}")) {
addError("The country attribute of a hyphenation-pattern configuration"
+ " element must consist of exactly two letters ("
+ location + ")", error);
}
country = country.toUpperCase();
}

filename = hyphPatConfig[i].getValue(null);
if (filename == null) {
addError("The value of a hyphenation-pattern configuration"
+ " element may not be empty (" + location + ")", error);
}

if (error.length() != 0) {
LogUtil.handleError(log, error.toString(), strict);
continue;
}

String llccKey = HyphenationTreeCache.constructLlccKey(lang, country);
hyphPatNames.put(llccKey, filename);
if (log.isDebugEnabled()) {
log.debug("Using hyphenation pattern filename " + filename
+ " for lang=\"" + lang + "\""
+ (country != null ? ", country=\"" + country + "\"" : ""));
}
}
factory.setHyphPatNames(hyphPatNames);
}

// renderer options
if (cfg.getChild("source-resolution", false) != null) {
factory.setSourceResolution(
cfg.getChild("source-resolution").getValueAsFloat(
FopFactoryConfigurator.DEFAULT_SOURCE_RESOLUTION));
if (log.isDebugEnabled()) {
log.debug("source-resolution set to: " + factory.getSourceResolution()
+ "dpi (px2mm=" + factory.getSourcePixelUnitToMillimeter() + ")");
}
}
if (cfg.getChild("target-resolution", false) != null) {
factory.setTargetResolution(
cfg.getChild("target-resolution").getValueAsFloat(
FopFactoryConfigurator.DEFAULT_TARGET_RESOLUTION));
if (log.isDebugEnabled()) {
log.debug("target-resolution set to: " + factory.getTargetResolution()
+ "dpi (px2mm=" + factory.getTargetPixelUnitToMillimeter()
+ ")");
}
}
if (cfg.getChild("break-indent-inheritance", false) != null) {
try {
factory.setBreakIndentInheritanceOnReferenceAreaBoundary(
cfg.getChild("break-indent-inheritance").getValueAsBoolean());
} catch (ConfigurationException e) {
LogUtil.handleException(log, e, strict);
}
}
Configuration pageConfig = cfg.getChild("default-page-settings");
if (pageConfig.getAttribute("height", null) != null) {
factory.setPageHeight(
pageConfig.getAttribute("height", FopFactoryConfigurator.DEFAULT_PAGE_HEIGHT));
if (log.isInfoEnabled()) {
log.info("Default page-height set to: " + factory.getPageHeight());
}
}
if (pageConfig.getAttribute("width", null) != null) {
factory.setPageWidth(
pageConfig.getAttribute("width", FopFactoryConfigurator.DEFAULT_PAGE_WIDTH));
if (log.isInfoEnabled()) {
log.info("Default page-width set to: " + factory.getPageWidth());
}
}

// prefer Renderer over IFDocumentHandler
if (cfg.getChild(PREFER_RENDERER, false) != null) {
try {
factory.getRendererFactory().setRendererPreferred(
cfg.getChild(PREFER_RENDERER).getValueAsBoolean());
} catch (ConfigurationException e) {
LogUtil.handleException(log, e, strict);
}
}

// configure complex script support
Configuration csConfig = cfg.getChild("complex-scripts");
if (csConfig != null) {
this.factory.setComplexScriptFeaturesEnabled
(!csConfig.getAttributeAsBoolean ( "disabled", false ));
}

// configure font manager
new FontManagerConfigurator(cfg, baseURI).configure(factory.getFontManager(), strict);

// configure image loader framework
configureImageLoading(cfg.getChild("image-loading", false), strict);
}

private static void addError(String message, StringBuffer error) {
if (error.length() != 0) {
error.append(". ");
}
error.append(message);
}

private void configureImageLoading(Configuration parent, boolean strict) throws FOPException {
if (parent == null) {
return;
}
ImageImplRegistry registry = factory.getImageManager().getRegistry();
Configuration[] penalties = parent.getChildren("penalty");
try {
for (int i = 0, c = penalties.length; i < c; i++) {
Configuration penaltyCfg = penalties[i];
String className = penaltyCfg.getAttribute("class");
String value = penaltyCfg.getAttribute("value");
Penalty p = null;
if (value.toUpperCase().startsWith("INF")) {
p = Penalty.INFINITE_PENALTY;
} else {
try {
p = Penalty.toPenalty(Integer.parseInt(value));
} catch (NumberFormatException nfe) {
LogUtil.handleException(log, nfe, strict);
}
}
if (p != null) {
registry.setAdditionalPenalty(className, p);
}
}
} catch (ConfigurationException e) {
LogUtil.handleException(log, e, strict);
}
}

/**
* Set the user configuration.
* @param userConfigFile the configuration file
* @throws IOException if an I/O error occurs
* @throws SAXException if a parsing error occurs
*/
public void setUserConfig(File userConfigFile) throws SAXException, IOException {
try {
DefaultConfigurationBuilder cfgBuilder = new DefaultConfigurationBuilder();
setUserConfig(cfgBuilder.buildFromFile(userConfigFile));
} catch (ConfigurationException e) {
throw new FOPException(e);
}
}

/**
* Set the user configuration from an URI.
* @param uri the URI to the configuration file
* @throws IOException if an I/O error occurs
* @throws SAXException if a parsing error occurs
*/
public void setUserConfig(String uri) throws SAXException, IOException {
try {
DefaultConfigurationBuilder cfgBuilder = new DefaultConfigurationBuilder();
setUserConfig(cfgBuilder.build(uri));
} catch (ConfigurationException e) {
throw new FOPException(e);
}
}

/**
* Set the user configuration.
* @param cfg avalon configuration
* @throws FOPException if a configuration problem occurs
*/
public void setUserConfig(Configuration cfg) throws FOPException {
this.cfg = cfg;
setBaseURI();
configure(this.factory);
}

/**
* Get the avalon user configuration.
* @return the user configuration
*/
public Configuration getUserConfig() {
return this.cfg;
}

/**
* @return the baseURI
*/
public URI getBaseURI() {
return baseURI;
}

/**
* @param baseURI the baseURI to set
*/
public void setBaseURI(URI baseURI) {
this.baseURI = baseURI;
}

private void setBaseURI() throws FOPException {
String loc = cfg.getLocation();
try {
if (loc != null && loc.startsWith("file:")) {
baseURI = new URI(loc);
baseURI = baseURI.resolve(".").normalize();
}
if (baseURI == null) {
baseURI = new File(System.getProperty("user.dir")).toURI();
}
} catch (URISyntaxException e) {
throw new FOPException(e);
}
}

}

+ 2
- 0
src/java/org/apache/fop/apps/MimeConstants.java View File

@@ -32,4 +32,6 @@ public interface MimeConstants extends org.apache.xmlgraphics.util.MimeConstants
String MIME_FOP_AREA_TREE = "application/X-fop-areatree";
/** Apache FOP's intermediate format XML */
String MIME_FOP_IF = "application/X-fop-intermediate-format";
/** Bitmap images */
String MIME_BITMAP = "image/x-bitmap";
}

+ 153
- 0
src/java/org/apache/fop/apps/io/InternalResourceResolver.java View File

@@ -0,0 +1,153 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/* $Id$ */

package org.apache.fop.apps.io;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.net.URISyntaxException;

import javax.xml.transform.Source;
import javax.xml.transform.TransformerException;
import javax.xml.transform.stream.StreamSource;

import org.apache.xmlgraphics.util.uri.DataURIResolver;

/**
* This object holds the base URI from which to resolve URIs against as well as the resolver for
* resource acquisition. It also does some URI sanitization of common URI syntactical errors. This
* class takes in a {@link org.apache.fop.apps.io.ResourceResolver} and delegates all relevant
* URIs to it.
*/
public class InternalResourceResolver {
private final URI baseUri;
private final ResourceResolver resourceResolver;
private final DataURIResolver dataSchemeResolver = new DataURIResolver();

/**
* @param baseUri the base URI from which to resolve relative URIs
* @param resourceResolver the resolver to delegate to
*/
InternalResourceResolver(URI baseUri, ResourceResolver resourceResolver) {
this.baseUri = baseUri;
this.resourceResolver = resourceResolver;
}

/**
* Returns the base URI from which to resolve all URIs against.
*
* @return the base URI
*/
public URI getBaseURI() {
return baseUri;
}

/**
* Retrieve a resource given a URI in String form. This also does some syntactical sanitaion on
* the URI.
*
* @param stringUri the URI in String form
* @return the resource
* @throws IOException if an I/O error occurred
* @throws URISyntaxException if the URI syntax was invalid
*/
public Resource getResource(String stringUri) throws IOException, URISyntaxException {
if (stringUri.startsWith("data:")) {
return new Resource(resolveDataURI(stringUri));
}
return getResource(cleanURI(stringUri));
}

/**
* Retrieve a resource given a URI in String form.
*
* @param uri the resource URI
* @return the resource
* @throws IOException if an I/O error occurred
*/
public Resource getResource(URI uri) throws IOException {
if (uri.getScheme() != null && uri.getScheme().startsWith("data")) {
return new Resource(resolveDataURI(uri.toASCIIString()));
}
return resourceResolver.getResource(resolveFromBase(uri));
}

/**
* Returns the OutputStream for a given URI.
*
* @param uri the URI for the inteded stream
* @return the output stream
* @throws IOException if an I/O error occurrred
*/
public OutputStream getOutputStream(URI uri) throws IOException {
return resourceResolver.getOutputStream(resolveFromBase(uri));
}

/**
* Resolves a URI against the base URI.
*
* @param uri the URI that requires resolution
* @return the resolved URI
*/
public URI resolveFromBase(URI uri) {
return baseUri.resolve(uri);
}

/**
* Performs some sanitation for some of the most common URI syntax mistakes.
*
* @param uriStr the URI in String form
* @return a valid URI
* @throws URISyntaxException if the given String was too erroneous to validate
*/
public static URI cleanURI(String uriStr) throws URISyntaxException {
// replace back slash with forward slash to ensure windows file:/// URLS are supported
if (uriStr == null) {
return null;
}
String fixedUri = uriStr.replace('\\', '/');
fixedUri = fixedUri.replace(" ", "%20");
URI baseURI = new URI(fixedUri);
return baseURI;
}

/**
* Performs some sanitation for some of the most common URI syntax mistakes but returns a
* directory URI rather than a file URI.
*
* @param base the directory URI in String form
* @return the directory URI
* @throws URISyntaxException if the given String was too erroneous to validate
*/
public static URI getBaseURI(String base) throws URISyntaxException {
String path = base + (base.endsWith("/") ? "" : "/");
return cleanURI(path);
}

private InputStream resolveDataURI(String dataURI) {
try {
Source src = dataSchemeResolver.resolve(dataURI, "");
return src == null ? null : ((StreamSource) src).getInputStream();
} catch (TransformerException e) {
throw new RuntimeException(e);
}
}
}

src/java/org/apache/fop/render/DefaultFontResolver.java → src/java/org/apache/fop/apps/io/Resource.java View File

@@ -17,36 +17,43 @@

/* $Id$ */

package org.apache.fop.render;
package org.apache.fop.apps.io;

import javax.xml.transform.Source;

import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.fonts.FontResolver;
import java.io.FilterInputStream;
import java.io.InputStream;

/**
* Default FontResolver implementation which uses the FOUserAgent to resolve font URIs.
* This class represents a resolved resource. The type property is used by FOP to identify the resource
* content.
*
*/
public class DefaultFontResolver implements FontResolver {
public class Resource extends FilterInputStream {

private FOUserAgent userAgent;
private final String type;

/**
* Main constructor.
* @param userAgent the user agent
* @param type resource type
* @param inputStream input stream of the resource
*/
public DefaultFontResolver(FOUserAgent userAgent) {
this.userAgent = userAgent;
public Resource(String type, InputStream inputStream) {
super(inputStream);
this.type = type;
}

/** {@inheritDoc} */
public Source resolve(String href) {
return userAgent.resolveURI(href, userAgent.getFactory().getFontManager().getFontBaseURL());
/**
* Constructs a resource of 'unknown' type.
*
* @param inputStream input stream of the resource
*/
public Resource(InputStream inputStream) {
this("unknown", inputStream);
}

/** {@inheritDoc} */
public boolean isComplexScriptFeaturesEnabled() {
return userAgent.isComplexScriptFeaturesEnabled();
/**
* @return the resource type
*/
public String getType() {
return this.type;
}

}

+ 51
- 0
src/java/org/apache/fop/apps/io/ResourceResolver.java View File

@@ -0,0 +1,51 @@
/*
* 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.apps.io;

import java.io.IOException;
import java.io.OutputStream;
import java.net.URI;

/**
* Implementations of this resource resolver allow FOP users to control the URI resolution
* mechanism. All resource and output stream acquisition goes through this when its implementation
* is given to the {@link org.apache.fop.apps.EnvironmentProfile}.
*/
public interface ResourceResolver {

/**
* Get a resource given the URI pointing to said resource.
*
* @param uri the resource URI
* @return the resource
* @throws IOException if an I/O error occured during resource acquisition
*/
Resource getResource(URI uri) throws IOException;

/**
* Gets an output stream of a given URI.
*
* @param uri the output stream URI
* @return the output stream
* @throws IOException if an I/O error occured while creating an output stream
*/
OutputStream getOutputStream(URI uri) throws IOException;

}

+ 276
- 0
src/java/org/apache/fop/apps/io/ResourceResolverFactory.java View File

@@ -0,0 +1,276 @@
/*
* 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.apps.io;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URI;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

/**
* A factory class for {@link ResourceResolver}s.
*/
public final class ResourceResolverFactory {

private ResourceResolverFactory() {
}

/**
* Returns the default resource resolver, this is most basic resolver which can be used when
* no there are no I/O or file access restrictions.
*
* @return the default resource resolver
*/
public static ResourceResolver createDefaultResourceResolver() {
return DefaultResourceResolver.INSTANCE;
}

/**
* A helper merthod that creates an internal resource resolver using the default resover:
* {@link ResourceResolverFactory#createDefaultResourceResolver()}.
*
* @param baseURI the base URI from which to resolve URIs
* @return the default internal resource resolver
*/
public static InternalResourceResolver createDefaultInternalResourceResolver(URI baseURI) {
return new InternalResourceResolver(baseURI, createDefaultResourceResolver());
}

/**
* Creates an interal resource resolver given a base URI and a resource resolver.
*
* @param baseURI the base URI from which to resolve URIs
* @param resolver the resource resolver
* @return the internal resource resolver
*/
public static InternalResourceResolver createInternalResourceResolver(URI baseURI,
ResourceResolver resolver) {
return new InternalResourceResolver(baseURI, resolver);
}

/**
* Creates a temporary-resource-schema aware resource resolver. Temporary resource URIs are
* created by {@link TempResourceURIGenerator}.
*
* @param tempResourceResolver the temporary-resource-schema resolver to use
* @param defaultResourceResolver the default resource resolver to use
* @return the ressource resolver
*/
public static ResourceResolver createTempAwareResourceResolver(
TempResourceResolver tempResourceResolver,
ResourceResolver defaultResourceResolver) {
return new TempAwareResourceResolver(tempResourceResolver, defaultResourceResolver);
}

public static SchemaAwareResourceResolverBuilder createSchemaAwareResourceResolverBuilder(
ResourceResolver defaultResolver) {
return new SchemaAwareResourceResolverBuilderImpl(defaultResolver);
}

private static final class DefaultResourceResolver implements ResourceResolver {

private static final ResourceResolver INSTANCE = new DefaultResourceResolver();

private final TempAwareResourceResolver delegate;

private DefaultResourceResolver() {
delegate = new TempAwareResourceResolver(new DefaultTempResourceResolver(),
new NormalResourceResolver());
}

public Resource getResource(URI uri) throws IOException {
return delegate.getResource(uri);
}

public OutputStream getOutputStream(URI uri) throws IOException {
return delegate.getOutputStream(uri);
}

}

private static final class TempAwareResourceResolver implements ResourceResolver {

private final TempResourceResolver tempResourceResolver;

private final ResourceResolver defaultResourceResolver;

public TempAwareResourceResolver(TempResourceResolver tempResourceHandler,
ResourceResolver defaultResourceResolver) {
this.tempResourceResolver = tempResourceHandler;
this.defaultResourceResolver = defaultResourceResolver;
}

private static boolean isTempUri(URI uri) {
return TempResourceURIGenerator.isTempUri(uri);
}

public Resource getResource(URI uri) throws IOException {
if (isTempUri(uri)) {
return tempResourceResolver.getResource(uri.getPath());
} else {
return defaultResourceResolver.getResource(uri);
}
}

public OutputStream getOutputStream(URI uri) throws IOException {
if (isTempUri(uri)) {
return tempResourceResolver.getOutputStream(uri.getPath());
} else {
return defaultResourceResolver.getOutputStream(uri);
}
}

}

private static class DefaultTempResourceResolver implements TempResourceResolver {
private static File getTempFile(String path) throws IOException {
File file = new File(System.getProperty("java.io.tmpdir"), path);
file.deleteOnExit();
return file;
}

public Resource getResource(String id) throws IOException {
return new Resource(getTempFile(id).toURI().toURL().openStream());
}

public OutputStream getOutputStream(String id) throws IOException {
File file = getTempFile(id);
if (file.createNewFile()) {
return new FileOutputStream(file);
} else {
throw new IOException("Filed to create temporary file: " + id);
}
}
}

private static class NormalResourceResolver implements ResourceResolver {
public Resource getResource(URI uri) throws IOException {
return new Resource(uri.toURL().openStream());
}

public OutputStream getOutputStream(URI uri) throws IOException {
return new FileOutputStream(new File(uri));
}
}

private static final class SchemaAwareResourceResolver implements ResourceResolver {

private final Map<String, ResourceResolver> schemaHandlingResourceResolvers;

private final ResourceResolver defaultResolver;

private SchemaAwareResourceResolver(
Map<String, ResourceResolver> schemaHandlingResourceResolvers,
ResourceResolver defaultResolver) {
this.schemaHandlingResourceResolvers = schemaHandlingResourceResolvers;
this.defaultResolver = defaultResolver;
}

private ResourceResolver getResourceResolverForSchema(URI uri) {
String schema = uri.getScheme();
if (schemaHandlingResourceResolvers.containsKey(schema)) {
return schemaHandlingResourceResolvers.get(schema);
} else {
return defaultResolver;
}
}

public Resource getResource(URI uri) throws IOException {
return getResourceResolverForSchema(uri).getResource(uri);
}

public OutputStream getOutputStream(URI uri) throws IOException {
return getResourceResolverForSchema(uri).getOutputStream(uri);
}
}

public interface SchemaAwareResourceResolverBuilder {

void registerResourceResolverForSchema(String schema, ResourceResolver resourceResolver);

ResourceResolver build();
}

private static final class CompletedSchemaAwareResourceResolverBuilder
implements SchemaAwareResourceResolverBuilder {

private static final SchemaAwareResourceResolverBuilder INSTANCE
= new CompletedSchemaAwareResourceResolverBuilder();

public ResourceResolver build() {
throw new IllegalStateException("Resource resolver already built");
}

public void registerResourceResolverForSchema(String schema,
ResourceResolver resourceResolver) {
throw new IllegalStateException("Resource resolver already built");
}
}

private static final class ActiveSchemaAwareResourceResolverBuilder
implements SchemaAwareResourceResolverBuilder {

private final Map<String, ResourceResolver> schemaHandlingResourceResolvers
= new HashMap<String, ResourceResolver>();

private final ResourceResolver defaultResolver;

private ActiveSchemaAwareResourceResolverBuilder(ResourceResolver defaultResolver) {
this.defaultResolver = defaultResolver;
}

public void registerResourceResolverForSchema(String schema,
ResourceResolver resourceResolver) {
schemaHandlingResourceResolvers.put(schema, resourceResolver);
}

public ResourceResolver build() {
return new SchemaAwareResourceResolver(
Collections.unmodifiableMap(schemaHandlingResourceResolvers), defaultResolver);
}

}

private static final class SchemaAwareResourceResolverBuilderImpl
implements SchemaAwareResourceResolverBuilder {

private SchemaAwareResourceResolverBuilder delegate;

private SchemaAwareResourceResolverBuilderImpl(ResourceResolver defaultResolver) {
this.delegate = new ActiveSchemaAwareResourceResolverBuilder(defaultResolver);
}

public void registerResourceResolverForSchema(String schema,
ResourceResolver resourceResolver) {
delegate.registerResourceResolverForSchema(schema, resourceResolver);
}

public ResourceResolver build() {
ResourceResolver resourceResolver = delegate.build();
delegate = CompletedSchemaAwareResourceResolverBuilder.INSTANCE;
return resourceResolver;
}
}

}

+ 48
- 0
src/java/org/apache/fop/apps/io/TempResourceResolver.java View File

@@ -0,0 +1,48 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/* $Id$ */

package org.apache.fop.apps.io;

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

/**
* Implementations of this interface resolve URIs for temporary files used by FOP. The temporary-
* resource URI scheme comes from {@link TempResourceURIGenerator#TMP_SCHEMA}.
*/
public interface TempResourceResolver {

/**
* Get a temporary-resource given the URI pointing to said resource.
*
* @param uri the resource URI
* @return the resource
* @throws IOException if an I/O error occured during resource acquisition
*/
Resource getResource(String id) throws IOException;

/**
* Gets an temporary-output stream of a given URI.
*
* @param uri the output stream URI
* @return the output stream
* @throws IOException if an I/O error occured while creating an output stream
*/
OutputStream getOutputStream(String id) throws IOException;
}

+ 57
- 0
src/java/org/apache/fop/apps/io/TempResourceURIGenerator.java View File

@@ -0,0 +1,57 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.apache.fop.apps.io;

import java.net.URI;
import java.util.concurrent.atomic.AtomicLong;

/**
* Creates a URI for any temporary resource used within FOP.
*/
public final class TempResourceURIGenerator {

public static final String TMP_SCHEMA = "tmp";

private final String tempURIPrefix;

private final AtomicLong counter;

/**
* @param uriPrefix a prefix used to name the unique URI
*/
public TempResourceURIGenerator(String uriPrefix) {
counter = new AtomicLong();
tempURIPrefix = URI.create(TMP_SCHEMA + ":///" + uriPrefix).toASCIIString();
}

/**
* Generate a unique URI for a temporary resource
* @return the URI
*/
public URI generate() {
return URI.create(tempURIPrefix + getUniqueId());
}

private String getUniqueId() {
return Long.toHexString(counter.getAndIncrement());
}

public static boolean isTempUri(URI uri) {
return TMP_SCHEMA.equals(uri.getScheme());
}
}

+ 6
- 0
src/java/org/apache/fop/apps/io/package.html View File

@@ -0,0 +1,6 @@
<HTML>
<TITLE>org.apache.fop.io Package</TITLE>
<BODY>
<P>Classes that control all IO in FOP.</P>
</BODY>
</HTML>

+ 1
- 1
src/java/org/apache/fop/area/AreaTreeHandler.java View File

@@ -104,7 +104,7 @@ public class AreaTreeHandler extends FOEventHandler {

setupModel(userAgent, outputFormat, stream);

this.lmMaker = userAgent.getFactory().getLayoutManagerMakerOverride();
this.lmMaker = userAgent.getLayoutManagerMakerOverride();
if (lmMaker == null) {
lmMaker = new LayoutManagerMapping();
}

+ 3
- 3
src/java/org/apache/fop/area/AreaTreeParser.java View File

@@ -132,7 +132,7 @@ public class AreaTreeParser {
*/
public ContentHandler getContentHandler(AreaTreeModel treeModel, FOUserAgent userAgent) {
ElementMappingRegistry elementMappingRegistry
= userAgent.getFactory().getElementMappingRegistry();
= userAgent.getElementMappingRegistry();
return new Handler(treeModel, userAgent, elementMappingRegistry);
}

@@ -292,7 +292,7 @@ public class AreaTreeParser {
}
} else {
ContentHandlerFactoryRegistry registry
= userAgent.getFactory().getContentHandlerFactoryRegistry();
= userAgent.getContentHandlerFactoryRegistry();
ContentHandlerFactory factory = registry.getFactory(uri);
if (factory != null) {
delegate = factory.createContentHandler();
@@ -1102,7 +1102,7 @@ public class AreaTreeParser {
bkg.setURL(uri);

try {
ImageManager manager = userAgent.getFactory().getImageManager();
ImageManager manager = userAgent.getImageManager();
ImageSessionContext sessionContext
= userAgent.getImageSessionContext();
ImageInfo info = manager.getImageInfo(uri, sessionContext);

+ 19
- 28
src/java/org/apache/fop/area/CachedRenderPagesModel.java View File

@@ -21,13 +21,12 @@ package org.apache.fop.area;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.net.URI;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
@@ -36,9 +35,9 @@ import org.xml.sax.SAXException;

import org.apache.commons.io.IOUtils;

import org.apache.fop.ResourceEventProducer;
import org.apache.fop.apps.FOPException;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.apps.io.TempResourceURIGenerator;
import org.apache.fop.fonts.FontInfo;

/**
@@ -49,10 +48,12 @@ import org.apache.fop.fonts.FontInfo;
*/
public class CachedRenderPagesModel extends RenderPagesModel {

private Map<PageViewport, String> pageMap = new HashMap<PageViewport, String>();
private Map<PageViewport, URI> pageMap = new HashMap<PageViewport, URI>();

/** Base directory to save temporary file in, typically points to the user's temp dir. */
protected File baseDir;
private final URI tempBaseURI;
private static final TempResourceURIGenerator TEMP_URI_GENERATOR
= new TempResourceURIGenerator("cached-pages");

/**
* Main Constructor
@@ -65,8 +66,7 @@ public class CachedRenderPagesModel extends RenderPagesModel {
public CachedRenderPagesModel (FOUserAgent userAgent, String outputFormat,
FontInfo fontInfo, OutputStream stream) throws FOPException {
super(userAgent, outputFormat, fontInfo, stream);
//TODO: Avoid System.getProperty()?
this.baseDir = new File(System.getProperty("java.io.tmpdir"));
tempBaseURI = TEMP_URI_GENERATOR.generate();
}

/** {@inheritDoc} */
@@ -78,27 +78,19 @@ public class CachedRenderPagesModel extends RenderPagesModel {
if (pageViewport != newpage) {
try {
// load page from cache
String name = pageMap.get(pageViewport);
File tempFile = new File(baseDir, name);
log.debug("Loading page from: " + tempFile);
ObjectInputStream in = new ObjectInputStream(
new BufferedInputStream(
new FileInputStream(tempFile)));
URI tempURI = pageMap.get(pageViewport);
log.debug("Loading page from: " + tempURI);
InputStream inStream = renderer.getUserAgent().getResourceResolver().getResource(tempURI);
ObjectInputStream in = new ObjectInputStream(new BufferedInputStream(inStream));
try {
pageViewport.loadPage(in);
} finally {
IOUtils.closeQuietly(inStream);
IOUtils.closeQuietly(in);
}
if (!tempFile.delete()) {
ResourceEventProducer eventProducer
= ResourceEventProducer.Provider.get(
renderer.getUserAgent().getEventBroadcaster());
eventProducer.cannotDeleteTempFile(this, tempFile);
}
pageMap.remove(pageViewport);
} catch (Exception e) {
AreaEventProducer eventProducer
= AreaEventProducer.Provider.get(
AreaEventProducer eventProducer = AreaEventProducer.Provider.get(
renderer.getUserAgent().getEventBroadcaster());
eventProducer.pageLoadError(this, pageViewport.getPageNumberString(), e);
}
@@ -131,18 +123,17 @@ public class CachedRenderPagesModel extends RenderPagesModel {
// save page to cache
ObjectOutputStream tempstream;
String fname = "fop-page-" + page.getPageIndex() + ".ser";
File tempFile = new File(baseDir, fname);
tempFile.deleteOnExit();
tempstream = new ObjectOutputStream(new BufferedOutputStream(
new FileOutputStream(tempFile)));
URI tempURI = tempBaseURI.resolve(fname);
OutputStream outStream = renderer.getUserAgent().getResourceResolver().getOutputStream(tempURI);
tempstream = new ObjectOutputStream(new BufferedOutputStream(outStream));
try {
page.savePage(tempstream);
} finally {
IOUtils.closeQuietly(tempstream);
}
pageMap.put(page, fname);
pageMap.put(page, tempURI);
if (log.isDebugEnabled()) {
log.debug("Page saved to temporary file: " + tempFile);
log.debug("Page saved to temporary file: " + tempURI);
}
} catch (IOException ioe) {
AreaEventProducer eventProducer

+ 44
- 28
src/java/org/apache/fop/cli/CommandLineOptions.java View File

@@ -24,6 +24,7 @@ import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintStream;
import java.net.URI;
import java.util.Locale;
import java.util.Map;
import java.util.Vector;
@@ -39,9 +40,10 @@ import org.apache.fop.Version;
import org.apache.fop.accessibility.Accessibility;
import org.apache.fop.apps.FOPException;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.apps.FopConfParser;
import org.apache.fop.apps.FopFactory;
import org.apache.fop.apps.FopFactoryBuilder;
import org.apache.fop.apps.MimeConstants;
import org.apache.fop.fonts.FontManager;
import org.apache.fop.pdf.PDFAMode;
import org.apache.fop.pdf.PDFEncryptionManager;
import org.apache.fop.pdf.PDFEncryptionParams;
@@ -51,7 +53,7 @@ import org.apache.fop.render.awt.AWTRenderer;
import org.apache.fop.render.intermediate.IFContext;
import org.apache.fop.render.intermediate.IFDocumentHandler;
import org.apache.fop.render.intermediate.IFSerializer;
import org.apache.fop.render.pdf.PDFConfigurationConstants;
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;
@@ -116,12 +118,14 @@ public class CommandLineOptions {
private Map renderingOptions = new java.util.HashMap();
/* target resolution (for the user agent) */
private int targetResolution = 0;

private boolean strictValidation = true;
/* control memory-conservation policy */
private boolean conserveMemoryPolicy = false;
/* true if a complex script features are enabled */
private boolean useComplexScriptFeatures = true;

private FopFactory factory = FopFactory.newInstance();
private FopFactory factory;
private FOUserAgent foUserAgent;

private InputHandler inputHandler;
@@ -134,6 +138,8 @@ public class CommandLineOptions {

private boolean flushCache = false;

private URI baseURI = new File(".").toURI();

/**
* Construct a command line option object.
*/
@@ -183,9 +189,10 @@ public class CommandLineOptions {
addXSLTParameter("fop-output-format", getOutputFormat());
addXSLTParameter("fop-version", Version.getVersion());
foUserAgent.setConserveMemoryPolicy(conserveMemoryPolicy);
if (!useComplexScriptFeatures) {
foUserAgent.setComplexScriptFeaturesEnabled(false);
}
// TODO: Handle this!!
//if (!useComplexScriptFeatures) {
// foUserAgent.setComplexScriptFeaturesEnabled(false);
//}
} else {
return false;
}
@@ -225,9 +232,7 @@ public class CommandLineOptions {
} else if (MimeConstants.MIME_FOP_IF.equals(outputmode)
&& mimicRenderer != null) {
// render from FO to Intermediate Format
IFSerializer serializer = new IFSerializer();
serializer.setContext(new IFContext(foUserAgent));

IFSerializer serializer = new IFSerializer(new IFContext(foUserAgent));
IFDocumentHandler targetHandler
= foUserAgent.getRendererFactory().createDocumentHandler(
foUserAgent, mimicRenderer);
@@ -288,7 +293,7 @@ public class CommandLineOptions {
} else if (args[i].equals("-d")) {
setLogOption("debug", "debug");
} else if (args[i].equals("-r")) {
factory.setStrictValidation(false);
strictValidation = false;
} else if (args[i].equals("-conserve")) {
conserveMemoryPolicy = true;
} else if (args[i].equals("-flush")) {
@@ -463,6 +468,7 @@ public class CommandLineOptions {
this.useStdIn = true;
} else {
fofile = new File(filename);
baseURI = fofile.toURI();
}
return 1;
}
@@ -492,6 +498,7 @@ public class CommandLineOptions {
this.useStdIn = true;
} else {
xmlfile = new File(filename);
baseURI = xmlfile.toURI();
}
return 1;
}
@@ -723,6 +730,7 @@ public class CommandLineOptions {
this.useStdIn = true;
} else {
fofile = new File(filename);
baseURI = fofile.toURI();
}
} else if (outputmode == null) {
outputmode = MimeConstants.MIME_PDF;
@@ -781,6 +789,7 @@ public class CommandLineOptions {
this.useStdIn = true;
} else {
areatreefile = new File(filename);
baseURI = areatreefile.toURI();
}
return 1;
}
@@ -797,6 +806,7 @@ public class CommandLineOptions {
this.useStdIn = true;
} else {
iffile = new File(filename);
baseURI = iffile.toURI();
}
return 1;
}
@@ -813,21 +823,21 @@ public class CommandLineOptions {
this.useStdIn = true;
} else {
imagefile = new File(filename);
baseURI = imagefile.toURI();
}
return 1;
}
}

private PDFEncryptionParams getPDFEncryptionParams() throws FOPException {
PDFEncryptionParams params = (PDFEncryptionParams)renderingOptions.get(
PDFConfigurationConstants.ENCRYPTION_PARAMS);
PDFEncryptionParams params = (PDFEncryptionParams) renderingOptions.get(PDFEncryptionOption.ENCRYPTION_PARAMS);
if (params == null) {
if (!PDFEncryptionManager.checkAvailableAlgorithms()) {
throw new FOPException("PDF encryption requested but it is not available."
+ " Please make sure MD5 and RC4 algorithms are available.");
}
params = new PDFEncryptionParams();
renderingOptions.put(PDFConfigurationConstants.ENCRYPTION_PARAMS, params);
renderingOptions.put(PDFEncryptionOption.ENCRYPTION_PARAMS, params);
}
return params;
}
@@ -860,7 +870,7 @@ public class CommandLineOptions {
throw new FOPException("You must specify a PDF profile");
} else {
String profile = args[i + 1];
PDFAMode pdfAMode = PDFAMode.valueOf(profile);
PDFAMode pdfAMode = PDFAMode.getValueOf(profile);
if (pdfAMode != null && pdfAMode != PDFAMode.DISABLED) {
if (renderingOptions.get("pdf-a-mode") != null) {
throw new FOPException("PDF/A mode already set");
@@ -868,7 +878,7 @@ public class CommandLineOptions {
renderingOptions.put("pdf-a-mode", pdfAMode.getName());
return 1;
} else {
PDFXMode pdfXMode = PDFXMode.valueOf(profile);
PDFXMode pdfXMode = PDFXMode.getValueOf(profile);
if (pdfXMode != null && pdfXMode != PDFXMode.DISABLED) {
if (renderingOptions.get("pdf-x-mode") != null) {
throw new FOPException("PDF/X mode already set");
@@ -1027,14 +1037,26 @@ public class CommandLineOptions {
* @throws IOException
*/
private void setUserConfig() throws FOPException, IOException {
FopFactoryBuilder fopFactoryBuilder;
if (userConfigFile == null) {
return;
}
try {
factory.setUserConfig(userConfigFile);
} catch (SAXException e) {
throw new FOPException(e);
fopFactoryBuilder = new FopFactoryBuilder(baseURI);
fopFactoryBuilder.setStrictFOValidation(strictValidation);
fopFactoryBuilder.setTargetResolution(targetResolution);
fopFactoryBuilder.setComplexScriptFeatures(useComplexScriptFeatures);
} else {
try {
fopFactoryBuilder = new FopConfParser(userConfigFile).getFopFactoryBuilder();
} catch (SAXException e) {
throw new FOPException(e);
}
if (!strictValidation) {
fopFactoryBuilder.setStrictFOValidation(strictValidation);
}
if (useComplexScriptFeatures) {
fopFactoryBuilder.setComplexScriptFeatures(useComplexScriptFeatures);
}
}
factory = fopFactoryBuilder.build();
}

/**
@@ -1390,13 +1412,7 @@ public class CommandLineOptions {
}

private void flushCache() throws FOPException {
FontManager fontManager = factory.getFontManager();
File cacheFile = fontManager.getCacheFile();
if (!fontManager.deleteCache()) {
System.err.println("Failed to flush the font cache file '"
+ cacheFile + "'.");
System.exit(1);
}
factory.getFontManager().deleteCache();
}
}


+ 1
- 1
src/java/org/apache/fop/cli/IFInputHandler.java View File

@@ -62,7 +62,7 @@ public class IFInputHandler extends InputHandler {
public void renderTo(FOUserAgent userAgent, String outputFormat, OutputStream out)
throws FOPException {
IFDocumentHandler documentHandler
= userAgent.getFactory().getRendererFactory().createDocumentHandler(
= userAgent.getRendererFactory().createDocumentHandler(
userAgent, outputFormat);
try {
documentHandler.setResult(new StreamResult(out));

+ 2
- 17
src/java/org/apache/fop/cli/InputHandler.java View File

@@ -51,7 +51,6 @@ import org.apache.fop.ResourceEventProducer;
import org.apache.fop.apps.FOPException;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.apps.Fop;
import org.apache.fop.apps.FopFactory;
import org.apache.fop.render.awt.viewer.Renderable;

/**
@@ -103,25 +102,11 @@ public class InputHandler implements ErrorListener, Renderable {
public void renderTo(FOUserAgent userAgent, String outputFormat, OutputStream out)
throws FOPException {

FopFactory factory = userAgent.getFactory();
Fop fop;
if (out != null) {
fop = factory.newFop(outputFormat, userAgent, out);
fop = userAgent.newFop(outputFormat, out);
} else {
fop = factory.newFop(outputFormat, userAgent);
}

// if base URL was not explicitly set in FOUserAgent, obtain here
if (fop.getUserAgent().getBaseURL() == null && sourcefile != null) {
String baseURL = null;

try {
baseURL = new File(sourcefile.getAbsolutePath())
.getParentFile().toURI().toURL().toExternalForm();
} catch (Exception e) {
baseURL = "";
}
fop.getUserAgent().setBaseURL(baseURL);
fop = userAgent.newFop(outputFormat);
}

// Resulting SAX events (the generated FO) must be piped through to FOP

+ 1
- 1
src/java/org/apache/fop/fo/FOTreeBuilder.java View File

@@ -99,7 +99,7 @@ public class FOTreeBuilder extends DefaultHandler {
throws FOPException {

this.userAgent = foUserAgent;
this.elementMappingRegistry = userAgent.getFactory().getElementMappingRegistry();
this.elementMappingRegistry = userAgent.getElementMappingRegistry();
//This creates either an AreaTreeHandler and ultimately a Renderer, or
//one of the RTF-, MIF- etc. Handlers.
foEventHandler = foUserAgent.getRendererFactory().createFOEventHandler(

+ 4
- 4
src/java/org/apache/fop/fo/PropertyList.java View File

@@ -27,7 +27,7 @@ import org.apache.commons.logging.LogFactory;

import org.apache.xmlgraphics.util.QName;

import org.apache.fop.apps.FopFactory;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.fo.expr.PropertyException;
import org.apache.fop.fo.properties.CommonAbsolutePosition;
import org.apache.fop.fo.properties.CommonAural;
@@ -308,7 +308,7 @@ public abstract class PropertyList {
String attributeNS;
String attributeName;
String attributeValue;
FopFactory factory = getFObj().getUserAgent().getFactory();
FOUserAgent userAgent = getFObj().getUserAgent();
for (int i = 0; i < attributes.getLength(); i++) {
/* convert all attributes with the same namespace as the fo element
* the "xml:lang" and "xml:base" properties are special cases */
@@ -319,8 +319,8 @@ public abstract class PropertyList {
|| "xml:lang".equals(attributeName)
|| "xml:base".equals(attributeName)) {
convertAttributeToProperty(attributes, attributeName, attributeValue);
} else if (!factory.isNamespaceIgnored(attributeNS)) {
ElementMapping mapping = factory.getElementMappingRegistry().getElementMapping(
} else if (!userAgent.isNamespaceIgnored(attributeNS)) {
ElementMapping mapping = userAgent.getElementMappingRegistry().getElementMapping(
attributeNS);
QName attr = new QName(attributeNS, attributeName);
if (mapping != null) {

+ 4
- 6
src/java/org/apache/fop/fo/extensions/svg/SVGElement.java View File

@@ -23,7 +23,7 @@ package org.apache.fop.fo.extensions.svg;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.net.URL;
import java.net.URI;

import org.w3c.dom.Element;

@@ -71,12 +71,10 @@ public class SVGElement extends SVGObj {
/* if width and height are zero, get the bounds of the content. */

try {
URL baseURL = new URL(getUserAgent().getBaseURL() == null
? new java.io.File("").toURI().toURL().toExternalForm()
: getUserAgent().getBaseURL());
if (baseURL != null) {
URI baseUri = getUserAgent().getResourceResolver().getBaseURI();
if (baseUri != null) {
SVGOMDocument svgdoc = (SVGOMDocument)doc;
svgdoc.setURLObject(baseURL);
svgdoc.setURLObject(baseUri.toURL());
//The following line should not be called to leave FOP compatible to Batik 1.6.
//svgdoc.setDocumentURI(baseURL.toString());
}

+ 1
- 1
src/java/org/apache/fop/fo/flow/ExternalGraphic.java View File

@@ -75,7 +75,7 @@ public class ExternalGraphic extends AbstractGraphics {
//Additional processing: obtain the image's intrinsic size and baseline information
url = URISpecification.getURL(src);
FOUserAgent userAgent = getUserAgent();
ImageManager manager = userAgent.getFactory().getImageManager();
ImageManager manager = userAgent.getImageManager();
ImageInfo info = null;
try {
info = manager.getImageInfo(url, userAgent.getImageSessionContext());

+ 1
- 1
src/java/org/apache/fop/fo/properties/CommonBorderPaddingBackground.java View File

@@ -374,7 +374,7 @@ public class CommonBorderPaddingBackground {
String uri = URISpecification.getURL(newInstance.backgroundImage);
FObj fobj = pList.getFObj();
FOUserAgent userAgent = pList.getFObj().getUserAgent();
ImageManager manager = userAgent.getFactory().getImageManager();
ImageManager manager = userAgent.getImageManager();
ImageSessionContext sessionContext = userAgent.getImageSessionContext();
ImageInfo info;
try {

+ 9
- 0
src/java/org/apache/fop/fonts/CIDFont.java View File

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

package org.apache.fop.fonts;

import org.apache.fop.apps.io.InternalResourceResolver;

//Java

/**
@@ -29,6 +31,13 @@ public abstract class CIDFont extends CustomFont {
/** Contains the character widths for all characters in the font */
protected int[] width = null;

/**
* @param resourceResolver the URI resolver for controlling file access
*/
public CIDFont(InternalResourceResolver resourceResolver) {
super(resourceResolver);
}

// ---- Required ----
/**
* Returns the type of the CID font.

+ 37
- 43
src/java/org/apache/fop/fonts/CustomFont.java View File

@@ -20,13 +20,15 @@
package org.apache.fop.fonts;

import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import javax.xml.transform.Source;
import org.apache.fop.apps.io.InternalResourceResolver;


/**
@@ -35,27 +37,27 @@ import javax.xml.transform.Source;
public abstract class CustomFont extends Typeface
implements FontDescriptor, MutableFont {

private String fontName = null;
private String fullName = null;
private Set<String> familyNames = null;
private String fontSubName = null;
private String embedFileName = null;
private String embedResourceName = null;
private FontResolver resolver = null;
private String fontName;
private String fullName;
private Set<String> familyNames;
private String fontSubName;
private URI embedFileURI;
private String embedResourceName;
private final InternalResourceResolver resourceResolver;
private EmbeddingMode embeddingMode = EmbeddingMode.AUTO;

private int capHeight = 0;
private int xHeight = 0;
private int ascender = 0;
private int descender = 0;
private int capHeight;
private int xHeight;
private int ascender;
private int descender;
private int[] fontBBox = {0, 0, 0, 0};
private int flags = 4;
private int weight = 0; //0 means unknown weight
private int stemV = 0;
private int italicAngle = 0;
private int missingWidth = 0;
private int weight; //0 means unknown weight
private int stemV;
private int italicAngle;
private int missingWidth;
private FontType fontType = FontType.TYPE1;
private int firstChar = 0;
private int firstChar;
private int lastChar = 255;

private Map<Integer, Map<Integer, Integer>> kerning;
@@ -63,6 +65,12 @@ public abstract class CustomFont extends Typeface
private boolean useKerning = true;
private boolean useAdvanced = true;

/**
* @param resourceResolver the URI resource resolver for controlling file access
*/
public CustomFont(InternalResourceResolver resourceResolver) {
this.resourceResolver = resourceResolver;
}
/** the character map, mapping Unicode ranges to glyph indices. */
protected CMapSegment[] cmap;

@@ -107,15 +115,16 @@ public abstract class CustomFont extends Typeface
}

/**
* Returns an URI representing an embeddable font file. The URI will often
* be a filename or an URL.
* Returns an URI representing an embeddable font file.
*
* @return URI to an embeddable font file or null if not available.
*/
public String getEmbedFileName() {
return embedFileName;
public URI getEmbedFileURI() {
return embedFileURI;
}

/**

* Returns the embedding mode for this font.
* @return embedding mode
*/
@@ -124,20 +133,13 @@ public abstract class CustomFont extends Typeface
}

/**
* Returns a Source representing an embeddable font file.
* @return Source for an embeddable font file
* Returns an {@link InputStream} representing an embeddable font file.
*
* @return {@link InputStream} for an embeddable font file
* @throws IOException if embedFileName is not null but Source is not found
*/
public Source getEmbedFileSource() throws IOException {
Source result = null;
if (resolver != null && embedFileName != null) {
result = resolver.resolve(embedFileName);
if (result == null) {
throw new IOException("Unable to resolve Source '"
+ embedFileName + "' for embedded font");
}
}
return result;
public InputStream getInputStream() throws IOException {
return resourceResolver.getResource(embedFileURI);
}

/**
@@ -335,8 +337,8 @@ public abstract class CustomFont extends Typeface
/**
* {@inheritDoc}
*/
public void setEmbedFileName(String path) {
this.embedFileName = path;
public void setEmbedURI(URI path) {
this.embedFileURI = path;
}

/**
@@ -463,14 +465,6 @@ public abstract class CustomFont extends Typeface
this.useAdvanced = enabled;
}

/**
* Sets the font resolver. Needed for URI resolution.
* @param resolver the font resolver
*/
public void setResolver(FontResolver resolver) {
this.resolver = resolver;
}

/** {@inheritDoc} */
public void putKerningEntry(Integer key, Map<Integer, Integer> value) {
if (kerning == null) {

+ 9
- 17
src/java/org/apache/fop/fonts/CustomFontCollection.java View File

@@ -21,13 +21,16 @@ package org.apache.fop.fonts;

import java.util.List;

import org.apache.fop.apps.io.InternalResourceResolver;

/**
* Sets up a set of custom (embedded) fonts
*/
public class CustomFontCollection implements FontCollection {

private FontResolver fontResolver;
private final List<EmbedFontInfo> embedFontInfoList;
private final InternalResourceResolver uriResolver;
private final boolean useComplexScripts;

/**
* Main constructor.
@@ -35,14 +38,11 @@ public class CustomFontCollection implements FontCollection {
* @param customFonts the list of custom fonts
* @param useComplexScriptFeatures true if complex script features enabled
*/
public CustomFontCollection(FontResolver fontResolver,
List<EmbedFontInfo> customFonts, boolean useComplexScriptFeatures) {
this.fontResolver = fontResolver;
if (this.fontResolver == null) {
//Ensure that we have minimal font resolution capabilities
this.fontResolver = FontManager.createMinimalFontResolver(useComplexScriptFeatures);
}
public CustomFontCollection(InternalResourceResolver fontResolver,
List<EmbedFontInfo> customFonts, boolean useComplexScriptFeatures) {
this.uriResolver = fontResolver;
this.embedFontInfoList = customFonts;
this.useComplexScripts = useComplexScriptFeatures;
}

/** {@inheritDoc} */
@@ -52,22 +52,14 @@ public class CustomFontCollection implements FontCollection {
}

String internalName = null;
//FontReader reader = null;

for (int i = 0; i < embedFontInfoList.size(); i++) {
EmbedFontInfo embedFontInfo = embedFontInfoList.get(i);

//String metricsFile = configFontInfo.getMetricsFile();
internalName = "F" + num;
num++;
/*
reader = new FontReader(metricsFile);
reader.useKerning(configFontInfo.getKerning());
reader.setFontEmbedPath(configFontInfo.getEmbedFile());
fontInfo.addMetrics(internalName, reader.getFont());
*/

LazyFont font = new LazyFont(embedFontInfo, this.fontResolver);
LazyFont font = new LazyFont(embedFontInfo, this.uriResolver, useComplexScripts);
fontInfo.addMetrics(internalName, font);

List<FontTriplet> triplets = embedFontInfo.getFontTriplets();

+ 343
- 0
src/java/org/apache/fop/fonts/DefaultFontConfig.java View File

@@ -0,0 +1,343 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/* $Id$ */

package org.apache.fop.fonts;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.ConfigurationException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.apache.fop.apps.FOPException;
import org.apache.fop.events.EventProducer;
import org.apache.fop.util.LogUtil;

/**
* The font configuration data for the more generic fonts such as TTF and Type1, that are used by
* most the renderers.
*/
public final class DefaultFontConfig implements FontConfig {

private static final Log log = LogFactory.getLog(DefaultFontConfig.class);

private final List<Directory> directories = new ArrayList<Directory>();

private final List<Font> fonts = new ArrayList<Font>();

private final List<String> referencedFontFamilies = new ArrayList<String>();

private final boolean autoDetectFonts;

private DefaultFontConfig(boolean autoDetectFonts) {
this.autoDetectFonts = autoDetectFonts;
}

/**
* Parses the morge generic font information.
*/
public static final class DefaultFontConfigParser implements FontConfig.FontConfigParser {
/**
* Parses the font configuration and return the configuration object.
*
* @param cfg the configuration data
* @param strict whether or not to enforce strict validation
* @return the font configuration object
* @throws FOPException if an error occurs when creating the configuration object
*/
public DefaultFontConfig parse(Configuration cfg, boolean strict) throws FOPException {
return new ParserHelper(cfg, strict).instance;
}

/** {@inheritDoc} */
public FontConfig parse(Configuration cfg, FontManager fontManager, boolean strict,
EventProducer eventProducer) throws FOPException {
return parse(cfg, strict);
}
}

private static final class ParserHelper {

private boolean strict;

private Configuration fontInfoCfg;

private DefaultFontConfig instance;

private ParserHelper(Configuration cfg, boolean strict) throws FOPException {
if (cfg == null || cfg.getChild("fonts", false) == null) {
instance = null;
} else {
this.strict = strict;
this.fontInfoCfg = cfg.getChild("fonts", false);
instance = new DefaultFontConfig(cfg.getChild("auto-detect", false) != null);
parse();
}
}

private void parse() throws FOPException {
parseFonts();
parseReferencedFonts();
parseDirectories();
}

private void parseFonts() throws FOPException {
for (Configuration fontCfg : fontInfoCfg.getChildren("font")) {
String embed = fontCfg.getAttribute("embed-url", null);
if (embed == null) {
LogUtil.handleError(log, "Font configuration without embed-url attribute",
strict);
continue;
}
Font font = new Font(fontCfg.getAttribute("metrics-url", null), embed,
fontCfg.getAttribute("sub-font", null), fontCfg.getAttributeAsBoolean(
"kerning", true), fontCfg.getAttributeAsBoolean("advanced", true),
fontCfg.getAttribute("encoding-mode", EncodingMode.AUTO.getName()),
fontCfg.getAttribute("embedding-mode", EncodingMode.AUTO.getName()));
instance.fonts.add(font);
boolean hasTriplets = false;
for (Configuration tripletCfg : fontCfg.getChildren("font-triplet")) {
FontTriplet fontTriplet = getFontTriplet(tripletCfg, strict);
font.tripletList.add(fontTriplet);
hasTriplets = true;
}
// no font triplet info
if (!hasTriplets) {
LogUtil.handleError(log, "font without font-triplet", strict);
}
}
}

private void parseReferencedFonts() throws FOPException {
Configuration referencedFontsCfg = fontInfoCfg.getChild("referenced-fonts", false);
if (referencedFontsCfg != null) {
for (Configuration match : referencedFontsCfg.getChildren("match")) {
try {
instance.referencedFontFamilies.add(match.getAttribute("font-family"));
} catch (ConfigurationException ce) {
LogUtil.handleException(log, ce, strict);
continue;
}
}
}
}

private void parseDirectories() throws FOPException {
for (Configuration directoriesCfg : fontInfoCfg.getChildren("directory")) {
boolean recursive = directoriesCfg.getAttributeAsBoolean("recursive", false);
String directory;
try {
directory = directoriesCfg.getValue();
} catch (ConfigurationException e) {
LogUtil.handleException(log, e, strict);
continue;
}
if (directory == null) {
LogUtil.handleException(log,
new FOPException("directory defined without value"), strict);
continue;
}
instance.directories.add(new Directory(directory, recursive));
}
}

/**
* Creates a new FontTriplet given a triple Configuration
*
* @param tripletCfg a triplet configuration
* @return a font triplet font key
* @throws FOPException thrown if a FOP exception occurs
*/
private FontTriplet getFontTriplet(Configuration tripletCfg, boolean strict)
throws FOPException {
try {
String name = tripletCfg.getAttribute("name");
if (name == null) {
LogUtil.handleError(log, "font-triplet without name", strict);
return null;
}
String weightStr = tripletCfg.getAttribute("weight");
if (weightStr == null) {
LogUtil.handleError(log, "font-triplet without weight", strict);
return null;
}
int weight = FontUtil.parseCSS2FontWeight(FontUtil.stripWhiteSpace(weightStr));
String style = tripletCfg.getAttribute("style");
if (style == null) {
LogUtil.handleError(log, "font-triplet without style", strict);
return null;
} else {
style = FontUtil.stripWhiteSpace(style);
}
return FontInfo.createFontKey(name, style, weight);
} catch (ConfigurationException e) {
LogUtil.handleException(log, e, strict);
}
return null;
}

}

/**
* Returns the list of fonts that were parsed.
* @return a list of fonts
*/
public List<Font> getFonts() {
return Collections.unmodifiableList(fonts);
}

/**
* Returns a list of directories that were parsed.
* @return a list of directories
*/
public List<Directory> getDirectories() {
return Collections.unmodifiableList(directories);
}

/**
* Returns a list of referenced font families.
* @return the referenced font families
*/
public List<String> getReferencedFontFamily() {
return Collections.unmodifiableList(referencedFontFamilies);
}

/**
* Whether or not to enable auto-detecting of fonts in the system.
* @return true to enable auto-detect
*/
public boolean isAutoDetectFonts() {
return autoDetectFonts;
}

/**
* The directory to find fonts within.
*/
public static final class Directory {

private final String directory;

private final boolean recursive;

private Directory(String directory, boolean recurse) {
this.directory = directory;
this.recursive = recurse;
}

/**
* Returns a String representing the directory to find fonts within.
* @return the directory
*/
public String getDirectory() {
return directory;
}

/**
* Returns whether or not to recurse through the directory when finding fonts.
* @return true to recurse through the directory and sub-directories
*/
public boolean isRecursive() {
return recursive;
}
}

/**
* Represents a font object within the FOP conf.
*/
public static final class Font {

private final String metrics;

private final String embedUri;

private final String subFont;

private final boolean kerning;

private final boolean advanced;

private final String encodingMode;

private final String embeddingMode;

public String getEncodingMode() {
return encodingMode;
}

private final List<FontTriplet> tripletList = new ArrayList<FontTriplet>();

public List<FontTriplet> getTripletList() {
return Collections.unmodifiableList(tripletList);
}

private Font(String metrics, String embed, String subFont, boolean kerning,
boolean advanced, String encodingMode, String embeddingMode) {
this.metrics = metrics;
this.embedUri = embed;
this.subFont = subFont;
this.kerning = kerning;
this.advanced = advanced;
this.encodingMode = encodingMode;
this.embeddingMode = embeddingMode;
}

/**
* Whether or not to allow kerning of glyphs.
* @return true to allow glyph kerning
*/
public boolean isKerning() {
return kerning;
}

public boolean isAdvanced() {
return advanced;
}

/**
* Gets the String representing the metrics file.
* @return the metrics file
*/
public String getMetrics() {
return metrics;
}

/**
* Gets the URI of the font to embed.
* @return the font URI
*/
public String getEmbedURI() {
return embedUri;
}

/**
* Gets the sub font within, for example, a TTC.
* @return the sub font name
*/
public String getSubFont() {
return subFont;
}

public String getEmbeddingMode() {
return embeddingMode;
}
}
}

+ 186
- 0
src/java/org/apache/fop/fonts/DefaultFontConfigurator.java View File

@@ -0,0 +1,186 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/* $Id$ */

package org.apache.fop.fonts;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

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

import org.apache.fop.apps.FOPException;
import org.apache.fop.apps.io.InternalResourceResolver;
import org.apache.fop.fonts.DefaultFontConfig.Directory;
import org.apache.fop.fonts.autodetect.FontFileFinder;
import org.apache.fop.fonts.autodetect.FontInfoFinder;
import org.apache.fop.util.LogUtil;

/**
* The default configurator for fonts. This configurator can configure the more generic fonts used
* by the renderers i.e. TTF, Type1 etc...
*/
public class DefaultFontConfigurator implements FontConfigurator<EmbedFontInfo> {
/** logger instance */
protected static final Log log = LogFactory.getLog(DefaultFontConfigurator.class);

private final FontManager fontManager;
private final InternalResourceResolver resourceResolver;
private final FontEventListener listener;
private final boolean strict;

/**
* Main constructor
* @param fontInfoConfig the configuration object
* @param fontManager the font manager
* @param listener the font event listener
* @param strict true if an Exception should be thrown if an error is found.
*/
public DefaultFontConfigurator(FontManager fontManager, FontEventListener listener, boolean strict) {
this.fontManager = fontManager;
this.resourceResolver = fontManager.getResourceResolver();
this.listener = listener;
this.strict = strict;
}

/**
* Initializes font info settings from the user configuration
* @throws FOPException if an exception occurs while processing the configuration
*/
public List<EmbedFontInfo> configure(FontConfig fontInfoConfig) throws FOPException {
List<EmbedFontInfo> fontInfoList = new ArrayList<EmbedFontInfo>();
if (fontInfoConfig != null) {
assert fontInfoConfig instanceof DefaultFontConfig;
DefaultFontConfig adobeFontInfoConfig = (DefaultFontConfig) fontInfoConfig;
long start = 0;
if (log.isDebugEnabled()) {
log.debug("Starting font configuration...");
start = System.currentTimeMillis();
}
FontAdder fontAdder = new FontAdder(fontManager, resourceResolver, listener);
// native o/s search (autodetect) configuration
fontManager.autoDetectFonts(adobeFontInfoConfig.isAutoDetectFonts(), fontAdder, strict,
listener, fontInfoList);
// Add configured directories to FontInfo list
addDirectories(adobeFontInfoConfig, fontAdder, fontInfoList);
// Add configured fonts to FontInfo
FontCache fontCache = fontManager.getFontCache();
try {
addFonts(adobeFontInfoConfig, fontCache, fontInfoList);
} catch (URISyntaxException use) {
LogUtil.handleException(log, use, strict);
}
// Update referenced fonts (fonts which are not to be embedded)
fontManager.updateReferencedFonts(fontInfoList);
// Renderer-specific referenced fonts
List<String> referencedFonts = adobeFontInfoConfig.getReferencedFontFamily();
if (referencedFonts.size() > 0) {
FontTriplet.Matcher matcher = FontManagerConfigurator.createFontsMatcher(
referencedFonts, strict);
fontManager.updateReferencedFonts(fontInfoList, matcher);
}
// Update font cache if it has changed
fontManager.saveCache();
if (log.isDebugEnabled()) {
log.debug("Finished font configuration in "
+ (System.currentTimeMillis() - start) + "ms");
}
}
return Collections.unmodifiableList(fontInfoList);
}

private void addDirectories(DefaultFontConfig fontInfoConfig, FontAdder fontAdder,
List<EmbedFontInfo> fontInfoList) throws FOPException {
// directory (multiple font) configuration
List<Directory> directories = fontInfoConfig.getDirectories();
for (Directory directory : directories) {
// add fonts found in directory
FontFileFinder fontFileFinder = new FontFileFinder(directory.isRecursive() ? -1 : 1, listener);
List<URL> fontURLList;
try {
fontURLList = fontFileFinder.find(directory.getDirectory());
fontAdder.add(fontURLList, fontInfoList);
} catch (IOException e) {
LogUtil.handleException(log, e, strict);
} catch (URISyntaxException use) {
LogUtil.handleException(log, use, strict);
}
}
}

private void addFonts(DefaultFontConfig fontInfoConfig, FontCache fontCache,
List<EmbedFontInfo> fontInfoList) throws FOPException, URISyntaxException {
// font file (singular) configuration
List<DefaultFontConfig.Font> fonts = fontInfoConfig.getFonts();
for (DefaultFontConfig.Font font : fonts) {
EmbedFontInfo embedFontInfo = getFontInfo(font, fontCache);
if (embedFontInfo != null) {
fontInfoList.add(embedFontInfo);
}
}
}

private EmbedFontInfo getFontInfo(DefaultFontConfig.Font font, FontCache fontCache)
throws FOPException, URISyntaxException {
String embed = font.getEmbedURI();
String metrics = font.getMetrics();
String subFont = font.getSubFont();
URI metricsUri = metrics == null ? null : InternalResourceResolver.cleanURI(metrics);
URI embedUri = InternalResourceResolver.cleanURI(embed);

List<FontTriplet> tripletList = font.getTripletList();

// no font triplet info
if (tripletList.size() == 0) {
URI fontUri = resourceResolver.resolveFromBase(embedUri);
FontInfoFinder finder = new FontInfoFinder();
finder.setEventListener(listener);
EmbedFontInfo[] infos = finder.find(fontUri, resourceResolver, fontCache);
return infos[0]; //When subFont is set, only one font is returned
}
EncodingMode encodingMode = EncodingMode.getValue(font.getEncodingMode());
EmbeddingMode embeddingMode = EmbeddingMode.getValue(font.getEmbeddingMode());
EmbedFontInfo embedFontInfo = new EmbedFontInfo(metricsUri, font.isKerning(),
font.isAdvanced(), tripletList, embedUri, subFont, encodingMode, embeddingMode);
if (fontCache != null) {
if (!fontCache.containsFont(embedFontInfo)) {
fontCache.addFont(embedFontInfo, resourceResolver);
}
}

if (log.isDebugEnabled()) {
URI embedFile = embedFontInfo.getEmbedURI();
log.debug("Adding font " + (embedFile != null ? embedFile + ", " : "")
+ "metrics URI " + embedFontInfo.getMetricsURI());
for (int j = 0; j < tripletList.size(); ++j) {
FontTriplet triplet = tripletList.get(j);
log.debug(" Font triplet "
+ triplet.getName() + ", "
+ triplet.getStyle() + ", "
+ triplet.getWeight());
}
}
return embedFontInfo;
}
}

+ 33
- 50
src/java/org/apache/fop/fonts/EmbedFontInfo.java View File

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

import java.io.IOException;
import java.io.Serializable;
import java.net.URI;
import java.util.List;

/**
@@ -33,62 +34,66 @@ public class EmbedFontInfo implements Serializable {
/** Serialization Version UID */
private static final long serialVersionUID = 8755432068669997369L;

/** filename of the metrics file */
protected String metricsFile;
/** filename of the main font file */
protected String embedFile;
protected final URI metricsURI;
protected final URI embedURI;
/** false, to disable kerning */
protected boolean kerning;
protected final boolean kerning;
/** false, to disable advanced typographic features */
protected boolean advanced;
protected final boolean advanced;
/** the requested encoding mode for the font */
protected EncodingMode encodingMode = EncodingMode.AUTO;
private final EncodingMode encodingMode;
/** the requested embedding mode for this font */
protected EmbeddingMode embeddingMode = EmbeddingMode.AUTO;
private final EmbeddingMode embeddingMode;

/** the PostScript name of the font */
protected String postScriptName = null;
protected String postScriptName;
/** the sub-fontname of the font (used for TrueType Collections, null otherwise) */
protected String subFontName = null;
protected String subFontName;

/** the list of associated font triplets */
private List<FontTriplet> fontTriplets = null;
private List<FontTriplet> fontTriplets;

private transient boolean embedded = true;

/**
* Main constructor
* @param metricsFile path to the xml file containing font metrics
* @param kerning true if kerning should be enabled
* @param metricsURI the URI of the XML resource containing font metrics
* @param kerning True if kerning should be enabled
* @param advanced true if advanced typography features should be enabled
* @param fontTriplets list of font triplets to associate with this font
* @param embedFile path to the embeddable font file (may be null)
* @param fontTriplets List of font triplets to associate with this font
* @param embedURI Path to the embeddable font file (may be null)
* @param subFontName the sub-fontname used for TrueType Collections (null otherwise)
* @param encodingMode the encoding mode to use for this font
*/
public EmbedFontInfo(String metricsFile, boolean kerning, boolean advanced,
List<FontTriplet> fontTriplets, String embedFile, String subFontName) {
this.metricsFile = metricsFile;
this.embedFile = embedFile;
public EmbedFontInfo(URI metricsURI, boolean kerning, boolean advanced,
List<FontTriplet> fontTriplets, URI embedURI, String subFontName,
EncodingMode encodingMode, EmbeddingMode embeddingMode) {
this.metricsURI = metricsURI;
this.embedURI = embedURI;
this.kerning = kerning;
this.advanced = advanced;
this.fontTriplets = fontTriplets;
this.subFontName = subFontName;
this.encodingMode = encodingMode;
this.embeddingMode = embeddingMode;
}

/**
* Returns the path to the metrics file
* Returns the URI of the metrics XML resource
*
* @return the metrics file path
*/
public String getMetricsFile() {
return metricsFile;
public URI getMetricsURI() {
return metricsURI;
}

/**
* Returns the path to the embeddable font file
* @return the font file path
* Returns the URI to the embeddable font resource
*
* @return the font resource URI
*/
public String getEmbedFile() {
return embedFile;
public URI getEmbedURI() {
return embedURI;
}

/**
@@ -145,7 +150,7 @@ public class EmbedFontInfo implements Serializable {
* @return true if the font is embedded, false if it is referenced.
*/
public boolean isEmbedded() {
if (metricsFile != null && embedFile == null) {
if (embedURI == null) {
return false;
} else {
return this.embedded;
@@ -176,28 +181,6 @@ public class EmbedFontInfo implements Serializable {
return this.encodingMode;
}

/**
* Sets the requested encoding mode for this font.
* @param mode the new encoding mode
*/
public void setEncodingMode(EncodingMode mode) {
if (mode == null) {
throw new NullPointerException("mode must not be null");
}
this.encodingMode = mode;
}

/**
* Sets the embedding mode for this font, currently not supported for Type 1 fonts.
* @param embeddingMode the new embedding mode.
*/
public void setEmbeddingMode(EmbeddingMode embeddingMode) {
if (embeddingMode == null) {
throw new NullPointerException("embeddingMode must not be null");
}
this.embeddingMode = embeddingMode;
}

private void readObject(java.io.ObjectInputStream in)
throws IOException, ClassNotFoundException {
in.defaultReadObject();
@@ -206,7 +189,7 @@ public class EmbedFontInfo implements Serializable {

/** {@inheritDoc} */
public String toString() {
return "metrics-url=" + metricsFile + ", embed-url=" + embedFile
return "metrics-uri=" + metricsURI + ", embed-uri=" + embedURI
+ ", kerning=" + kerning
+ ", advanced=" + advanced
+ ", enc-mode=" + encodingMode

+ 11
- 6
src/java/org/apache/fop/fonts/FontAdder.java View File

@@ -19,9 +19,11 @@

package org.apache.fop.fonts;

import java.net.URISyntaxException;
import java.net.URL;
import java.util.List;

import org.apache.fop.apps.io.InternalResourceResolver;
import org.apache.fop.fonts.autodetect.FontInfoFinder;

/**
@@ -29,18 +31,19 @@ import org.apache.fop.fonts.autodetect.FontInfoFinder;
*/
public class FontAdder {
private final FontEventListener listener;
private final FontResolver resolver;
private final InternalResourceResolver resourceResolver;
private final FontManager manager;

/**
* Main constructor
* @param manager a font manager
* @param resolver a font resolver
* @param resourceResolver a font resolver
* @param listener a font event handler
*/
public FontAdder(FontManager manager, FontResolver resolver, FontEventListener listener) {
public FontAdder(FontManager manager, InternalResourceResolver resourceResolver,
FontEventListener listener) {
this.manager = manager;
this.resolver = resolver;
this.resourceResolver = resourceResolver;
this.listener = listener;
}

@@ -48,14 +51,16 @@ public class FontAdder {
* Iterates over font url list adding to font info list
* @param fontURLList font file list
* @param fontInfoList a configured font info list
* @throws URISyntaxException if a URI syntax error is found
*/
public void add(List<URL> fontURLList, List<EmbedFontInfo> fontInfoList) {
public void add(List<URL> fontURLList, List<EmbedFontInfo> fontInfoList)
throws URISyntaxException {
FontCache cache = manager.getFontCache();
FontInfoFinder finder = new FontInfoFinder();
finder.setEventListener(listener);

for (URL fontURL : fontURLList) {
EmbedFontInfo[] embedFontInfos = finder.find(fontURL, resolver, cache);
EmbedFontInfo[] embedFontInfos = finder.find(fontURL.toURI(), resourceResolver, cache);
if (embedFontInfos == null) {
continue;
}

+ 11
- 9
src/java/org/apache/fop/fonts/FontCache.java View File

@@ -29,6 +29,7 @@ import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.net.URLConnection;
import java.util.HashMap;
@@ -40,6 +41,7 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.apache.fop.apps.FOPException;
import org.apache.fop.apps.io.InternalResourceResolver;
import org.apache.fop.util.LogUtil;

/**
@@ -234,9 +236,9 @@ public final class FontCache implements Serializable {
*/
protected static String getCacheKey(EmbedFontInfo fontInfo) {
if (fontInfo != null) {
String embedFile = fontInfo.getEmbedFile();
String metricsFile = fontInfo.getMetricsFile();
return (embedFile != null) ? embedFile : metricsFile;
URI embedFile = fontInfo.getEmbedURI();
URI metricsFile = fontInfo.getMetricsURI();
return (embedFile != null) ? embedFile.toASCIIString() : metricsFile.toASCIIString();
}
return null;
}
@@ -318,7 +320,7 @@ public final class FontCache implements Serializable {
* @param fontInfo
* font info
*/
public void addFont(EmbedFontInfo fontInfo) {
public void addFont(EmbedFontInfo fontInfo, InternalResourceResolver resourceResolver) {
String cacheKey = getCacheKey(fontInfo);
synchronized (changeLock) {
CachedFontFile cachedFontFile;
@@ -329,10 +331,9 @@ public final class FontCache implements Serializable {
}
} else {
// try and determine modified date
File fontFile = getFileFromUrls(new String[] {
fontInfo.getEmbedFile(), fontInfo.getMetricsFile() });
long lastModified = (fontFile != null ? fontFile.lastModified()
: -1);
URI fontUri = resourceResolver.resolveFromBase(fontInfo.getEmbedURI());
File fontFile = new File(fontUri);
long lastModified = fontFile.lastModified();
cachedFontFile = new CachedFontFile(lastModified);
if (log.isTraceEnabled()) {
log.trace("Font added to cache: " + cacheKey);
@@ -467,8 +468,9 @@ public final class FontCache implements Serializable {
* the URL
* @return the last modified date/time
*/
public static long getLastModified(URL url) {
public static long getLastModified(URI uri) {
try {
URL url = uri.toURL();
URLConnection conn = url.openConnection();
try {
return conn.getLastModified();

src/java/org/apache/fop/fonts/FontResolver.java → src/java/org/apache/fop/fonts/FontCacheManager.java View File

@@ -19,27 +19,35 @@

package org.apache.fop.fonts;

import javax.xml.transform.Source;
import java.io.File;

import org.apache.fop.apps.FOPException;


/**
* This interface is used to resolve absolute and relative font URIs.
* Fop cache (currently only used for font info caching)
*/
public interface FontResolver {
public interface FontCacheManager {

/**
* Loads the font cache into memory from the given file.
* @param file the serialized font cache
* @return the de-serialized font cache
*/
FontCache load(File file);

/**
* Called to resolve an URI to a Source instance. The base URI needed by the URIResolver's
* resolve() method is defined to be implicitly available in this case. If the URI cannot
* be resolved, null is returned and it is assumed that the FontResolver implementation
* already warned the user about the problem.
* @param href An href attribute, which may be relative or absolute.
* @return A Source object, or null if the href could not resolved.
* Serializes the font cache to file.
* @param file the file to serialize the font cache to
* @throws FOPException if an error occurs serializing the font cache
*/
Source resolve(String href);
void save(File file) throws FOPException;

/**
* Check whether complex script features are enabled.
* @return true if FOP is to use complex script features
* Deletes the font cache from the file-system.
* @param file delete the serialized font cache
* @throws FOPException if an error occurs deleting the font cache
*/
boolean isComplexScriptFeaturesEnabled();
void delete(File file) throws FOPException;

}

+ 92
- 0
src/java/org/apache/fop/fonts/FontCacheManagerFactory.java View File

@@ -0,0 +1,92 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/* $Id$ */

package org.apache.fop.fonts;

import java.io.File;

import org.apache.fop.apps.FOPException;

/**
* A factory that provides the font caching manager mechanism.
*
*/
public final class FontCacheManagerFactory {

private FontCacheManagerFactory() {
}

/**
* Create the default font caching mechanism.
* @return the font cache manager
*/
public static FontCacheManager createDefault() {
return new FontCacheManagerImpl();
}

/**
* Create a disabled font caching mechanism which by definition does nothing to cache fonts.
* @return a completely restricted font cache manager
*/
public static FontCacheManager createDisabled() {
return new DisabledFontCacheManager();
}

private static final class FontCacheManagerImpl implements FontCacheManager {

private FontCache fontCache;

public FontCache load(File cacheFile) {
if (fontCache == null) {
fontCache = FontCache.loadFrom(cacheFile);
if (fontCache == null) {
fontCache = new FontCache();
}
}
return fontCache;
}

public void save(File cacheFile) throws FOPException {
if (fontCache != null && fontCache.hasChanged()) {
fontCache.saveTo(cacheFile);
}
}

public void delete(File cacheFile) throws FOPException {
if (!cacheFile.delete()) {
throw new FOPException("Failed to flush the font cache file '" + cacheFile + "'.");
}
}
}

private static final class DisabledFontCacheManager implements FontCacheManager {

public FontCache load(File cacheFile) {
return null;
}

public void save(File cacheFile) throws FOPException {
// nop
}

public void delete(File cacheFile) throws FOPException {
throw new FOPException("Font Cache disabled");
}
}
}

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

@@ -0,0 +1,50 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/* $Id$ */

package org.apache.fop.fonts;

import org.apache.avalon.framework.configuration.Configuration;

import org.apache.fop.apps.FOPException;
import org.apache.fop.events.EventProducer;

/**
* An interface for font configuration information.
*/
public interface FontConfig {

/**
* An interface for parsing font configuration information.
*/
public interface FontConfigParser {

/**
* Parse the font configuration and return an object containing all the necessary data.
*
* @param cfg the configuration object
* @param fontManager the font manager
* @param strict whether or not to enforce strict validation
* @param eventProducer the event producer for handling font events
* @return the configuration object
* @throws FOPException if an error occurs creating the font configuration object
*/
FontConfig parse(Configuration cfg, FontManager fontManager, boolean strict,
EventProducer eventProducer) throws FOPException;
}
}

+ 39
- 0
src/java/org/apache/fop/fonts/FontConfigurator.java View File

@@ -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.
*/

/* $Id$ */

package org.apache.fop.fonts;

import java.util.List;

import org.apache.fop.apps.FOPException;

/**
* An abstract FontInfo configurator
*/
// TODO: Make T extends some interface to make the font info type explicit
public interface FontConfigurator<T> {

/**
* Initializes font info settings from the user configuration
* @return a font info list
* @throws FOPException if an exception occurs while processing the configuration
*/
List<T> configure(FontConfig fontInfoConfig) throws FOPException;

}

+ 5
- 77
src/java/org/apache/fop/fonts/FontDetector.java View File

@@ -17,90 +17,18 @@

/* $Id$ */


package org.apache.fop.fonts;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.List;

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

import org.apache.xmlgraphics.util.ClasspathResource;

import org.apache.fop.apps.FOPException;
import org.apache.fop.fonts.autodetect.FontFileFinder;
import org.apache.fop.util.LogUtil;

/**
* Detector of operating system and classpath fonts
* An interface for the font detecting mechanism.
*/
public class FontDetector {
private static Log log = LogFactory.getLog(FontDetector.class);

private static final String[] FONT_MIMETYPES = {
"application/x-font", "application/x-font-truetype"
};

private final FontManager fontManager;
private final FontAdder fontAdder;
private final boolean strict;
private final FontEventListener eventListener;

/**
* Main constructor
* @param manager the font manager
* @param adder the font adder
* @param strict true if an Exception should be thrown if an error is found.
* @param listener for throwing font related events
*/
public FontDetector(FontManager manager, FontAdder adder, boolean strict,
FontEventListener listener) {
this.fontManager = manager;
this.fontAdder = adder;
this.strict = strict;
this.eventListener = listener;
}

/**
* Detect installed fonts on the system
* @param fontInfoList a list of fontinfo to populate
* @throws FOPException thrown if a problem occurred during detection
*/
public void detect(List<EmbedFontInfo> fontInfoList) throws FOPException {
// search in font base if it is defined and
// is a directory but don't recurse
FontFileFinder fontFileFinder = new FontFileFinder(eventListener);
String fontBaseURL = fontManager.getFontBaseURL();
if (fontBaseURL != null) {
try {
File fontBase = FileUtils.toFile(new URL(fontBaseURL));
if (fontBase != null) {
List<URL> fontURLList = fontFileFinder.find(fontBase.getAbsolutePath());
fontAdder.add(fontURLList, fontInfoList);

//Can only use the font base URL if it's a file URL
}
} catch (IOException e) {
LogUtil.handleException(log, e, strict);
}
}

// native o/s font directory finding
List<URL> systemFontList;
try {
systemFontList = fontFileFinder.find();
fontAdder.add(systemFontList, fontInfoList);
} catch (IOException e) {
LogUtil.handleException(log, e, strict);
}

// classpath font finding
ClasspathResource resource = ClasspathResource.getInstance();
for (int i = 0; i < FONT_MIMETYPES.length; i++) {
fontAdder.add(resource.listResourcesOfMimeType(FONT_MIMETYPES[i]), fontInfoList);
}
}
public interface FontDetector {
void detect(FontManager fontManager, FontAdder fontAdder, boolean strict,
FontEventListener eventListener, List<EmbedFontInfo> fontInfoList) throws FOPException;
}

+ 119
- 0
src/java/org/apache/fop/fonts/FontDetectorFactory.java View File

@@ -0,0 +1,119 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/* $Id$ */

package org.apache.fop.fonts;

import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.List;

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

import org.apache.xmlgraphics.util.ClasspathResource;

import org.apache.fop.apps.FOPException;
import org.apache.fop.fonts.autodetect.FontFileFinder;
import org.apache.fop.util.LogUtil;

/**
* A factory that provides the font detecting machanism.
*/
public final class FontDetectorFactory {
private FontDetectorFactory() {
}

/**
* Creates the default font detector
* @return the default font detector
*/
public static FontDetector createDefault() {
return new DefaultFontDetector();
}

/**
* Creates a disabled font detector which, by definition, does nothing to detect fonts.
* @return the completely restricted font detector
*/
public static FontDetector createDisabled() {
return new DisabledFontDetector();
}


private static class DisabledFontDetector implements FontDetector {
public void detect(FontManager fontManager, FontAdder fontAdder, boolean strict,
FontEventListener eventListener, List<EmbedFontInfo> fontInfoList)
throws FOPException {
// nop
}
}

/**
* Detector of operating system and classpath fonts
*/
private static class DefaultFontDetector implements FontDetector {
private static Log log = LogFactory.getLog(DefaultFontDetector.class);

private static final String[] FONT_MIMETYPES = {
"application/x-font", "application/x-font-truetype"
};

/**
* Detect installed fonts on the system
* @param fontInfoList a list of fontinfo to populate
* @throws FOPException thrown if a problem occurred during detection
*/
public void detect(FontManager fontManager, FontAdder fontAdder, boolean strict,
FontEventListener eventListener, List<EmbedFontInfo> fontInfoList)
throws FOPException {
try {
// search in font base if it is defined and
// is a directory but don't recurse
FontFileFinder fontFileFinder = new FontFileFinder(eventListener);
URI fontBaseURI = fontManager.getResourceResolver().getBaseURI();
File fontBase = FileUtils.toFile(fontBaseURI.toURL());
if (fontBase != null) {
List<URL> fontURLList = fontFileFinder.find(fontBase.getAbsolutePath());
fontAdder.add(fontURLList, fontInfoList);

//Can only use the font base URL if it's a file URL
}

// native o/s font directory finding
List<URL> systemFontList;
systemFontList = fontFileFinder.find();
fontAdder.add(systemFontList, fontInfoList);

// classpath font finding
ClasspathResource resource = ClasspathResource.getInstance();
for (String mimeTypes : FONT_MIMETYPES) {
fontAdder.add(resource.listResourcesOfMimeType(mimeTypes), fontInfoList);
}
} catch (IOException e) {
LogUtil.handleException(log, e, strict);
} catch (URISyntaxException use) {
LogUtil.handleException(log, use, strict);
}
}
}
}

+ 0
- 328
src/java/org/apache/fop/fonts/FontInfoConfigurator.java View File

@@ -1,328 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/* $Id$ */

package org.apache.fop.fonts;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.List;

import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;

import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.ConfigurationException;
import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.apache.fop.apps.FOPException;
import org.apache.fop.fonts.autodetect.FontFileFinder;
import org.apache.fop.fonts.autodetect.FontInfoFinder;
import org.apache.fop.util.LogUtil;

/**
* An abstract FontInfo configurator
*/
public class FontInfoConfigurator {
/** logger instance */
protected static final Log log = LogFactory.getLog(FontInfoConfigurator.class);

private final Configuration cfg;
private final FontManager fontManager;
private final FontResolver fontResolver;
private final FontEventListener listener;
private final boolean strict;

/**
* Main constructor
* @param cfg the configuration object
* @param fontManager the font manager
* @param fontResolver the font resolver
* @param listener the font event listener
* @param strict true if an Exception should be thrown if an error is found.
*/
public FontInfoConfigurator(Configuration cfg, FontManager fontManager,
FontResolver fontResolver, FontEventListener listener, boolean strict) {
this.cfg = cfg;
this.fontManager = fontManager;
this.fontResolver = fontResolver;
this.listener = listener;
this.strict = strict;
}

/**
* Initializes font info settings from the user configuration
* @param fontInfoList a font info list
* @throws FOPException if an exception occurs while processing the configuration
*/
public void configure(List<EmbedFontInfo> fontInfoList)
throws FOPException {
Configuration fontsCfg = cfg.getChild("fonts", false);
if (fontsCfg != null) {
long start = 0;
if (log.isDebugEnabled()) {
log.debug("Starting font configuration...");
start = System.currentTimeMillis();
}

FontAdder fontAdder = new FontAdder(fontManager, fontResolver, listener);

// native o/s search (autodetect) configuration
boolean autodetectFonts = (fontsCfg.getChild("auto-detect", false) != null);
if (autodetectFonts) {
FontDetector fontDetector = new FontDetector(fontManager, fontAdder, strict,
listener);
fontDetector.detect(fontInfoList);
}

// Add configured directories to FontInfo list
addDirectories(fontsCfg, fontAdder, fontInfoList);

// Add fonts from configuration to FontInfo list
addFonts(fontsCfg, fontManager.getFontCache(), fontInfoList);

// Update referenced fonts (fonts which are not to be embedded)
fontManager.updateReferencedFonts(fontInfoList);

// Renderer-specific referenced fonts
Configuration referencedFontsCfg = fontsCfg.getChild("referenced-fonts", false);
if (referencedFontsCfg != null) {
FontTriplet.Matcher matcher = FontManagerConfigurator.createFontsMatcher(
referencedFontsCfg, strict);
fontManager.updateReferencedFonts(fontInfoList, matcher);
}

// Update font cache if it has changed
fontManager.saveCache();

if (log.isDebugEnabled()) {
log.debug("Finished font configuration in "
+ (System.currentTimeMillis() - start) + "ms");
}
}
}

private void addDirectories(Configuration fontsCfg,
FontAdder fontAdder, List<EmbedFontInfo> fontInfoList) throws FOPException {
// directory (multiple font) configuration
Configuration[] directories = fontsCfg.getChildren("directory");
for (int i = 0; i < directories.length; i++) {
boolean recursive = directories[i].getAttributeAsBoolean("recursive", false);
String directory = null;
try {
directory = directories[i].getValue();
} catch (ConfigurationException e) {
LogUtil.handleException(log, e, strict);
continue;
}
if (directory == null) {
LogUtil.handleException(log,
new FOPException("directory defined without value"), strict);
continue;
}

// add fonts found in directory
FontFileFinder fontFileFinder = new FontFileFinder(recursive ? -1 : 1, listener);
List<URL> fontURLList;
try {
fontURLList = fontFileFinder.find(directory);
fontAdder.add(fontURLList, fontInfoList);
} catch (IOException e) {
LogUtil.handleException(log, e, strict);
}
}
}

/**
* Populates the font info list from the fonts configuration
* @param fontsCfg a fonts configuration
* @param fontCache a font cache
* @param fontInfoList a font info list
* @throws FOPException if an exception occurs while processing the configuration
*/
protected void addFonts(Configuration fontsCfg, FontCache fontCache,
List<EmbedFontInfo> fontInfoList) throws FOPException {
// font file (singular) configuration
Configuration[] font = fontsCfg.getChildren("font");
for (int i = 0; i < font.length; i++) {
EmbedFontInfo embedFontInfo = getFontInfo(
font[i], fontCache);
if (embedFontInfo != null) {
fontInfoList.add(embedFontInfo);
}
}
}

private static void closeSource(Source src) {
if (src instanceof StreamSource) {
StreamSource streamSource = (StreamSource)src;
IOUtils.closeQuietly(streamSource.getInputStream());
IOUtils.closeQuietly(streamSource.getReader());
}
}

/**
* Returns a font info from a font node Configuration definition
*
* @param fontCfg Configuration object (font node)
* @param fontCache the font cache (or null if it is disabled)
* @return the embedded font info
* @throws FOPException if something's wrong with the config data
*/
protected EmbedFontInfo getFontInfo(Configuration fontCfg, FontCache fontCache)
throws FOPException {
String metricsUrl = fontCfg.getAttribute("metrics-url", null);
String embedUrl = fontCfg.getAttribute("embed-url", null);
String subFont = fontCfg.getAttribute("sub-font", null);

if (metricsUrl == null && embedUrl == null) {
LogUtil.handleError(log,
"Font configuration without metric-url or embed-url attribute",
strict);
return null;
}
if (strict) {
//This section just checks early whether the URIs can be resolved
//Stream are immediately closed again since they will never be used anyway
if (embedUrl != null) {
Source source = fontResolver.resolve(embedUrl);
closeSource(source);
if (source == null) {
LogUtil.handleError(log,
"Failed to resolve font with embed-url '" + embedUrl + "'", strict);
return null;
}
}
if (metricsUrl != null) {
Source source = fontResolver.resolve(metricsUrl);
closeSource(source);
if (source == null) {
LogUtil.handleError(log,
"Failed to resolve font with metric-url '" + metricsUrl + "'", strict);
return null;
}
}
}

Configuration[] tripletCfg = fontCfg.getChildren("font-triplet");

// no font triplet info
if (tripletCfg.length == 0) {
LogUtil.handleError(log, "font without font-triplet", strict);

File fontFile = FontCache.getFileFromUrls(new String[] {embedUrl, metricsUrl});
URL fontURL = null;
try {
fontURL = fontFile.toURI().toURL();
} catch (MalformedURLException e) {
LogUtil.handleException(log, e, strict);
}
if (fontFile != null) {
FontInfoFinder finder = new FontInfoFinder();
finder.setEventListener(listener);
EmbedFontInfo[] infos = finder.find(fontURL, fontResolver, fontCache);
return infos[0]; //When subFont is set, only one font is returned
} else {
return null;
}
}

List<FontTriplet> tripletList = new java.util.ArrayList<FontTriplet>();
for (int j = 0; j < tripletCfg.length; j++) {
FontTriplet fontTriplet = getFontTriplet(tripletCfg[j]);
tripletList.add(fontTriplet);
}

boolean useKerning = fontCfg.getAttributeAsBoolean("kerning", true);
boolean useAdvanced = fontCfg.getAttributeAsBoolean("advanced", true);
EncodingMode encodingMode = EncodingMode.getValue(
fontCfg.getAttribute("encoding-mode", EncodingMode.AUTO.getName()));
EmbeddingMode embeddingMode = EmbeddingMode.getValue(
fontCfg.getAttribute("embedding-mode", EmbeddingMode.AUTO.toString()));
EmbedFontInfo embedFontInfo
= new EmbedFontInfo(metricsUrl, useKerning, useAdvanced, tripletList, embedUrl,
subFont);
embedFontInfo.setEncodingMode(encodingMode);
embedFontInfo.setEmbeddingMode(embeddingMode);

boolean skipCachedFont = false;
if (fontCache != null) {
if (!fontCache.containsFont(embedFontInfo)) {
fontCache.addFont(embedFontInfo);
} else {
skipCachedFont = true;
}
}

if (log.isDebugEnabled()) {
String embedFile = embedFontInfo.getEmbedFile();
log.debug( ( skipCachedFont ? "Skipping (cached) font " : "Adding font " )
+ (embedFile != null ? embedFile + ", " : "")
+ "metric file " + embedFontInfo.getMetricsFile());
for (int j = 0; j < tripletList.size(); ++j) {
FontTriplet triplet = tripletList.get(j);
log.debug(" Font triplet "
+ triplet.getName() + ", "
+ triplet.getStyle() + ", "
+ triplet.getWeight());
}
}
return embedFontInfo;
}

/**
* Creates a new FontTriplet given a triple Configuration
*
* @param tripletCfg a triplet configuration
* @return a font triplet font key
* @throws FOPException thrown if a FOP exception occurs
*/
private FontTriplet getFontTriplet(Configuration tripletCfg) throws FOPException {
try {
String name = tripletCfg.getAttribute("name");
if (name == null) {
LogUtil.handleError(log, "font-triplet without name", strict);
return null;
}

String weightStr = tripletCfg.getAttribute("weight");
if (weightStr == null) {
LogUtil.handleError(log, "font-triplet without weight", strict);
return null;
}
int weight = FontUtil.parseCSS2FontWeight(FontUtil.stripWhiteSpace(weightStr));

String style = tripletCfg.getAttribute("style");
if (style == null) {
LogUtil.handleError(log, "font-triplet without style", strict);
return null;
} else {
style = FontUtil.stripWhiteSpace(style);
}
return FontInfo.createFontKey(name, style, weight);
} catch (ConfigurationException e) {
LogUtil.handleException(log, e, strict);
}
return null;
}

}

+ 17
- 95
src/java/org/apache/fop/fonts/FontLoader.java View File

@@ -19,18 +19,13 @@

package org.apache.fop.fonts;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;

import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import java.net.URI;

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.type1.Type1FontLoader;

@@ -43,9 +38,9 @@ public abstract class FontLoader {
protected static final Log log = LogFactory.getLog(FontLoader.class);

/** URI representing the font file */
protected String fontFileURI;
/** the FontResolver to use for font URI resolution */
protected FontResolver resolver;
protected final URI fontFileURI;
/** the resource resolver to use for font URI resolution */
protected final InternalResourceResolver resourceResolver;
/** the loaded font */
protected CustomFont returnFont;

@@ -65,56 +60,19 @@ public abstract class FontLoader {
* @param useKerning indicates whether kerning information shall be loaded if available
* @param useAdvanced indicates whether advanced typographic information shall be loaded if
* available
* @param resolver the font resolver used to resolve URIs
* @param resourceResolver the font resolver used to resolve URIs
*/
public FontLoader(String fontFileURI, boolean embedded, boolean useKerning,
boolean useAdvanced, FontResolver resolver) {
public FontLoader(URI fontFileURI, boolean embedded, boolean useKerning,
boolean useAdvanced, InternalResourceResolver resourceResolver) {
this.fontFileURI = fontFileURI;
this.embedded = embedded;
this.useKerning = useKerning;
this.useAdvanced = useAdvanced;
this.resolver = resolver;
this.resourceResolver = resourceResolver;
}

private static boolean isType1(String fontURI) {
return fontURI.toLowerCase().endsWith(".pfb");
}

/**
* Loads a custom font from a File. In the case of Type 1 fonts, the PFB file must be specified.
* @param fontFile the File representation of the font
* @param subFontName the sub-fontname of a font (for TrueType Collections, null otherwise)
* @param embedded indicates whether the font is embedded or referenced
* @param embeddingMode the embedding mode
* @param encodingMode the requested encoding mode
* @param resolver the font resolver to use when resolving URIs
* @return the newly loaded font
* @throws IOException In case of an I/O error
*/
public static CustomFont loadFont(File fontFile, String subFontName,
boolean embedded, EmbeddingMode embeddingMode, EncodingMode encodingMode,
FontResolver resolver) throws IOException {
return loadFont(fontFile.toURI().toURL(), subFontName,
embedded, embeddingMode, encodingMode, resolver);
}

/**
* Loads a custom font from an URL. In the case of Type 1 fonts, the PFB file must be specified.
* @param fontUrl the URL representation of the font
* @param subFontName the sub-fontname of a font (for TrueType Collections, null otherwise)
* @param embedded indicates whether the font is embedded or referenced
* @param embeddingMode the embedding mode of the font
* @param encodingMode the requested encoding mode
* @param resolver the font resolver to use when resolving URIs
* @return the newly loaded font
* @throws IOException In case of an I/O error
*/
public static CustomFont loadFont(URL fontUrl, String subFontName,
boolean embedded, EmbeddingMode embeddingMode, EncodingMode encodingMode,
FontResolver resolver) throws IOException {
return loadFont(fontUrl.toExternalForm(), subFontName,
embedded, embeddingMode, encodingMode, true, true,
resolver);
private static boolean isType1(URI fontURI) {
return fontURI.toASCIIString().toLowerCase().endsWith(".pfb");
}

/**
@@ -127,14 +85,13 @@ public abstract class FontLoader {
* @param useKerning indicates whether kerning information should be loaded if available
* @param useAdvanced indicates whether advanced typographic information shall be loaded if
* available
* @param resolver the font resolver to use when resolving URIs
* @param resourceResolver the font resolver to use when resolving URIs
* @return the newly loaded font
* @throws IOException In case of an I/O error
*/
public static CustomFont loadFont(String fontFileURI, String subFontName,
public static CustomFont loadFont(URI fontFileURI, String subFontName,
boolean embedded, EmbeddingMode embeddingMode, EncodingMode encodingMode,
boolean useKerning, boolean useAdvanced, FontResolver resolver) throws IOException {
fontFileURI = fontFileURI.trim();
boolean useKerning, boolean useAdvanced, InternalResourceResolver resourceResolver) throws IOException {
boolean type1 = isType1(fontFileURI);
FontLoader loader;
if (type1) {
@@ -146,49 +103,14 @@ public abstract class FontLoader {
throw new IllegalArgumentException(
"Subset embedding for Type 1 fonts is not supported");
}
loader = new Type1FontLoader(fontFileURI, embedded, useKerning, resolver);
loader = new Type1FontLoader(fontFileURI, embedded, useKerning, resourceResolver);
} else {
loader = new TTFFontLoader(fontFileURI, subFontName,
embedded, embeddingMode, encodingMode, useKerning, useAdvanced, resolver);
loader = new TTFFontLoader(fontFileURI, subFontName, embedded, embeddingMode,
encodingMode, useKerning, useAdvanced, resourceResolver);
}
return loader.getFont();
}

/**
* Opens a font URI and returns an input stream.
* @param resolver the FontResolver to use for font URI resolution
* @param uri the URI representing the font
* @return the InputStream to read the font from.
* @throws IOException In case of an I/O error
* @throws MalformedURLException If an invalid URL is built
*/
public static InputStream openFontUri(FontResolver resolver, String uri)
throws IOException, MalformedURLException {
InputStream in = null;
if (resolver != null) {
Source source = resolver.resolve(uri);
if (source == null) {
String err = "Cannot load font: failed to create Source for font file "
+ uri;
throw new IOException(err);
}
if (source instanceof StreamSource) {
in = ((StreamSource) source).getInputStream();
}
if (in == null && source.getSystemId() != null) {
in = new java.net.URL(source.getSystemId()).openStream();
}
if (in == null) {
String err = "Cannot load font: failed to create InputStream from"
+ " Source for font file " + uri;
throw new IOException(err);
}
} else {
in = new URL(uri).openStream();
}
return in;
}

/**
* Reads/parses the font data.
* @throws IOException In case of an I/O error

+ 51
- 100
src/java/org/apache/fop/fonts/FontManager.java View File

@@ -20,13 +20,10 @@
package org.apache.fop.fonts;

import java.io.File;
import java.net.MalformedURLException;
import java.util.List;

import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;

import org.apache.fop.apps.FOPException;
import org.apache.fop.apps.io.InternalResourceResolver;
import org.apache.fop.fonts.FontTriplet.Matcher;
import org.apache.fop.fonts.substitute.FontSubstitutions;

@@ -38,14 +35,13 @@ import org.apache.fop.fonts.substitute.FontSubstitutions;
* font substitution, referenced fonts and similar.
*/
public class FontManager {
/** Use cache (record previously detected font triplet info) */
public static final boolean DEFAULT_USE_CACHE = true;

/** The base URL for all font URL resolutions. */
private String fontBase = null;
/** The resource resolver */
private InternalResourceResolver resourceResolver;

private final FontDetector fontDetector;

/** Font cache to speed up auto-font configuration (null if disabled) */
private FontCache fontCache = null;
private FontCacheManager fontCacheManager;

/** Font substitutions */
private FontSubstitutions fontSubstitutions = null;
@@ -56,33 +52,33 @@ public class FontManager {
/** FontTriplet matcher for fonts that shall be referenced rather than embedded. */
private FontTriplet.Matcher referencedFontsMatcher;

/** Enables/disables the use of font caching */
private boolean useCache = DEFAULT_USE_CACHE;

/** Provides a font cache file path **/
private File cacheFile;

/**
* Main constructor
*
* @param resourceResolver the URI resolver
* @param fontDetector the font detector
* @param fontCacheManager the font cache manager
*/
public FontManager() {
public FontManager(InternalResourceResolver resourceResolver, FontDetector fontDetector,
FontCacheManager fontCacheManager) {
this.resourceResolver = resourceResolver;
this.fontDetector = fontDetector;
this.fontCacheManager = fontCacheManager;
}

/**
* Sets the font base URL.
* @param fontBase font base URL
* @throws MalformedURLException if there's a problem with a URL
* Sets the font resource resolver
* @param resourceResolver resource resolver
*/
public void setFontBaseURL(String fontBase) throws MalformedURLException {
this.fontBase = fontBase;
public void setResourceResolver(InternalResourceResolver resourceResolver) {
this.resourceResolver = resourceResolver;
}

/**
* Returns the font base URL.
* @return the font base URL (or null if none was set)
*/
public String getFontBaseURL() {
return this.fontBase;
public InternalResourceResolver getResourceResolver() {
return this.resourceResolver;
}

/** @return true if kerning on base 14 fonts is enabled */
@@ -130,29 +126,22 @@ public class FontManager {
* @return the font cache file
*/
public File getCacheFile() {
return getCacheFile(false);
}

private File getCacheFile(boolean writable) {
if (cacheFile != null) {
return this.cacheFile;
return cacheFile;
}
return FontCache.getDefaultCacheFile(false);
return FontCache.getDefaultCacheFile(writable);
}

/**
* Whether or not to cache results of font triplet detection/auto-config
* @param useCache use cache or not
*/
public void setUseCache(boolean useCache) {
this.useCache = useCache;
if (!useCache) {
this.fontCache = null;
}
}

/**
* Cache results of font triplet detection/auto-config?
* @return true if this font manager uses the cache
*/
public boolean useCache() {
return useCache;
public void disableFontCache() {
fontCacheManager = FontCacheManagerFactory.createDisabled();
}

/**
@@ -160,19 +149,7 @@ public class FontManager {
* @return the font cache
*/
public FontCache getFontCache() {
if (fontCache == null) {
if (useCache) {
if (cacheFile != null) {
fontCache = FontCache.loadFrom(cacheFile);
} else {
fontCache = FontCache.load();
}
if (fontCache == null) {
fontCache = new FontCache();
}
}
}
return fontCache;
return fontCacheManager.load(getCacheFile());
}

/**
@@ -181,31 +158,16 @@ public class FontManager {
* @throws FOPException fop exception
*/
public void saveCache() throws FOPException {
if (useCache) {
if (fontCache != null && fontCache.hasChanged()) {
if (cacheFile != null) {
fontCache.saveTo(cacheFile);
} else {
fontCache.save();
}
}
}
fontCacheManager.save(getCacheFile());
}

/**
* Deletes the current FontCache file
* @return Returns true if the font cache file was successfully deleted.
* @throws FOPException -
*/
public boolean deleteCache() {
boolean deleted = false;
if (useCache) {
if (cacheFile != null) {
deleted = cacheFile.delete();
} else {
deleted = FontCache.getDefaultCacheFile(true).delete();
}
}
return deleted;
public void deleteCache() throws FOPException {
fontCacheManager.delete(getCacheFile(true));
}

/**
@@ -224,34 +186,6 @@ public class FontManager {
getFontSubstitutions().adjustFontInfo(fontInfo);
}

/**
* Minimum implemenation of FontResolver.
*/
public static class MinimalFontResolver implements FontResolver {
private boolean useComplexScriptFeatures;
MinimalFontResolver(boolean useComplexScriptFeatures) {
this.useComplexScriptFeatures = useComplexScriptFeatures;
}
/** {@inheritDoc} */
public Source resolve(String href) {
//Minimal functionality here
return new StreamSource(href);
}
/** {@inheritDoc} */
public boolean isComplexScriptFeaturesEnabled() {
return useComplexScriptFeatures;
}
}

/**
* Create minimal font resolver.
* @param useComplexScriptFeatures true if complex script features enabled
* @return a new FontResolver to be used by the font subsystem
*/
public static FontResolver createMinimalFontResolver(boolean useComplexScriptFeatures) {
return new MinimalFontResolver ( useComplexScriptFeatures );
}

/**
* Sets the {@link FontTriplet.Matcher} that can be used to identify the fonts that shall
* be referenced rather than embedded.
@@ -298,4 +232,21 @@ public class FontManager {
}
}
}

/**
* Detect fonts from the operating system via FOPs autodetect mechanism.
*
* @param autoDetectFonts if autodetect has been enabled
* @param fontAdder the font adding mechanism
* @param strict whether to enforce strict validation
* @param listener the listener for font related events
* @param fontInfoList a list of font info objects
* @throws FOPException if an exception was thrown auto-detecting fonts
*/
public void autoDetectFonts(boolean autoDetectFonts, FontAdder fontAdder, boolean strict,
FontEventListener listener, List<EmbedFontInfo> fontInfoList) throws FOPException {
if (autoDetectFonts) {
fontDetector.detect(this, fontAdder, strict, listener, fontInfoList);
}
}
}

+ 48
- 32
src/java/org/apache/fop/fonts/FontManagerConfigurator.java View File

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

import java.io.File;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.List;
import java.util.regex.Pattern;

@@ -31,6 +31,9 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.apache.fop.apps.FOPException;
import org.apache.fop.apps.io.InternalResourceResolver;
import org.apache.fop.apps.io.ResourceResolver;
import org.apache.fop.apps.io.ResourceResolverFactory;
import org.apache.fop.fonts.substitute.FontSubstitutions;
import org.apache.fop.fonts.substitute.FontSubstitutionsConfigurator;
import org.apache.fop.util.LogUtil;
@@ -45,24 +48,21 @@ public class FontManagerConfigurator {

private final Configuration cfg;

private URI baseURI = null;
private final URI defaultBaseUri;

/**
* Main constructor
* @param cfg the font manager configuration object
*/
public FontManagerConfigurator(Configuration cfg) {
this.cfg = cfg;
}
private final ResourceResolver resourceResolver;

/**
* Main constructor
* @param cfg the font manager configuration object
* @param baseURI the base URI of the configuration
* @param defaultBaseUri the default URI base to use for URI resolution
* @param resourceResolver the resource resolver
*/
public FontManagerConfigurator(Configuration cfg, URI baseURI) {
public FontManagerConfigurator(Configuration cfg, URI defaultBaseUri,
ResourceResolver resourceResolver) {
this.cfg = cfg;
this.baseURI = baseURI;
this.defaultBaseUri = defaultBaseUri;
this.resourceResolver = resourceResolver;
}

/**
@@ -75,28 +75,29 @@ public class FontManagerConfigurator {
// caching (fonts)
if (cfg.getChild("use-cache", false) != null) {
try {
fontManager.setUseCache(cfg.getChild("use-cache").getValueAsBoolean());
} catch (ConfigurationException e) {
LogUtil.handleException(log, e, true);
}
}
if (cfg.getChild("cache-file", false) != null) {
try {
fontManager.setCacheFile(new File(cfg.getChild("cache-file").getValue()));
} catch (ConfigurationException e) {
LogUtil.handleException(log, e, true);
if (!cfg.getChild("use-cache").getValueAsBoolean()) {
fontManager.disableFontCache();
} else {
if (cfg.getChild("cache-file", false) != null) {
fontManager.setCacheFile(new File(cfg.getChild("cache-file").getValue()));
}
}
} catch (ConfigurationException mfue) {
LogUtil.handleException(log, mfue, true);
}
}
if (cfg.getChild("font-base", false) != null) {
String path = cfg.getChild("font-base").getValue(null);
if (baseURI != null) {
path = baseURI.resolve(path).normalize().toString();
}
try {
fontManager.setFontBaseURL(path);
} catch (MalformedURLException mfue) {
LogUtil.handleException(log, mfue, true);
URI fontBase = InternalResourceResolver.getBaseURI(cfg.getChild("font-base").getValue(
null));
fontManager.setResourceResolver(ResourceResolverFactory.createInternalResourceResolver(
defaultBaseUri.resolve(fontBase), resourceResolver));
} catch (URISyntaxException use) {
LogUtil.handleException(log, use, true);
}
} else {
fontManager.setResourceResolver(ResourceResolverFactory.createInternalResourceResolver(
defaultBaseUri, resourceResolver));
}

// [GA] permit configuration control over base14 kerning; without this,
@@ -114,7 +115,6 @@ public class FontManagerConfigurator {
// global font configuration
Configuration fontsCfg = cfg.getChild("fonts", false);
if (fontsCfg != null) {

// font substitution
Configuration substitutionsCfg = fontsCfg.getChild("substitutions", false);
if (substitutionsCfg != null) {
@@ -122,7 +122,6 @@ public class FontManagerConfigurator {
new FontSubstitutionsConfigurator(substitutionsCfg).configure(substitutions);
fontManager.setFontSubstitutions(substitutions);
}

// referenced fonts (fonts which are not to be embedded)
Configuration referencedFontsCfg = fontsCfg.getChild("referenced-fonts", false);
if (referencedFontsCfg != null) {
@@ -130,7 +129,6 @@ public class FontManagerConfigurator {
referencedFontsCfg, strict);
fontManager.setReferencedFontsMatcher(matcher);
}

}
}

@@ -159,6 +157,24 @@ public class FontManagerConfigurator {
return orMatcher;
}

/**
* Creates a font triplet matcher from a configuration object.
* @param fontFamilies the list of font families
* @param strict true for strict configuraton error handling
* @return the font matcher
* @throws FOPException if an error occurs while building the matcher
*/
public static FontTriplet.Matcher createFontsMatcher(
List<String> fontFamilies, boolean strict) throws FOPException {
List<FontTriplet.Matcher> matcherList = new java.util.ArrayList<FontTriplet.Matcher>();
for (String fontFamily : fontFamilies) {
matcherList.add(new FontFamilyRegExFontTripletMatcher(fontFamily));
}
FontTriplet.Matcher orMatcher = new OrFontTripletMatcher(
matcherList.toArray(new FontTriplet.Matcher[matcherList.size()]));
return orMatcher;
}

private static class OrFontTripletMatcher implements FontTriplet.Matcher {

private final FontTriplet.Matcher[] matchers;

+ 49
- 63
src/java/org/apache/fop/fonts/FontReader.java View File

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

//Java
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
@@ -32,12 +34,12 @@ import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;

import org.apache.fop.apps.FOPException;
import org.apache.fop.apps.io.InternalResourceResolver;
import org.apache.fop.fonts.apps.TTFReader;

/**
@@ -52,19 +54,30 @@ import org.apache.fop.fonts.apps.TTFReader;
*/
public class FontReader extends DefaultHandler {

// private Locator locator = null; // not used at present
private boolean isCID = false;
private CustomFont returnFont = null;
private MultiByteFont multiFont = null;
private SingleByteFont singleFont = null;
private boolean isCID;
private CustomFont returnFont;
private MultiByteFont multiFont;
private SingleByteFont singleFont;
private final InternalResourceResolver resourceResolver;
private StringBuffer text = new StringBuffer();

private List<Integer> cidWidths = null;
private int cidWidthIndex = 0;
private List<Integer> cidWidths;
//private int cidWidthIndex;

private Map<Integer, Integer> currentKerning = null;
private Map<Integer, Integer> currentKerning;

private List<CMapSegment> bfranges = null;
private List<CMapSegment> bfranges;

/**
* Construct a FontReader object from a path to a metric.xml file
* and read metric data
* @param source Source of the font metric file
* @throws FOPException if loading the font fails
*/
public FontReader(InputSource source, InternalResourceResolver resourceResolver) throws FOPException {
this.resourceResolver = resourceResolver;
createFont(source);
}

private void createFont(InputSource source) throws FOPException {
XMLReader parser = null;
@@ -81,11 +94,9 @@ public class FontReader extends DefaultHandler {
}

try {
parser.setFeature("http://xml.org/sax/features/namespace-prefixes",
false);
parser.setFeature("http://xml.org/sax/features/namespace-prefixes", false);
} catch (SAXException e) {
throw new FOPException("You need a SAX parser which supports SAX version 2",
e);
throw new FOPException("You need a SAX parser which supports SAX version 2", e);
}

parser.setContentHandler(this);
@@ -104,8 +115,8 @@ public class FontReader extends DefaultHandler {
* Sets the path to embed a font. A null value disables font embedding.
* @param path URI for the embeddable file
*/
public void setFontEmbedPath(String path) {
returnFont.setEmbedFileName(path);
public void setFontEmbedURI(URI path) {
returnFont.setEmbedURI(path);
}

/**
@@ -124,15 +135,6 @@ public class FontReader extends DefaultHandler {
returnFont.setAdvancedEnabled(enabled);
}

/**
* Sets the font resolver. Needed for URI resolution.
* @param resolver the font resolver
*/
public void setResolver(FontResolver resolver) {
returnFont.setResolver(resolver);
}


/**
* Get the generated font object
* @return the font
@@ -141,16 +143,6 @@ public class FontReader extends DefaultHandler {
return returnFont;
}

/**
* Construct a FontReader object from a path to a metric.xml file
* and read metric data
* @param source Source of the font metric file
* @throws FOPException if loading the font fails
*/
public FontReader(InputSource source) throws FOPException {
createFont(source);
}

/**
* {@inheritDoc}
*/
@@ -161,45 +153,41 @@ public class FontReader extends DefaultHandler {
/**
* {@inheritDoc}
*/
@Override
public void setDocumentLocator(Locator locator) {
// this.locator = locator; // not used at present
}

/**
* {@inheritDoc}
*/
@Override
public void startElement(String uri, String localName, String qName,
Attributes attributes) throws SAXException {
public void startElement(String uri, String localName, String qName, Attributes attributes)
throws SAXException {
if (localName.equals("font-metrics")) {
if ("TYPE0".equals(attributes.getValue("type"))) {
multiFont = new MultiByteFont();
multiFont = new MultiByteFont(resourceResolver);
returnFont = multiFont;
isCID = true;
TTFReader.checkMetricsVersion(attributes);
} else if ("TRUETYPE".equals(attributes.getValue("type"))) {
singleFont = new SingleByteFont();
singleFont = new SingleByteFont(resourceResolver);
singleFont.setFontType(FontType.TRUETYPE);
returnFont = singleFont;
isCID = false;
TTFReader.checkMetricsVersion(attributes);
} else {
singleFont = new SingleByteFont();
singleFont = new SingleByteFont(resourceResolver);
singleFont.setFontType(FontType.TYPE1);
returnFont = singleFont;
isCID = false;
}
} else if ("embed".equals(localName)) {
returnFont.setEmbedFileName(attributes.getValue("file"));
try {
returnFont.setEmbedURI(InternalResourceResolver.cleanURI(attributes.getValue("file")));
} catch (URISyntaxException e) {
throw new SAXException("URI syntax error in metrics file: " + e.getMessage(), e);
}
returnFont.setEmbedResourceName(attributes.getValue("class"));
} else if ("cid-widths".equals(localName)) {
cidWidthIndex = getInt(attributes.getValue("start-index"));
// This is unused
// cidWidthIndex = getInt(attributes.getValue("start-index"));
cidWidths = new ArrayList<Integer>();
} else if ("kerning".equals(localName)) {
currentKerning = new HashMap<Integer, Integer>();
returnFont.putKerningEntry(new Integer(attributes.getValue("kpx1")),
currentKerning);
returnFont.putKerningEntry(getInt(attributes.getValue("kpx1")),
currentKerning);
} else if ("bfranges".equals(localName)) {
bfranges = new ArrayList<CMapSegment>();
} else if ("bf".equals(localName)) {
@@ -208,20 +196,19 @@ public class FontReader extends DefaultHandler {
getInt(attributes.getValue("gi")));
bfranges.add(entry);
} else if ("wx".equals(localName)) {
cidWidths.add(new Integer(attributes.getValue("w")));
} else if ("widths".equals(localName)) {
//singleFont.width = new int[256];
cidWidths.add(getInt(attributes.getValue("w")));
// } else if ("widths".equals(localName)) {
// singleFont.width = new int[256];
} else if ("char".equals(localName)) {
try {
singleFont.setWidth(Integer.parseInt(attributes.getValue("idx")),
Integer.parseInt(attributes.getValue("wdt")));
singleFont.setWidth(getInt(attributes.getValue("idx")),
getInt(attributes.getValue("wdt")));
} catch (NumberFormatException ne) {
throw new SAXException("Malformed width in metric file: "
+ ne.getMessage(), ne);
throw new SAXException("Malformed width in metric file: " + ne.getMessage(), ne);
}
} else if ("pair".equals(localName)) {
currentKerning.put(new Integer(attributes.getValue("kpx2")),
new Integer(attributes.getValue("kern")));
currentKerning.put(getInt(attributes.getValue("kpx2")),
getInt(attributes.getValue("kern")));
}

}
@@ -319,5 +306,4 @@ public class FontReader extends DefaultHandler {
public void characters(char[] ch, int start, int length) {
text.append(ch, start, length);
}

}

+ 10
- 45
src/java/org/apache/fop/fonts/FontSetup.java View File

@@ -22,9 +22,7 @@ package org.apache.fop.fonts;
// FOP (base 14 fonts)
import java.util.List;

import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;

import org.apache.fop.apps.io.InternalResourceResolver;
import org.apache.fop.fonts.base14.Courier;
import org.apache.fop.fonts.base14.CourierBold;
import org.apache.fop.fonts.base14.CourierBoldOblique;
@@ -71,11 +69,11 @@ public final class FontSetup {
*
* @param fontInfo the font info object to set up
* @param embedFontInfoList a list of EmbedFontInfo objects
* @param resolver the font resolver
* @param resourceResolver the font resolver
* @param base14Kerning true if base14 kerning applies
*/
public static void setup(FontInfo fontInfo, List<EmbedFontInfo> embedFontInfoList,
FontResolver resolver, boolean base14Kerning) {
public static void setup(FontInfo fontInfo, List embedFontInfoList,
InternalResourceResolver resourceResolver, boolean base14Kerning) {
fontInfo.addMetrics("F1", new Helvetica(base14Kerning));
fontInfo.addMetrics("F2", new HelveticaOblique(base14Kerning));
fontInfo.addMetrics("F3", new HelveticaBold(base14Kerning));
@@ -181,7 +179,7 @@ public final class FontSetup {
final int startNum = 15;

/* Add configured fonts */
addConfiguredFonts(fontInfo, embedFontInfoList, startNum, resolver, base14Kerning);
addConfiguredFonts(fontInfo, embedFontInfoList, startNum, resourceResolver, base14Kerning);
}

/**
@@ -189,21 +187,16 @@ public final class FontSetup {
* @param fontInfo the font info to set up
* @param embedFontInfoList a list of EmbedFontInfo objects
* @param num starting index for internal font numbering
* @param resolver the font resolver
* @param resourceResolver the font resolver
*/
private static void addConfiguredFonts(FontInfo fontInfo,
List<EmbedFontInfo> embedFontInfoList, int num, FontResolver resolver,
List<EmbedFontInfo> embedFontInfoList, int num,
InternalResourceResolver resourceResolver,
boolean base14Kerning) {
if (embedFontInfoList == null) {
return; //No fonts to process
}

if (resolver == null) {
//Ensure that we have minimal font resolution capabilities
//None of the built-in base14 fonts have advanced typographic data
boolean useAdvanced = false;
resolver = createMinimalFontResolver(useAdvanced);
}
assert resourceResolver != null;

String internalName = null;

@@ -211,7 +204,7 @@ public final class FontSetup {
internalName = "F" + num;
num++;

LazyFont font = new LazyFont(embedFontInfo, resolver);
LazyFont font = new LazyFont(embedFontInfo, resourceResolver, false);
fontInfo.addMetrics(internalName, font);

List<FontTriplet> triplets = embedFontInfo.getFontTriplets();
@@ -221,32 +214,4 @@ public final class FontSetup {
}
}
}

/**
* Minimum implemenation of FontResolver.
*/
public static class MinimalFontResolver implements FontResolver {
private boolean useComplexScriptFeatures;
MinimalFontResolver(boolean useComplexScriptFeatures) {
this.useComplexScriptFeatures = useComplexScriptFeatures;
}
/** {@inheritDoc} */
public Source resolve(String href) {
//Minimal functionality here
return new StreamSource(href);
}
/** {@inheritDoc} */
public boolean isComplexScriptFeaturesEnabled() {
return useComplexScriptFeatures;
}
}

/**
* Create minimal font resolver.
* @param useComplexScriptFeatures true if complex script features enabled
* @return a new FontResolver to be used by the font subsystem
*/
public static FontResolver createMinimalFontResolver(boolean useComplexScriptFeatures) {
return new MinimalFontResolver ( useComplexScriptFeatures );
}
}

+ 3
- 10
src/java/org/apache/fop/fonts/FontTriplet.java View File

@@ -27,6 +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);

/** serial version UID */
private static final long serialVersionUID = 1168991106658033508L;

@@ -38,14 +40,6 @@ public class FontTriplet implements Comparable<FontTriplet>, Serializable {
//This is only a cache
private transient String key;

/**
* Creates a new font triplet (for base14 use).
* @param name font name
*/
public FontTriplet(String name) {
this.name = name;
}

/**
* Creates a new font triplet.
* @param name font name
@@ -64,7 +58,7 @@ public class FontTriplet implements Comparable<FontTriplet>, Serializable {
* @param priority priority of this triplet/font mapping
*/
public FontTriplet(String name, String style, int weight, int priority) {
this(name);
this.name = name;
this.style = style;
this.weight = weight;
this.priority = priority;
@@ -143,6 +137,5 @@ public class FontTriplet implements Comparable<FontTriplet>, Serializable {
*/
boolean matches(FontTriplet triplet);
}

}


+ 38
- 76
src/java/org/apache/fop/fonts/LazyFont.java View File

@@ -20,19 +20,17 @@
package org.apache.fop.fonts;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URI;
import java.util.Map;
import java.util.Set;

import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;

import org.xml.sax.InputSource;

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

import org.apache.fop.apps.FOPException;
import org.apache.fop.apps.io.InternalResourceResolver;
import org.apache.fop.complexscripts.fonts.Positionable;
import org.apache.fop.complexscripts.fonts.Substitutable;

@@ -43,49 +41,50 @@ public class LazyFont extends Typeface implements FontDescriptor, Substitutable,

private static Log log = LogFactory.getLog(LazyFont.class);

private String metricsFileName;
private String fontEmbedPath;
private boolean useKerning;
private boolean useAdvanced;
private EncodingMode encodingMode = EncodingMode.AUTO;
private EmbeddingMode embeddingMode = EmbeddingMode.AUTO;
private boolean embedded = true;
private String subFontName;
private final URI metricsURI;
private final URI fontEmbedURI;
private final boolean useKerning;
private final boolean useAdvanced;
private final EncodingMode encodingMode;
private final EmbeddingMode embeddingMode;
private final String subFontName;
private final boolean embedded;
private final InternalResourceResolver resourceResolver;

private boolean isMetricsLoaded;
private Typeface realFont;
private FontDescriptor realFontDescriptor;

private FontResolver resolver;

/**
* Main constructor
* @param fontInfo the font info to embed
* @param resolver the font resolver to handle font URIs
* @param resourceResolver the font resolver to handle font URIs
*/
public LazyFont(EmbedFontInfo fontInfo, FontResolver resolver) {
this.metricsFileName = fontInfo.getMetricsFile();
this.fontEmbedPath = fontInfo.getEmbedFile();
public LazyFont(EmbedFontInfo fontInfo, InternalResourceResolver resourceResolver,
boolean useComplexScripts) {
this.metricsURI = fontInfo.getMetricsURI();
this.fontEmbedURI = fontInfo.getEmbedURI();
this.useKerning = fontInfo.getKerning();
if ( resolver != null ) {
this.useAdvanced = resolver.isComplexScriptFeaturesEnabled();
if (resourceResolver != null) {
this.useAdvanced = useComplexScripts;
} else {
this.useAdvanced = fontInfo.getAdvanced();
}
this.encodingMode = fontInfo.getEncodingMode();
this.embeddingMode = fontInfo.getEmbeddingMode();
this.encodingMode = fontInfo.getEncodingMode() != null ? fontInfo.getEncodingMode()
: EncodingMode.AUTO;
this.embeddingMode = fontInfo.getEmbeddingMode() != null ? fontInfo.getEmbeddingMode()
: EmbeddingMode.AUTO;
this.subFontName = fontInfo.getSubFontName();
this.embedded = fontInfo.isEmbedded();
this.resolver = resolver;
this.resourceResolver = resourceResolver;
}

/** {@inheritDoc} */
public String toString() {
StringBuffer sbuf = new StringBuffer(super.toString());
sbuf.append('{');
sbuf.append("metrics-url=" + metricsFileName);
sbuf.append(",embed-url=" + fontEmbedPath);
sbuf.append("metrics-url=" + metricsURI);
sbuf.append(",embed-url=" + fontEmbedURI);
sbuf.append(",kerning=" + useKerning);
sbuf.append(",advanced=" + useAdvanced);
sbuf.append('}');
@@ -95,75 +94,38 @@ public class LazyFont extends Typeface implements FontDescriptor, Substitutable,
private void load(boolean fail) {
if (!isMetricsLoaded) {
try {
if (metricsFileName != null) {
if (metricsURI != null) {
/**@todo Possible thread problem here */
FontReader reader = null;
if (resolver != null) {
Source source = resolver.resolve(metricsFileName);
if (source == null) {
String err
= "Cannot load font: failed to create Source from metrics file "
+ metricsFileName;
if (fail) {
throw new RuntimeException(err);
} else {
log.error(err);
}
return;
}
InputStream in = null;
if (source instanceof StreamSource) {
in = ((StreamSource) source).getInputStream();
}
if (in == null && source.getSystemId() != null) {
in = new java.net.URL(source.getSystemId()).openStream();
}
if (in == null) {
String err = "Cannot load font: After URI resolution, the returned"
+ " Source object does not contain an InputStream"
+ " or a valid URL (system identifier) for metrics file: "
+ metricsFileName;
if (fail) {
throw new RuntimeException(err);
} else {
log.error(err);
}
return;
}
InputSource src = new InputSource(in);
src.setSystemId(source.getSystemId());
reader = new FontReader(src);
} else {
reader = new FontReader(new InputSource(
new URL(metricsFileName).openStream()));
}
InputStream in = resourceResolver.getResource(metricsURI);
InputSource src = new InputSource(in);
src.setSystemId(metricsURI.toASCIIString());
reader = new FontReader(src, resourceResolver);
reader.setKerningEnabled(useKerning);
reader.setAdvancedEnabled(useAdvanced);
if (this.embedded) {
reader.setFontEmbedPath(fontEmbedPath);
reader.setFontEmbedURI(fontEmbedURI);
}
reader.setResolver(resolver);
realFont = reader.getFont();
} else {
if (fontEmbedPath == null) {
if (fontEmbedURI == null) {
throw new RuntimeException("Cannot load font. No font URIs available.");
}
realFont = FontLoader.loadFont(fontEmbedPath, subFontName,
embedded, embeddingMode, encodingMode,
useKerning, useAdvanced, resolver);
realFont = FontLoader.loadFont(fontEmbedURI, subFontName, embedded,
embeddingMode, encodingMode, useKerning, useAdvanced, resourceResolver);
}
if (realFont instanceof FontDescriptor) {
realFontDescriptor = (FontDescriptor) realFont;
}
} catch (FOPException fopex) {
log.error("Failed to read font metrics file " + metricsFileName, fopex);
log.error("Failed to read font metrics file " + metricsURI, fopex);
if (fail) {
throw new RuntimeException(fopex.getMessage());
throw new RuntimeException(fopex);
}
} catch (IOException ioex) {
log.error("Failed to read font metrics file " + metricsFileName, ioex);
log.error("Failed to read font metrics file " + metricsURI, ioex);
if (fail) {
throw new RuntimeException(ioex.getMessage());
throw new RuntimeException(ioex);
}
}
realFont.setEventListener(this.eventListener);

+ 5
- 3
src/java/org/apache/fop/fonts/MultiByteFont.java View File

@@ -26,6 +26,7 @@ import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.apache.fop.apps.io.InternalResourceResolver;
import org.apache.fop.complexscripts.fonts.GlyphDefinitionTable;
import org.apache.fop.complexscripts.fonts.GlyphPositioningTable;
import org.apache.fop.complexscripts.fonts.GlyphSubstitutionTable;
@@ -68,7 +69,8 @@ public class MultiByteFont extends CIDFont implements Substitutable, Positionabl
/**
* Default constructor
*/
public MultiByteFont() {
public MultiByteFont(InternalResourceResolver resourceResolver) {
super(resourceResolver);
subset.setupFirstGlyph();
setFontType(FontType.TYPE0);
}
@@ -123,7 +125,7 @@ public class MultiByteFont extends CIDFont implements Substitutable, Positionabl

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

public boolean isSubsetEmbedded() {
@@ -553,7 +555,7 @@ public class MultiByteFont extends CIDFont implements Substitutable, Positionabl
}
}
cb.flip();
return (CharSequence) cb;
return cb;
}

}

+ 4
- 3
src/java/org/apache/fop/fonts/MutableFont.java View File

@@ -19,6 +19,7 @@

package org.apache.fop.fonts;

import java.net.URI;
import java.util.Map;
import java.util.Set;

@@ -49,10 +50,10 @@ public interface MutableFont {
void setFamilyNames(Set<String> names);

/**
* Sets the path to the embeddable font file.
* @param path URI to the file
* Sets the URI to the embeddable font.
* @param path URI to the font
*/
void setEmbedFileName(String path);
void setEmbedURI(URI path);

/**
* Sets the resource name of the embeddable font file.

+ 5
- 3
src/java/org/apache/fop/fonts/SingleByteFont.java View File

@@ -31,6 +31,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;

/**
@@ -53,15 +54,16 @@ public class SingleByteFont extends CustomFont {
private PostScriptVersion ttPostScriptVersion;

/**
* Main constructor.
* @param resourceResolver the URI resolver for controlling file access
*/
public SingleByteFont() {
public SingleByteFont(InternalResourceResolver resourceResolver) {
super(resourceResolver);
setEncoding(CodePointMapping.WIN_ANSI_ENCODING);
}

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


+ 0
- 0
src/java/org/apache/fop/fonts/apps/TTFReader.java View File


Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save