Ultranium
Ultranium

Reputation: 372

Avoid escaping line breaks in a custom attribute using Thymeleaf

I process SVG templates using Thymeleaf and encountered a problem replacing base64-encoded embedded images.

So, the source tag looks like this:

<image
  width="100"
  height="100"
  id="image123"
  x="0"
  y="0"
  preserveAspectRatio="none"

  th:attr="'xlink:href'='data:image/png;base64,' + ${imageBase64}"

  xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAADICAIAAAAiOjnJAAAAAXNSR0IArs4c6QAAAARnQU1BAACx
jwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAoRSURBVHhe7d09cts6FAVg663FSuHxCqgVyG5c

<MORE BASE64 DATA HERE...>

ggUqECxQgWCBCgQLVCBYoALBAhUIFqhAsEAFggUqECxQgWABwFicnf0PDRO+cnclXPUAAAAASUVO
RK5CYII=
"
/>

Each line of base64-encoded image data consists of 76 characters and a line break (LF), and I want to keep this structure, because some SVG renderers break when a file contains very long lines.

The problem is, when I set the imageBase64 context variable to a base64 string with line breaks, Thymeleaf escapes LF characters as &#xa; and, obviously, this breaks base64 decoders.

I know, there is th:utext attribute that works for tag inner text, but is there a way to avoid contents escaping while using a custom attribute?

Reproducer

https://github.com/d-bykov/thymeleaf-escaping-reproducer

Running this project will generate this SVG file, which contains Base64-encoded image with &#xd;&#xa; tokens instead of line break characters.

GitHub renders it just fine, but some other applications don't.

Upvotes: 1

Views: 265

Answers (1)

andrewJames
andrewJames

Reputation: 21910

In Summary

XML handlers will escape newlines to &#xa; as you have seen. But that is not something which HTML requires, so try using the HTML template mode instead.


My Test

In your Kotlin you are setting the template mode to XML:

templateResolver.templateMode = TemplateMode.XML

Instead, use HTML

templateResolver.templateMode = TemplateMode.HTML

In my Java version of your code, I actually used this:

templateResolver.setTemplateMode(TemplateMode.HTML);

The end result, when I used my own PNG, was the following content:

<svg width="512" height="512" viewBox="0 0 135.46666 135.46667" version="1.1" id="svg33251" inkscape:version="1.1.1 (3bf5ae0d25, 2021-09-20)" sodipodi:docname="template.svg" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
  <sodipodi:namedview id="namedview33253" pagecolor="#ffffff" bordercolor="#666666" borderopacity="1.0" inkscape:pageshadow="2" inkscape:pageopacity="0.0" inkscape:pagecheckerboard="0" inkscape:document-units="mm" showgrid="false" units="px" inkscape:zoom="0.77771465" inkscape:cx="270.02192" inkscape:cy="560.61693" inkscape:window-width="1718" inkscape:window-height="1368" inkscape:window-x="1713" inkscape:window-y="0" inkscape:window-maximized="0" inkscape:current-layer="layer1"/>
  <defs id="defs33248"/>
  <g inkscape:label="Layer 1" inkscape:groupmode="layer" id="layer1">
    <image width="67.73333" height="67.73333" preserveAspectRatio="none" id="image33388" x="33.866669" y="33.866669" xlink:href="data:image/png;base64,
iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAADd0lEQVRoge1aW2sTQRT+UshDLBTi
pYJP6kPxB4hUwQsqlP0DQi1CHwRvfahihSoqKBIr1CLoT5DihaSvlqpQEW99aBvanbRo6WssfanE
pjtwfJhsdjfJ7k72kk2KAx+BZM7M+XLmzJw5Z4CAGhHiPAeFM0xwht+cgWsqyAzOwEu/TfAcFCLE
g5rfVyNCXFOR4iqKlUrLoiSbioQUrSKpqch6Vd4BWVpFMnwChDaNYToEAlYwTBOhLRQSmoqe0AlU
oydQElxFOgISug+lgyHBsBIViTIZhhW/lliLmoTJMmstawnflonSJyQsI+czWjS7U71w3s2I0NYE
SkrB8Zzxetg9GAQB4jNMGQsYpmtbQ4QddQ+4MSsU0rExG45MTavUCmc0j7HTYL9VqcH+cGRskK30
jbjXtZrssCqV7AhHxtYq5qhZU5HyOlAsZlUqFgtHxgGpMhE/9wmzQjrCkLEDV1H0vayagUh5efEc
FD+DNMHSIp6DAs6Q8TNI1M6uqSDOkAFnyPsZJOLtVyeSR61sRz2I8kA0EeHwM4COSEKUCgRCpBnw
n0izwdXZ176DRodBl3tBxcXGKVZcBF3qBT0dFjo49eUM3Hb7LWRBe3Zad5fxscYReTlqnbtzF+hv
1pZI3vZAfHbXOtCjG41fLg+vW3V4ft+WSMY2RNlSQekXoFNHxCBXzjeeyMVzYu7T3UKXLbullYPi
GjT++hBMPOQFsZIlVj469yvfSdzC+PaEGHBkqHEkHt8Uc7YnnPuVw3iZi9XXN8Y6Ldg4XJAozBvz
/Ui79jcuVjJ3kq79xu4RNhF9tzx00L1vVYFIc0k+bC4Y94gzR8Mjcbrb8Mnigmt/a/IBkEsHLU0a
Jj97LDwSAGh5SsIadtUtmQTdp3Fjsr27g/GZwjyo03T4fn4lIWeXoCv5ilTKlL2zXlef3PJOYmTI
euVdmpSTcy3NaZJJ7M0FYwPQlRi4AFqfcZddnwFd67P+GV0HpHxCh1xJrp6ywpfXoB2J6mTCvk6Q
chJ0tU9AOSG+q0w8tCdA397KW7DuUly9hZ7lKdDxw9WK1kIsJvr+fF/fMvRcgvNaetuYE5Yauw26
NyAwdkd892fOmy95Lr15tUwY8F0MNVmm9cvTetO2w4MBvW2LJxwWQq3+qKaKUKs/c6rVTA/PMpwh
7/DwLM8ZMkE/PPsHuS6jvp3tkTwAAAAASUVORK5CYII="/>
  </g>
</svg>

(I added a newline at the start of the base64 string, also, just to get the entire string content aligned to the left in the file - you probably don't need or want to do that).

Upvotes: 1

Related Questions