飞碟 XHTML 中的相对路径?

发布于 2024-08-23 23:15:51 字数 324 浏览 8 评论 0原文

我正在使用 Flying Saucer 将一些 PDF 文档从字符串渲染为 XHTML。我的代码是这样的:

iTextRenderer.setDocument(documentGenerator.generate(xhtmlDocumentAsString));
iTextRenderer.layout();
iTextRenderer.createPDF(outputStream);

我想要理解的是,当使用此方法时,XHTML 中的相对路径是从哪里解析的?例如,对于图像或样式表。我能够使用此方法成功生成基于文本的文档,但我需要了解如何引用我的图像和 CSS。

I am using Flying Saucer to render some PDF documents from strings to XHTML. My code is something like:

iTextRenderer.setDocument(documentGenerator.generate(xhtmlDocumentAsString));
iTextRenderer.layout();
iTextRenderer.createPDF(outputStream);

What I'm trying to understand is, when using this method, where are relative paths in the XHTML resolved from? For example, for images or stylesheets. I am able to use this method to successfully generate a text-based document, but I need to understand how to reference my images and CSS.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

孤千羽 2024-08-30 23:15:51

setDocument() 方法有两个参数:document 和 url。
url 参数指示用于添加到 xhtml 中出现的相对路径(例如 img 标签中)的基本 url。

假设您有:

<img src="images/img1.jpg">

现在假设文件夹“images”位于:

C:/physical/route/to/app/images/

您可以将 setDocument() 用作:

renderer.setDocument(xhtmlDoc, "file:///C:/physical/route/to/app/");

注意尾部斜杠,没有它它将无法工作。

这就是它对我有用的方式。我假设您可以使用其他类型的网址,例如“http://...”。

The setDocument() method takes two parameters: document and url.
The url parameter indicates the base url used to prepend to relative paths that appear in the xhtml, such as in img tags.

Suppose you have:

<img src="images/img1.jpg">

Now suppose the folder "images" is located at:

C:/physical/route/to/app/images/

You may use setDocument() as:

renderer.setDocument(xhtmlDoc, "file:///C:/physical/route/to/app/");

Notice the trailing slash, it won't work without it.

This is the way it worked for me. I assume you could use other types of urls such as "http://...".

小梨窩很甜 2024-08-30 23:15:51

这周我做了这个工作,我给你一些对我来说效果很好的东西。

在现实生活中,您的 XHTML 文档通过相对路径指向多个资源(图像、CSS 等)。
你还必须向飞碟解释在哪里可以找到它们。它们可以位于您的类路径中,也可以位于您的文件系统中。 (如果它们在网络上,您可以只设置基本 url,所以这不会有帮助)

所以您必须像这样扩展 ITextUserAgent:

private static class ResourceLoaderUserAgent extends ITextUserAgent {

    public ResourceLoaderUserAgent(ITextOutputDevice outputDevice) {
        super(outputDevice);
    }

