c++qtsvgqtsvg

QSvgGenerator converts QSvgGraphicsItem to image when generating Svg


I have a bunch of SVG files that I've loaded them (as QGraphicsSvgItem) into a QGraphicsScene for designing and everything is Ok, now I want to save the scene into another output SVG file (including all the Svg items) with QSvgGenerator with the code below, but when it is exporting SVG items turn into images in the output file and their vectors are not anymore scalable.

I'm looking forward for XML-manipulation methods if there is no direct solution with this Qt framework.

QSvgGenerator generator;
generator.setFileName(savePath);
generator.setSize(QSize(static_cast<int>(currentScene->width()), static_cast<int>(currentScene->height())));
generator.setViewBox(currentScene->sceneRect());
generator.setTitle(tr("SVG Generated using SVG Generator"));
generator.setDescription(tr("an SVG drawing used by Software Package"));
QPainter painter;
painter.begin(&generator);
currentScene->clearSelection();
currentScene->render(&painter);
painter.end();

I'm expecting an output svg file that contains tags and nodes of the included internal SVG items (not converting them to images data)

right now it is converting internal svg items into these image tags:

<image x="131" y="127" width="102" height="102" preserveAspectRatio="none" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...

UPDATE 1 this tiny application will show an svg file (QGraphicsSvgItem) in the graphical scene and will use QSvgGenerator to export the scene into another output svg file:

#include <QApplication>
#include <QtSvg>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    // Creating and showing an svg icon in the graphical scene view
    QGraphicsSvgItem *item = new QGraphicsSvgItem(":The Pirate Bay Logo.svg");
    QGraphicsScene *scene = new QGraphicsScene;
    scene->setSceneRect(0, 0, 1000, 1000);
    QGraphicsView *view = new QGraphicsView(scene);

    scene->addItem(item);
    item->setPos(view->rect().center());

    view->show();

    // Saving the graphical scene to another svg output device file using QSvgGenerator
    QSvgGenerator generator;
    generator.setFileName("output.svg");
    generator.setSize(QSize(static_cast<int>(scene->width()), static_cast<int>(scene->height())));
    generator.setViewBox(scene->sceneRect());
    generator.setTitle("SVG Generated using SVG Generator");
    generator.setDescription("an SVG drawing used by Software Package");
    QPainter painter;
    painter.begin(&generator);
    scene->clearSelection();
    scene->render(&painter);
    painter.end();

    return a.exec();
}

but what I get is an svg file includes converted corrupted bits of a very low quality image of the initial svg file. what I expect is that QSvgGenerator takes the initial svg elements from the source file (maybe saved in the QGraphicsSvgItem in the scene) and put them into the last generated file.


Solution

  • The quick answer turned out to be disabling cache mode on each QGraphicsItem before rendering it to SVG*.

    QGraphicsSvgItem *item = new QGraphicsSvgItem(":The Pirate Bay Logo.svg");
    item->setCacheMode(QGraphicsItem::NoCache);
    

    The reason is that when cache mode is enabled, the graphics item caches its painting in a bitmap image (which of course is raster format). So when asked to render itself on to the QSvgGenerator device, it just draws the cached bitmap. Which the generator correctly encodes as a bitmap image, not vectors.

    This extends to any painting which uses bitmaps/QPixmap to paint or cache itself. For example QSvgIconEngine (which generates QIcons from SVG files) will use bitmaps for painting (even when the original source is a vector). So rendering a QIcon to QSvgGenerator produces raster images.

    * I would consider leaving the cache enabled and only disabling it before rendering back to SVG, then re-enable after. You'd have to loop over all the scene items, but OTOH the performance gain with cache on the rest of the time could far outweigh that.