Apache FOP:从 1.1 升级到 2.1
Apache FOP: upgrading from 1.1 to 2.1
我正在关注 migration guide,但我似乎没听懂。
在 FOP 1.1 中我有这个工作代码:
public class XsltFactory {
private static final String FO_CONFIG_FILE = "/path/to/fop-config.xml";
private static FopFactory fopFactory;
private static synchronized void initFopFactory(final ServletContext context) throws Exception {
Configuration cfg = new DefaultConfigurationBuilder().build(XsltFactory.class.getResourceAsStream(FO_CONFIG_FILE));
fopFactory = FopFactory.newInstance();
fopFactory.setURIResolver(new ServletContextURIResolver(context));
fopFactory.setUserConfig(cfg);
}
}
我修改了上面的代码以坚持使用 FOP 2.1:
public class XsltFactory {
private static final String FO_CONFIG_FILE = "/path/to/fop-config.xml";
private static FopFactory fopFactory;
private static synchronized void initFopFactory(final ServletContext context) throws Exception {
Configuration cfg = new DefaultConfigurationBuilder().build(XsltFactory.class.getResourceAsStream(FO_CONFIG_FILE));
FopFactoryBuilder fopFactoryBuilder = new FopFactoryBuilder(
new URI(ServletContextURIResolver.SERVLET_CONTEXT_PROTOCOL),
new URIResolverAdapter(new ServletContextURIResolver(context))
);
fopFactoryBuilder.setConfiguration(cfg);
fopFactory = fopFactoryBuilder.build();
}
}
但我收到以下错误:
java.lang.Exception: Fail to create PDF
at ....web.controller.PrintPdfController.renderPdf(PrintPdfController.java:181)
[...]
at weblogic.work.ExecuteThread.run(ExecuteThread.java:263)
Caused by: java.net.URISyntaxException: Expected scheme-specific part at index 16: servlet-context:
at java.net.URI$Parser.fail(URI.java:2829)
at java.net.URI$Parser.failExpecting(URI.java:2835)
at java.net.URI$Parser.parse(URI.java:3038)
at java.net.URI.<init>(URI.java:595)
[...]
... 42 common frames omitted
PDF 无法加载,因为创建失败。
编辑:
在 SERVLET_CONTEXT_PROTOCOL
上下文之后添加 + "///"
之后,我现在得到:
Caused by: java.net.MalformedURLException: unknown protocol: servlet-context
at java.net.URL.<init>(URL.java:592)
at java.net.URL.<init>(URL.java:482)
at java.net.URL.<init>(URL.java:431)
at java.net.URI.toURL(URI.java:1096)
at org.apache.fop.fonts.FontDetectorFactory$DefaultFontDetector.detect(FontDetectorFactory.java:94)
... 59 common frames omitted
经过几天的考察,终于迁移成功了。问题来自 URI 解析器,解决这个问题会产生新问题,我随后解决了这些问题。
https://xmlgraphics.apache.org/fop/2.1/upgrading.html 上的指南提供的帮助相对有限。
问题的核心是URI解析器。您现在必须定义一个自定义解析器,但不是在以下位置提供的示例中:
https://xmlgraphics.apache.org/fop/2.0/servlets.html
ResourceResolver resolver = new ResourceResolver() {
public OutputStream getOutputStream(URI uri) throws IOException {
URL url = getServletContext().getResource(uri.toASCIIString());
return url.openConnection().getOutputStream();
}
public Resource getResource(URI uri) throws IOException {
return new Resource(getServletContext().getResourceAsStream(uri.toASCIIString()));
}
};
正确的做法是:
ResourceResolver resolver = new ResourceResolver() {
public OutputStream getOutputStream(URI uri) throws IOException {
URL url = context.getResource(uri.getPath());
return url.openConnection().getOutputStream();
}
public Resource getResource(URI uri) throws IOException {
return new Resource(context.getResourceAsStream(uri.getPath()));
}
};
而不是 uri.toASCIIString()
,正确的语法是 uri.getPath()
。
此外,我们必须删除字体 URI(在 fop-config.xml 中)和图像 URI(在任何 XSL 转换文件或模板中)中的所有 "servlet-context:" 标记。
最后,我遇到了断字问题:FOP 再也找不到 .hyp
文件,因为出于某种原因,使用了 baseUri
而不是自定义上下文解析器(我不得不深入研究 FOP 的源文件以找出答案)。因此,我不得不修改自定义解析器的 getResource
方法。我知道这是一个 hack,但它有效并且对我来说已经足够了,因为我已经在这个问题上花了三天时间):
public OutputStream getOutputStream(URI uri) throws IOException {
URL url = context.getResource(uri.getPath());
return url.openConnection().getOutputStream();
}
public Resource getResource(URI uri) throws IOException {
InputStream stream = null;
/*
* For some reason, in FOP 2.x, the hyphenator does not use the
* classpath fop-hyph.jar.
*
* This causes trouble as FOP tries to find "none.hyp" in the
* war directory. Setting
* <hyphenation-base>/WEB-INF/hyph</hyphenation-base> in the
* fop-config.xml file does not solve the issue. The only
* solution I could find is to programmatically detect when a
* .hyp file is trying to be loaded. When this occurs, I modify
* the path so that the resolver gets the right resource.
*
* This is a hack, but after spending three days on it, I just
* went straight to the point and got a workaround.
*/
if (uri.getPath().endsWith('.hyp')) {
String relUri = uri.getPath().substring(uri.getPath().indexOf(baseUri.getPath()) + baseUri.getPath().length());
stream = context.getResourceAsStream(FopManager.HYPH_DIR + relUri);
} else {
stream = context.getResourceAsStream(uri.getPath());
}
Resource res = new Resource(stream);
return res;
}
};
请注意,我还必须手动创建 none.hyp
文件,因为它不存在于 OFFO 提供的 .hyp
文件中。我刚刚复制了 en.hyp
并重命名为 none.hyp
。这解决了我的最后一个问题。
我希望这可以节省几天的工作;)
我正在关注 migration guide,但我似乎没听懂。
在 FOP 1.1 中我有这个工作代码:
public class XsltFactory {
private static final String FO_CONFIG_FILE = "/path/to/fop-config.xml";
private static FopFactory fopFactory;
private static synchronized void initFopFactory(final ServletContext context) throws Exception {
Configuration cfg = new DefaultConfigurationBuilder().build(XsltFactory.class.getResourceAsStream(FO_CONFIG_FILE));
fopFactory = FopFactory.newInstance();
fopFactory.setURIResolver(new ServletContextURIResolver(context));
fopFactory.setUserConfig(cfg);
}
}
我修改了上面的代码以坚持使用 FOP 2.1:
public class XsltFactory {
private static final String FO_CONFIG_FILE = "/path/to/fop-config.xml";
private static FopFactory fopFactory;
private static synchronized void initFopFactory(final ServletContext context) throws Exception {
Configuration cfg = new DefaultConfigurationBuilder().build(XsltFactory.class.getResourceAsStream(FO_CONFIG_FILE));
FopFactoryBuilder fopFactoryBuilder = new FopFactoryBuilder(
new URI(ServletContextURIResolver.SERVLET_CONTEXT_PROTOCOL),
new URIResolverAdapter(new ServletContextURIResolver(context))
);
fopFactoryBuilder.setConfiguration(cfg);
fopFactory = fopFactoryBuilder.build();
}
}
但我收到以下错误:
java.lang.Exception: Fail to create PDF
at ....web.controller.PrintPdfController.renderPdf(PrintPdfController.java:181)
[...]
at weblogic.work.ExecuteThread.run(ExecuteThread.java:263)
Caused by: java.net.URISyntaxException: Expected scheme-specific part at index 16: servlet-context:
at java.net.URI$Parser.fail(URI.java:2829)
at java.net.URI$Parser.failExpecting(URI.java:2835)
at java.net.URI$Parser.parse(URI.java:3038)
at java.net.URI.<init>(URI.java:595)
[...]
... 42 common frames omitted
PDF 无法加载,因为创建失败。
编辑:
在 SERVLET_CONTEXT_PROTOCOL
上下文之后添加 + "///"
之后,我现在得到:
Caused by: java.net.MalformedURLException: unknown protocol: servlet-context
at java.net.URL.<init>(URL.java:592)
at java.net.URL.<init>(URL.java:482)
at java.net.URL.<init>(URL.java:431)
at java.net.URI.toURL(URI.java:1096)
at org.apache.fop.fonts.FontDetectorFactory$DefaultFontDetector.detect(FontDetectorFactory.java:94)
... 59 common frames omitted
经过几天的考察,终于迁移成功了。问题来自 URI 解析器,解决这个问题会产生新问题,我随后解决了这些问题。
https://xmlgraphics.apache.org/fop/2.1/upgrading.html 上的指南提供的帮助相对有限。
问题的核心是URI解析器。您现在必须定义一个自定义解析器,但不是在以下位置提供的示例中: https://xmlgraphics.apache.org/fop/2.0/servlets.html
ResourceResolver resolver = new ResourceResolver() {
public OutputStream getOutputStream(URI uri) throws IOException {
URL url = getServletContext().getResource(uri.toASCIIString());
return url.openConnection().getOutputStream();
}
public Resource getResource(URI uri) throws IOException {
return new Resource(getServletContext().getResourceAsStream(uri.toASCIIString()));
}
};
正确的做法是:
ResourceResolver resolver = new ResourceResolver() {
public OutputStream getOutputStream(URI uri) throws IOException {
URL url = context.getResource(uri.getPath());
return url.openConnection().getOutputStream();
}
public Resource getResource(URI uri) throws IOException {
return new Resource(context.getResourceAsStream(uri.getPath()));
}
};
而不是 uri.toASCIIString()
,正确的语法是 uri.getPath()
。
此外,我们必须删除字体 URI(在 fop-config.xml 中)和图像 URI(在任何 XSL 转换文件或模板中)中的所有 "servlet-context:" 标记。
最后,我遇到了断字问题:FOP 再也找不到 .hyp
文件,因为出于某种原因,使用了 baseUri
而不是自定义上下文解析器(我不得不深入研究 FOP 的源文件以找出答案)。因此,我不得不修改自定义解析器的 getResource
方法。我知道这是一个 hack,但它有效并且对我来说已经足够了,因为我已经在这个问题上花了三天时间):
public OutputStream getOutputStream(URI uri) throws IOException {
URL url = context.getResource(uri.getPath());
return url.openConnection().getOutputStream();
}
public Resource getResource(URI uri) throws IOException {
InputStream stream = null;
/*
* For some reason, in FOP 2.x, the hyphenator does not use the
* classpath fop-hyph.jar.
*
* This causes trouble as FOP tries to find "none.hyp" in the
* war directory. Setting
* <hyphenation-base>/WEB-INF/hyph</hyphenation-base> in the
* fop-config.xml file does not solve the issue. The only
* solution I could find is to programmatically detect when a
* .hyp file is trying to be loaded. When this occurs, I modify
* the path so that the resolver gets the right resource.
*
* This is a hack, but after spending three days on it, I just
* went straight to the point and got a workaround.
*/
if (uri.getPath().endsWith('.hyp')) {
String relUri = uri.getPath().substring(uri.getPath().indexOf(baseUri.getPath()) + baseUri.getPath().length());
stream = context.getResourceAsStream(FopManager.HYPH_DIR + relUri);
} else {
stream = context.getResourceAsStream(uri.getPath());
}
Resource res = new Resource(stream);
return res;
}
};
请注意,我还必须手动创建 none.hyp
文件,因为它不存在于 OFFO 提供的 .hyp
文件中。我刚刚复制了 en.hyp
并重命名为 none.hyp
。这解决了我的最后一个问题。
我希望这可以节省几天的工作;)