PDA

View Full Version : Help With A Custom Implementation of QGraphicsSvgItem



Abion47
23rd February 2014, 19:37
So I'm trying to follow the code on this thread:

http://www.qtcentre.org/threads/53946-Is-it-possible-to-change-color-of-a-QGraphicsSvgItem

where the guy apparently discovered that the SVG attributes for things like color can't be changed within the QGraphicsSvgItem itself, so I need to have some kind of wrapper that holds the XML data and can edit it on the fly, then have the QSvgRenderer reload the information. The problem I am getting is that, although the renderer will load information from a file just fine, I can't get it to read from a byte array.

Here's my code snippet:


QDomDocument *doc = new QDomDocument();
QGraphicsSvgItem *item = new QGraphicsSvgItem();

QFile *file = new QFile(":/assets/outline.svg");
file->open(QIODevice::ReadOnly);
QTextStream *stream = new QTextStream(file);
QString str = stream->readAll();
doc->setContent(str);
QByteArray barr = doc->toByteArray();
file->close();

QSvgRenderer *renderer = new QSvgRenderer();

std::cout << renderer->load(QString(":/assets/outline.svg")) << std::endl; // Returns true
std::cout << renderer->load(barr) << std::endl; // Returns false

item->setSharedRenderer(renderer);

scene->addItem(item);

(My actual code doesn't have both "renderer->load" lines in series like this. I just put them in the snippet to show what I am using.)

As the comments show, when the renderer loads the SVG straight from the file, it loads fine and I see my graphic, but when I try and load the byte array that went through the QDomDocument, I get nothing. Does anyone have any idea as to why this might be the case?

anda_skoa
23rd February 2014, 20:47
Is the QDomDocument still ok?

Does the content of "barr" look correct?

Btw, create QFile on the stack or delete it manually, otherwise it leaks. No need for QTextStream, QDomDocument can read from a QIODevice (the file) directly.

Cheers,
_

Abion47
24th February 2014, 02:08
Is the QDomDocument still ok?

Does the content of "barr" look correct?
Yes and yes.



Btw, create QFile on the stack or delete it manually, otherwise it leaks. No need for QTextStream, QDomDocument can read from a QIODevice (the file) directly.


I know that about the QDomDocument. I just made an intermediary QTextStream to make 100% sure that it wasn't some error in reading the file.

anda_skoa
24th February 2014, 08:26
Hmm.

Can you try the third overload? The one taking a QXmlStreamReader? You should be able to initialize one with the byte array.

If it fails as well, its error/errorString methods might provide more insight into what went wrong.

Cheers,
_

Abion47
24th February 2014, 15:25
I did the QXmlXtreamReader route, and the error string is telling me that there was a "Premature end of document." I don't know what could be causing that. It shouldn't be the vector file itself, right? If it was, the load-directly-from-file method shouldn't be working either.

anda_skoa
24th February 2014, 16:03
One possibility would be that the byte array data is missing a terminating element, maybe not closing the root tag.

You could try using QDomDocument::save(), with a QTextStream that operates on a QBuffer.
The same buffer could then also be used for the QXmlStreamReader (but it can also return its content as a QByteArray).

Cheers,
_

Abion47
24th February 2014, 20:51
Still nothing. Here's the code I'm using to try these things, on the chance that I'm doing it wrong:

Qt:

QDomDocument *doc = new QDomDocument();
QGraphicsSvgItem *item = new QGraphicsSvgItem();

QFile *file = new QFile(":/assets/outline.svg");
file->open(QIODevice::ReadOnly);
QTextStream *stream = new QTextStream(file);
QString str = stream->readAll();
doc->setContent(str);
file->close();

QByteArray barr = doc->toByteArray();

QBuffer *buf = new QBuffer();
buf->open(QIODevice::ReadWrite);
QTextStream *bufStream = new QTextStream(buf);
doc->save(*bufStream, 0);

QXmlStreamReader *xmlReader = new QXmlStreamReader(buf);
QSvgRenderer *renderer = new QSvgRenderer();

std::cout << renderer->load(xmlReader) << std::endl;
item->setSharedRenderer(renderer);
scene->addItem(item);
buf->close();

Here's the SVG file at various stages of the program:

String input from file (characters '\r' and '\n' formatted out for readability):


<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="480px" height="360px" viewBox="0 0 480 360" enable-background="new 0 0 480 360" xml:space="preserve">
<path fill="#FFFFFF" stroke="#000000" stroke-miterlimit="10" d="M317.665,110.518c-13.627,0-26.354,3.846-37.165,10.5V29.5h-200v158h51.483l-18.221,86.857l74.429,66.658l94.942-31.128l12.747-60.766c6.866,2.212,14.184,3.419,21.785,3.419c39.21 9,0,71.012-31.793,71.012-71.012S356.884,110.518,317.665,110.518z"/>
</svg>

Contents of QDomDocument via doc->toByteArray() (character '\n' formatted out for readability):

<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg xmlns:xlink="http://www.w3.org/1999/xlink" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xml:space="preserve" x="0px" y="0px" height="360px" version="1.1" enable-background="new 0 0 480 360" width="480px" viewBox="0 0 480 360">
<path stroke-miterlimit="10" stroke="#000000" d="M317.665,110.518c-13.627,0-26.354,3.846-37.165,10.5V29.5h-200v158h51.483l-18.221,86.857l74.429,66.658l94.942-31.128l12.747-60.766c6.866,2.212,14.184,3.419,21.785,3.419c39.21 9,0,71.012-31.793,71.012-71.012S356.884,110.518,317.665,110.518z" fill="#FFFFFF"/>
</svg>

Contents of the QBuffer (character '\n' formatted out for readability):

<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg xmlns:xlink="http://www.w3.org/1999/xlink" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xml:space="preserve" x="0px" y="0px" height="360px" version="1.1" enable-background="new 0 0 480 360" width="480px" viewBox="0 0 480 360">
<path stroke-miterlimit="10" stroke="#000000" d="M317.665,110.518c-13.627,0-26.354,3.846-37.165,10.5V29.5h-200v158h51.483l-18.221,86.857l74.429,66.658l94.942-31.128l12.747-60.766c6.866,2.212,14.184,3.419,21.785,3.419c39.21 9,0,71.012-31.793,71.012-71.012S356.884,110.518,317.665,110.518z" fill="#FFFFFF"/>
</svg>


Every SVG file looks like it has correct syntax, albeit with a bit of reorganizing with the attribute orders. Maybe there's something in it that the QSvgRenderer doesn't like?

Abion47
27th February 2014, 22:11
Alright, I found a workaround that works, but it's weird. Apparently the issue was with the QByteArray, as taking the output of doc->toString() and putting it into the QXmlStreamReader constructor made the whole thing work. I don't know if this is an issue of the output from QDomDocument->toByteArray or with the QSvgRenderer trying to load it or with the QByteArray itself, but as the above output shows, the data itself remains valid at every state prior to the renderer trying to load it. I hesitate to call this a fault of Qt (especially since that seems so taboo and I don't really have the experience or evidence to make a call like that), but this is a really confusing case and I can't imagine what else it could be.