Skip to content

Commit 3086a19

Browse files
Artyom YanchevskyiText-CI
authored andcommitted
Add support to utf-8 image format in svg
DEVSIX-3847 Autoported commit. Original commit hash: [c22318c5]
1 parent 765248c commit 3086a19

File tree

7 files changed

+153
-8
lines changed

7 files changed

+153
-8
lines changed

itext.tests/itext.html2pdf.tests/itext/html2pdf/resolver/resource/HtmlResourceResolverTest.cs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,39 @@ public virtual void ResourceResolverSvgWithImageObjectTest() {
345345
NUnit.Framework.Assert.IsNull(new CompareTool().CompareByContent(outPdf, cmpPdf, destinationFolder));
346346
}
347347

348+
[NUnit.Framework.Test]
349+
[LogMessage(iText.StyledXmlParser.LogMessageConstant.UNABLE_TO_RETRIEVE_IMAGE_WITH_GIVEN_DATA_URI, Count =
350+
3)]
351+
[LogMessage(iText.Html2pdf.LogMessageConstant.WORKER_UNABLE_TO_PROCESS_OTHER_WORKER, Count = 3)]
352+
public virtual void ResourceResolverSvgDifferentFormatsTest() {
353+
String html = sourceFolder + "resourceResolverSvgDifferentFormats.html";
354+
String outPdf = destinationFolder + "resourceResolverSvgDifferentFormats.pdf";
355+
String cmpPdf = sourceFolder + "cmp_resourceResolverSvgDifferentFormats.pdf";
356+
using (FileStream htmlInput = new FileStream(html, FileMode.Open, FileAccess.Read)) {
357+
using (FileStream pdfOutput = new FileStream(outPdf, FileMode.Create)) {
358+
HtmlConverter.ConvertToPdf(htmlInput, pdfOutput, new ConverterProperties().SetBaseUri(sourceFolder));
359+
}
360+
}
361+
NUnit.Framework.Assert.IsNull(new CompareTool().CompareByContent(outPdf, cmpPdf, destinationFolder, "diffCorruptedSvg_"
362+
));
363+
}
364+
365+
[NUnit.Framework.Test]
366+
[LogMessage(iText.StyledXmlParser.LogMessageConstant.UNABLE_TO_RETRIEVE_IMAGE_WITH_GIVEN_DATA_URI)]
367+
[LogMessage(iText.Html2pdf.LogMessageConstant.WORKER_UNABLE_TO_PROCESS_OTHER_WORKER)]
368+
public virtual void ResourceResolverNotValidInlineSvgTest() {
369+
String html = sourceFolder + "resourceResolverNotValidInlineSvg.html";
370+
String outPdf = destinationFolder + "resourceResolverNotValidInlineSvg.pdf";
371+
String cmpPdf = sourceFolder + "cmp_resourceResolverNotValidInlineSvg.pdf";
372+
using (FileStream htmlInput = new FileStream(html, FileMode.Open, FileAccess.Read)) {
373+
using (FileStream pdfOutput = new FileStream(outPdf, FileMode.Create)) {
374+
HtmlConverter.ConvertToPdf(htmlInput, pdfOutput, new ConverterProperties().SetBaseUri(sourceFolder));
375+
}
376+
}
377+
NUnit.Framework.Assert.IsNull(new CompareTool().CompareByContent(outPdf, cmpPdf, destinationFolder, "diffCorruptedSvg_"
378+
));
379+
}
380+
348381
[NUnit.Framework.Test]
349382
[LogMessage(SvgLogMessageConstant.NOROOT)]
350383
[LogMessage(iText.Html2pdf.LogMessageConstant.WORKER_UNABLE_TO_PROCESS_OTHER_WORKER)]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<html>
2+
<body>
3+
<p>Document with not valid svg</p>
4+
<img type='image/svg+xml' src='data:image/svg+xml, xmlns="http://www.w3.org/2000/svg"
5+
xmlns:xlink="http://www.w3.org/1999/xlink" width="300" height="200">
6+
<polygon points="100,10 40,198 190,78 10,78 160,198"
7+
style="fill:lime;stroke:purple;stroke-width:5;fill-rule:evenodd;"'>
8+
</body>
9+
</html>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<html>
2+
<body>
3+
<style>
4+
.svgimg {
5+
width: 200px;
6+
height: 200px;
7+
background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="300" height="200"> <polygon points="100,10 40,198 190,78 10,78 160,198" style="fill:lime;stroke:purple;stroke-width:5;fill-rule:evenodd;" /></svg>');
8+
}
9+
</style>
10+
<p>Document with svg images. Some of them are corrupted</p>
11+
<p>+ style</p>
12+
<div class="svgimg"></div>
13+
<p>+</p>
14+
<img type='image/svg+xml' src='data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciICAgICB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgd2lkdGg9IjUwMCIgaGVpZ2h0PSI1MDAiPiAgICA8bGluZSB4MT0iMCIgeTE9IjEwMCIgeDI9IjUwMCIgeTI9IjEwMCIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLXdpZHRoPSIyIiAgLz4gICAgPGxpbmUgeDE9IjAiIHkxPSIyMDAiIHgyPSI1MDAiIHkyPSIyMDAiIHN0cm9rZT0iYmxhY2siIHN0cm9rZS13aWR0aD0iMiIgIC8+ICAgIDxsaW5lIHgxPSIwIiB5MT0iMzAwIiB4Mj0iNTAwIiB5Mj0iMzAwIiBzdHJva2U9ImJsYWNrIiBzdHJva2Utd2lkdGg9IjIiICAvPiAgICA8bGluZSB4MT0iMCIgeTE9IjQwMCIgeDI9IjUwMCIgeTI9IjQwMCIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLXdpZHRoPSIyIiAgLz4gICAgPGxpbmUgeDE9IjEwMCIgeTE9IjAiIHgyPSIxMDAiIHkyPSI1MDAiIHN0cm9rZT0iYmxhY2siIHN0cm9rZS13aWR0aD0iMiIgIC8+ICAgIDxsaW5lIHgxPSIyMDAiIHkxPSIwIiB4Mj0iMjAwIiB5Mj0iNTAwIiBzdHJva2U9ImJsYWNrIiBzdHJva2Utd2lkdGg9IjIiICAvPiAgICA8bGluZSB4MT0iMzAwIiB5MT0iMCIgeDI9IjMwMCIgeTI9IjUwMCIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLXdpZHRoPSIyIiAgLz4gICAgPGxpbmUgeDE9IjQwMCIgeTE9IjAiIHgyPSI0MDAiIHkyPSI1MDAiIHN0cm9rZT0iYmxhY2siIHN0cm9rZS13aWR0aD0iMiIgIC8+PC9zdmc+'/>
15+
<p>- Corrupted</p>
16+
<img type='image/svg+xml' src='data:image/svg+xml;utf8,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciICAgICB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5v'>
17+
<p>+;utf8,</p>
18+
<img type='image/svg+xml' src='data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg"
19+
xmlns:xlink="http://www.w3.org/1999/xlink" width="300" height="200">
20+
<polygon points="100,10 40,198 190,78 10,78 160,198"
21+
style="fill:lime;stroke:purple;stroke-width:5;fill-rule:evenodd;" /></svg>'>
22+
<p>+</p>
23+
<img type='image/svg+xml' src='data:image/svg+xml;UTF-8,<svg xmlns="http://www.w3.org/2000/svg"
24+
xmlns:xlink="http://www.w3.org/1999/xlink" width="300" height="200">
25+
<polygon points="100,10 40,198 190,78 10,78 160,198"
26+
style="fill:lime;stroke:purple;stroke-width:5;fill-rule:evenodd;" /></svg>'>
27+
<p>+ utf8</p>
28+
<img type='image/svg+xml' src='data:image/svg+xml ; utf8 , <svg xmlns="http://www.w3.org/2000/svg"
29+
xmlns:xlink="http://www.w3.org/1999/xlink" width="300" height="200">
30+
<polygon points="100,10 40,198 190,78 10,78 160,198"
31+
style="fill:lime;stroke:purple;stroke-width:5;fill-rule:evenodd;" /></svg>'>
32+
<p>+;,</p>
33+
<img type='image/svg+xml' src=' data:image/svg+xml;,<svg xmlns="http://www.w3.org/2000/svg"
34+
xmlns:xlink="http://www.w3.org/1999/xlink" width="300" height="200">
35+
<polygon points="100,10 40,198 190,78 10,78 160,198"
36+
style="fill:lime;stroke:purple;stroke-width:5;fill-rule:evenodd;" /></svg>'>
37+
<p>+ fake base64</p>
38+
<img type='image/svg+xml' src='data:image/svg+xml; fakebase64 ,<svg xmlns="http://www.w3.org/2000/svg"
39+
xmlns:xlink="http://www.w3.org/1999/xlink" width="300" height="200">
40+
<polygon points="100,10 40,198 190,78 10,78 160,198"
41+
style="fill:lime;stroke:purple;stroke-width:5;fill-rule:evenodd;" /></svg>'>
42+
<p>- base 64</p>
43+
<img type='image/svg+xml' src='data:image/svg+xml; base64 ,<svg xmlns="http://www.w3.org/2000/svg"
44+
xmlns:xlink="http://www.w3.org/1999/xlink" width="300" height="200">
45+
<polygon points="100,10 40,198 190,78 10,78 160,198"
46+
style="fill:lime;stroke:purple;stroke-width:5;fill-rule:evenodd;" /></svg>'>
47+
<p>-e</p>
48+
<img type='image/svg+xml' src='data:image/svg+xml; , e <svg xmlns="http://www.w3.org/2000/svg"
49+
xmlns:xlink="http://www.w3.org/1999/xlink" width="300" height="200">
50+
<polygon points="100,10 40,198 190,78 10,78 160,198"
51+
style="fill:lime;stroke:purple;stroke-width:5;fill-rule:evenodd;" /></svg>'>
52+
<p>+,</p>
53+
<img type='image/svg+xml' src='data:image/svg+xml, <svg xmlns="http://www.w3.org/2000/svg"
54+
xmlns:xlink="http://www.w3.org/1999/xlink" width="300" height="200">
55+
<polygon points="100,10 40,198 190,78 10,78 160,198"
56+
style="fill:lime;stroke:purple;stroke-width:5;fill-rule:evenodd;" /></svg>'>
57+
<p>-;</p>
58+
<img type='image/svg+xml' src='data:image/svg+xml; <svg xmlns="http://www.w3.org/2000/svg"
59+
xmlns:xlink="http://www.w3.org/1999/xlink" width="300" height="200">
60+
<polygon points="100,10 40,198 190,78 10,78 160,198"
61+
style="fill:lime;stroke:purple;stroke-width:5;fill-rule:evenodd;" /></svg>'>
62+
</body>
63+
</html>

