diff --git a/logPlus/TransparentDraggableRect.h b/logPlus/TransparentDraggableRect.h index d1fb14e..c0a6bea 100644 --- a/logPlus/TransparentDraggableRect.h +++ b/logPlus/TransparentDraggableRect.h @@ -161,14 +161,14 @@ private: // 左边界矩形控制点 mLeftHandle->topLeft->setParentAnchor(mRect->topLeft); mLeftHandle->bottomRight->setParentAnchor(mRect->topRight);//(mRect->bottomLeft); - mLeftHandle->topLeft->setCoords(-5, 5); // 矩形大小 - mLeftHandle->bottomRight->setCoords(5, -5); // 矩形大小 + mLeftHandle->topLeft->setCoords(-0.5, 0.5); // 矩形大小 + mLeftHandle->bottomRight->setCoords(0.5, -0.5); // 矩形大小 // 右边界矩形控制点 mRightHandle->topLeft->setParentAnchor(mRect->bottomLeft); mRightHandle->bottomRight->setParentAnchor(mRect->bottomRight); - mRightHandle->topLeft->setCoords(-5, 5); // 矩形大小 - mRightHandle->bottomRight->setCoords(5, -5); // 矩形大小 + mRightHandle->topLeft->setCoords(-0.5, 0.5); // 矩形大小 + mRightHandle->bottomRight->setCoords(0.5, -0.5); // 矩形大小 } diff --git a/logPlus/TransparentDraggableResult.h b/logPlus/TransparentDraggableResult.h index 31650fd..a0ded18 100644 --- a/logPlus/TransparentDraggableResult.h +++ b/logPlus/TransparentDraggableResult.h @@ -36,7 +36,56 @@ public: // mPlot->removeItem(mRightHandle); } } - + + void DrawSVGNormal(QPainter* painter,QString svgFileName,QRectF borderRect,bool IsWellSectonHorizonLayout) + { + QString svg=svgFileName; + QRectF boundingRect = painter->transform().mapRect(borderRect); + painter->save(); + QTransform transform; + transform.reset(); + if (!IsWellSectonHorizonLayout) + { + painter->setWorldTransform(transform); + } + else + { + } + QPixmap tiledmap(svg); + QRect border(boundingRect.left(),boundingRect.top(),boundingRect.width(),boundingRect.height()); + painter->drawPixmap(border,tiledmap); + painter->restore(); + } + //拉伸 + void DrawSVGSteched(QPainter* painter,QString svgFileName,QRectF borderRect,bool IsWellSectonHorizonLayout) + { + QString svg; + QSvgRenderer m_SvgRenderer; + svg=svgFileName; + m_SvgRenderer.load(svg); + m_SvgRenderer.render(painter,borderRect); + } + //平铺 + void DrawSVGTiled(QPainter* painter,QString svgFileName,QRectF borderRect,bool IsWellSectonHorizonLayout) + { + QString svg=svgFileName; + QRectF boundingRect = painter->transform().mapRect(borderRect); + painter->save(); + QTransform transform; + transform.reset(); + if (!IsWellSectonHorizonLayout) + { + painter->setWorldTransform(transform); + } + else + { + } + QPixmap tiledmap(svg); + painter->drawTiledPixmap(boundingRect,tiledmap); + painter->restore(); + } + + //设置最小宽度 void setMinWidth(double minWidth){ mMinWidth = minWidth; @@ -50,25 +99,54 @@ public: } //设置解释结论 - void setResult(QString result){ - m_Result = result; + void setResult(QString filePath){ + m_Result = filePath; // - QString strLast = result.right(4); + QString strLast = filePath.right(4); if(strLast.toLower()==".svg") { - QSvgRenderer* svgRender = new QSvgRenderer(); - svgRender->load(result); - // - QPixmap* pixmap = new QPixmap(10, 10); - pixmap->fill(Qt::transparent);//设置背景透明 - QPainter p(pixmap); - svgRender->render(&p); - // - mPixmap->setPixmap(*pixmap); // 设置图片 + QString path,filename; + GetWellNameAndPath(filePath, filename, path); + QString basename = filename.left(filename.size()-4); + + QString val=filePath; + QImage image(320,160,QImage::Format_RGB32); + QPainter painter(&image); + QRectF fillRect(0,0,320,160); + painter.fillRect(fillRect,Qt::white); + //拉伸 + DrawSVGSteched(&painter,filePath,fillRect,0); + //平铺 + //DrawSVGTiled(&painter,filePath,fillRect,0); + //正常 + //DrawSVGNormal(&painter,filePath,fillRect,0); + + val=GetImagePath()+"TempNew"; + QDir ss; + if(!ss.exists(val)) { + ss.mkdir(val); + } + val+=QDir::separator(); + val+=basename+".png"; + image.save(val); + + // + mPixmap->setPixmap(QPixmap(val)); // 设置图片 + + +// QSvgRenderer* svgRender = new QSvgRenderer(); +// svgRender->load(result); +// // +// QPixmap* pixmap = new QPixmap(10, 10); +// pixmap->fill(Qt::transparent);//设置背景透明 +// QPainter p(pixmap); +// svgRender->render(&p); +// // +// mPixmap->setPixmap(*pixmap); // 设置图片 } else { - mPixmap->setPixmap(QPixmap(result)); // 设置图片 + mPixmap->setPixmap(QPixmap(filePath)); // 设置图片 } @@ -197,14 +275,14 @@ private: // 左边界矩形控制点 mLeftHandle->topLeft->setParentAnchor(mRect->topLeft); mLeftHandle->bottomRight->setParentAnchor(mRect->topRight);//(mRect->bottomLeft); - mLeftHandle->topLeft->setCoords(-5, 5); // 矩形大小 - mLeftHandle->bottomRight->setCoords(5, -5); // 矩形大小 + mLeftHandle->topLeft->setCoords(-0.5, 0.5); // 矩形大小 + mLeftHandle->bottomRight->setCoords(0.5, -0.5); // 矩形大小 // 右边界矩形控制点 mRightHandle->topLeft->setParentAnchor(mRect->bottomLeft); mRightHandle->bottomRight->setParentAnchor(mRect->bottomRight); - mRightHandle->topLeft->setCoords(-5, 5); // 矩形大小 - mRightHandle->bottomRight->setCoords(5, -5); // 矩形大小 + mRightHandle->topLeft->setCoords(-0.5, 0.5); // 矩形大小 + mRightHandle->bottomRight->setCoords(0.5, -0.5); // 矩形大小 // if(m_upDraggableResult) // { diff --git a/svg/XSVG_LICENSE.txt b/svg/XSVG_LICENSE.txt new file mode 100644 index 0000000..9ad4931 --- /dev/null +++ b/svg/XSVG_LICENSE.txt @@ -0,0 +1,22 @@ +Copyright 2002 USC/Information Sciences Institute + +Permission to use, copy, modify, distribute, and sell this software +and its documentation for any purpose is hereby granted without +fee, provided that the above copyright notice appear in all copies +and that both that copyright notice and this permission notice +appear in supporting documentation, and that the name of +Information Sciences Institute not be used in advertising or +publicity pertaining to distribution of the software without +specific, written prior permission. Information Sciences Institute +makes no representations about the suitability of this software for +any purpose. It is provided "as is" without express or implied +warranty. + +INFORMATION SCIENCES INSTITUTE DISCLAIMS ALL WARRANTIES WITH REGARD +TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL INFORMATION SCIENCES +INSTITUTE BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL +DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA +OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. diff --git a/svg/doc/qtsvg.qdocconf b/svg/doc/qtsvg.qdocconf new file mode 100644 index 0000000..b180daa --- /dev/null +++ b/svg/doc/qtsvg.qdocconf @@ -0,0 +1,49 @@ +include($QT_INSTALL_DOCS/global/qt-module-defaults.qdocconf) + +project = QtSvg +description = Qt SVG Reference Documentation +version = $QT_VERSION + +qhp.projects = QtSvg + +qhp.QtSvg.file = qtsvg.qhp +qhp.QtSvg.namespace = org.qt-project.qtsvg.$QT_VERSION_TAG +qhp.QtSvg.virtualFolder = qtsvg +qhp.QtSvg.indexTitle = Qt SVG +qhp.QtSvg.indexRoot = + +qhp.QtSvg.filterAttributes = qtsvg $QT_VERSION qtrefdoc +qhp.QtSvg.customFilters.Qt.name = QtSvg $QT_VERSION +qhp.QtSvg.customFilters.Qt.filterAttributes = qtsvg $QT_VERSION +qhp.QtSvg.subprojects = classes +qhp.QtSvg.subprojects.classes.title = C++ Classes +qhp.QtSvg.subprojects.classes.indexTitle = Qt SVG C++ Classes +qhp.QtSvg.subprojects.classes.selectors = class fake:headerfile +qhp.QtSvg.subprojects.classes.sortPages = true + +depends += qtcore \ + qtwidgets \ + qtgui \ + qtdesigner \ + qtdoc \ + qmake + +tagfile = ../../../doc/qtsvg/qtsvg.tags + +headerdirs += .. \ + ../../plugins/imageformats \ + ../../plugins/iconengines + +sourcedirs += .. \ + ../../plugins/imageformats \ + ../../plugins/iconengines + +exampledirs += ../../../examples/svg \ + snippets/ + +examplesinstallpath = svg + +imagedirs += images + +navigation.landingpage = "Qt SVG" +navigation.cppclassespage = "Qt SVG C++ Classes" diff --git a/svg/doc/snippets/doc_src_qtsvg.cpp b/svg/doc/snippets/doc_src_qtsvg.cpp new file mode 100644 index 0000000..dc754c3 --- /dev/null +++ b/svg/doc/snippets/doc_src_qtsvg.cpp @@ -0,0 +1,53 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//! [0] +#include +//! [0] diff --git a/svg/doc/snippets/doc_src_qtsvg.pro b/svg/doc/snippets/doc_src_qtsvg.pro new file mode 100644 index 0000000..0c9aeed --- /dev/null +++ b/svg/doc/snippets/doc_src_qtsvg.pro @@ -0,0 +1,53 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#! [1] +QT += svg +#! [1] diff --git a/svg/doc/snippets/src_svg_qgraphicssvgitem.cpp b/svg/doc/snippets/src_svg_qgraphicssvgitem.cpp new file mode 100644 index 0000000..60018b6 --- /dev/null +++ b/svg/doc/snippets/src_svg_qgraphicssvgitem.cpp @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//! [0] +QSvgRenderer *renderer = new QSvgRenderer(QLatin1String("SvgCardDeck.svg")); +QGraphicsSvgItem *black = new QGraphicsSvgItem(); +QGraphicsSvgItem *red = new QGraphicsSvgItem(); + +black->setSharedRenderer(renderer); +black->setElementId(QLatin1String("black_joker")); + +red->setSharedRenderer(renderer); +red->setElementId(QLatin1String("red_joker")); +//! [0] diff --git a/svg/doc/src/qtsvg-index.qdoc b/svg/doc/src/qtsvg-index.qdoc new file mode 100644 index 0000000..b559d27 --- /dev/null +++ b/svg/doc/src/qtsvg-index.qdoc @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt SVG module. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \title Qt SVG + \page qtsvg-index.html + \brief Qt SVG provides functionality for handling SVG images. + + Scalable Vector Graphics (SVG) is an XML-based language for + describing two-dimensional vector graphics. Qt provides classes + for rendering and displaying SVG drawings in widgets and on other + paint devices. + + \section1 Getting Started + + Qt SVG provides classes for rendering SVG files. To include the definitions + of the module's classes, use the following directive: + + \snippet doc_src_qtsvg.cpp 0 + + To link against the module, add this line to your \l qmake \c + .pro file: + + \snippet doc_src_qtsvg.pro 1 + + \section1 Licenses and Attributions + + Qt SVG is available under commercial licenses from \l{The Qt Company}. + In addition, it is available under the + \l{GNU Lesser General Public License, version 3}, or + the \l{GNU General Public License, version 2}. + See \l{Qt Licensing} for further details. + + Furthermore Qt SVG contains third party + code under following permissive licenses: + + \generatelist{groupsbymodule attributions-qtsvg} + + \section1 Articles + \list + \li \l{Rendering SVG Files} contains information about how to + render SVG files + \endlist + + \section1 References + \list + \li \l{Qt SVG C++ Classes} + \endlist + + Qt SVG comes with the following examples: + + \list + \li \l{SVG Generator Example} + \li \l{SVG Viewer Example} + \endlist +*/ diff --git a/svg/doc/src/qtsvg.qdoc b/svg/doc/src/qtsvg.qdoc new file mode 100644 index 0000000..c607bd0 --- /dev/null +++ b/svg/doc/src/qtsvg.qdoc @@ -0,0 +1,41 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt SVG module. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \module QtSvg + \title Qt SVG C++ Classes + \ingroup modules + \qtvariable svg + + \brief The Qt SVG module provides functionality for handling SVG images. + + Scalable Vector Graphics (SVG) is an XML-based language for + describing two-dimensional vector graphics. Qt provides classes + for rendering and displaying SVG drawings in widgets and on other + paint devices. See the class descriptions for further details. + +*/ diff --git a/svg/doc/src/svgrendering.qdoc b/svg/doc/src/svgrendering.qdoc new file mode 100644 index 0000000..48a8d14 --- /dev/null +++ b/svg/doc/src/svgrendering.qdoc @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt SVG module. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \page svgrendering.html + \title Rendering SVG Files + \brief Rendering SVG files with the Qt SVG module + + Qt SVG provides classes for rendering SVG files. To include the definitions + of the module's classes, use the following directive: + + \snippet doc_src_qtsvg.cpp 0 + + To link against the module, add this line to your \l qmake \c + .pro file: + + \snippet doc_src_qtsvg.pro 1 + + \section1 Rendering SVG Files + + Scalable Vector Graphics (SVG) is a language for describing two-dimensional + graphics and graphical applications in XML. SVG 1.1 is a W3C Recommendation + and forms the core of the current SVG developments in Qt. SVG 1.2 is the + specification currently being developed by the \l{SVG Working Group}, and it + is \l{http://www.w3.org/TR/SVG12/}{available in draft form}. The \l{Mobile + SVG Profiles} (SVG Basic and SVG Tiny) are aimed at resource-limited devices + and are part of the 3GPP platform for third generation mobile phones. You + can read more about SVG at \l{About SVG}. + + Qt supports the \l{SVG 1.2 Tiny Static Features}{static features} of + \l{http://www.w3.org/TR/SVGMobile12}{SVG 1.2 Tiny}. ECMA scripts and DOM manipulation are currently not + supported. + + SVG drawings can be rendered onto any QPaintDevice subclass. This + approach gives developers the flexibility to experiment, in order + to find the best solution for each application. + + The easiest way to render SVG files is to construct a QSvgWidget and + load an SVG file using one of the QSvgWidget::load() functions. + + QSvgRenderer is the class responsible for rendering SVG files for + QSvgWidget, and it can be used directly to provide SVG support for + custom widgets. + To load an SVG file, construct a QSvgRenderer with a file name or the + contents of a file, or call QSvgRenderer::load() on an existing + renderer. If the SVG file has been loaded successfully the + QSvgRenderer::isValid() will return true. + + Once you have loaded the SVG file successfully, you can render it + with the QSvgRenderer::render() function. Note that this scheme allows + you to render SVG files on all paint devices supported by Qt, including + QWidget, QGLWidget, and QImage. See the \l{SVG Viewer Example}{SVG Viewer} + example for more details. +*/ diff --git a/svg/qgraphicssvgitem.cpp b/svg/qgraphicssvgitem.cpp new file mode 100644 index 0000000..fe6e92e --- /dev/null +++ b/svg/qgraphicssvgitem.cpp @@ -0,0 +1,388 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt SVG module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "qgraphicssvgitem.h" + +#if !defined(QT_NO_GRAPHICSVIEW) && !defined(QT_NO_WIDGETS) + +#include "qpainter.h" +#include "qstyleoption.h" +#include "qsvgrenderer.h" +#include "qdebug.h" + +#include "private/qobject_p.h" +#include "private/qgraphicsitem_p.h" + +QT_BEGIN_NAMESPACE + +class QGraphicsSvgItemPrivate : public QGraphicsItemPrivate +{ +public: + Q_DECLARE_PUBLIC(QGraphicsSvgItem) + + QGraphicsSvgItemPrivate() + : renderer(0), shared(false) + { + } + + void init(QGraphicsItem *parent) + { + Q_Q(QGraphicsSvgItem); + q->setParentItem(parent); + renderer = new QSvgRenderer(q); + QObject::connect(renderer, SIGNAL(repaintNeeded()), + q, SLOT(_q_repaintItem())); + q->setCacheMode(QGraphicsItem::DeviceCoordinateCache); + q->setMaximumCacheSize(QSize(1024, 768)); + } + + void _q_repaintItem() + { + q_func()->update(); + } + + inline void updateDefaultSize() + { + QRectF bounds; + if (elemId.isEmpty()) { + bounds = QRectF(QPointF(0, 0), renderer->defaultSize()); + } else { + bounds = renderer->boundsOnElement(elemId); + } + if (boundingRect.size() != bounds.size()) { + q_func()->prepareGeometryChange(); + boundingRect.setSize(bounds.size()); + } + } + + QSvgRenderer *renderer; + QRectF boundingRect; + bool shared; + QString elemId; +}; + +/*! + \class QGraphicsSvgItem + \inmodule QtSvg + \ingroup graphicsview-api + \brief The QGraphicsSvgItem class is a QGraphicsItem that can be used to render + the contents of SVG files. + + \since 4.2 + + QGraphicsSvgItem provides a way of rendering SVG files onto QGraphicsView. + QGraphicsSvgItem can be created by passing the SVG file to be rendered to + its constructor or by explicit setting a shared QSvgRenderer on it. + + Note that setting QSvgRenderer on a QGraphicsSvgItem doesn't make the item take + ownership of the renderer, therefore if using setSharedRenderer() method one has + to make sure that the lifetime of the QSvgRenderer object will be at least as long + as that of the QGraphicsSvgItem. + + QGraphicsSvgItem provides a way of rendering only parts of the SVG files via + the setElementId. If setElementId() method is called, only the SVG element + (and its children) with the passed id will be renderer. This provides a convenient + way of selectively rendering large SVG files that contain a number of discrete + elements. For example the following code renders only jokers from a SVG file + containing a whole card deck: + + \snippet src_svg_qgraphicssvgitem.cpp 0 + + Size of the item can be set via direct manipulation of the items + transformation matrix. + + By default the SVG rendering is cached using QGraphicsItem::DeviceCoordinateCache + mode to speedup the display of items. Caching can be disabled by passing + QGraphicsItem::NoCache to the QGraphicsItem::setCacheMode() method. + + \sa QSvgWidget, {Qt SVG C++ Classes}, QGraphicsItem, QGraphicsView +*/ + +/*! + Constructs a new SVG item with the given \a parent. +*/ +QGraphicsSvgItem::QGraphicsSvgItem(QGraphicsItem *parent) + : QGraphicsObject(*new QGraphicsSvgItemPrivate(), 0) +{ + Q_D(QGraphicsSvgItem); + d->init(parent); +} + +/*! + Constructs a new item with the given \a parent and loads the contents of the + SVG file with the specified \a fileName. +*/ +QGraphicsSvgItem::QGraphicsSvgItem(const QString &fileName, QGraphicsItem *parent) + : QGraphicsObject(*new QGraphicsSvgItemPrivate(), 0) +{ + Q_D(QGraphicsSvgItem); + d->init(parent); + d->renderer->load(fileName); + d->updateDefaultSize(); +} + +/*! + Returns the currently use QSvgRenderer. +*/ +QSvgRenderer *QGraphicsSvgItem::renderer() const +{ + return d_func()->renderer; +} + + +/*! + Returns the bounding rectangle of this item. +*/ +QRectF QGraphicsSvgItem::boundingRect() const +{ + Q_D(const QGraphicsSvgItem); + return d->boundingRect; +} + +/*! + \internal + + Highlights \a item as selected. + + NOTE: This function is a duplicate of qt_graphicsItem_highlightSelected() in qgraphicsitem.cpp! +*/ +static void qt_graphicsItem_highlightSelected( + QGraphicsItem *item, QPainter *painter, const QStyleOptionGraphicsItem *option) +{ + const QRectF murect = painter->transform().mapRect(QRectF(0, 0, 1, 1)); + if (qFuzzyIsNull(qMax(murect.width(), murect.height()))) + return; + + const QRectF mbrect = painter->transform().mapRect(item->boundingRect()); + if (qMin(mbrect.width(), mbrect.height()) < qreal(1.0)) + return; + + qreal itemPenWidth; + switch (item->type()) { + case QGraphicsEllipseItem::Type: + itemPenWidth = static_cast(item)->pen().widthF(); + break; + case QGraphicsPathItem::Type: + itemPenWidth = static_cast(item)->pen().widthF(); + break; + case QGraphicsPolygonItem::Type: + itemPenWidth = static_cast(item)->pen().widthF(); + break; + case QGraphicsRectItem::Type: + itemPenWidth = static_cast(item)->pen().widthF(); + break; + case QGraphicsSimpleTextItem::Type: + itemPenWidth = static_cast(item)->pen().widthF(); + break; + case QGraphicsLineItem::Type: + itemPenWidth = static_cast(item)->pen().widthF(); + break; + default: + itemPenWidth = 1.0; + } + const qreal pad = itemPenWidth / 2; + + const qreal penWidth = 0; // cosmetic pen + + const QColor fgcolor = option->palette.windowText().color(); + const QColor bgcolor( // ensure good contrast against fgcolor + fgcolor.red() > 127 ? 0 : 255, + fgcolor.green() > 127 ? 0 : 255, + fgcolor.blue() > 127 ? 0 : 255); + + painter->setPen(QPen(bgcolor, penWidth, Qt::SolidLine)); + painter->setBrush(Qt::NoBrush); + painter->drawRect(item->boundingRect().adjusted(pad, pad, -pad, -pad)); + + painter->setPen(QPen(option->palette.windowText(), 0, Qt::DashLine)); + painter->setBrush(Qt::NoBrush); + painter->drawRect(item->boundingRect().adjusted(pad, pad, -pad, -pad)); +} + +/*! + \reimp +*/ +void QGraphicsSvgItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, + QWidget *widget) +{ +// Q_UNUSED(option); + Q_UNUSED(widget); + + Q_D(QGraphicsSvgItem); + if (!d->renderer->isValid()) + return; + + if (d->elemId.isEmpty()) + d->renderer->render(painter, d->boundingRect); + else + d->renderer->render(painter, d->elemId, d->boundingRect); + + if (option->state & QStyle::State_Selected) + qt_graphicsItem_highlightSelected(this, painter, option); +} + +/*! + \reimp +*/ +int QGraphicsSvgItem::type() const +{ + return Type; +} + +/*! + \property QGraphicsSvgItem::maximumCacheSize + \since 4.6 + + This property holds the maximum size of the device coordinate cache + for this item. + */ + +/*! + Sets the maximum device coordinate cache size of the item to \a size. + If the item is cached using QGraphicsItem::DeviceCoordinateCache mode, + caching is bypassed if the extension of the item in device coordinates + is larger than \a size. + + The cache corresponds to the QPixmap which is used to cache the + results of the rendering. + Use QPixmapCache::setCacheLimit() to set limitations on the whole cache + and use setMaximumCacheSize() when setting cache size for individual + items. + + \sa QGraphicsItem::cacheMode() +*/ +void QGraphicsSvgItem::setMaximumCacheSize(const QSize &size) +{ + QGraphicsItem::d_ptr->setExtra(QGraphicsItemPrivate::ExtraMaxDeviceCoordCacheSize, size); + update(); +} + +/*! + Returns the current maximum size of the device coordinate cache for this item. + If the item is cached using QGraphicsItem::DeviceCoordinateCache mode, + caching is bypassed if the extension of the item in device coordinates + is larger than the maximum size. + + The default maximum cache size is 1024x768. + QPixmapCache::cacheLimit() gives the + cumulative bounds of the whole cache, whereas maximumCacheSize() refers + to a maximum cache size for this particular item. + + \sa QGraphicsItem::cacheMode() +*/ +QSize QGraphicsSvgItem::maximumCacheSize() const +{ + return QGraphicsItem::d_ptr->extra(QGraphicsItemPrivate::ExtraMaxDeviceCoordCacheSize).toSize(); +} + +/*! + \property QGraphicsSvgItem::elementId + \since 4.6 + + This property holds the element's XML ID. + */ + +/*! + Sets the XML ID of the element to \a id. +*/ +void QGraphicsSvgItem::setElementId(const QString &id) +{ + Q_D(QGraphicsSvgItem); + d->elemId = id; + d->updateDefaultSize(); + update(); +} + +/*! + Returns the XML ID the element that is currently + being rendered. Returns an empty string if the whole + file is being rendered. +*/ +QString QGraphicsSvgItem::elementId() const +{ + Q_D(const QGraphicsSvgItem); + return d->elemId; +} + +/*! + Sets \a renderer to be a shared QSvgRenderer on the item. By + using this method one can share the same QSvgRenderer on a number + of items. This means that the SVG file will be parsed only once. + QSvgRenderer passed to this method has to exist for as long as + this item is used. +*/ +void QGraphicsSvgItem::setSharedRenderer(QSvgRenderer *renderer) +{ + Q_D(QGraphicsSvgItem); + if (!d->shared) + delete d->renderer; + + d->renderer = renderer; + d->shared = true; + + d->updateDefaultSize(); + + update(); +} + +/*! + \obsolete + + Use QGraphicsItem::setCacheMode() instead. Passing true to this function is equivalent + to QGraphicsItem::setCacheMode(QGraphicsItem::DeviceCoordinateCache). +*/ +void QGraphicsSvgItem::setCachingEnabled(bool caching) +{ + setCacheMode(caching ? QGraphicsItem::DeviceCoordinateCache : QGraphicsItem::NoCache); +} + +/*! + \obsolete + + Use QGraphicsItem::cacheMode() instead. +*/ +bool QGraphicsSvgItem::isCachingEnabled() const +{ + return cacheMode() != QGraphicsItem::NoCache; +} + +QT_END_NAMESPACE + +#include "moc_qgraphicssvgitem.cpp" + +#endif // QT_NO_WIDGETS diff --git a/svg/qgraphicssvgitem.h b/svg/qgraphicssvgitem.h new file mode 100644 index 0000000..5102b91 --- /dev/null +++ b/svg/qgraphicssvgitem.h @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt SVG module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QGRAPHICSSVGITEM_H +#define QGRAPHICSSVGITEM_H + +#include + +#if !defined(QT_NO_GRAPHICSVIEW) && !defined(QT_NO_WIDGETS) + +#include + +#include + +QT_BEGIN_NAMESPACE + + +class QSvgRenderer; +class QGraphicsSvgItemPrivate; + +class Q_SVG_EXPORT QGraphicsSvgItem : public QGraphicsObject +{ + Q_OBJECT + Q_INTERFACES(QGraphicsItem) + Q_PROPERTY(QString elementId READ elementId WRITE setElementId) + Q_PROPERTY(QSize maximumCacheSize READ maximumCacheSize WRITE setMaximumCacheSize) + +public: + QGraphicsSvgItem(QGraphicsItem *parentItem = nullptr); + QGraphicsSvgItem(const QString &fileName, QGraphicsItem *parentItem = nullptr); + + void setSharedRenderer(QSvgRenderer *renderer); + QSvgRenderer *renderer() const; + + void setElementId(const QString &id); + QString elementId() const; + + void setCachingEnabled(bool); + bool isCachingEnabled() const; + + void setMaximumCacheSize(const QSize &size); + QSize maximumCacheSize() const; + + QRectF boundingRect() const override; + + void paint(QPainter *painter, + const QStyleOptionGraphicsItem *option, + QWidget *widget = nullptr) override; + + enum { Type = 13 }; + int type() const override; + +private: + Q_DISABLE_COPY(QGraphicsSvgItem) + Q_DECLARE_PRIVATE_D(QGraphicsItem::d_ptr.data(), QGraphicsSvgItem) + + Q_PRIVATE_SLOT(d_func(), void _q_repaintItem()) +}; + +QT_END_NAMESPACE + +#endif // QT_NO_GRAPHICSVIEW or QT_NO_WIDGETS + +#endif // QGRAPHICSSVGITEM_H diff --git a/svg/qsvgfont.cpp b/svg/qsvgfont.cpp new file mode 100644 index 0000000..6487de4 --- /dev/null +++ b/svg/qsvgfont.cpp @@ -0,0 +1,136 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt SVG module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsvgfont_p.h" + +#include "qpainter.h" +#include "qpen.h" +#include "qdebug.h" +#include "qpicture.h" + +QT_BEGIN_NAMESPACE + +QSvgGlyph::QSvgGlyph(QChar unicode, const QPainterPath &path, qreal horizAdvX) + : m_unicode(unicode), m_path(path), m_horizAdvX(horizAdvX) +{ + +} + + +QSvgFont::QSvgFont(qreal horizAdvX) + : m_horizAdvX(horizAdvX) +{ +} + + +QString QSvgFont::familyName() const +{ + return m_familyName; +} + + +void QSvgFont::addGlyph(QChar unicode, const QPainterPath &path, qreal horizAdvX ) +{ + m_glyphs.insert(unicode, QSvgGlyph(unicode, path, + (horizAdvX==-1)?m_horizAdvX:horizAdvX)); +} + + +void QSvgFont::draw(QPainter *p, const QPointF &point, const QString &str, qreal pixelSize, Qt::Alignment alignment) const +{ + p->save(); + p->translate(point); + p->scale(pixelSize / m_unitsPerEm, -pixelSize / m_unitsPerEm); + + // Calculate the text width to be used for alignment + int textWidth = 0; + QString::const_iterator itr = str.constBegin(); + for ( ; itr != str.constEnd(); ++itr) { + QChar unicode = *itr; + if (!m_glyphs.contains(*itr)) { + unicode = 0; + if (!m_glyphs.contains(unicode)) + continue; + } + textWidth += static_cast(m_glyphs[unicode].m_horizAdvX); + } + + QPoint alignmentOffset(0, 0); + if (alignment == Qt::AlignHCenter) { + alignmentOffset.setX(-textWidth / 2); + } else if (alignment == Qt::AlignRight) { + alignmentOffset.setX(-textWidth); + } + + p->translate(alignmentOffset); + + // since in SVG the embedded font ain't really a path + // the outline has got to stay untransformed... + qreal penWidth = p->pen().widthF(); + penWidth /= (pixelSize/m_unitsPerEm); + QPen pen = p->pen(); + pen.setWidthF(penWidth); + p->setPen(pen); + + itr = str.constBegin(); + for ( ; itr != str.constEnd(); ++itr) { + QChar unicode = *itr; + if (!m_glyphs.contains(*itr)) { + unicode = 0; + if (!m_glyphs.contains(unicode)) + continue; + } + p->drawPath(m_glyphs[unicode].m_path); + p->translate(m_glyphs[unicode].m_horizAdvX, 0); + } + + p->restore(); +} + +void QSvgFont::setFamilyName(const QString &name) +{ + m_familyName = name; +} + +void QSvgFont::setUnitsPerEm(qreal upem) +{ + m_unitsPerEm = upem; +} + +QT_END_NAMESPACE diff --git a/svg/qsvgfont_p.h b/svg/qsvgfont_p.h new file mode 100644 index 0000000..fd0a3fa --- /dev/null +++ b/svg/qsvgfont_p.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt SVG module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSVGFONT_P_H +#define QSVGFONT_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qpainterpath.h" +#include "qhash.h" +#include "qstring.h" +#include "qsvgstyle_p.h" +#include "qtsvgglobal_p.h" + +QT_BEGIN_NAMESPACE + +class Q_SVG_PRIVATE_EXPORT QSvgGlyph +{ +public: + QSvgGlyph(QChar unicode, const QPainterPath &path, qreal horizAdvX); + QSvgGlyph() : m_unicode(0), m_horizAdvX(0) {} + + QChar m_unicode; + QPainterPath m_path; + qreal m_horizAdvX; +}; + + +class Q_SVG_PRIVATE_EXPORT QSvgFont : public QSvgRefCounted +{ +public: + QSvgFont(qreal horizAdvX); + + void setFamilyName(const QString &name); + QString familyName() const; + + void setUnitsPerEm(qreal upem); + + void addGlyph(QChar unicode, const QPainterPath &path, qreal horizAdvX = -1); + + void draw(QPainter *p, const QPointF &point, const QString &str, qreal pixelSize, Qt::Alignment alignment) const; +public: + QString m_familyName; + qreal m_unitsPerEm; + qreal m_ascent; + qreal m_descent; + qreal m_horizAdvX; + QHash m_glyphs; +}; + +QT_END_NAMESPACE + +#endif // QSVGFONT_P_H diff --git a/svg/qsvggenerator.cpp b/svg/qsvggenerator.cpp new file mode 100644 index 0000000..07f8d74 --- /dev/null +++ b/svg/qsvggenerator.cpp @@ -0,0 +1,1163 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt SVG module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsvggenerator.h" + +#ifndef QT_NO_SVGGENERATOR + +#include "qpainterpath.h" + +#include "private/qpaintengine_p.h" +#include "private/qtextengine_p.h" +#include "private/qdrawhelper_p.h" + +#include "qfile.h" +#include "qtextcodec.h" +#include "qtextstream.h" +#include "qbuffer.h" +#include "qmath.h" +#include "qbitmap.h" + +#include "qdebug.h" + +QT_BEGIN_NAMESPACE + +static void translate_color(const QColor &color, QString *color_string, + QString *opacity_string) +{ + Q_ASSERT(color_string); + Q_ASSERT(opacity_string); + + *color_string = + QString::fromLatin1("#%1%2%3") + .arg(color.red(), 2, 16, QLatin1Char('0')) + .arg(color.green(), 2, 16, QLatin1Char('0')) + .arg(color.blue(), 2, 16, QLatin1Char('0')); + *opacity_string = QString::number(color.alphaF()); +} + +static void translate_dashPattern(const QVector &pattern, qreal width, QString *pattern_string) +{ + Q_ASSERT(pattern_string); + + // Note that SVG operates in absolute lengths, whereas Qt uses a length/width ratio. + for (qreal entry : pattern) + *pattern_string += QString::fromLatin1("%1,").arg(entry * width); + + pattern_string->chop(1); +} + +class QSvgPaintEnginePrivate : public QPaintEnginePrivate +{ +public: + QSvgPaintEnginePrivate() + { + size = QSize(); + viewBox = QRectF(); + outputDevice = 0; + resolution = 72; + + attributes.document_title = QLatin1String("Qt SVG Document"); + attributes.document_description = QLatin1String("Generated with Qt"); + attributes.font_family = QLatin1String("serif"); + attributes.font_size = QLatin1String("10pt"); + attributes.font_style = QLatin1String("normal"); + attributes.font_weight = QLatin1String("normal"); + + afterFirstUpdate = false; + numGradients = 0; + } + + QSize size; + QRectF viewBox; + QIODevice *outputDevice; + QTextStream *stream; + int resolution; + + QString header; + QString defs; + QString body; + bool afterFirstUpdate; + + QBrush brush; + QPen pen; + QMatrix matrix; + QFont font; + + QString generateGradientName() { + ++numGradients; + currentGradientName = QString::fromLatin1("gradient%1").arg(numGradients); + return currentGradientName; + } + + QString currentGradientName; + int numGradients; + + QStringList savedPatternBrushes; + QStringList savedPatternMasks; + + struct _attributes { + QString document_title; + QString document_description; + QString font_weight; + QString font_size; + QString font_family; + QString font_style; + QString stroke, strokeOpacity; + QString dashPattern, dashOffset; + QString fill, fillOpacity; + } attributes; +}; + +static inline QPaintEngine::PaintEngineFeatures svgEngineFeatures() +{ + return QPaintEngine::PaintEngineFeatures( + QPaintEngine::AllFeatures + & ~QPaintEngine::PerspectiveTransform + & ~QPaintEngine::ConicalGradientFill + & ~QPaintEngine::PorterDuff); +} + +Q_GUI_EXPORT QImage qt_imageForBrush(int brushStyle, bool invert); + +class QSvgPaintEngine : public QPaintEngine +{ + Q_DECLARE_PRIVATE(QSvgPaintEngine) +public: + + QSvgPaintEngine() + : QPaintEngine(*new QSvgPaintEnginePrivate, + svgEngineFeatures()) + { + } + + bool begin(QPaintDevice *device) override; + bool end() override; + + void updateState(const QPaintEngineState &state) override; + void popGroup(); + + void drawEllipse(const QRectF &r) override; + void drawPath(const QPainterPath &path) override; + void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr) override; + void drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode) override; + void drawRects(const QRectF *rects, int rectCount) override; + void drawTextItem(const QPointF &pt, const QTextItem &item) override; + void drawImage(const QRectF &r, const QImage &pm, const QRectF &sr, + Qt::ImageConversionFlags flags = Qt::AutoColor) override; + + QPaintEngine::Type type() const override { return QPaintEngine::SVG; } + + QSize size() const { return d_func()->size; } + void setSize(const QSize &size) { + Q_ASSERT(!isActive()); + d_func()->size = size; + } + + QRectF viewBox() const { return d_func()->viewBox; } + void setViewBox(const QRectF &viewBox) { + Q_ASSERT(!isActive()); + d_func()->viewBox = viewBox; + } + + QString documentTitle() const { return d_func()->attributes.document_title; } + void setDocumentTitle(const QString &title) { + d_func()->attributes.document_title = title; + } + + QString documentDescription() const { return d_func()->attributes.document_description; } + void setDocumentDescription(const QString &description) { + d_func()->attributes.document_description = description; + } + + QIODevice *outputDevice() const { return d_func()->outputDevice; } + void setOutputDevice(QIODevice *device) { + Q_ASSERT(!isActive()); + d_func()->outputDevice = device; + } + + int resolution() { return d_func()->resolution; } + void setResolution(int resolution) { + Q_ASSERT(!isActive()); + d_func()->resolution = resolution; + } + + QString savePatternMask(Qt::BrushStyle style) + { + QString maskId = QString(QStringLiteral("patternmask%1")).arg(style); + if (!d_func()->savedPatternMasks.contains(maskId)) { + QImage img = qt_imageForBrush(style, true); + QRegion reg(QBitmap::fromData(img.size(), img.constBits())); + QString rct(QStringLiteral("")); + QTextStream str(&d_func()->defs, QIODevice::Append); + str << "" << endl; + for (QRect r : reg) + str << rct.arg(r.x()).arg(r.y()).arg(r.width()).arg(r.height()) << endl; + str << QStringLiteral("") << endl << endl; + d_func()->savedPatternMasks.append(maskId); + } + return maskId; + } + + QString savePatternBrush(const QString &color, const QBrush &brush) + { + QString patternId = QString(QStringLiteral("fillpattern%1_")).arg(brush.style()) + color.midRef(1); + if (!d_func()->savedPatternBrushes.contains(patternId)) { + QString maskId = savePatternMask(brush.style()); + QString geo(QStringLiteral("x=\"0\" y=\"0\" width=\"8\" height=\"8\"")); + QTextStream str(&d_func()->defs, QIODevice::Append); + str << QString(QStringLiteral("")).arg(patternId, geo) << endl; + str << QString(QStringLiteral("")).arg(geo, color, maskId) << endl; + str << QStringLiteral("") << endl << endl; + d_func()->savedPatternBrushes.append(patternId); + } + return patternId; + } + + void saveLinearGradientBrush(const QGradient *g) + { + QTextStream str(&d_func()->defs, QIODevice::Append); + const QLinearGradient *grad = static_cast(g); + str << QLatin1String("start().x()<< QLatin1String("\" ") + << QLatin1String("y1=\"") <start().y()<< QLatin1String("\" ") + << QLatin1String("x2=\"") <finalStop().x() << QLatin1String("\" ") + << QLatin1String("y2=\"") <finalStop().y() << QLatin1String("\" "); + } + + str << QLatin1String("id=\"") << d_func()->generateGradientName() << QLatin1String("\">\n"); + saveGradientStops(str, g); + str << QLatin1String("") <defs, QIODevice::Append); + const QRadialGradient *grad = static_cast(g); + str << QLatin1String("center().x()<< QLatin1String("\" ") + << QLatin1String("cy=\"") <center().y()<< QLatin1String("\" ") + << QLatin1String("r=\"") <radius() << QLatin1String("\" ") + << QLatin1String("fx=\"") <focalPoint().x() << QLatin1String("\" ") + << QLatin1String("fy=\"") <focalPoint().y() << QLatin1String("\" "); + } + str << QLatin1String("id=\"") <generateGradientName()<< QLatin1String("\">\n"); + saveGradientStops(str, g); + str << QLatin1String("") << endl; + } + void saveConicalGradientBrush(const QGradient *) + { + qWarning("svg's don't support conical gradients!"); + } + + void saveGradientStops(QTextStream &str, const QGradient *g) { + QGradientStops stops = g->stops(); + + if (g->interpolationMode() == QGradient::ColorInterpolation) { + bool constantAlpha = true; + int alpha = stops.at(0).second.alpha(); + for (int i = 1; i < stops.size(); ++i) + constantAlpha &= (stops.at(i).second.alpha() == alpha); + + if (!constantAlpha) { + const qreal spacing = qreal(0.02); + QGradientStops newStops; + QRgb fromColor = qPremultiply(stops.at(0).second.rgba()); + QRgb toColor; + for (int i = 0; i + 1 < stops.size(); ++i) { + int parts = qCeil((stops.at(i + 1).first - stops.at(i).first) / spacing); + newStops.append(stops.at(i)); + toColor = qPremultiply(stops.at(i + 1).second.rgba()); + + if (parts > 1) { + qreal step = (stops.at(i + 1).first - stops.at(i).first) / parts; + for (int j = 1; j < parts; ++j) { + QRgb color = qUnpremultiply(INTERPOLATE_PIXEL_256(fromColor, 256 - 256 * j / parts, toColor, 256 * j / parts)); + newStops.append(QGradientStop(stops.at(i).first + j * step, QColor::fromRgba(color))); + } + } + fromColor = toColor; + } + newStops.append(stops.back()); + stops = newStops; + } + } + + for (const QGradientStop &stop : qAsConst(stops)) { + const QString color = stop.second.name(QColor::HexRgb); + str << QLatin1String(" \n"); + } + } + + void saveGradientUnits(QTextStream &str, const QGradient *gradient) + { + str << QLatin1String("gradientUnits=\""); + if (gradient && (gradient->coordinateMode() == QGradient::ObjectBoundingMode || gradient->coordinateMode() == QGradient::ObjectMode)) + str << QLatin1String("objectBoundingBox"); + else + str << QLatin1String("userSpaceOnUse"); + str << QLatin1String("\" "); + } + + void generateQtDefaults() + { + *d_func()->stream << QLatin1String("fill=\"none\" "); + *d_func()->stream << QLatin1String("stroke=\"black\" "); + *d_func()->stream << QLatin1String("stroke-width=\"1\" "); + *d_func()->stream << QLatin1String("fill-rule=\"evenodd\" "); + *d_func()->stream << QLatin1String("stroke-linecap=\"square\" "); + *d_func()->stream << QLatin1String("stroke-linejoin=\"bevel\" "); + *d_func()->stream << QLatin1String(">\n"); + } + inline QTextStream &stream() + { + return *d_func()->stream; + } + + + void qpenToSvg(const QPen &spen) + { + d_func()->pen = spen; + + switch (spen.style()) { + case Qt::NoPen: + stream() << QLatin1String("stroke=\"none\" "); + + d_func()->attributes.stroke = QLatin1String("none"); + d_func()->attributes.strokeOpacity = QString(); + return; + break; + case Qt::SolidLine: { + QString color, colorOpacity; + + translate_color(spen.color(), &color, + &colorOpacity); + d_func()->attributes.stroke = color; + d_func()->attributes.strokeOpacity = colorOpacity; + + stream() << QLatin1String("stroke=\"")<attributes.stroke = color; + d_func()->attributes.strokeOpacity = colorOpacity; + d_func()->attributes.dashPattern = dashPattern; + d_func()->attributes.dashOffset = dashOffset; + + stream() << QLatin1String("stroke=\"")<brush = sbrush; + switch (sbrush.style()) { + case Qt::SolidPattern: { + QString color, colorOpacity; + translate_color(sbrush.color(), &color, &colorOpacity); + stream() << "fill=\"" << color << "\" " + "fill-opacity=\"" + << colorOpacity << "\" "; + d_func()->attributes.fill = color; + d_func()->attributes.fillOpacity = colorOpacity; + } + break; + case Qt::Dense1Pattern: + case Qt::Dense2Pattern: + case Qt::Dense3Pattern: + case Qt::Dense4Pattern: + case Qt::Dense5Pattern: + case Qt::Dense6Pattern: + case Qt::Dense7Pattern: + case Qt::HorPattern: + case Qt::VerPattern: + case Qt::CrossPattern: + case Qt::BDiagPattern: + case Qt::FDiagPattern: + case Qt::DiagCrossPattern: { + QString color, colorOpacity; + translate_color(sbrush.color(), &color, &colorOpacity); + QString patternId = savePatternBrush(color, sbrush); + QString patternRef = QString(QStringLiteral("url(#%1)")).arg(patternId); + stream() << "fill=\"" << patternRef << "\" fill-opacity=\"" << colorOpacity << "\" "; + d_func()->attributes.fill = patternRef; + d_func()->attributes.fillOpacity = colorOpacity; + break; + } + case Qt::LinearGradientPattern: + saveLinearGradientBrush(sbrush.gradient()); + d_func()->attributes.fill = QString::fromLatin1("url(#%1)").arg(d_func()->currentGradientName); + d_func()->attributes.fillOpacity = QString(); + stream() << QLatin1String("fill=\"url(#") << d_func()->currentGradientName << QLatin1String(")\" "); + break; + case Qt::RadialGradientPattern: + saveRadialGradientBrush(sbrush.gradient()); + d_func()->attributes.fill = QString::fromLatin1("url(#%1)").arg(d_func()->currentGradientName); + d_func()->attributes.fillOpacity = QString(); + stream() << QLatin1String("fill=\"url(#") << d_func()->currentGradientName << QLatin1String(")\" "); + break; + case Qt::ConicalGradientPattern: + saveConicalGradientBrush(sbrush.gradient()); + d_func()->attributes.fill = QString::fromLatin1("url(#%1)").arg(d_func()->currentGradientName); + d_func()->attributes.fillOpacity = QString(); + stream() << QLatin1String("fill=\"url(#") << d_func()->currentGradientName << QLatin1String(")\" "); + break; + case Qt::NoBrush: + stream() << QLatin1String("fill=\"none\" "); + d_func()->attributes.fill = QLatin1String("none"); + d_func()->attributes.fillOpacity = QString(); + return; + break; + default: + break; + } + } + void qfontToSvg(const QFont &sfont) + { + Q_D(QSvgPaintEngine); + + d->font = sfont; + + if (d->font.pixelSize() == -1) + d->attributes.font_size = QString::number(d->font.pointSizeF() * d->resolution / 72); + else + d->attributes.font_size = QString::number(d->font.pixelSize()); + + int svgWeight = d->font.weight(); + switch (svgWeight) { + case QFont::Light: + svgWeight = 100; + break; + case QFont::Normal: + svgWeight = 400; + break; + case QFont::Bold: + svgWeight = 700; + break; + default: + svgWeight *= 10; + } + + d->attributes.font_weight = QString::number(svgWeight); + d->attributes.font_family = d->font.family(); + d->attributes.font_style = d->font.italic() ? QLatin1String("italic") : QLatin1String("normal"); + + *d->stream << "font-family=\"" << d->attributes.font_family << "\" " + "font-size=\"" << d->attributes.font_size << "\" " + "font-weight=\"" << d->attributes.font_weight << "\" " + "font-style=\"" << d->attributes.font_style << "\" " + << endl; + } +}; + +class QSvgGeneratorPrivate +{ +public: + QSvgPaintEngine *engine; + + uint owns_iodevice : 1; + QString fileName; +}; + +/*! + \class QSvgGenerator + \ingroup painting + \inmodule QtSvg + \since 4.3 + \brief The QSvgGenerator class provides a paint device that is used to create SVG drawings. + \reentrant + + This paint device represents a Scalable Vector Graphics (SVG) drawing. Like QPrinter, it is + designed as a write-only device that generates output in a specific format. + + To write an SVG file, you first need to configure the output by setting the \l fileName + or \l outputDevice properties. It is usually necessary to specify the size of the drawing + by setting the \l size property, and in some cases where the drawing will be included in + another, the \l viewBox property also needs to be set. + + \snippet svggenerator/window.cpp configure SVG generator + + Other meta-data can be specified by setting the \a title, \a description and \a resolution + properties. + + As with other QPaintDevice subclasses, a QPainter object is used to paint onto an instance + of this class: + + \snippet svggenerator/window.cpp begin painting + \dots + \snippet svggenerator/window.cpp end painting + + Painting is performed in the same way as for any other paint device. However, + it is necessary to use the QPainter::begin() and \l{QPainter::}{end()} to + explicitly begin and end painting on the device. + + The \l{SVG Generator Example} shows how the same painting commands can be used + for painting a widget and writing an SVG file. + + \sa QSvgRenderer, QSvgWidget, {Qt SVG C++ Classes} +*/ + +/*! + Constructs a new generator. +*/ +QSvgGenerator::QSvgGenerator() + : d_ptr(new QSvgGeneratorPrivate) +{ + Q_D(QSvgGenerator); + + d->engine = new QSvgPaintEngine; + d->owns_iodevice = false; +} + +/*! + Destroys the generator. +*/ +QSvgGenerator::~QSvgGenerator() +{ + Q_D(QSvgGenerator); + if (d->owns_iodevice) + delete d->engine->outputDevice(); + delete d->engine; +} + +/*! + \property QSvgGenerator::title + \brief the title of the generated SVG drawing + \since 4.5 + \sa description +*/ +QString QSvgGenerator::title() const +{ + Q_D(const QSvgGenerator); + + return d->engine->documentTitle(); +} + +void QSvgGenerator::setTitle(const QString &title) +{ + Q_D(QSvgGenerator); + + d->engine->setDocumentTitle(title); +} + +/*! + \property QSvgGenerator::description + \brief the description of the generated SVG drawing + \since 4.5 + \sa title +*/ +QString QSvgGenerator::description() const +{ + Q_D(const QSvgGenerator); + + return d->engine->documentDescription(); +} + +void QSvgGenerator::setDescription(const QString &description) +{ + Q_D(QSvgGenerator); + + d->engine->setDocumentDescription(description); +} + +/*! + \property QSvgGenerator::size + \brief the size of the generated SVG drawing + \since 4.5 + + By default this property is set to \c{QSize(-1, -1)}, which + indicates that the generator should not output the width and + height attributes of the \c element. + + \note It is not possible to change this property while a + QPainter is active on the generator. + + \sa viewBox, resolution +*/ +QSize QSvgGenerator::size() const +{ + Q_D(const QSvgGenerator); + return d->engine->size(); +} + +void QSvgGenerator::setSize(const QSize &size) +{ + Q_D(QSvgGenerator); + if (d->engine->isActive()) { + qWarning("QSvgGenerator::setSize(), cannot set size while SVG is being generated"); + return; + } + d->engine->setSize(size); +} + +/*! + \property QSvgGenerator::viewBox + \brief the viewBox of the generated SVG drawing + \since 4.5 + + By default this property is set to \c{QRect(0, 0, -1, -1)}, which + indicates that the generator should not output the viewBox attribute + of the \c element. + + \note It is not possible to change this property while a + QPainter is active on the generator. + + \sa viewBox(), size, resolution +*/ +QRectF QSvgGenerator::viewBoxF() const +{ + Q_D(const QSvgGenerator); + return d->engine->viewBox(); +} + +/*! + \since 4.5 + + Returns viewBoxF().toRect(). + + \sa viewBoxF() +*/ +QRect QSvgGenerator::viewBox() const +{ + Q_D(const QSvgGenerator); + return d->engine->viewBox().toRect(); +} + +void QSvgGenerator::setViewBox(const QRectF &viewBox) +{ + Q_D(QSvgGenerator); + if (d->engine->isActive()) { + qWarning("QSvgGenerator::setViewBox(), cannot set viewBox while SVG is being generated"); + return; + } + d->engine->setViewBox(viewBox); +} + +void QSvgGenerator::setViewBox(const QRect &viewBox) +{ + setViewBox(QRectF(viewBox)); +} + +/*! + \property QSvgGenerator::fileName + \brief the target filename for the generated SVG drawing + \since 4.5 + + \sa outputDevice +*/ +QString QSvgGenerator::fileName() const +{ + Q_D(const QSvgGenerator); + return d->fileName; +} + +void QSvgGenerator::setFileName(const QString &fileName) +{ + Q_D(QSvgGenerator); + if (d->engine->isActive()) { + qWarning("QSvgGenerator::setFileName(), cannot set file name while SVG is being generated"); + return; + } + + if (d->owns_iodevice) + delete d->engine->outputDevice(); + + d->owns_iodevice = true; + + d->fileName = fileName; + QFile *file = new QFile(fileName); + d->engine->setOutputDevice(file); +} + +/*! + \property QSvgGenerator::outputDevice + \brief the output device for the generated SVG drawing + \since 4.5 + + If both output device and file name are specified, the output device + will have precedence. + + \sa fileName +*/ +QIODevice *QSvgGenerator::outputDevice() const +{ + Q_D(const QSvgGenerator); + return d->engine->outputDevice(); +} + +void QSvgGenerator::setOutputDevice(QIODevice *outputDevice) +{ + Q_D(QSvgGenerator); + if (d->engine->isActive()) { + qWarning("QSvgGenerator::setOutputDevice(), cannot set output device while SVG is being generated"); + return; + } + d->owns_iodevice = false; + d->engine->setOutputDevice(outputDevice); + d->fileName = QString(); +} + +/*! + \property QSvgGenerator::resolution + \brief the resolution of the generated output + \since 4.5 + + The resolution is specified in dots per inch, and is used to + calculate the physical size of an SVG drawing. + + \sa size, viewBox +*/ +int QSvgGenerator::resolution() const +{ + Q_D(const QSvgGenerator); + return d->engine->resolution(); +} + +void QSvgGenerator::setResolution(int dpi) +{ + Q_D(QSvgGenerator); + d->engine->setResolution(dpi); +} + +/*! + Returns the paint engine used to render graphics to be converted to SVG + format information. +*/ +QPaintEngine *QSvgGenerator::paintEngine() const +{ + Q_D(const QSvgGenerator); + return d->engine; +} + +/*! + \reimp +*/ +int QSvgGenerator::metric(QPaintDevice::PaintDeviceMetric metric) const +{ + Q_D(const QSvgGenerator); + switch (metric) { + case QPaintDevice::PdmDepth: + return 32; + case QPaintDevice::PdmWidth: + return d->engine->size().width(); + case QPaintDevice::PdmHeight: + return d->engine->size().height(); + case QPaintDevice::PdmDpiX: + return d->engine->resolution(); + case QPaintDevice::PdmDpiY: + return d->engine->resolution(); + case QPaintDevice::PdmHeightMM: + return qRound(d->engine->size().height() * 25.4 / d->engine->resolution()); + case QPaintDevice::PdmWidthMM: + return qRound(d->engine->size().width() * 25.4 / d->engine->resolution()); + case QPaintDevice::PdmNumColors: + return 0xffffffff; + case QPaintDevice::PdmPhysicalDpiX: + return d->engine->resolution(); + case QPaintDevice::PdmPhysicalDpiY: + return d->engine->resolution(); + case QPaintDevice::PdmDevicePixelRatio: + return 1; + case QPaintDevice::PdmDevicePixelRatioScaled: + return 1 * QPaintDevice::devicePixelRatioFScale(); + default: + qWarning("QSvgGenerator::metric(), unhandled metric %d\n", metric); + break; + } + return 0; +} + +/***************************************************************************** + * class QSvgPaintEngine + */ + +bool QSvgPaintEngine::begin(QPaintDevice *) +{ + Q_D(QSvgPaintEngine); + if (!d->outputDevice) { + qWarning("QSvgPaintEngine::begin(), no output device"); + return false; + } + + if (!d->outputDevice->isOpen()) { + if (!d->outputDevice->open(QIODevice::WriteOnly | QIODevice::Text)) { + qWarning("QSvgPaintEngine::begin(), could not open output device: '%s'", + qPrintable(d->outputDevice->errorString())); + return false; + } + } else if (!d->outputDevice->isWritable()) { + qWarning("QSvgPaintEngine::begin(), could not write to read-only output device: '%s'", + qPrintable(d->outputDevice->errorString())); + return false; + } + + d->stream = new QTextStream(&d->header); + + // stream out the header... + *d->stream << "" << endl << "size.isValid()) { + qreal wmm = d->size.width() * 25.4 / d->resolution; + qreal hmm = d->size.height() * 25.4 / d->resolution; + *d->stream << " width=\"" << wmm << "mm\" height=\"" << hmm << "mm\"" << endl; + } + + if (d->viewBox.isValid()) { + *d->stream << " viewBox=\"" << d->viewBox.left() << ' ' << d->viewBox.top(); + *d->stream << ' ' << d->viewBox.width() << ' ' << d->viewBox.height() << '\"' << endl; + } + + *d->stream << " xmlns=\"http://www.w3.org/2000/svg\"" + " xmlns:xlink=\"http://www.w3.org/1999/xlink\" " + " version=\"1.2\" baseProfile=\"tiny\">" << endl; + + if (!d->attributes.document_title.isEmpty()) { + *d->stream << "" << d->attributes.document_title << "" << endl; + } + + if (!d->attributes.document_description.isEmpty()) { + *d->stream << "" << d->attributes.document_description << "" << endl; + } + + d->stream->setString(&d->defs); + *d->stream << "\n"; + + d->stream->setString(&d->body); + // Start the initial graphics state... + *d->stream << "stream << endl; + + return true; +} + +bool QSvgPaintEngine::end() +{ + Q_D(QSvgPaintEngine); + + d->stream->setString(&d->defs); + *d->stream << "\n"; + + d->stream->setDevice(d->outputDevice); +#ifndef QT_NO_TEXTCODEC + d->stream->setCodec(QTextCodec::codecForName("UTF-8")); +#endif + + *d->stream << d->header; + *d->stream << d->defs; + *d->stream << d->body; + if (d->afterFirstUpdate) + *d->stream << "" << endl; // close the updateState + + *d->stream << "" << endl // close the Qt defaults + << "" << endl; + + delete d->stream; + + return true; +} + +void QSvgPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, + const QRectF &sr) +{ + drawImage(r, pm.toImage(), sr); +} + +void QSvgPaintEngine::drawImage(const QRectF &r, const QImage &image, + const QRectF &sr, + Qt::ImageConversionFlags flags) +{ + //Q_D(QSvgPaintEngine); + + Q_UNUSED(sr); + Q_UNUSED(flags); + stream() << "\n"; +} + +void QSvgPaintEngine::updateState(const QPaintEngineState &state) +{ + Q_D(QSvgPaintEngine); + QPaintEngine::DirtyFlags flags = state.state(); + + // always stream full gstate, which is not required, but... + flags |= QPaintEngine::AllDirty; + + // close old state and start a new one... + if (d->afterFirstUpdate) + *d->stream << "\n\n"; + + *d->stream << "matrix = state.matrix(); + *d->stream << "transform=\"matrix(" << d->matrix.m11() << ',' + << d->matrix.m12() << ',' + << d->matrix.m21() << ',' << d->matrix.m22() << ',' + << d->matrix.dx() << ',' << d->matrix.dy() + << ")\"" + << endl; + } + + if (flags & QPaintEngine::DirtyFont) { + qfontToSvg(state.font()); + } + + if (flags & QPaintEngine::DirtyOpacity) { + if (!qFuzzyIsNull(state.opacity() - 1)) + stream() << "opacity=\""<stream << '>' << endl; + + d->afterFirstUpdate = true; +} + +void QSvgPaintEngine::drawEllipse(const QRectF &r) +{ + Q_D(QSvgPaintEngine); + + const bool isCircle = r.width() == r.height(); + *d->stream << '<' << (isCircle ? "circle" : "ellipse"); + if (state->pen().isCosmetic()) + *d->stream << " vector-effect=\"non-scaling-stroke\""; + const QPointF c = r.center(); + *d->stream << " cx=\"" << c.x() << "\" cy=\"" << c.y(); + if (isCircle) + *d->stream << "\" r=\"" << r.width() / qreal(2.0); + else + *d->stream << "\" rx=\"" << r.width() / qreal(2.0) << "\" ry=\"" << r.height() / qreal(2.0); + *d->stream << "\"/>" << endl; +} + +void QSvgPaintEngine::drawPath(const QPainterPath &p) +{ + Q_D(QSvgPaintEngine); + + *d->stream << "pen().isCosmetic() ? "non-scaling-stroke" : "none") + << "\" fill-rule=\"" + << (p.fillRule() == Qt::OddEvenFill ? "evenodd" : "nonzero") + << "\" d=\""; + + for (int i=0; istream << 'M' << e.x << ',' << e.y; + break; + case QPainterPath::LineToElement: + *d->stream << 'L' << e.x << ',' << e.y; + break; + case QPainterPath::CurveToElement: + *d->stream << 'C' << e.x << ',' << e.y; + ++i; + while (i < p.elementCount()) { + const QPainterPath::Element &e = p.elementAt(i); + if (e.type != QPainterPath::CurveToDataElement) { + --i; + break; + } else + *d->stream << ' '; + *d->stream << e.x << ',' << e.y; + ++i; + } + break; + default: + break; + } + if (i != p.elementCount() - 1) { + *d->stream << ' '; + } + } + + *d->stream << "\"/>" << endl; +} + +void QSvgPaintEngine::drawPolygon(const QPointF *points, int pointCount, + PolygonDrawMode mode) +{ + Q_ASSERT(pointCount >= 2); + + //Q_D(QSvgPaintEngine); + + QPainterPath path(points[0]); + for (int i=1; ipen().isCosmetic() ? "non-scaling-stroke" : "none") + << "\" points=\""; + for (int i = 0; i < pointCount; ++i) { + const QPointF &pt = points[i]; + stream() << pt.x() << ',' << pt.y() << ' '; + } + stream() << "\" />" <stream << "pen().isCosmetic()) + *d->stream << " vector-effect=\"non-scaling-stroke\""; + *d->stream << " x=\"" << rect.x() << "\" y=\"" << rect.y() + << "\" width=\"" << rect.width() << "\" height=\"" << rect.height() + << "\"/>" << endl; + } +} + +void QSvgPaintEngine::drawTextItem(const QPointF &pt, const QTextItem &textItem) +{ + Q_D(QSvgPaintEngine); + if (d->pen.style() == Qt::NoPen) + return; + + const QTextItemInt &ti = static_cast(textItem); + if (ti.chars == 0) + QPaintEngine::drawTextItem(pt, ti); // Draw as path + QString s = QString::fromRawData(ti.chars, ti.num_chars); + + *d->stream << "attributes.stroke << "\" " + "fill-opacity=\"" << d->attributes.strokeOpacity << "\" " + "stroke=\"none\" " + "xml:space=\"preserve\" " + "x=\"" << pt.x() << "\" y=\"" << pt.y() << "\" "; + qfontToSvg(textItem.font()); + *d->stream << " >" + << s.toHtmlEscaped() + << "" + << endl; +} + +QT_END_NAMESPACE + +#endif // QT_NO_SVGGENERATOR diff --git a/svg/qsvggenerator.h b/svg/qsvggenerator.h new file mode 100644 index 0000000..bd215b2 --- /dev/null +++ b/svg/qsvggenerator.h @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt SVG module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSVGGENERATOR_H +#define QSVGGENERATOR_H + +#include + +#ifndef QT_NO_SVGGENERATOR + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + + +class QSvgGeneratorPrivate; + +class Q_SVG_EXPORT QSvgGenerator : public QPaintDevice +{ + Q_DECLARE_PRIVATE(QSvgGenerator) + + Q_PROPERTY(QSize size READ size WRITE setSize) + Q_PROPERTY(QRectF viewBox READ viewBoxF WRITE setViewBox) + Q_PROPERTY(QString title READ title WRITE setTitle) + Q_PROPERTY(QString description READ description WRITE setDescription) + Q_PROPERTY(QString fileName READ fileName WRITE setFileName) + Q_PROPERTY(QIODevice* outputDevice READ outputDevice WRITE setOutputDevice) + Q_PROPERTY(int resolution READ resolution WRITE setResolution) +public: + QSvgGenerator(); + ~QSvgGenerator(); + + QString title() const; + void setTitle(const QString &title); + + QString description() const; + void setDescription(const QString &description); + + QSize size() const; + void setSize(const QSize &size); + + QRect viewBox() const; + QRectF viewBoxF() const; + void setViewBox(const QRect &viewBox); + void setViewBox(const QRectF &viewBox); + + QString fileName() const; + void setFileName(const QString &fileName); + + QIODevice *outputDevice() const; + void setOutputDevice(QIODevice *outputDevice); + + void setResolution(int dpi); + int resolution() const; +protected: + QPaintEngine *paintEngine() const override; + int metric(QPaintDevice::PaintDeviceMetric metric) const override; + +private: + QScopedPointer d_ptr; +}; + +QT_END_NAMESPACE + +#endif // QT_NO_SVGGENERATOR +#endif // QSVGGENERATOR_H diff --git a/svg/qsvggraphics.cpp b/svg/qsvggraphics.cpp new file mode 100644 index 0000000..474215e --- /dev/null +++ b/svg/qsvggraphics.cpp @@ -0,0 +1,1035 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt SVG module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsvggraphics_p.h" + +#include "qsvgfont_p.h" + +#include +#include +#include +#include +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +#define QT_SVG_DRAW_SHAPE(command) \ + qreal oldOpacity = p->opacity(); \ + QBrush oldBrush = p->brush(); \ + QPen oldPen = p->pen(); \ + p->setPen(Qt::NoPen); \ + p->setOpacity(oldOpacity * states.fillOpacity); \ + command; \ + p->setPen(oldPen); \ + if (oldPen != Qt::NoPen && oldPen.brush() != Qt::NoBrush && oldPen.widthF() != 0) { \ + p->setOpacity(oldOpacity * states.strokeOpacity); \ + p->setBrush(Qt::NoBrush); \ + command; \ + p->setBrush(oldBrush); \ + } \ + p->setOpacity(oldOpacity); + + +void QSvgAnimation::draw(QPainter *, QSvgExtraStates &) +{ + qWarning(" no implemented"); +} + +static inline QRectF boundsOnStroke(QPainter *p, const QPainterPath &path, qreal width) +{ + QPainterPathStroker stroker; + stroker.setWidth(width); + QPainterPath stroke = stroker.createStroke(path); + return p->transform().map(stroke).boundingRect(); +} + +QSvgEllipse::QSvgEllipse(QSvgNode *parent, const QRectF &rect) + : QSvgNode(parent), m_bounds(rect) +{ +} + + +QRectF QSvgEllipse::bounds(QPainter *p, QSvgExtraStates &) const +{ + QPainterPath path; + path.addEllipse(m_bounds); + qreal sw = strokeWidth(p); + return qFuzzyIsNull(sw) ? p->transform().map(path).boundingRect() : boundsOnStroke(p, path, sw); +} + +void QSvgEllipse::draw(QPainter *p, QSvgExtraStates &states) +{ + applyStyle(p, states); + QT_SVG_DRAW_SHAPE(p->drawEllipse(m_bounds)); + revertStyle(p, states); +} + +QSvgArc::QSvgArc(QSvgNode *parent, const QPainterPath &path) + : QSvgNode(parent), m_path(path) +{ +} + +void QSvgArc::draw(QPainter *p, QSvgExtraStates &states) +{ + applyStyle(p, states); + if (p->pen().widthF() != 0) { + qreal oldOpacity = p->opacity(); + p->setOpacity(oldOpacity * states.strokeOpacity); + p->drawPath(m_path); + p->setOpacity(oldOpacity); + } + revertStyle(p, states); +} + +QSvgImage::QSvgImage(QSvgNode *parent, const QImage &image, + const QRectF &bounds) + : QSvgNode(parent), m_image(image), + m_bounds(bounds) +{ + if (m_bounds.width() == 0.0) + m_bounds.setWidth(static_cast(m_image.width())); + if (m_bounds.height() == 0.0) + m_bounds.setHeight(static_cast(m_image.height())); +} + +void QSvgImage::draw(QPainter *p, QSvgExtraStates &states) +{ + applyStyle(p, states); + p->drawImage(m_bounds, m_image); + revertStyle(p, states); +} + + +QSvgLine::QSvgLine(QSvgNode *parent, const QLineF &line) + : QSvgNode(parent), m_line(line) +{ +} + + +void QSvgLine::draw(QPainter *p, QSvgExtraStates &states) +{ + applyStyle(p, states); + if (p->pen().widthF() != 0) { + qreal oldOpacity = p->opacity(); + p->setOpacity(oldOpacity * states.strokeOpacity); + p->drawLine(m_line); + p->setOpacity(oldOpacity); + } + revertStyle(p, states); +} + +QSvgPath::QSvgPath(QSvgNode *parent, const QPainterPath &qpath) + : QSvgNode(parent), m_path(qpath) +{ +} + +void QSvgPath::draw(QPainter *p, QSvgExtraStates &states) +{ + applyStyle(p, states); + m_path.setFillRule(states.fillRule); + QT_SVG_DRAW_SHAPE(p->drawPath(m_path)); + revertStyle(p, states); +} + +QRectF QSvgPath::bounds(QPainter *p, QSvgExtraStates &) const +{ + qreal sw = strokeWidth(p); + return qFuzzyIsNull(sw) ? p->transform().map(m_path).boundingRect() + : boundsOnStroke(p, m_path, sw); +} + +QSvgPolygon::QSvgPolygon(QSvgNode *parent, const QPolygonF &poly) + : QSvgNode(parent), m_poly(poly) +{ +} + +QRectF QSvgPolygon::bounds(QPainter *p, QSvgExtraStates &) const +{ + qreal sw = strokeWidth(p); + if (qFuzzyIsNull(sw)) { + return p->transform().map(m_poly).boundingRect(); + } else { + QPainterPath path; + path.addPolygon(m_poly); + return boundsOnStroke(p, path, sw); + } +} + +void QSvgPolygon::draw(QPainter *p, QSvgExtraStates &states) +{ + applyStyle(p, states); + QT_SVG_DRAW_SHAPE(p->drawPolygon(m_poly, states.fillRule)); + revertStyle(p, states); +} + + +QSvgPolyline::QSvgPolyline(QSvgNode *parent, const QPolygonF &poly) + : QSvgNode(parent), m_poly(poly) +{ + +} + +void QSvgPolyline::draw(QPainter *p, QSvgExtraStates &states) +{ + applyStyle(p, states); + qreal oldOpacity = p->opacity(); + if (p->brush().style() != Qt::NoBrush) { + QPen save = p->pen(); + p->setPen(QPen(Qt::NoPen)); + p->setOpacity(oldOpacity * states.fillOpacity); + p->drawPolygon(m_poly, states.fillRule); + p->setPen(save); + } + if (p->pen().widthF() != 0) { + p->setOpacity(oldOpacity * states.strokeOpacity); + p->drawPolyline(m_poly); + } + p->setOpacity(oldOpacity); + revertStyle(p, states); +} + +QSvgRect::QSvgRect(QSvgNode *node, const QRectF &rect, int rx, int ry) + : QSvgNode(node), + m_rect(rect), m_rx(rx), m_ry(ry) +{ +} +void QSvgRect::SetRect(QRectF rect) +{ + m_rect=rect; +} +QRectF &QSvgRect::rect() +{ + return m_rect; +} + +QRectF QSvgRect::bounds(QPainter *p, QSvgExtraStates &) const +{ + qreal sw = strokeWidth(p); + if (qFuzzyIsNull(sw)) { + return p->transform().mapRect(m_rect); + } else { + QPainterPath path; + path.addRect(m_rect); + return boundsOnStroke(p, path, sw); + } +} + +void QSvgRect::draw(QPainter *p, QSvgExtraStates &states) +{ + applyStyle(p, states); + if (m_rx || m_ry) { + QT_SVG_DRAW_SHAPE(p->drawRoundedRect(m_rect, m_rx, m_ry, Qt::RelativeSize)); + } else { + QT_SVG_DRAW_SHAPE(p->drawRect(m_rect)); + } + revertStyle(p, states); +} + +QSvgTspan * const QSvgText::LINEBREAK = 0; + +QSvgText::QSvgText(QSvgNode *parent, const QPointF &coord) + : QSvgNode(parent) + , m_coord(coord) + , m_type(TEXT) + , m_size(0, 0) + , m_mode(Default) +{ +} + +QSvgText::~QSvgText() +{ + for (int i = 0; i < m_tspans.size(); ++i) { + if (m_tspans[i] != LINEBREAK) + delete m_tspans[i]; + } +} + +void QSvgText::setTextArea(const QSizeF &size) +{ + m_size = size; + m_type = TEXTAREA; +} + +//QRectF QSvgText::bounds(QPainter *p, QSvgExtraStates &) const {} + +void QSvgText::draw(QPainter *p, QSvgExtraStates &states) +{ + applyStyle(p, states); + qreal oldOpacity = p->opacity(); + p->setOpacity(oldOpacity * states.fillOpacity); + + // Force the font to have a size of 100 pixels to avoid truncation problems + // when the font is very small. + qreal scale = 100.0 / p->font().pointSizeF(); + Qt::Alignment alignment = states.textAnchor; + + QTransform oldTransform = p->worldTransform(); + p->scale(1 / scale, 1 / scale); + + qreal y = 0; + bool initial = true; + qreal px = m_coord.x() * scale; + qreal py = m_coord.y() * scale; + QSizeF scaledSize = m_size * scale; + + if (m_type == TEXTAREA) { + if (alignment == Qt::AlignHCenter) + px += scaledSize.width() / 2; + else if (alignment == Qt::AlignRight) + px += scaledSize.width(); + } + + QRectF bounds; + if (m_size.height() != 0) + bounds = QRectF(0, py, 1, scaledSize.height()); // x and width are not used. + + bool appendSpace = false; + QVector paragraphs; + QVector > formatRanges(1); + paragraphs.push_back(QString()); + + for (int i = 0; i < m_tspans.size(); ++i) { + if (m_tspans[i] == LINEBREAK) { + if (m_type == TEXTAREA) { + if (paragraphs.back().isEmpty()) { + QFont font = p->font(); + font.setPixelSize(font.pointSizeF() * scale); + + QTextLayout::FormatRange range; + range.start = 0; + range.length = 1; + range.format.setFont(font); + formatRanges.back().append(range); + + paragraphs.back().append(QLatin1Char(' '));; + } + appendSpace = false; + paragraphs.push_back(QString()); + formatRanges.resize(formatRanges.size() + 1); + } + } else { + WhitespaceMode mode = m_tspans[i]->whitespaceMode(); + m_tspans[i]->applyStyle(p, states); + + QFont font = p->font(); + font.setPixelSize(font.pointSizeF() * scale); + + QString newText(m_tspans[i]->text()); + newText.replace(QLatin1Char('\t'), QLatin1Char(' ')); + newText.replace(QLatin1Char('\n'), QLatin1Char(' ')); + + bool prependSpace = !appendSpace && !m_tspans[i]->isTspan() && (mode == Default) && !paragraphs.back().isEmpty() && newText.startsWith(QLatin1Char(' ')); + if (appendSpace || prependSpace) + paragraphs.back().append(QLatin1Char(' ')); + + bool appendSpaceNext = (!m_tspans[i]->isTspan() && (mode == Default) && newText.endsWith(QLatin1Char(' '))); + + if (mode == Default) { + newText = newText.simplified(); + if (newText.isEmpty()) + appendSpaceNext = false; + } + + QTextLayout::FormatRange range; + range.start = paragraphs.back().length(); + range.length = newText.length(); + range.format.setFont(font); + range.format.setTextOutline(p->pen()); + range.format.setForeground(p->brush()); + + if (appendSpace) { + Q_ASSERT(!formatRanges.back().isEmpty()); + ++formatRanges.back().back().length; + } else if (prependSpace) { + --range.start; + ++range.length; + } + formatRanges.back().append(range); + + appendSpace = appendSpaceNext; + paragraphs.back() += newText; + + m_tspans[i]->revertStyle(p, states); + } + } + + if (states.svgFont) { + // SVG fonts not fully supported... + QString text = paragraphs.front(); + for (int i = 1; i < paragraphs.size(); ++i) { + text.append(QLatin1Char('\n')); + text.append(paragraphs[i]); + } + states.svgFont->draw(p, m_coord * scale, text, p->font().pointSizeF() * scale, states.textAnchor); + } else { + for (int i = 0; i < paragraphs.size(); ++i) { + QTextLayout tl(paragraphs[i]); + QTextOption op = tl.textOption(); + op.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); + tl.setTextOption(op); + tl.setFormats(formatRanges[i]); + tl.beginLayout(); + + forever { + QTextLine line = tl.createLine(); + if (!line.isValid()) + break; + if (m_size.width() != 0) + line.setLineWidth(scaledSize.width()); + } + tl.endLayout(); + + bool endOfBoundsReached = false; + for (int i = 0; i < tl.lineCount(); ++i) { + QTextLine line = tl.lineAt(i); + + qreal x = 0; + if (alignment == Qt::AlignHCenter) + x -= 0.5 * line.naturalTextWidth(); + else if (alignment == Qt::AlignRight) + x -= line.naturalTextWidth(); + + if (initial && m_type == TEXT) + y -= line.ascent(); + initial = false; + + line.setPosition(QPointF(x, y)); + + // Check if the current line fits into the bounding rectangle. + if ((m_size.width() != 0 && line.naturalTextWidth() > scaledSize.width()) + || (m_size.height() != 0 && y + line.height() > scaledSize.height())) { + // I need to set the bounds height to 'y-epsilon' to avoid drawing the current + // line. Since the font is scaled to 100 units, 1 should be a safe epsilon. + bounds.setHeight(y - 1); + endOfBoundsReached = true; + break; + } + + y += 1.1 * line.height(); + } + tl.draw(p, QPointF(px, py), QVector(), bounds); + + if (endOfBoundsReached) + break; + } + } + + p->setWorldTransform(oldTransform, false); + p->setOpacity(oldOpacity); + revertStyle(p, states); +} + +int isPrint() +{ + QSharedMemory m_SharedMemory; + m_SharedMemory.setKey("SharedName"); + m_SharedMemory.attach(); + if(m_SharedMemory.isAttached()) + { + QSharedMemory m_isColor; + int *isNo=(int*)m_SharedMemory.data(); + QString cs=QString::number(*isNo)+"isScaleAndColor"; + m_isColor.setKey(cs); + m_isColor.attach(); + if(m_isColor.isAttached()){ + float *temp=(float*)m_isColor.data(); + return (int)temp[4]; + } + else return 1; + } + else return 1; +} + +void QSvgText::draw(QPainter *p, QSvgExtraStates &states,QRectF bounds,QTransform &Transform,QRectF &sourceRect1,QMatrix &matrix) +{ + float isXScale=1; + float isYScale=1; + QRectF sourceRect=sourceRect1; + sourceRect.setHeight(sourceRect1.height()*isYScale); + sourceRect.setWidth(sourceRect1.width()*isXScale); + applyStyle(p, states); + qreal oldOpacity = p->opacity(); + p->setOpacity(oldOpacity * states.fillOpacity); + + // Force the font to have a size of 100 pixels to avoid truncation problems + // when the font is very small. + qreal scale =1;// 100.0 / p->font().pointSizeF(); + Qt::Alignment alignment = states.textAnchor; + QFont font1=p->font(); + if(font1.family()=="Cambria") font1.setFamily("微软雅黑"); + QTransform oldTransform = p->worldTransform(); + p->setWorldTransform(Transform, false); + p->scale(1 / scale, 1 / scale); + QTransform oldTransform1 = p->worldTransform(); + + qreal y = 0; + bool initial = true; + if(sourceRect.width()==0) sourceRect.setWidth(1); + if(sourceRect.height()==0) sourceRect.setHeight(1); + qreal e=bounds.width() / sourceRect.width(); + qreal b=bounds.height() / sourceRect.height(); + + qreal dx=matrix.dx(); + qreal dy=matrix.dy(); + qreal w=matrix.m11(); + qreal m12=matrix.m12(); + qreal m21=matrix.m21(); + qreal c=e; + if(c>1) c=1; + qreal h=matrix.m22()*c; + qreal px = bounds.x() * scale+(bounds.width()+bounds.width()*dx/100.0)* scale; + qreal py = bounds.y() * scale+(m_coord.y()-sourceRect.height()*dy/100)*b* scale;//百分比 + if(m12==1) py=bounds.y() * scale+(bounds.height()+h)/2* scale;//距中 + else if(m12==2) py=bounds.y() * scale+(bounds.height()-h)*0.9*scale;//距底 + else if(m12==3) py=bounds.y() * scale+h*1.1*scale;//距顶 + QSizeF scaledSize = bounds.size() * scale; + + if (m_type == TEXTAREA) { + if (alignment == Qt::AlignHCenter) + px += scaledSize.width() / 2; + else if (alignment == Qt::AlignRight) + px += scaledSize.width(); + } + float dpi=65; + if(p->device()) { + if(isPrint()) { + dpi=200; + } + else { + dpi=p->device()->logicalDpiX(); + if(!dpi) dpi=p->device()->physicalDpiX(); + } + } + bool appendSpace = false; + QVector paragraphs; + QStack formats; + QVector > formatRanges; + paragraphs.push_back(QString()); + formatRanges.push_back(QList()); + + for (int i = 0; i < m_tspans.size(); ++i) { + if (m_tspans[i] == LINEBREAK) { + if (m_type == TEXTAREA) { + if (paragraphs.back().isEmpty()) { + QFont font = p->font(); + font.setPixelSize(font.pointSizeF() * scale); + + QTextLayout::FormatRange range; + range.start = 0; + range.length = 1; + range.format.setFont(font); + formatRanges.back().append(range); + + paragraphs.back().append(QLatin1Char(' '));; + } + appendSpace = false; + paragraphs.push_back(QString()); + formatRanges.push_back(QList()); + } + } else { + WhitespaceMode mode = m_tspans[i]->whitespaceMode(); + m_tspans[i]->applyStyle(p, states); + + QFont font= p->font(); + font.setPixelSize(font.pointSizeF() * scale); + QString newText(m_tspans[i]->text()); + newText.replace(QLatin1Char('\t'), QLatin1Char(' ')); + newText.replace(QLatin1Char('\n'), QLatin1Char(' ')); + + bool prependSpace = !appendSpace && !m_tspans[i]->isTspan() && (mode == Default) && !paragraphs.back().isEmpty() && newText.startsWith(QLatin1Char(' ')); + if (appendSpace || prependSpace) + paragraphs.back().append(QLatin1Char(' ')); + + bool appendSpaceNext = (!m_tspans[i]->isTspan() && (mode == Default) && newText.endsWith(QLatin1Char(' '))); + + if (mode == Default) { + newText = newText.simplified(); + if (newText.isEmpty()) + appendSpaceNext = false; + } + + QTextLayout::FormatRange range; + range.start = paragraphs.back().length(); + range.length = newText.length(); + range.format.setFont(font); + range.format.setTextOutline(p->pen()); + range.format.setForeground(p->brush()); + + if (appendSpace) { + Q_ASSERT(!formatRanges.back().isEmpty()); + ++formatRanges.back().back().length; + } else if (prependSpace) { + --range.start; + ++range.length; + } + formatRanges.back().append(range); + + appendSpace = appendSpaceNext; + paragraphs.back() += newText; + + m_tspans[i]->revertStyle(p, states); + } + } + + if (states.svgFont) { + // SVG fonts not fully supported... + QString text = paragraphs.front(); + for (int i = 1; i < paragraphs.size(); ++i) { + text.append(QLatin1Char('\n')); + text.append(paragraphs[i]); + } + states.svgFont->draw(p, m_coord * scale, text, p->font().pointSizeF() * scale, states.textAnchor); + } else { + for (int m = 0; m < paragraphs.size(); ++m) { + QTextLayout tl(paragraphs[m]); + QTextOption op = tl.textOption(); + tl.setAdditionalFormats(formatRanges[m]); + tl.beginLayout(); + forever + { + QTextLine line=tl.createLine(); + if (!line.isValid()) + break; + if (m_size.width() != 0) + line.setLineWidth(scaledSize.width()); + } + + tl.endLayout(); + + bool endOfBoundsReached = false; + for (int i = 0; i < tl.lineCount(); ++i) { + QTextLine line = tl.lineAt(i); + + qreal x = 0; + if (alignment == Qt::AlignHCenter) + x -= 0.5 * line.naturalTextWidth(); + else if (alignment == Qt::AlignRight) + x -= line.naturalTextWidth(); + + if (initial && m_type == TEXT) + y -= line.ascent(); + initial = false; + + line.setPosition(QPointF(x, y)); + + // Check if the current line fits into the bounding rectangle. + if ((m_size.width() != 0 && line.naturalTextWidth() > scaledSize.width()) + || (m_size.height() != 0 && y + line.height() > scaledSize.height())) { + // I need to set the bounds height to 'y-epsilon' to avoid drawing the current + // line. Since the font is scaled to 100 units, 1 should be a safe epsilon. + bounds.setHeight(y - 1); + endOfBoundsReached = true; + break; + } + y += 1.1 * line.height(); + } + QString name1=font1.rawName(); + float aaaa=font1.pointSizeF(); +// font1.setPointSizeF(-1); + font1.setPixelSize(aaaa); + font1.setWeight(1); + QFont p1=p->font(); + float si=h*0.8/10/c; + if(b==e||(e==1&&b>2*e)) si = h/2; + QPointF pos(px,py); + QString s=""; + + QPointF textCenterInGeo =pos; + p->save(); + QPointF t(bounds.width(),bounds.height()); + QPointF nxy=p->transform().map(t); + QRectF trect=p->transform().mapRect(bounds); + QPointF textCenterInPixel = p->transform().map(textCenterInGeo); + p->setWorldTransform(QTransform());//临时切回像素坐标系 + QString a=paragraphs[m]; + int va=0,rep=1; + float heig=sourceRect.height()*10*c; + if(a.indexOf("$")>-1) { + rep=trect.height()/(heig); + if(rep<1) rep=1; + if(rep>1) textCenterInPixel.setY(trect.top()+(trect.height()-heig*rep)/2+heig/2); + a.replace("$",""); + } + if(a.indexOf("^^")>-1) va=1; + a.replace("^^","^"); + QStringList aa=a.split('^'); + QStringList aa1,aa2; + int fn=3; + for (int k=0;k1) fn=1; + aa1.append(aa2); + } + aa=aa1; + QTextOption op1=op; + op1.setAlignment(Qt::AlignLeft); + float temp =8; + int textWidth=10; + int textHeight=10; + for(int k=0;k1) { + ssss=stq[1].toFloat(); + } + if(!va) { + if(p->device()) { + temp=si*isXScale*1/72.*dpi; + font1.setPixelSize(temp); + } + else { + font1.setPixelSize(si*isXScale); + } + QFontMetrics fm(font1); + p->setFont(font1); + textWidth=fm.width(aa[j])+1; + textHeight = fm.height(); + QRectF textRect(textCenterInPixel.x()+textWidth/5.0-textWidth,textCenterInPixel.y()+heig*k-textHeight/2.0+j*textHeight/fn,textWidth,textHeight); + // textRect.adjust(0,ssss*isXScale*1/72.*dpi,0,ssss*isXScale*1/72.*dpi); + // if(textRect.y()trect.bottom()) textRect.setBottom(trect.bottom()); + if(aa[j].indexOf("@")>-1) { + const QPen pen=p->pen(); + float lw=pen.widthF(); + QString w; + if(stq.size()>1) w=stq[0].mid(1); + else w=aa[j].mid(1); + if (w.isEmpty()) + { + w="2"; + } + QPen pp=pen; + pp.setWidthF(w.toFloat()*isXScale*1/72.*dpi); + p->setPen(pp); + QPointF lt=trect.topLeft(); + QPointF rt=trect.topRight(); + lt.setY(textRect.y()+ssss*isXScale*1/72.*dpi); + rt.setY(textRect.y()+ssss*isXScale*1/72.*dpi); + if(lt.y()trect.bottom()) lt.setY(trect.bottom()); + if(rt.y()trect.bottom()) rt.setY(trect.bottom()); + p->drawLine(lt,rt); + pp.setWidthF(lw); + p->setPen(pp); + } + else if(name1.indexOf("@")<0) + { + p->drawText(textRect,aa[j],op1);//绘制解释层号 + } + else { + p->save(); + /////////////// + QPointF textCenterInGeo =textRect.center(); + QPointF textCenterInPixel = p->transform().map(textCenterInGeo); + QRectF trect = p->transform().mapRect(textRect); + QTransform transform; + qreal rotatex=0; + qreal rotatey=0; + rotatex=textCenterInPixel.x(); + rotatey=textCenterInPixel.y(); + transform.reset(); + { + transform.translate(rotatex,rotatey); + transform.rotate(-90.0); + transform.translate(-rotatex,-rotatey); + p->setWorldTransform(transform); + } + p->drawText(textRect,aa[j],op1);//绘制解释层号 + p->restore(); + } + } + else { + if(p->device()) { + temp = si/(aa.size())*isXScale*1/72.*dpi; + font1.setPixelSize(temp); + } + else { + font1.setPixelSize(si/(aa.size())*isXScale); + } + QFontMetrics fm(font1); + p->setFont(font1); + textWidth = fm.width(aa[j])+1; + textHeight = fm.height(); + if(textHeight>trect.height()) textHeight=trect.height(); + QRectF textRect(textCenterInPixel.x()+textWidth/5.0-textWidth+textWidth*j*3.0/4.0,textCenterInPixel.y()-textHeight/2.0-j*textHeight/fn+heig*k,textWidth,textHeight); + if(aa[j].indexOf("@")>-1) { + const QPen pen=p->pen(); + float lw=pen.widthF(); + QString w; + if(stq.size()>1) w=stq[0].mid(1); + else w=aa[j].mid(1); + if (w.isEmpty()) + { + w="2"; + } + if(stq.size()>1) w=stq[0]; + /*QStringList stq=w.split(","); + stq.removeAll(""); + float ssss=0; + if(stq.size()>1) { + w=stq[0]; + ssss=stq[1].toFloat(); + }*/ + QPen pp=pen; + pp.setWidthF(w.toFloat()*isXScale*1/72.*dpi); + p->setPen(pp); + QPointF lt=trect.topLeft(); + QPointF rt=trect.topRight(); + lt.setY(textRect.y()+ssss*isXScale*1/72.*dpi); + rt.setY(textRect.y()+ssss*isXScale*1/72.*dpi); + if(lt.y()trect.bottom()) lt.setY(trect.bottom()); + if(rt.y()trect.bottom()) rt.setY(trect.bottom()); + lt.setY(textRect.y()+ssss*isXScale*1/72.*dpi); + rt.setY(textRect.y()+ssss*isXScale*1/72.*dpi); + p->drawLine(lt,rt); + pp.setWidthF(lw); + p->setPen(pp); + } + else if(name1.indexOf("@")<0) + { + p->drawText(textRect,aa[j],op1);//绘制解释层号 + } + else { + p->save(); + /////////////// + QPointF textCenterInGeo =textRect.center(); + QPointF textCenterInPixel = p->transform().map(textCenterInGeo); + QRectF trect = p->transform().mapRect(textRect); + QTransform transform; + qreal rotatex=0; + qreal rotatey=0; + rotatex=textCenterInPixel.x(); + rotatey=textCenterInPixel.y(); + transform.reset(); + { + transform.translate(rotatex,rotatey); + transform.rotate(-90.0); + transform.translate(-rotatex,-rotatey); + p->setWorldTransform(transform); + } + p->drawText(textRect,aa[j],op1);//绘制解释层号 + p->restore(); + } + } + } + } + p->restore();//退出像素坐标系 + p->setFont(p1); + if (endOfBoundsReached) + break; + } + } + + p->setWorldTransform(oldTransform, false); + p->setOpacity(oldOpacity); + revertStyle(p, states); +} + +void QSvgText::addText(const QString &text) +{ + m_tspans.append(new QSvgTspan(this, false)); + m_tspans.back()->setWhitespaceMode(m_mode); + m_tspans.back()->addText(text); +} + +QSvgUse::QSvgUse(const QPointF &start, QSvgNode *parent, QSvgNode *node) + : QSvgNode(parent), m_link(node), m_start(start), m_recursing(false) +{ + +} + +void QSvgUse::draw(QPainter *p, QSvgExtraStates &states) +{ + if (Q_UNLIKELY(!m_link || isDescendantOf(m_link) || m_recursing)) + return; + + applyStyle(p, states); + + if (!m_start.isNull()) { + p->translate(m_start); + } + { + QScopedValueRollback guard(m_recursing, true); + m_link->draw(p, states); + } + if (!m_start.isNull()) { + p->translate(-m_start); + } + + revertStyle(p, states); +} + +void QSvgVideo::draw(QPainter *p, QSvgExtraStates &states) +{ + applyStyle(p, states); + + revertStyle(p, states); +} + +QSvgNode::Type QSvgAnimation::type() const +{ + return ANIMATION; +} + +QSvgNode::Type QSvgArc::type() const +{ + return ARC; +} + +QSvgNode::Type QSvgCircle::type() const +{ + return CIRCLE; +} + +QSvgNode::Type QSvgEllipse::type() const +{ + return ELLIPSE; +} + +QSvgNode::Type QSvgImage::type() const +{ + return IMAGE; +} + +QSvgNode::Type QSvgLine::type() const +{ + return LINE; +} + +QSvgNode::Type QSvgPath::type() const +{ + return PATH; +} + +QSvgNode::Type QSvgPolygon::type() const +{ + return POLYGON; +} + +QSvgNode::Type QSvgPolyline::type() const +{ + return POLYLINE; +} + +QSvgNode::Type QSvgRect::type() const +{ + return RECT; +} + +QSvgNode::Type QSvgText::type() const +{ + return m_type; +} + +QSvgNode::Type QSvgUse::type() const +{ + return USE; +} + +QSvgNode::Type QSvgVideo::type() const +{ + return VIDEO; +} + +QRectF QSvgUse::bounds(QPainter *p, QSvgExtraStates &states) const +{ + QRectF bounds; + if (Q_LIKELY(m_link && !isDescendantOf(m_link) && !m_recursing)) { + QScopedValueRollback guard(m_recursing, true); + p->translate(m_start); + bounds = m_link->transformedBounds(p, states); + p->translate(-m_start); + } + return bounds; +} + +QRectF QSvgPolyline::bounds(QPainter *p, QSvgExtraStates &) const +{ + qreal sw = strokeWidth(p); + if (qFuzzyIsNull(sw)) { + return p->transform().map(m_poly).boundingRect(); + } else { + QPainterPath path; + path.addPolygon(m_poly); + return boundsOnStroke(p, path, sw); + } +} + +QRectF QSvgArc::bounds(QPainter *p, QSvgExtraStates &) const +{ + qreal sw = strokeWidth(p); + return qFuzzyIsNull(sw) ? p->transform().map(m_path).boundingRect() + : boundsOnStroke(p, m_path, sw); +} + +QRectF QSvgImage::bounds(QPainter *p, QSvgExtraStates &) const +{ + return p->transform().mapRect(m_bounds); +} + +QRectF QSvgLine::bounds(QPainter *p, QSvgExtraStates &) const +{ + qreal sw = strokeWidth(p); + if (qFuzzyIsNull(sw)) { + QPointF p1 = p->transform().map(m_line.p1()); + QPointF p2 = p->transform().map(m_line.p2()); + qreal minX = qMin(p1.x(), p2.x()); + qreal minY = qMin(p1.y(), p2.y()); + qreal maxX = qMax(p1.x(), p2.x()); + qreal maxY = qMax(p1.y(), p2.y()); + return QRectF(minX, minY, maxX - minX, maxY - minY); + } else { + QPainterPath path; + path.moveTo(m_line.p1()); + path.lineTo(m_line.p2()); + return boundsOnStroke(p, path, sw); + } +} + +QT_END_NAMESPACE diff --git a/svg/qsvggraphics_p.h b/svg/qsvggraphics_p.h new file mode 100644 index 0000000..0e68788 --- /dev/null +++ b/svg/qsvggraphics_p.h @@ -0,0 +1,274 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt SVG module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSVGGRAPHICS_P_H +#define QSVGGRAPHICS_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qsvgnode_p.h" +#include "qtsvgglobal_p.h" + +#include "QtGui/qpainterpath.h" +#include "QtGui/qimage.h" +#include "QtGui/qtextlayout.h" +#include "QtGui/qtextoption.h" +#include "QtCore/qstack.h" + +QT_BEGIN_NAMESPACE + +class QTextCharFormat; + +class Q_SVG_PRIVATE_EXPORT QSvgAnimation : public QSvgNode +{ +public: + void draw(QPainter *p, QSvgExtraStates &states) override; + Type type() const override; +}; + +class Q_SVG_PRIVATE_EXPORT QSvgArc : public QSvgNode +{ +public: + QSvgArc(QSvgNode *parent, const QPainterPath &path); + void draw(QPainter *p, QSvgExtraStates &states) override; + Type type() const override; + QRectF bounds(QPainter *p, QSvgExtraStates &states) const override; +private: + QPainterPath m_path; +}; + +class Q_SVG_PRIVATE_EXPORT QSvgEllipse : public QSvgNode +{ +public: + QSvgEllipse(QSvgNode *parent, const QRectF &rect); + void draw(QPainter *p, QSvgExtraStates &states) override; + Type type() const override; + QRectF bounds(QPainter *p, QSvgExtraStates &states) const override; +private: + QRectF m_bounds; +}; + +class Q_SVG_PRIVATE_EXPORT QSvgCircle : public QSvgEllipse +{ +public: + QSvgCircle(QSvgNode *parent, const QRectF &rect) : QSvgEllipse(parent, rect) { } + Type type() const override; +}; + +class Q_SVG_PRIVATE_EXPORT QSvgImage : public QSvgNode +{ +public: + QSvgImage(QSvgNode *parent, const QImage &image, + const QRectF &bounds); + void draw(QPainter *p, QSvgExtraStates &states) override; + Type type() const override; + QRectF bounds(QPainter *p, QSvgExtraStates &states) const override; +private: + QImage m_image; + QRectF m_bounds; +}; + +class Q_SVG_PRIVATE_EXPORT QSvgLine : public QSvgNode +{ +public: + QSvgLine(QSvgNode *parent, const QLineF &line); + void draw(QPainter *p, QSvgExtraStates &states) override; + Type type() const override; + QRectF bounds(QPainter *p, QSvgExtraStates &states) const override; +private: + QLineF m_line; +}; + +class Q_SVG_PRIVATE_EXPORT QSvgPath : public QSvgNode +{ +public: + QSvgPath(QSvgNode *parent, const QPainterPath &qpath); + void draw(QPainter *p, QSvgExtraStates &states) override; + Type type() const override; + QRectF bounds(QPainter *p, QSvgExtraStates &states) const override; + + QPainterPath *qpath() { + return &m_path; + } +private: + QPainterPath m_path; +}; + +class Q_SVG_PRIVATE_EXPORT QSvgPolygon : public QSvgNode +{ +public: + QSvgPolygon(QSvgNode *parent, const QPolygonF &poly); + void draw(QPainter *p, QSvgExtraStates &states) override; + Type type() const override; + QRectF bounds(QPainter *p, QSvgExtraStates &states) const override; +private: + QPolygonF m_poly; +}; + +class Q_SVG_PRIVATE_EXPORT QSvgPolyline : public QSvgNode +{ +public: + QSvgPolyline(QSvgNode *parent, const QPolygonF &poly); + void draw(QPainter *p, QSvgExtraStates &states) override; + Type type() const override; + QRectF bounds(QPainter *p, QSvgExtraStates &states) const override; +private: + QPolygonF m_poly; +}; + +class Q_SVG_PRIVATE_EXPORT QSvgRect : public QSvgNode +{ +public: + QSvgRect(QSvgNode *paren, const QRectF &rect, int rx=0, int ry=0); + Type type() const override; + void draw(QPainter *p, QSvgExtraStates &states) override; + QRectF bounds(QPainter *p, QSvgExtraStates &states) const override; + void SetRect(QRectF rect); + QRectF &rect(); +private: + QRectF m_rect; + int m_rx, m_ry; +}; + +class QSvgTspan; + +class Q_SVG_PRIVATE_EXPORT QSvgText : public QSvgNode +{ +public: + enum WhitespaceMode + { + Default, + Preserve + }; + + QSvgText(QSvgNode *parent, const QPointF &coord); + ~QSvgText(); + void setTextArea(const QSizeF &size); + + void draw(QPainter *p, QSvgExtraStates &states) override; + Type type() const override; + + void addTspan(QSvgTspan *tspan) {m_tspans.append(tspan);} + void addText(const QString &text); + void addLineBreak() {m_tspans.append(LINEBREAK);} + void setWhitespaceMode(WhitespaceMode mode) {m_mode = mode;} + + //QRectF bounds(QPainter *p, QSvgExtraStates &states) const override; +private: + static QSvgTspan * const LINEBREAK; + + QPointF m_coord; + + // 'm_tspans' is also used to store characters outside tspans and line breaks. + // If a 'm_tspan' item is null, it indicates a line break. + QVector m_tspans; + + Type m_type; + QSizeF m_size; + WhitespaceMode m_mode; +public: + void draw(QPainter *p, QSvgExtraStates &states,QRectF bounds,QTransform &oldTransform,QRectF &sourceRect,QMatrix &b); + QVector&Tspans() + { + return m_tspans; + } +}; + +class Q_SVG_PRIVATE_EXPORT QSvgTspan : public QSvgNode +{ +public: + // tspans are also used to store normal text, so the 'isProperTspan' is used to separate text from tspan. + QSvgTspan(QSvgNode *parent, bool isProperTspan = true) + : QSvgNode(parent), m_mode(QSvgText::Default), m_isTspan(isProperTspan) + { + } + ~QSvgTspan() { }; + Type type() const override { return TSPAN; } + void draw(QPainter *, QSvgExtraStates &) override { Q_ASSERT(!"Tspans should be drawn through QSvgText::draw()."); } + void addText(const QString &text) {m_text += text;} + const QString &text() const {return m_text;} + bool isTspan() const {return m_isTspan;} + void setWhitespaceMode(QSvgText::WhitespaceMode mode) {m_mode = mode;} + QSvgText::WhitespaceMode whitespaceMode() const {return m_mode;} +private: + QString m_text; + QSvgText::WhitespaceMode m_mode; + bool m_isTspan; +}; + +class QSvgUse : public QSvgNode +{ +public: + QSvgUse(const QPointF &start, QSvgNode *parent, QSvgNode *link); + QSvgUse(const QPointF &start, QSvgNode *parent, const QString &linkId) + : QSvgUse(start, parent, nullptr) + { m_linkId = linkId; } + void draw(QPainter *p, QSvgExtraStates &states) override; + Type type() const override; + QRectF bounds(QPainter *p, QSvgExtraStates &states) const override; + bool isResolved() const { return m_link != nullptr; } + QString linkId() const { return m_linkId; } + void setLink(QSvgNode *link) { m_link = link; } + +private: + QSvgNode *m_link; + QPointF m_start; + QString m_linkId; + mutable bool m_recursing; +}; + +class QSvgVideo : public QSvgNode +{ +public: + void draw(QPainter *p, QSvgExtraStates &states) override; + Type type() const override; +}; + +QT_END_NAMESPACE + +#endif // QSVGGRAPHICS_P_H diff --git a/svg/qsvghandler.cpp b/svg/qsvghandler.cpp new file mode 100644 index 0000000..5f1c125 --- /dev/null +++ b/svg/qsvghandler.cpp @@ -0,0 +1,4120 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt SVG module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplatformdefs.h" + +#include "qsvghandler_p.h" + +#include "qsvgtinydocument_p.h" +#include "qsvgstructure_p.h" +#include "qsvggraphics_p.h" +#include "qsvgnode_p.h" +#include "qsvgfont_p.h" + +#include "qpen.h" +#include "qpainterpath.h" +#include "qbrush.h" +#include "qcolor.h" +#include "qtextformat.h" +#include "qvector.h" +#include "qfileinfo.h" +#include "qfile.h" +#include "qdir.h" +#include "qdebug.h" +#include "qmath.h" +#include "qnumeric.h" +#include +#include "qvarlengtharray.h" +#include "private/qmath_p.h" + +#include "float.h" +#include "qsharedmemory.h" + +QT_BEGIN_NAMESPACE + +Q_LOGGING_CATEGORY(lcSvgHandler, "qt.svg") + +static const char *qt_inherit_text = "inherit"; +#define QT_INHERIT QLatin1String(qt_inherit_text) + +static QByteArray prefixMessage(const QByteArray &msg, const QXmlStreamReader *r) +{ + QByteArray result; + if (r) { + if (const QFile *file = qobject_cast(r->device())) + result.append(QFile::encodeName(QDir::toNativeSeparators(file->fileName()))); + else + result.append(QByteArrayLiteral("