Hello,I have studied some of the bugs from the svgio module of LibreOffice. As described in the svgio/README.md, "svgio module uses sax for reading xml and turns it into drawinglayer primitives. The rendering is done via drawinglayer primitives".
There are problems with the current implementation in different aspects: 1. Format supportThe supported subset of SVG that is supported for import is limited. Some of the problems with SVG import are listed in the meta bug:
Bug 88278 (SVG-Import) - [META] SVG import image filter (all modules) https://bugs.documentfoundation.org/showdependencytree.cgi?id=88278&hide_resolved=1 2. PerformanceWhile trying to load, and/or converting the SVG files into PDF, the performance is sometimes very slow. Compared to Chrome, displaying SVG and converting it to PDF is several times slower. See this bug report:
Bug 146319 - [SVG] load/export of W3C SVG example car.svg is slow https://bugs.documentfoundation.org/show_bug.cgi?id=146319In the previous week, I mentioned this in the IRC chat, and one answer was that at the time there was no good SVG implementation in C++. Now that we build and use Skia in LibreOffice (external/skia), one option can be using Skia SVG module. So, I tried to take a look at it, and here are the results:
Skia SVG module is out of experimental mode since 2020, so it is a good candidate:
[svg] Relocate out of experimental https://skia.googlesource.com/skia/+/6fc4106a9d641933abccc6703516206c8ae6d7eb Let's look at the comparison: 1. Format supportI've tested with a complex SVG example (car.svg), that used in tdf#146319. The result with the Skia/PDF m111 onwards is very good. One can simply use Chromium PDF output to see the result.
2. PerformanceThe performance difference is significant. Comparing the LibreOffice and Chrome, I see a ten times difference:
$ time libreoffice7.5 --headless --convert-to pdf car.svg convert /out/car.svg -> /out/car.pdf using filter : draw_pdf_Export real 0m4.372s user 0m3.414s sys 0m0.679s $ time google-chrome --headless --disable-gpu --print-to-pdf car.svg 1348088 bytes written to file output.pdf real 0m0.497s user 0m0.070s sys 0m0.069sI even tried to write an example using Python and C++. See the difference. Please note that skia-python uses an older version of Skia, so it is faster.
------------------- import skia from PIL import Image skia_stream = skia.Stream.MakeFromFile("input.svg") skia_svg = skia.SVGDOM.MakeFromStream(skia_stream) svg_width, svg_height = skia_svg.containerSize() surface_width, surface_height = 1024, 720 stream = skia.FILEWStream("output.pdf") with skia.PDF.MakeDocument(stream) as document: with document.page(surface_width, surface_height) as canvas:canvas.scale(surface_width / svg_width, surface_height / svg_height)
skia_svg.render(canvas) ------------------- Python (see the attached skia-svg2pdf.py) $ time python3 car.py real 0m0.116s user 0m0.066s sys 0m0.013s C++ (see the attached skia-svg2pdf.cpp) $ time ./main real 0m0.126s user 0m0.111s sys 0m0.000s 3. ComplexityThe code in skia/modules/svg seem to be mostly the work of one developer, and it is 6.7k C++ code:
$ cloc ~/skia/modules/svg/ 99 text files. 98 unique files. 3 files ignored.github.com/AlDanial/cloc v 1.90 T=0.02 s (4638.4 files/s, 465529.1 lines/s)
-------------------------------------------------------------------------------Language files blank comment code
-------------------------------------------------------------------------------C++ 45 893 594 4355 C/C++ Header 48 824 426 2419 Bazel 3 9 3 112
-------------------------------------------------------------------------------SUM: 96 1726 1023 6886
------------------------------------------------------------------------------- Compared to this, svgio is even bigger, around 12.5K line of code. $ cloc ~/libreoffice/core/svgio/ 137 text files. 137 unique files. 0 files ignored.github.com/AlDanial/cloc v 1.90 T=0.06 s (2360.2 files/s, 332764.5 lines/s)
-----------------------------------------------------------------------------------Language files blank comment code
-----------------------------------------------------------------------------------C++ 39 1931 1619 10818 C/C++ Header 35 569 956 1708
4. Output sizeYou can compare the PDF output size from LibreOffice and Skia. With the latest version, Skia output is around 700 KB, but LO output is around 3.2 MB.
5. ArchitectureIt may be possible to bypass svgio/cairo/etc. for creating the output, or when the Skia backend is used in GUI. Then it would make sense to create the output, or load the SVG in GUI, much faster.
I appreciate your comments. Regards, Hossein
Attachment:
skia-m111.pdf
Description: Adobe PDF document
Attachment:
libreoffice-7.6-dev.pdf
Description: Adobe PDF document
Attachment:
skia-m114-chromium.pdf
Description: Adobe PDF document
#!/usr/bin/python3 import skia from PIL import Image skia_stream = skia.Stream.MakeFromFile("input.svg") skia_svg = skia.SVGDOM.MakeFromStream(skia_stream) svg_width, svg_height = skia_svg.containerSize() surface_width, surface_height = 1024, 720 stream = skia.FILEWStream("output.pdf") with skia.PDF.MakeDocument(stream) as document: with document.page(surface_width, surface_height) as canvas: canvas.scale(surface_width / svg_width, surface_height / svg_height) skia_svg.render(canvas)
/* skia-svg2pdf.cpp Use Skia for converting svg to pdf Compile: $ g++ skia-svg2pdf.cpp -I ~/skia libsvg.a libskia.a libskia-bindings.a libskparagraph.a libskresources.a libskshaper.a libskunicode.a -lharfbuzz -lfreetype -lEGL -lGL -lfontconfig -o main Headers: Skia headers for appropriate version is assumed to be in ~/skia Library: Download Skia binaries from: https://github.com/rust-skia/skia-binaries/releases and copy the .a files in the source directory */ #include "include/core/SkCanvas.h" #include "include/core/SkDocument.h" #include "include/core/SkStream.h" #include "include/docs/SkPDFDocument.h" #include "modules/svg/include/SkSVGDOM.h" int main() { auto skia_stream = SkStream::MakeFromFile("input.svg"); auto svgDom(SkSVGDOM::MakeFromStream(*skia_stream)); int svg_width = svgDom->containerSize().width(); int svg_height = svgDom->containerSize().height(); int surface_width = 1024; int surface_height = 720; auto stream = SkFILEWStream("output.pdf"); auto document = SkPDF::MakeDocument(&stream); auto canvas = document->beginPage(SkIntToScalar(surface_width), SkIntToScalar(surface_height)); canvas->scale(SkIntToScalar(surface_width) / SkIntToScalar(svg_width), SkIntToScalar(surface_height) / SkIntToScalar(svg_height)); svgDom->render(canvas); document->endPage(); document->close(); return 0; }