PDA

View Full Version : How to make svg painters measure text correctly



ceraolo
4th January 2014, 12:20
I'm writing a program to create scientific plots on screen, and to produce Scalable Vector Graphics (SVG) copies on the hard disk.
I came across an issue on SVG's. To show it I've created the following code.
I know that to create the SVG in the paintEvent method is silly, but I just wanted to show the issue in a very compact code.


#include "mainwindow.h"
#include <QPainter>
#include <QSvgGenerator>
#include <QMainWindow>

MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
}

MainWindow::~MainWindow()
{

}
void MainWindow::paintEvent(QPaintEvent *){
int xPos;
QPainter p;
QRect r;
QString s="This is some significant text";

//Writing on MainWindow:
p.begin(this);
p.drawText(0,0,0,0,0,s,&r);
p.drawText(r,s);
p.drawRect(r);

p.drawText(0,40,s);
QFontMetrics m=p.fontMetrics();
xPos=m.width(s);
p.drawLine(QPoint(xPos,40),QPoint(xPos,40-m.height()));
p.end();

//Creating and writing on the svg:
QSvgGenerator generator;
generator.setFileName("svgIssue.svg");
p.begin(&generator);
p.drawText(0,0,0,0,0,s,&r);
p.drawText(r,s);
p.drawRect(r);

p.drawText(0,40,s);
QFontMetrics m1=p.fontMetrics();
xPos=m1.width(s);
p.drawLine(QPoint(xPos,40),QPoint(xPos,40-m.height()));
p.end();
}



This is what I see on screen (i.e. it is correct)
9906

And this is what I see when I browse the svg file using google chrome (i.e. the right part of the rectangle and the vertical bar are misplaced:
9907

I.e. my software computes correctly the text width on screen, but DOES NOT inside the SVG.
You can imagine what such an error can cause on a program that creates scientific plots!

Can anyone suggest a workaround for this issue?
Thanks a lot in advance.
MC

ChrisW67
5th January 2014, 23:40
The metrics are calculated using the font information returned by the system for the screen, and the text is rendered from the same data.

When the same is painted to the SVG generator the line is drawn based on the screen rendering, but only the font name and size are output for the text (read you SVG file). How the SVG renderer maps the font name to a font it knows about, and how it determines the size of that font, is up to the renderer. This is where the discrepancy comes from. I was able to manufacture examples where the text spilled past the line in the SVG as rendered by Inkscape. If I use the Qt SVG renderer I get the expected result because it is using the same font information as the generator.



#include <QApplication>
#include <QPixmap>
#include <QPainter>
#include <QSvgGenerator>
#include <QSvgRenderer>

void draw(QPainter &p) {
const QString s("This is some significant text");
QRect r;
p.setFont(QFont("Comic Sans MS", 15));
p.drawText(0,0,0,0,0,s,&r);
p.drawText(r,s);
p.drawRect(r);

p.drawText(0,40,s);
QFontMetrics m=p.fontMetrics();
int xPos=m.width(s);
p.drawLine(QPoint(xPos,40),QPoint(xPos,40-m.height()));
}

int main(int argc, char **argv) {
QApplication app(argc, argv);

QPixmap pixmap(320,240);
pixmap.fill(Qt::white);
QPainter p(&pixmap);
draw(p);
p.end();
pixmap.save("test.png");

QSvgGenerator gen;
gen.setFileName("test.svg");
gen.setSize(QSize(320,240));
QPainter p2(&gen);
draw(p2);
p2.end();

QSvgRenderer render(QString("test.svg"));
pixmap.fill(Qt::white);
QPainter p3(&pixmap);
draw(p3);
p3.end();
pixmap.save("test_via_svg.png");

return 0;
}

If you want pixel based reproduction then you should use a raster format.

ceraolo
6th January 2014, 23:32
Hi,
your conclusion confirms my suspects.
when thinking that the SVG renderer might use fonts that are only approximately equivalent to those I used to create the SVG, I expected that one of the two following workarounds could be used to better the text-graphics alignment issue.

1) Storing the font. It might have been possible that people defining SVG standard would have envisaged the possibility of storing the font definition inside the SVG. Something similar to what is done in some PDF's. This would ensure that the renderer can reproduce text exactly using the same algorithm to display on screen. Unfortunately I did not find such option in Qt SVG library

2) using right alignment. Indeed in my case what I best need is the right alignment of the text (think of the numbers of the vertical axis on a plot). I mean, if the text is in the right right position, but is a bit longer or shorter than on screen, this would not be a big issue. So I tried a code that wrote text with right vertical alignment and I expected that the rightmost pixel would be in an accurately evaluated position. Unfortunately I tested this and it did not work.

It is really strange to me that the people that have worked on SVG standard or library have not dealt with this important issue in a systematic way.
My two workarounds above seem to me to be reasonable and easy to implement.

MC

ChrisW67
7th January 2014, 00:04
There is font support in SVG 1.1 (http://www.w3.org/TR/SVG/fonts.html) but support is patchy in browsers (not so bad in dedicated SVG viewers/editors). That font support is not supported at all in SVG Tiny (http://www.w3.org/TR/SVGTiny12/) (Qt implements this) AFAICT.

ceraolo
11th January 2014, 10:35
I understand.
However, QT should guarantee the starting position of text, I think. So, when I say some text is to be right aligned, the its right position should be correct (and now is not).
If this was correctly behaving, a scientific plot could be made satisfactorily even without font support: The numbers on the left-aligned vertical axis could be right-aligned and the numbers on the horizontal axis centre-aligned.
The incorrect right position of a right-aligned text can be viewed as a Qt bug, I think.
I've posted something on this topic on the Qt bug reporting site. We will see if something happens.
In the meanwhile I'm going to change my software so that it always creates both SVG and PNG outputs. The user will use which one is better or he likes.

MC