    protected InputStream resolveAndOpenStream(String uri) {

        InputStream is = super.resolveAndOpenStream(uri);
        String fileName = "";
        try {
            String[] split = uri.split("/");
            fileName = split[split.length - 1];
        } catch (Exception e) {
            return null;
        }

        if (is == null) {
            // Resource is on the classpath
            try{
                is = ResourceLoaderUserAgent.class.getResourceAsStream("/etc/images/" + fileName);
            } catch (Exception e) {
        }

        if (is == null) {
            // Resource is in the file system
            try {
                is = new FileInputStream(new File("C:\\images\\" + fileName));
            } catch (Exception e) {
            }
        }

        return is;
    }
}

并且您像这样使用它:

// Output stream containing the result
ByteArrayOutputStream baos = new ByteArrayOutputStream();

ITextRenderer renderer = new ITextRenderer();
ResourceLoaderUserAgent callback = new ResourceLoaderUserAgent(renderer.getOutputDevice());
callback.setSharedContext(renderer.getSharedContext());
renderer.getSharedContext().setUserAgentCallback(callback);

renderer.setDocumentFromString(htmlSourceAsString);

renderer.layout();
renderer.createPDF(baos);
renderer.finishPDF();

干杯。

This week I worked on this, and I give you what worked fine for me.

In real life, your XHTML document points to multiple resources (images, css, etc.) with relative paths.
You also have to explain to Flying Saucer where to find them. They can be in your classpath, or in your file system. (If they are on the network, you can just set the base url, so this won't help)

So you have to extend the ITextUserAgent like this:

private static class ResourceLoaderUserAgent extends ITextUserAgent {

    public ResourceLoaderUserAgent(ITextOutputDevice outputDevice) {
        super(outputDevice);
    }

    protected InputStream resolveAndOpenStream(String uri) {

        InputStream is = super.resolveAndOpenStream(uri);
        String fileName = "";
        try {
            String[] split = uri.split("/");
            fileName = split[split.length - 1];
        } catch (Exception e) {
            return null;
        }

        if (is == null) {
            // Resource is on the classpath
            try{
                is = ResourceLoaderUserAgent.class.getResourceAsStream("/etc/images/" + fileName);
            } catch (Exception e) {
        }

        if (is == null) {
            // Resource is in the file system
            try {
                is = new FileInputStream(new File("C:\\images\\" + fileName));
            } catch (Exception e) {
            }
        }

        return is;
    }
}

And you use it like this:

// Output stream containing the result
ByteArrayOutputStream baos = new ByteArrayOutputStream();

ITextRenderer renderer = new ITextRenderer();
ResourceLoaderUserAgent callback = new ResourceLoaderUserAgent(renderer.getOutputDevice());
callback.setSharedContext(renderer.getSharedContext());
renderer.getSharedContext().setUserAgentCallback(callback);

renderer.setDocumentFromString(htmlSourceAsString);

renderer.layout();
renderer.createPDF(baos);
renderer.finishPDF();

Cheers.

扭转时空 2024-08-30 23:15:51

对我来说最好的解决方案是:

renderer.setDocumentFromString(htmlContent,  new ClassPathResource("/META-INF/pdfTemplates/").getURL().toExternalForm());

然后 html 中提供的所有样式和图像(如

<img class="logo" src="images/logo.png" />
<link rel="stylesheet" type="text/css" media="all" href="css/style.css"></link>

)都按预期呈现。

The best solution for me was:

renderer.setDocumentFromString(htmlContent,  new ClassPathResource("/META-INF/pdfTemplates/").getURL().toExternalForm());

Then all the provided styles and images in html (like

<img class="logo" src="images/logo.png" />
<link rel="stylesheet" type="text/css" media="all" href="css/style.css"></link>

) were rendered as expected.

葬シ愛 2024-08-30 23:15:51

AtilaUy 的答案对于 Flying Saucer 中默认的工作方式来说是准确的。

更一般的答案是它询问 UserAgentContext。当文档被设置时,它将调用 UserAgentContext 上的 setBaseURL() 。然后,当它想要读取实际的资源数据时,它将调用resolveURL()来解析相对URL,并最终调用resolveAndOpenStream()。

好吧,这个答案对于您来说可能已经太晚了,无论如何都无法使用它,但是当我出发时,我需要这样的答案,而设置自定义用户代理上下文是我最终使用的解决方案。

AtilaUy's answer is spot-on for the default way things work in Flying Saucer.

The more general answer is that it asks the UserAgentContext. It will call setBaseURL() on the UserAgentContext when the document is set in. Then it will call resolveURL() to resolve relative URLs and ultimately resolveAndOpenStream() when it wants to read the actual resource data.

Well, this answer is probably way too late for you to make use of it anyway, but I needed an answer like this when I set out, and setting a custom user agent context is the solution I ended up using.

怀中猫帐中妖 2024-08-30 23:15:51

您可以使用绝对文件路径,也可以使用 http:// url。相对路径可以工作,但不可移植,因为它取决于您运行程序的目录

You can either have file paths, which should be absolute, or http:// urls. Relative paths can work but aren't portable because it depends on what directory you ran your program from

少跟Wǒ拽 2024-08-30 23:15:51

我认为更简单的方法是:

                DomNodeList<DomElement> images = result.getElementsByTagName("img");
                for (DomElement e : images) { 
                    e.setAttribute("src", result.getFullyQualifiedUrl(e.getAttribute("src")).toString());
                }

I think a easier approach would be:

                DomNodeList<DomElement> images = result.getElementsByTagName("img");
                for (DomElement e : images) { 
                    e.setAttribute("src", result.getFullyQualifiedUrl(e.getAttribute("src")).toString());
                }
深爱成瘾 2024-08-30 23:15:51

解析路径的另一种方法是覆盖 UserAgentCallback#resolveURI,它提供比固定 URL 更动态的行为(如 AtilaUy 的答案,在大多数情况下看起来相当有效)。

这就是我如何让 XHTMLPane 使用动态生成的样式表:

public static UserAgentCallback interceptCssResourceLoading(
    final UserAgentCallback defaultAgentCallback,
    final Map< URI, CSSResource > cssResources
) {
  return new UserAgentCallback() {
    @Override
    public CSSResource getCSSResource( final String uriAsString ) {
      final URI uri = uriQuiet( uriAsString ) ; // Just rethrow unchecked exception.
      final CSSResource cssResource = cssResources.get( uri )  ;
      if( cssResource == null ) {
        return defaultAgentCallback.getCSSResource( uriAsString ) ;
      } else {
        return cssResource ;
      }
    }

    @Override
    public String resolveURI( final String uriAsString ) {
      final URI uri = uriQuiet( uriAsString ) ;
      if( cssResources.containsKey( uri ) ) {
        return uriAsString ;
      } else {
        return defaultAgentCallback.resolveURI( uriAsString ) ;
      }
    }

    // Delegate all other methods to defaultUserAgentCallback.

  } ;
}

然后我这样使用它:

  final UserAgentCallback defaultAgentCallback =
      xhtmlPanel.getSharedContext().getUserAgentCallback() ;
  xhtmlPanel.getSharedContext().setUserAgentCallback(
      interceptCssResourceLoading( defaultAgentCallback, cssResources ) ) ;
  xhtmlPanel.setDocumentFromString( xhtml, null, new XhtmlNamespaceHandler() ) ;

Another way to resolve paths is to override UserAgentCallback#resolveURI, which offers a more dynamic behavior than a fixed URL (as in AtilaUy's answer, which looks quite valid for most cases).

This is how I make an XHTMLPane use dynamically-generated stylesheets:

public static UserAgentCallback interceptCssResourceLoading(
    final UserAgentCallback defaultAgentCallback,
    final Map< URI, CSSResource > cssResources
) {
  return new UserAgentCallback() {
    @Override
    public CSSResource getCSSResource( final String uriAsString ) {
      final URI uri = uriQuiet( uriAsString ) ; // Just rethrow unchecked exception.
      final CSSResource cssResource = cssResources.get( uri )  ;
      if( cssResource == null ) {
        return defaultAgentCallback.getCSSResource( uriAsString ) ;
      } else {
        return cssResource ;
      }
    }

    @Override
    public String resolveURI( final String uriAsString ) {
      final URI uri = uriQuiet( uriAsString ) ;
      if( cssResources.containsKey( uri ) ) {
        return uriAsString ;
      } else {
        return defaultAgentCallback.resolveURI( uriAsString ) ;
      }
    }

    // Delegate all other methods to defaultUserAgentCallback.

  } ;
}

Then I use it like that:

  final UserAgentCallback defaultAgentCallback =
      xhtmlPanel.getSharedContext().getUserAgentCallback() ;
  xhtmlPanel.getSharedContext().setUserAgentCallback(
      interceptCssResourceLoading( defaultAgentCallback, cssResources ) ) ;
  xhtmlPanel.setDocumentFromString( xhtml, null, new XhtmlNamespaceHandler() ) ;
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文