1

I need to save the items from my QGraphicsScene to an svg, and be able to load that svg back on the scene.
I can do it...
But each time the canvas is saved to svg, upon load the items are somewhat bigger (and repeatedly saving and loading the same svg causes it to grow).
I can't find the cause.

I am attaching a sample code - and the result.

test1.pro

QT       += gui svg
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets core
TARGET = test1
TEMPLATE = app
SOURCES += \
    svggenerator.cpp

svggenerator.cpp

#include <QApplication>
#include <QGraphicsView>
#include <QGraphicsEllipseItem>
#include <QGraphicsSvgItem>
#include <QSvgGenerator>
#include <QSvgRenderer>
#include <QFile>
#include <QByteArray>
#include <QMessageBox>

void saveSceneToSvg(QGraphicsScene* s, const QString &filename) {
    QRectF newSceneRect;
    QGraphicsScene *tempScene = new QGraphicsScene(s->sceneRect());
    tempScene->setBackgroundBrush(QBrush(Qt::transparent));
    tempScene->setItemIndexMethod(QGraphicsScene::BspTreeIndex);
    foreach(QGraphicsItem* item, s->items()) {
        newSceneRect |= item->mapToScene(item->boundingRect()).boundingRect();
        tempScene->addItem(item);
    }
    tempScene->setSceneRect(newSceneRect);
    tempScene->clearSelection();
    QSize sceneSize = newSceneRect.size().toSize();

    QSvgGenerator generator;
    generator.setFileName(filename);
    generator.setSize(sceneSize);
    generator.setViewBox(QRect(0, 0, sceneSize.width(), sceneSize.height()));
    generator.setDescription(QObject::tr("My canvas exported to Svg"));
    generator.setTitle(filename);
    QPainter painter;
    painter.begin(&generator);
    tempScene->render(&painter);
    painter.end();

    tempScene->clear();
    delete tempScene;
}

void loadSvg(QGraphicsScene* s, const QString &filename, const QPointF& p) {
    QGraphicsSvgItem* item = new QGraphicsSvgItem();
    QFile file(filename);
    file.open(QFile::ReadOnly);
    QByteArray contents = file.readAll();
    item->setSharedRenderer(new QSvgRenderer(contents));
    file.close();
    item->setPos(p);
    s->addItem(item);
}

void processScene(QGraphicsScene* s) {
    QGraphicsEllipseItem* eli = new QGraphicsEllipseItem();
    eli->setRect(QRectF(0, 0, 100, 100));
    eli->setPen(Qt::NoPen);
    eli->setBrush(Qt::red);
    eli->setPos(100, 300);
    s->addItem(eli);

    QGraphicsEllipseItem* eli1 = new QGraphicsEllipseItem();
    eli1->setRect(QRectF(0, 0, 100, 100));
    eli1->setPen(Qt::NoPen);
    eli1->setBrush(Qt::yellow);
    eli1->setPos(150, 300);
    s->addItem(eli1);

    QMessageBox::information(NULL, "hi", "click");

    saveSceneToSvg(s, "abcd.svg");
    loadSvg(s, "abcd.svg", QPointF(100,300));

    QMessageBox::information(NULL, "hi", "click");

    saveSceneToSvg(s, "abcd1.svg"); // saved with a dif name so I can see
    loadSvg(s, "abcd1.svg", QPointF(100,300));

    QMessageBox::information(NULL, "hi", "click");

    saveSceneToSvg(s, "abcd2.svg");
    loadSvg(s, "abcd2.svg", QPointF(100,300));

    // .... each time i call them they grow larger
}

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);
    QGraphicsScene s;
    s.setSceneRect(50, 0, 1000, 800);
    QGraphicsView view(&s);
    view.show();
    processScene(&s);
    return app.exec();
}

Result:

enter image description here

Looking at the svgs themselves, I can see that the svgs increase in size by approx 1.25... I can't explain, and can't be sure this will be true for other examples. (it seems to)

What is causing this growth ? How can I stop it ?

(Also I notice the ordering is different ... I just noticed and that is a different problem... But since in my "real" code I also have z order I don't care.)

Saving I think is fine - the resulting svg has size and view box of expected size.

Loading other svgs is fine - saving an svg from outside source creates an svg similar in size.

It seems the problem is when I am loading an svg created by the svg generator that it increases in size.

(If I could be sure it is always the case I could try scaling it down on load, but the ratio is not exactly 1.25 each time, close though... and I don't know how to tell the difference between an outside svg and a generated one).

Thalia
  • 13,637
  • 22
  • 96
  • 190
  • Just as a guess..can it be related to your scenRect s.setSceneRect(50, 0, 1000, 800);? Can you change it and check if the ratio changes? – Alexander Tyapkov Dec 09 '15 at 18:16
  • @AlexanderTyapkov - It hasn't. I have played with it to make my sample pretty :-) but I have experienced the same thing in the "real" program with complete different object types and sizes. Loading other svg is also fine. The only thing that is bad is loading a self-generated svg. – Thalia Dec 09 '15 at 18:20
  • ok. I will try to reproduce the bug right now – Alexander Tyapkov Dec 09 '15 at 20:32

1 Answers1

2

It looks like a bug of SvgGenerator. The size which you provide to SVG generator is used only to form the header of svg. The actual size of .svg file differs from one written in the header. The only workaround I found is simple 25 percent decrease of size on save similar to:

int width = qCeil(qreal(sceneSize.width()/1.25));
int height = qCeil(qreal(sceneSize.height()/1.25));

generator.setSize(QSize(width, height));
generator.setViewBox(QRect(0, 0, width, height));
Alexander Tyapkov
  • 4,837
  • 5
  • 39
  • 65
  • Thanks... I wish there was a better (and more accurate) way. If I save an item size 100x100 and multiply by 0.8, I get an svg of size 99x99. I have to create the svg then edit the file.... – Thalia Dec 10 '15 at 14:51
  • Also in Qt4, each time I load the saved svg, all the items - and the svg itself - get a black stroke. – Thalia Dec 10 '15 at 14:52
  • You forgot to qCeil after multiplying on 0.8. If you will do it then the size will be 100 by 100 for all the svgs. Probably it is also will solve the bug with the stroke. I am using qt.5.5 and haven't seen any strokes on the output images. – Alexander Tyapkov Dec 10 '15 at 18:00
  • There is no stroke in Qt5, only in Qt4. The stroke appears if the saved svg is loaded in the scene, then the scene saved again as svg. The cause of the stroke is its interpretation of the "defaults" inserted before every entity - removing those (manually) fixes the problem. I use 4.7.4 (and 5.5). – Thalia Dec 11 '15 at 15:59
  • On Qt 5.5 adding int width = qCeil(sceneSize.width()*0.8); changes the final size of svg file. Do you make it in the same way? – Alexander Tyapkov Dec 11 '15 at 18:00
  • I did - but re-adding the svg back to the scene and checking its width gives 99, from an initial width of 100. I think it is due to their rounding when converting to mm in the svg header, and converting back to points on load. – Thalia Dec 14 '15 at 17:35
  • My current implementation is - after allowing the svg generator to do its thing - I reload the svg file as xml document, and replace its width and height with the actual values (like 100). (leaving the viewBox at 80%...). In Qt4 I also remove nodes that are likely to have been added by the generator, by guessing :-( – Thalia Dec 14 '15 at 17:38