Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to show img use relative path #631

Closed
xu-weize opened this issue Jan 6, 2021 · 5 comments
Closed

How to show img use relative path #631

xu-weize opened this issue Jan 6, 2021 · 5 comments

Comments

@xu-weize
Copy link

xu-weize commented Jan 6, 2021

I want to show a page in the pdf header,and use relative path to do it.I am try many methods which I know,but load unnormally.
The variable of baseUrl value pass from the java code.How can i do it?
For example:

<title>Title</title> <style> @page { size: A4 portrait; margin: 50px; @top-left { content: url('${baseUrl}/static/images/xxx.png'); width: 80px; border-bottom: 1px dotted black ; }
    }
    page,html, body {
        font-family: 'simsun', serif;
    }

</style>
@danfickle
Copy link
Owner

I have a feeling that using a image url in the content property of a page margin is not yet supported although it is supported in the main flow of the document. You can use a running element. I'll provide a working sample as an answer to #630.

As for relative urls, I have put together this sample with notes that may help. If you find it helpful, I will put it on the tips page on the wiki.

package com.example;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;

import com.openhtmltopdf.pdfboxout.PdfRendererBuilder;

public class App9 {
    /**
     * Returns a URI string usable as a base document uri in the form:
     * file:/C:/Users/dan/Desktop/images/doc.html
     * 
     * NOTE: The lack of slashes after the file: protocol string.
     * 
     * NOTE: It does not matter whether doc.html exists or not in baseDirectory.
     */
    public static String createBaseDocumentUri1(File baseDirectory) {
        try {
            return new File(baseDirectory, "doc.html").toURI().toURL().toExternalForm();
        } catch (MalformedURLException e) {
            return null;
        }
    }

    /**
     * Returns a URI string usable as a base document uri in the form:
     * file:///C://directory/directory/doc.html
     * 
     * NOTE: This is the preferred format for most applications although
     * OpenHtmltoPdf will accept both formats.
     * 
     * NOTE: It does not matter whether doc.html exists or not in baseDirectory.
     */
    public static String createBaseDocumentUri2(File baseDirectory) {
        return baseDirectory.toPath().resolve("doc.html").toUri().toString();
    }

    private final static String BASE_DIRECTORY = "C:\\Users\\dan\\Desktop\\images";

    /**
     * file:/C:/Users/dan/Desktop/image.jpg
     * NOTE: Double dot means parent directory.
     * NOTE: There is no file jail with the default URI resolver. Files
     * in ancestor directories can be read.
     */
    private final static String HTML1 = "<body><img src=\"../image.jpg\" /></body>";

    /**
     * file:/C:/Users/dan/Desktop/images/image.jpg
     * NOTE: Single dot means current directory. Some people like
     * to use it as a matter of style.
     */
    private final static String HTML2 = "<body><img src=\"./image.jpg\" /></body>";

    /**
     * file:/C:/Users/dan/Desktop/images/jpegs/image.jpg
     * NOTE: IF we don't start with a slash, means relative to current directory.
     */
    private final static String HTML3 = "<body><img src=\"jpegs/image.jpg\" /></body>";

    /**
     * file:/C:/Users/dan/Desktop/images/image.jpg
     * NOTE: File in current directory, equivalent to HTML2.
     */
    private final static String HTML4 = "<body><img src=\"image.jpg\" /></body>";

    /**
     * file:/image.jpg
     * NOTE: A beginning slash means an absolute file path. 
     * NOTE: Resolves via new File(new URI("file:/image.jpg")).getAbsolutePath() to 
     * C:\image.jpg (on my machine)
     * NOTE: Should definitely avoid on Windows as the drive
     * letter is not encoded in the URI.
     */
    private final static String HTML5 = "<body><img src=\"/image.jpg\" /></body>";

    public static void main(String[] args) throws IOException {
        PdfRendererBuilder builder = new PdfRendererBuilder();

        String baseDocumentUri = createBaseDocumentUri1(new File(BASE_DIRECTORY));

        System.out.println(baseDocumentUri);

        builder.withHtmlContent(HTML2, baseDocumentUri);
        // NOTE: Should auto-close our output stream. We are being naughty in this sample.
        builder.toStream(new FileOutputStream("C:\\Users\\dan\\Desktop\\out.pdf"));
        builder.useFastMode();

        builder.run();
    }
}

@danfickle
Copy link
Owner

Closing now. Please re-open if required.

@oryan-block
Copy link

oryan-block commented Feb 24, 2022

How is this meant to work in a spring boot type app?
If my images are in src/main/resources/pdf/
do I have to provide an absolute path as the baseDocumentUri or is there a way to use a relative one?

@danfickle
Copy link
Owner

Hi @oryan-block

I've added a FAQ entry on getting a base URI which I'll reproduce below for your convenience:

The easiest way to get the baseUri argument for the withHtmlContent method is to create an empty or dummy (content not important) file in the folder you wish to use as base directory.

The following example assumes the following files are present at the base of the resources folder which is typically src/main/resources when using Maven.

  • /pdf/ - base directory for our generator related resources
  • /pdf/base-uri.htm - dummy file to resolve uri, may be empty, however a useful comment is suggested
  • /pdf/images/
  • /pdf/images/image.jpg - Sample image, we will include this image using a relative path
public class Demo {
    // Typically our html is created using a template engine
    // We can use relative URLs such as "./images/image.jpg"
    private static final String HTML = "<html><body><img alt=\"Demo\" src=\"./images/image.jpg\"/></body></html>";

    public byte[] createPDF() throws IOException {
        // This shows how to get the URL of a resource (which must exist)
        String resBaseUri = Demo.class.getResource("/pdf/base-uri.htm").toString();

        // Alternatively, this shows how to get the URL of a normal file
        // In this case, we reuse the source project in our home directory
        String home = System.getProperty("user.home");
        String fileBaseUri = Paths.get(home + "/demo-project/src/main/resources/pdf/base-uri.htm").toUri().toString();

        try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
            PdfRendererBuilder builder = new PdfRendererBuilder();

            // We use the URL to the empty resource as the baseUri argument
            // Could also change to fileBaseUri to use normal file
            builder.withHtmlContent(HTML, resBaseUri);
            builder.toStream(os);
            builder.run();

            return os.toByteArray();
        }
    }
}

In case it is helpful, here is the Spring Boot controller I used to test:

@Controller
@RequestMapping(value = "/test.pdf")
public class DemoController {
    private static final String HTML = "<html><body><img alt=\"Demo\" src=\"./images/image.jpg\"/></body></html>";

    @GetMapping
    public ResponseEntity<Resource> getPDF() throws IOException {
        String baseUri = DemoController.class.getResource("/pdf/base-uri.htm").toString();

        try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
            PdfRendererBuilder builder = new PdfRendererBuilder();
            builder.withHtmlContent(HTML, baseUri);
            builder.toStream(os);
            builder.run();

            return ResponseEntity.ok()
              .contentLength(os.size())
              .contentType(MediaType.APPLICATION_PDF)
              .body(new ByteArrayResource(os.toByteArray()));
        }
    }
}

@oryan-block
Copy link

Thanks @danfickle
My understanding then is that we do require an absolute path?
Since both Demo.class.getResource("/pdf/base-uri.htm").toString() and System.getProperty("user.home") evaluate to absolute paths.

My concern was that this would not work in a deployed jar but I see that that was addressed in #125

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants