From c1f6754989a9ccf5fdec2a48e282b6287d382261 Mon Sep 17 00:00:00 2001 From: Tim Hutt Date: Tue, 16 Jan 2024 11:06:38 +0000 Subject: [PATCH] Minor tweaks to size/resolution handling I think the original code was correct but I've clarified it and used a value of 1 pixel per mm for rendering to make it clear that that doesn't matter. --- CMakeLists.txt | 4 ++-- MainWindow.cpp | 15 ++++++++++----- PathPaintDevice.cpp | 29 ++++++++++------------------- PathPaintDevice.h | 7 +++---- SvgRenderer.cpp | 9 +++++++-- SvgRenderer.h | 1 + 6 files changed, 33 insertions(+), 32 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 21a492a9..ada33548 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,8 +16,8 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED TRUE) -# Set the CMAKE_PREFIX_PATH environment variable to (e.g) ~/Qt/5.8/clang_64 so it can find Qt. -# CoreTools is for win/macdeployqt +# Set the CMAKE_PREFIX_PATH environment variable to (e.g) ~/Qt/6.6.1/gcc_64/lib/cmake/ +# so it can find Qt. CoreTools is for win/macdeployqt. find_package(Qt6 REQUIRED COMPONENTS Core Widgets Svg CoreTools) qt_standard_project_setup() diff --git a/MainWindow.cpp b/MainWindow.cpp index bb0b4b22..d7701229 100644 --- a/MainWindow.cpp +++ b/MainWindow.cpp @@ -231,10 +231,16 @@ void MainWindow::loadFile(QString filename) StateFileLoaded loadedState; - // See - // https://code.woboq.org/qt5/qtsvg/src/svg/qsvghandler.cpp.html#_ZL15convertToPixelsdbN11QSvgHandler10LengthTypeE - // The default size is derived from the width="" height="" svg attribute tags - // assuming 90 DPI. + // When Qt renders an SVG it sets its width and height based on the + // width="" height="" svg attributes. These can be given in physical units + // e.g. width="25mm". In that case Qt renders at 90 DPI. However that is + // incorrect. SVG/CSS use 96 DPI. + // + // See `convertToPixels()` here: https://codebrowser.dev/qt6/qtsvg/src/svg/qsvghandler.cpp.html#932 + // + + // This is the size of the entire document in mm, calculated from the + // width/height attributes, using the correct conversion from pixels (96 DPI). QSizeF mediaSize(render.widthMm, render.heightMm); loadedState.mediaSize = mediaSize; @@ -903,4 +909,3 @@ void MainWindow::on_actionExport_HPGL_triggered() return; } } - diff --git a/PathPaintDevice.cpp b/PathPaintDevice.cpp index 089a7c46..bda8f605 100644 --- a/PathPaintDevice.cpp +++ b/PathPaintDevice.cpp @@ -18,20 +18,13 @@ size_t qHash(const QPolygonF& key) return qHash(repr); } -PathPaintDevice::PathPaintDevice(double widthInMm, double heightInMm, double pixelsPerMm) +PathPaintDevice::PathPaintDevice(double width, double height) + : width(width), height(height) { - engine = nullptr; - width = widthInMm; - height = heightInMm; - ppm = pixelsPerMm; - if (ppm == 0.0) - ppm = 1.0; } PathPaintDevice::~PathPaintDevice() { - if (engine) - delete engine; } void PathPaintDevice::addPath(const QPolygonF& path) @@ -45,8 +38,8 @@ void PathPaintDevice::addPath(const QPolygonF& path) QPaintEngine* PathPaintDevice::paintEngine() const { if (!engine) - engine = new PathPaintEngine(); - return engine; + engine = std::make_unique(); + return engine.get(); } QList PathPaintDevice::paths() @@ -60,9 +53,9 @@ int PathPaintDevice::metric(PaintDeviceMetric metric) const { // Width in pixels. case PdmWidth: - return static_cast(width * ppm); + return static_cast(width); case PdmHeight: - return static_cast(height * ppm); + return static_cast(height); case PdmWidthMM: return static_cast(width); case PdmHeightMM: @@ -72,19 +65,17 @@ int PathPaintDevice::metric(PaintDeviceMetric metric) const case PdmDepth: return 1; case PdmDpiX: - return static_cast(25.4 * ppm); // Convert to inches. + return static_cast(25.4); case PdmDpiY: - return static_cast(25.4 * ppm); + return static_cast(25.4); case PdmPhysicalDpiX: - return static_cast(25.4 * ppm); + return static_cast(25.4); case PdmPhysicalDpiY: - return static_cast(25.4 * ppm); -#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) + return static_cast(25.4); case PdmDevicePixelRatio: return 1; case PdmDevicePixelRatioScaled: return 1; -#endif } return 0; } diff --git a/PathPaintDevice.h b/PathPaintDevice.h index 3948225f..bf1984fe 100644 --- a/PathPaintDevice.h +++ b/PathPaintDevice.h @@ -14,7 +14,8 @@ size_t qHash(const QPolygonF& key); class PathPaintDevice : public QPaintDevice { public: - PathPaintDevice(double widthInMm, double heightInMm, double pixelsPerMm = 90.0 / 25.4); + // Width/height in pixels. + PathPaintDevice(double width, double height); ~PathPaintDevice() override; // Adds a path to the device. @@ -31,7 +32,7 @@ class PathPaintDevice : public QPaintDevice int metric(PaintDeviceMetric metric) const override; private: - mutable PathPaintEngine* engine; + mutable std::unique_ptr engine; // The paths added. QList pagePaths; @@ -40,6 +41,4 @@ class PathPaintDevice : public QPaintDevice double width; double height; - - double ppm; }; diff --git a/SvgRenderer.cpp b/SvgRenderer.cpp index ac7fe55e..c4d42fec 100644 --- a/SvgRenderer.cpp +++ b/SvgRenderer.cpp @@ -13,7 +13,10 @@ namespace { -// 1 px is 1/96th of an inch. +// If the user specifies width="96px" then we assume 96 DPI and return +// a size of 1 inch (this matches the latest versions of Inkscape). +// Note Qt assumes 90 DPI when rendering and SVG but we account for that so +// it has no effect. const double MM_PER_CM = 10.0; const double MM_PER_Q = 0.25; const double MM_PER_IN = 25.4; @@ -184,10 +187,12 @@ SResult svgToPaths(const QString& filename, bool searchForTspans) render.viewBox = renderer.viewBoxF(); - // So here we pretend that the viewbox is in mm. + // Give the size of the canvas. We just use 1 user unit per pixel and + // then convert later. PathPaintDevice pg(render.viewBox.width(), render.viewBox.height()); QPainter p(&pg); + // Render, this will assume 1 user unit = 1px. renderer.render(&p, render.viewBox); // These are the paths in user units. diff --git a/SvgRenderer.h b/SvgRenderer.h index 10ab65b2..5646bc87 100644 --- a/SvgRenderer.h +++ b/SvgRenderer.h @@ -25,6 +25,7 @@ struct SvgRender // from the width= and height= attributes, if they have physical units. If they // are in user units then it is assumed to be mm. If they are in % or are not present // then the view box size is used and is assumed to be in mm. + // If they are in pixels then they are converted at 96 DPI per the SVG spec. double widthMm; double heightMm;