Using SVG module from Skia

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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 support
The 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. Performance
While 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=146319

In 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 support
I'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. Performance
The 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.069s

I 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. Complexity
The 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 size
You 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. Architecture
It 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;
}

[Index of Archives]     [LARTC]     [Bugtraq]     [Yosemite Forum]     [Photo]

  Powered by Linux