QtSvg игнорирует единицы - PullRequest
0 голосов
/ 28 марта 2020

Есть ли способ заставить QSvgWidget / QSvgRenderer работать с такими единицами измерения, как см / мм / дюйм / р c?

Вот пример SVG, где все строки должны иметь одинаковую длину (корректно отображается в Firefox и Chrome):

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" height="500" width="500">
  <line x1="100" y1="50" x2="100" y2="450" style="stroke:rgb(0,0,0)" />
  <line x1="400" y1="50" x2="400" y2="450" style="stroke:rgb(0,0,0)" />
  <line x1="100" y1="100" x2="400" y2="100" style="stroke:rgb(255,0,0);stroke-width:20" />
  <line x1="100" y1="200" x2="105.82mm" y2="200" style="stroke:rgb(0,255,0);stroke-width:20" />
  <line x1="100" y1="300" x2="4.17in" y2="300" style="stroke:rgb(0,0,255);stroke-width:20" />
  <line x1="100" y1="400" x2="25pc" y2="400" style="stroke:rgb(127,127,127);stroke-width:20" />
</svg>

А вот мой зритель:

from PySide2 import QtWidgets, QtSvg
import PySide2

app = QtWidgets.QApplication(sys.argv)
w = QtSvg.QSvgWidget("demo.svg")
w.show()
sys.exit(app.exec_())

Если я открываю SVG с помощью этого средства просмотра, кажется, что QT игнорирует все единицы и читает атрибуты, такие как x2="105.82mm" как x2="105.82"

Результат в Qt:

enter image description here

Результат в Firefox:

enter image description here

QT и PySide2 являются версиями 5.14.1

1 Ответ

2 голосов
/ 29 марта 2020

Объяснение:

Анализируя исходный код QSvgHandler Я обнаружил, что только единицы длины (атрибуты ширины и высоты) поддерживают единицы, а не координаты, чтобы понять это, достаточно проанализировать следующие 2 фрагменты кода:

// https://code.qt.io/cgit/qt/qtsvg.git/tree/src/svg/qsvghandler.cpp#n2816
static QSvgNode *createLineNode(QSvgNode *parent,
                                const QXmlStreamAttributes &attributes,
                                QSvgHandler *)
{
    const QStringRef x1 = attributes.value(QLatin1String("x1"));
    const QStringRef y1 = attributes.value(QLatin1String("y1"));
    const QStringRef x2 = attributes.value(QLatin1String("x2"));
    const QStringRef y2 = attributes.value(QLatin1String("y2"));
    qreal nx1 = toDouble(x1);
    qreal ny1 = toDouble(y1);
    qreal nx2 = toDouble(x2);
    qreal ny2 = toDouble(y2);

    QLineF lineBounds(nx1, ny1, nx2, ny2);
    QSvgNode *line = new QSvgLine(parent, lineBounds);
    return line;
}
// https://code.qt.io/cgit/qt/qtsvg.git/tree/src/svg/qsvghandler.cpp#n3054
static QSvgNode *createRectNode(QSvgNode *parent,
                                const QXmlStreamAttributes &attributes,
                                QSvgHandler *handler)
{
    const QStringRef x      = attributes.value(QLatin1String("x"));
    const QStringRef y      = attributes.value(QLatin1String("y"));
    const QStringRef width  = attributes.value(QLatin1String("width"));
    const QStringRef height = attributes.value(QLatin1String("height"));
    const QStringRef rx      = attributes.value(QLatin1String("rx"));
    const QStringRef ry      = attributes.value(QLatin1String("ry"));

    QSvgHandler::LengthType type;
    qreal nwidth = parseLength(width, type, handler);
    nwidth = convertToPixels(nwidth, true, type);

    qreal nheight = parseLength(height, type, handler);
    nheight = convertToPixels(nheight, true, type);
    qreal nrx = toDouble(rx);
    qreal nry = toDouble(ry);

    QRectF bounds(toDouble(x), toDouble(y),
                  nwidth, nheight);

    //9.2 The 'rect'  element clearly specifies it
    // but the case might in fact be handled because
    // we draw rounded rectangles differently
    if (nrx > bounds.width()/2)
        nrx = bounds.width()/2;
    if (nry > bounds.height()/2)
        nry = bounds.height()/2;

    if (!rx.isEmpty() && ry.isEmpty())
        nry = nrx;
    else if (!ry.isEmpty() && rx.isEmpty())
        nrx = nry;

    //we draw rounded rect from 0...99
    //svg from 0...bounds.width()/2 so we're adjusting the
    //coordinates
    nrx *= (100/(bounds.width()/2));
    nry *= (100/(bounds.height()/2));

    QSvgNode *rect = new QSvgRect(parent, bounds,
                                  int(nrx),
                                  int(nry));
    return rect;
}

Метод преобразования единиц: parseLength(), который используется только в словах "ширина" и "высота".

Решение:

Обходной путь должен использовать QWebEngineView:

import os
import sys

from PySide2 import QtCore, QtWidgets, QtSvg, QtWebEngineWidgets


CURRENT_DIR = os.path.dirname(os.path.realpath(__file__))

if __name__ == "__main__":

    app = QtWidgets.QApplication(sys.argv)
    filename = os.path.join(CURRENT_DIR, "demo.svg")

    qsvgwidget = QtSvg.QSvgWidget(filename)

    qwebengineview = QtWebEngineWidgets.QWebEngineView()
    qwebengineview.load(QtCore.QUrl.fromLocalFile(filename))

    w = QtWidgets.QWidget()
    lay = QtWidgets.QHBoxLayout(w)
    lay.addWidget(qsvgwidget, strecth=1)
    lay.addWidget(qwebengineview, strecth=1)
    w.show()
    sys.exit(app.exec_())

enter image description here

...