Reputation: 2548
I am evaluating Thymeleaf and Flying Saucer for pdf generation from templates, and I am having a problem with applying css to my Thymeleaf template. I already read the relevant questions & answers here, here, and here; but none of the suggested solutions fixed my problem.
This is how my resources folder looks like:
So I am using the default directories that Spring will look for. And that's how the head tag looks like in my template.html
:
<head>
<title>Spring Boot and Thymeleaf Example</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<link rel="stylesheet" type="text/css" href="../static/css/style.css" th:href="@{/css/style.css}"/>
</head>
If I inline my css in template.html
then the generated pdf file will be styled properly (so there shouldn't be a problem with how I generate the pdf). However, when I try to link to the css file as shown above the generated pdf is not styled (so the css is not applied).
Lastly, I can access my css file at http://localhost:8080/css/style.css
, so there doesn't seem to be a problem with Spring serving the static content.
For completeness, this is how I generate the pdf:
private final SpringTemplateEngine templateEngine;
private final Log log;
@Autowired
public PdfGenerator(SpringTemplateEngine templateEngine) {
this.templateEngine = templateEngine;
log = LogFactory.getLog(getClass());
}
public void generate(HttpServletRequest servletRequest, HttpServletResponse servletResponse, ServletContext servletContext) {
// Parse the pdf template with Thymeleaf
Locale locale = getLocale(servletRequest);
WebContext context = new WebContext(servletRequest, servletResponse, servletContext, locale);
context.setVariable("user", buildDummyUser());
context.setVariable("discounts", buildDummyDiscounts());
String html = templateEngine.process("template", context);
// Create the pdf with Flying Saucer
try (OutputStream outputStream = new FileOutputStream("generated.pdf")) {
ITextRenderer renderer = new ITextRenderer();
renderer.setDocumentFromString(html);
renderer.layout();
renderer.createPDF(outputStream);
} catch (IOException | DocumentException e) {
log.error("Error while generating pdf", e);
}
}
I am using WebContext
instead of Context
because I was getting the following error with Context
:
org.thymeleaf.exceptions.TemplateProcessingException: Link base "/css/style.css" cannot be context relative (/...) unless the context used for executing the engine implements the org.thymeleaf.context.IWebContext interface
What am I missing here, why is my style.css
not applied to template.html
?
Upvotes: 1
Views: 15501
Reputation: 170
For those who struggle with this, if you are using Spring Security dependency, make sure to give request to your folders like this:
Then put your CSS file under static/css folder.
And then add CSS link into your HTML file like this:
<link rel="stylesheet" th:href="@{/css/index.css}">
Hope this help someone!
Upvotes: 2
Reputation: 1
I found a lazy man's way of taking care of this. It works, with a very simple approach. The 'inserted' fragment is just a CSS style tag in the body of a simple HTML document. I place this in the HEAD of my target file, right where I would have put the LINK REL tag:
<th:block th:insert="std-reports/std-reports-css-fragment.html :: style"></th:block>
Upvotes: 0
Reputation: 29
I solved this problem by changing the path structure in href. I had the same directory structure as you (html files are in templates doc, css files are in static doc).
<head>
<title>Spring Boot and Thymeleaf Example</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<link rel="stylesheet" type="text/css" href="/css/style.css"/>
</head>
It might help you to apply css to your html page.
Upvotes: 2
Reputation: 1385
I had same problems and I was also trying to use thymeleaf template resolver for pdf generation. I did lots research on thymeleaf and spring framework, I tried WebContext, I tried HttpServletRequest, I tried some of Spring Thymeleaf integration solutions it was not working either. So I think it was not syntax error, and I finally end up with using absolute path instead of relative. Url for reference
Here the reason with my assumption, lets say our resources are served on localhost:8080/myapp/css/style.css
. And the relative path to request resource is really ups to what context it relatives to.
For eaxmple a normal thymeleaf model Veiw, which return as html pages on browser for client, so the context in that case would be the request hostname, port and application context(eg: localhost:8080/myapp). And relative path will be based on that. So if relative path is /css/style.css, context + relative path will result to be localhost:8080/myapp/css/style.css
Unlike web context, in our case, offline template is on server backend, so the context I assume would be the server running context, which would be the local server path + appcontext(eg: D:/myServer/apps/myapp), relative path /css/style.css on this would be D:/myServer/apps/myapp/css/style.css
, this is not make sense. In order to use static resources, I have to pass it's absolute path.
I started use :
<link rel="stylesheet" type="text/css" th:href="@{http://localhost:8080/myapp/css/style.css}"/>
It's working fine but what if there are multiple host names or server is running on a proxy, then this is going to be a hard coded solution. It's better to know what is the real base url the user is requesting. So we can't really get rid off HttpSevletRequest.
Here is my code:
1.Config resource handler:
@Override
public void addResourceHandlers(final ResourceHandlerRegistry registry) {
registry.addResourceHandler("/css/**")
.addResourceLocations("classpath:/css/")
.setCachePeriod(31556926);
}
Get base url from HttpServletRequest, you can inject it in method or autowired in your service class, or get from RequestContextHolder. I write this in my Service class:
private static String getCurrentBaseUrl() {
ServletRequestAttributes sra = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
HttpServletRequest req = sra.getRequest();
return req.getScheme() + "://" + req.getServerName() + ":" + req.getServerPort() + req.getContextPath();
}
This is the place I use template engine in my class:
Context context = new Context();
context.setVariable("variales", variables);
context.setVariable("baseUrl", getCurrentBaseUrl());
String content = springTemplateEngine.process("myTemplate",context);
In my template, I use absolute css url like this:
<link type="stylesheet" th:src="@{|${baseUrl}/css/style.css|}" />
Upvotes: 11
Reputation: 9
I had a similar problem - my css was not applied to my template page.
My problem was that the css file was in css sass format
.table
margin: 0 0 40px 0
when I convert it to the normal css format like
.table {
margin: 0 0 40px 0;
}
it worked
Upvotes: -1
Reputation: 15878
Syntax looks fine so the problem is not with the syntax.
Also you cannot use @{...}
syntax without an IWebContext
interface so You are getting this exception.
Upvotes: 0