itext/itext.html2pdf/itext/html2pdf/resolver/resource/HtmlResourceResolver.cs

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ source product.
4242
*/
4343
using System;
4444
using System.IO;
45+
using System.Text.RegularExpressions;
4546
using iText.Html2pdf.Attach;
4647
using iText.Html2pdf.Attach.Util;
4748
using iText.Html2pdf.Util;
@@ -60,7 +61,10 @@ namespace iText.Html2pdf.Resolver.Resource {
6061
/// to also support SVG images
6162
/// </summary>
6263
public class HtmlResourceResolver : ResourceResolver {
63-
private const String SVG_BASE64_PREFIX = "data:image/svg+xml";
64+
private const String SVG_PREFIX = "data:image/svg+xml";
65+
66+
private static readonly Regex SVG_IDENTIFIER_PATTERN = iText.IO.Util.StringUtil.RegexCompile(",[\\s]*(<svg )"
67+
);
6468

6569
private ProcessorContext context;
6670

@@ -123,15 +127,36 @@ public HtmlResourceResolver(String baseUri, ProcessorContext context, IResourceR
123127
this.context = context;
124128
}
125129

130+
public override PdfXObject RetrieveImageExtended(String src) {
131+
if (src != null && src.Trim().StartsWith(SVG_PREFIX) && iText.IO.Util.Matcher.Match(SVG_IDENTIFIER_PATTERN
132+
, src).Find()) {
133+
PdfXObject imageXObject = TryResolveSvgImageSource(src);
134+
if (imageXObject != null) {
135+
return imageXObject;
136+
}
137+
}
138+
return base.RetrieveImageExtended(src);
139+
}
140+
141+
/// <summary>
142+
/// Retrieve image as either
143+
/// <see cref="iText.Kernel.Pdf.Xobject.PdfImageXObject"/>
144+
/// , or
145+
/// <see cref="iText.Kernel.Pdf.Xobject.PdfFormXObject"/>.
146+
/// </summary>
147+
/// <param name="src">either link to file or base64 encoded stream</param>
148+
/// <returns>PdfXObject on success, otherwise null</returns>
126149
protected override PdfXObject TryResolveBase64ImageSource(String src) {
127150
String fixedSrc = iText.IO.Util.StringUtil.ReplaceAll(src, "\\s", "");
128-
if (fixedSrc.StartsWith(SVG_BASE64_PREFIX)) {
129-
fixedSrc = fixedSrc.Substring(fixedSrc.IndexOf(BASE64IDENTIFIER, StringComparison.Ordinal) + 7);
151+
if (fixedSrc.StartsWith(SVG_PREFIX)) {
152+
fixedSrc = fixedSrc.Substring(fixedSrc.IndexOf(BASE64_IDENTIFIER, StringComparison.Ordinal) + BASE64_IDENTIFIER
153+
.Length + 1);
130154
try {
131-
PdfXObject xObject = iText.Html2pdf.Resolver.Resource.HtmlResourceResolver.ProcessAsSvg(new MemoryStream(Convert.FromBase64String
132-
(fixedSrc)), context, null);
133-
if (xObject != null) {
134-
return xObject;
155+
using (MemoryStream stream = new MemoryStream(Convert.FromBase64String(fixedSrc))) {
156+
PdfFormXObject xObject = ProcessAsSvg(stream, context, null);
157+
if (xObject != null) {
158+
return xObject;
159+
}
135160
}
136161
}
137162
catch (Exception) {
@@ -152,6 +177,21 @@ protected override PdfXObject CreateImageByUrl(Uri url) {
152177
}
153178
}
154179

180+
private PdfXObject TryResolveSvgImageSource(String src) {
181+
try {
182+
using (MemoryStream stream = new MemoryStream(src.GetBytes(System.Text.Encoding.UTF8))) {
183+
PdfFormXObject xObject = ProcessAsSvg(stream, context, null);
184+
if (xObject != null) {
185+
return xObject;
186+
}
187+
}
188+
}
189+
catch (Exception) {
190+
}
191+
//Logs an error in a higher-level method if null is returned
192+
return null;
193+
}
194+
155195
private static PdfFormXObject ProcessAsSvg(Stream stream, ProcessorContext context, String parentDir) {
156196
SvgConverterProperties svgConverterProperties = ContextMappingHelper.MapToSvgConverterProperties(context);
157197
if (parentDir != null) {

port-hash

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1cd1799a687ccc6ab80428d25b9afeca6be91309
1+
c22318c5cc7d9ae4224d05e722e8cf176b1cec07

0 commit comments

Comments
 (0)