[2] | 1 | /****************************************************************************
|
---|
| 2 | **
|
---|
[846] | 3 | ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
---|
[561] | 4 | ** All rights reserved.
|
---|
| 5 | ** Contact: Nokia Corporation ([email protected])
|
---|
[2] | 6 | **
|
---|
| 7 | ** This file is part of the QtSvg module of the Qt Toolkit.
|
---|
| 8 | **
|
---|
| 9 | ** $QT_BEGIN_LICENSE:LGPL$
|
---|
| 10 | ** Commercial Usage
|
---|
| 11 | ** Licensees holding valid Qt Commercial licenses may use this file in
|
---|
| 12 | ** accordance with the Qt Commercial License Agreement provided with the
|
---|
| 13 | ** Software or, alternatively, in accordance with the terms contained in
|
---|
| 14 | ** a written agreement between you and Nokia.
|
---|
| 15 | **
|
---|
| 16 | ** GNU Lesser General Public License Usage
|
---|
| 17 | ** Alternatively, this file may be used under the terms of the GNU Lesser
|
---|
| 18 | ** General Public License version 2.1 as published by the Free Software
|
---|
| 19 | ** Foundation and appearing in the file LICENSE.LGPL included in the
|
---|
| 20 | ** packaging of this file. Please review the following information to
|
---|
| 21 | ** ensure the GNU Lesser General Public License version 2.1 requirements
|
---|
| 22 | ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
---|
| 23 | **
|
---|
[561] | 24 | ** In addition, as a special exception, Nokia gives you certain additional
|
---|
| 25 | ** rights. These rights are described in the Nokia Qt LGPL Exception
|
---|
| 26 | ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
---|
[2] | 27 | **
|
---|
| 28 | ** GNU General Public License Usage
|
---|
| 29 | ** Alternatively, this file may be used under the terms of the GNU
|
---|
| 30 | ** General Public License version 3.0 as published by the Free Software
|
---|
| 31 | ** Foundation and appearing in the file LICENSE.GPL included in the
|
---|
| 32 | ** packaging of this file. Please review the following information to
|
---|
| 33 | ** ensure the GNU General Public License version 3.0 requirements will be
|
---|
| 34 | ** met: http://www.gnu.org/copyleft/gpl.html.
|
---|
| 35 | **
|
---|
[561] | 36 | ** If you have questions regarding the use of this file, please contact
|
---|
| 37 | ** Nokia at [email protected].
|
---|
[2] | 38 | ** $QT_END_LICENSE$
|
---|
| 39 | **
|
---|
| 40 | ****************************************************************************/
|
---|
| 41 |
|
---|
| 42 | #include "qsvggraphics_p.h"
|
---|
| 43 |
|
---|
| 44 | #ifndef QT_NO_SVG
|
---|
| 45 |
|
---|
| 46 | #include "qsvgfont_p.h"
|
---|
| 47 |
|
---|
| 48 | #include "qpainter.h"
|
---|
| 49 | #include "qtextdocument.h"
|
---|
| 50 | #include "qabstracttextdocumentlayout.h"
|
---|
| 51 | #include "qtextcursor.h"
|
---|
| 52 | #include "qdebug.h"
|
---|
| 53 |
|
---|
| 54 | #include <math.h>
|
---|
| 55 | #include <limits.h>
|
---|
| 56 |
|
---|
| 57 | QT_BEGIN_NAMESPACE
|
---|
| 58 |
|
---|
[561] | 59 | #define QT_SVG_DRAW_SHAPE(command) \
|
---|
| 60 | qreal oldOpacity = p->opacity(); \
|
---|
| 61 | QBrush oldBrush = p->brush(); \
|
---|
| 62 | QPen oldPen = p->pen(); \
|
---|
| 63 | p->setPen(Qt::NoPen); \
|
---|
| 64 | p->setOpacity(oldOpacity * states.fillOpacity); \
|
---|
| 65 | command; \
|
---|
| 66 | p->setPen(oldPen); \
|
---|
| 67 | if (oldPen.widthF() != 0) { \
|
---|
| 68 | p->setOpacity(oldOpacity * states.strokeOpacity); \
|
---|
| 69 | p->setBrush(Qt::NoBrush); \
|
---|
| 70 | command; \
|
---|
| 71 | p->setBrush(oldBrush); \
|
---|
| 72 | } \
|
---|
| 73 | p->setOpacity(oldOpacity);
|
---|
[2] | 74 |
|
---|
| 75 |
|
---|
| 76 | void QSvgAnimation::draw(QPainter *, QSvgExtraStates &)
|
---|
| 77 | {
|
---|
| 78 | qWarning("<animation> no implemented");
|
---|
| 79 | }
|
---|
| 80 |
|
---|
[846] | 81 | static inline QRectF boundsOnStroke(QPainter *p, const QPainterPath &path, qreal width)
|
---|
[2] | 82 | {
|
---|
| 83 | QPainterPathStroker stroker;
|
---|
| 84 | stroker.setWidth(width);
|
---|
| 85 | QPainterPath stroke = stroker.createStroke(path);
|
---|
[846] | 86 | return p->transform().map(stroke).boundingRect();
|
---|
[2] | 87 | }
|
---|
| 88 |
|
---|
[846] | 89 | QSvgEllipse::QSvgEllipse(QSvgNode *parent, const QRectF &rect)
|
---|
[2] | 90 | : QSvgNode(parent), m_bounds(rect)
|
---|
| 91 | {
|
---|
| 92 | }
|
---|
| 93 |
|
---|
| 94 |
|
---|
[846] | 95 | QRectF QSvgEllipse::bounds(QPainter *p, QSvgExtraStates &) const
|
---|
[2] | 96 | {
|
---|
[846] | 97 | QPainterPath path;
|
---|
| 98 | path.addEllipse(m_bounds);
|
---|
| 99 | qreal sw = strokeWidth(p);
|
---|
| 100 | return qFuzzyIsNull(sw) ? p->transform().map(path).boundingRect() : boundsOnStroke(p, path, sw);
|
---|
[2] | 101 | }
|
---|
| 102 |
|
---|
[846] | 103 | void QSvgEllipse::draw(QPainter *p, QSvgExtraStates &states)
|
---|
[2] | 104 | {
|
---|
[561] | 105 | applyStyle(p, states);
|
---|
[2] | 106 | QT_SVG_DRAW_SHAPE(p->drawEllipse(m_bounds));
|
---|
[561] | 107 | revertStyle(p, states);
|
---|
[2] | 108 | }
|
---|
| 109 |
|
---|
| 110 | QSvgArc::QSvgArc(QSvgNode *parent, const QPainterPath &path)
|
---|
[846] | 111 | : QSvgNode(parent), m_path(path)
|
---|
[2] | 112 | {
|
---|
| 113 | }
|
---|
| 114 |
|
---|
| 115 | void QSvgArc::draw(QPainter *p, QSvgExtraStates &states)
|
---|
| 116 | {
|
---|
| 117 | applyStyle(p, states);
|
---|
[561] | 118 | if (p->pen().widthF() != 0) {
|
---|
| 119 | qreal oldOpacity = p->opacity();
|
---|
| 120 | p->setOpacity(oldOpacity * states.strokeOpacity);
|
---|
[846] | 121 | p->drawPath(m_path);
|
---|
[561] | 122 | p->setOpacity(oldOpacity);
|
---|
| 123 | }
|
---|
[2] | 124 | revertStyle(p, states);
|
---|
| 125 | }
|
---|
| 126 |
|
---|
| 127 | QSvgImage::QSvgImage(QSvgNode *parent, const QImage &image,
|
---|
| 128 | const QRect &bounds)
|
---|
| 129 | : QSvgNode(parent), m_image(image),
|
---|
| 130 | m_bounds(bounds)
|
---|
| 131 | {
|
---|
| 132 | if (m_bounds.width() == 0)
|
---|
| 133 | m_bounds.setWidth(m_image.width());
|
---|
| 134 | if (m_bounds.height() == 0)
|
---|
| 135 | m_bounds.setHeight(m_image.height());
|
---|
| 136 | }
|
---|
| 137 |
|
---|
| 138 | void QSvgImage::draw(QPainter *p, QSvgExtraStates &states)
|
---|
| 139 | {
|
---|
| 140 | applyStyle(p, states);
|
---|
| 141 | p->drawImage(m_bounds, m_image);
|
---|
| 142 | revertStyle(p, states);
|
---|
| 143 | }
|
---|
| 144 |
|
---|
| 145 |
|
---|
| 146 | QSvgLine::QSvgLine(QSvgNode *parent, const QLineF &line)
|
---|
[846] | 147 | : QSvgNode(parent), m_line(line)
|
---|
[2] | 148 | {
|
---|
| 149 | }
|
---|
| 150 |
|
---|
| 151 |
|
---|
| 152 | void QSvgLine::draw(QPainter *p, QSvgExtraStates &states)
|
---|
| 153 | {
|
---|
| 154 | applyStyle(p, states);
|
---|
[561] | 155 | if (p->pen().widthF() != 0) {
|
---|
| 156 | qreal oldOpacity = p->opacity();
|
---|
| 157 | p->setOpacity(oldOpacity * states.strokeOpacity);
|
---|
[846] | 158 | p->drawLine(m_line);
|
---|
[561] | 159 | p->setOpacity(oldOpacity);
|
---|
| 160 | }
|
---|
[2] | 161 | revertStyle(p, states);
|
---|
| 162 | }
|
---|
| 163 |
|
---|
| 164 | QSvgPath::QSvgPath(QSvgNode *parent, const QPainterPath &qpath)
|
---|
| 165 | : QSvgNode(parent), m_path(qpath)
|
---|
| 166 | {
|
---|
| 167 | }
|
---|
| 168 |
|
---|
| 169 | void QSvgPath::draw(QPainter *p, QSvgExtraStates &states)
|
---|
| 170 | {
|
---|
[561] | 171 | applyStyle(p, states);
|
---|
| 172 | m_path.setFillRule(states.fillRule);
|
---|
[2] | 173 | QT_SVG_DRAW_SHAPE(p->drawPath(m_path));
|
---|
[561] | 174 | revertStyle(p, states);
|
---|
[2] | 175 | }
|
---|
| 176 |
|
---|
[846] | 177 | QRectF QSvgPath::bounds(QPainter *p, QSvgExtraStates &) const
|
---|
[2] | 178 | {
|
---|
[846] | 179 | qreal sw = strokeWidth(p);
|
---|
| 180 | return qFuzzyIsNull(sw) ? p->transform().map(m_path).boundingRect()
|
---|
| 181 | : boundsOnStroke(p, m_path, sw);
|
---|
[2] | 182 | }
|
---|
| 183 |
|
---|
| 184 | QSvgPolygon::QSvgPolygon(QSvgNode *parent, const QPolygonF &poly)
|
---|
| 185 | : QSvgNode(parent), m_poly(poly)
|
---|
| 186 | {
|
---|
| 187 | }
|
---|
| 188 |
|
---|
[846] | 189 | QRectF QSvgPolygon::bounds(QPainter *p, QSvgExtraStates &) const
|
---|
[2] | 190 | {
|
---|
[846] | 191 | qreal sw = strokeWidth(p);
|
---|
| 192 | if (qFuzzyIsNull(sw)) {
|
---|
| 193 | return p->transform().map(m_poly).boundingRect();
|
---|
| 194 | } else {
|
---|
[2] | 195 | QPainterPath path;
|
---|
| 196 | path.addPolygon(m_poly);
|
---|
[846] | 197 | return boundsOnStroke(p, path, sw);
|
---|
[2] | 198 | }
|
---|
| 199 | }
|
---|
| 200 |
|
---|
| 201 | void QSvgPolygon::draw(QPainter *p, QSvgExtraStates &states)
|
---|
| 202 | {
|
---|
[561] | 203 | applyStyle(p, states);
|
---|
| 204 | QT_SVG_DRAW_SHAPE(p->drawPolygon(m_poly, states.fillRule));
|
---|
| 205 | revertStyle(p, states);
|
---|
[2] | 206 | }
|
---|
| 207 |
|
---|
| 208 |
|
---|
| 209 | QSvgPolyline::QSvgPolyline(QSvgNode *parent, const QPolygonF &poly)
|
---|
| 210 | : QSvgNode(parent), m_poly(poly)
|
---|
| 211 | {
|
---|
| 212 |
|
---|
| 213 | }
|
---|
| 214 |
|
---|
| 215 | void QSvgPolyline::draw(QPainter *p, QSvgExtraStates &states)
|
---|
| 216 | {
|
---|
| 217 | applyStyle(p, states);
|
---|
[561] | 218 | qreal oldOpacity = p->opacity();
|
---|
[2] | 219 | if (p->brush().style() != Qt::NoBrush) {
|
---|
| 220 | QPen save = p->pen();
|
---|
| 221 | p->setPen(QPen(Qt::NoPen));
|
---|
[561] | 222 | p->setOpacity(oldOpacity * states.fillOpacity);
|
---|
| 223 | p->drawPolygon(m_poly, states.fillRule);
|
---|
[2] | 224 | p->setPen(save);
|
---|
| 225 | }
|
---|
[561] | 226 | if (p->pen().widthF() != 0) {
|
---|
| 227 | p->setOpacity(oldOpacity * states.strokeOpacity);
|
---|
| 228 | p->drawPolyline(m_poly);
|
---|
| 229 | }
|
---|
| 230 | p->setOpacity(oldOpacity);
|
---|
[2] | 231 | revertStyle(p, states);
|
---|
| 232 | }
|
---|
| 233 |
|
---|
| 234 | QSvgRect::QSvgRect(QSvgNode *node, const QRectF &rect, int rx, int ry)
|
---|
| 235 | : QSvgNode(node),
|
---|
| 236 | m_rect(rect), m_rx(rx), m_ry(ry)
|
---|
| 237 | {
|
---|
| 238 | }
|
---|
| 239 |
|
---|
[846] | 240 | QRectF QSvgRect::bounds(QPainter *p, QSvgExtraStates &) const
|
---|
[2] | 241 | {
|
---|
[846] | 242 | qreal sw = strokeWidth(p);
|
---|
| 243 | if (qFuzzyIsNull(sw)) {
|
---|
| 244 | return p->transform().mapRect(m_rect);
|
---|
| 245 | } else {
|
---|
[2] | 246 | QPainterPath path;
|
---|
| 247 | path.addRect(m_rect);
|
---|
[846] | 248 | return boundsOnStroke(p, path, sw);
|
---|
[2] | 249 | }
|
---|
| 250 | }
|
---|
| 251 |
|
---|
| 252 | void QSvgRect::draw(QPainter *p, QSvgExtraStates &states)
|
---|
| 253 | {
|
---|
[561] | 254 | applyStyle(p, states);
|
---|
[2] | 255 | if (m_rx || m_ry) {
|
---|
| 256 | QT_SVG_DRAW_SHAPE(p->drawRoundedRect(m_rect, m_rx, m_ry, Qt::RelativeSize));
|
---|
| 257 | } else {
|
---|
| 258 | QT_SVG_DRAW_SHAPE(p->drawRect(m_rect));
|
---|
| 259 | }
|
---|
[561] | 260 | revertStyle(p, states);
|
---|
[2] | 261 | }
|
---|
| 262 |
|
---|
[561] | 263 | QSvgTspan * const QSvgText::LINEBREAK = 0;
|
---|
| 264 |
|
---|
[2] | 265 | QSvgText::QSvgText(QSvgNode *parent, const QPointF &coord)
|
---|
| 266 | : QSvgNode(parent)
|
---|
| 267 | , m_coord(coord)
|
---|
| 268 | , m_type(TEXT)
|
---|
| 269 | , m_size(0, 0)
|
---|
[561] | 270 | , m_mode(Default)
|
---|
[2] | 271 | {
|
---|
| 272 | }
|
---|
| 273 |
|
---|
| 274 | QSvgText::~QSvgText()
|
---|
| 275 | {
|
---|
[561] | 276 | for (int i = 0; i < m_tspans.size(); ++i) {
|
---|
| 277 | if (m_tspans[i] != LINEBREAK)
|
---|
| 278 | delete m_tspans[i];
|
---|
| 279 | }
|
---|
[2] | 280 | }
|
---|
| 281 |
|
---|
| 282 | void QSvgText::setTextArea(const QSizeF &size)
|
---|
| 283 | {
|
---|
| 284 | m_size = size;
|
---|
| 285 | m_type = TEXTAREA;
|
---|
| 286 | }
|
---|
| 287 |
|
---|
[846] | 288 | //QRectF QSvgText::bounds(QPainter *p, QSvgExtraStates &) const {}
|
---|
[2] | 289 |
|
---|
| 290 | void QSvgText::draw(QPainter *p, QSvgExtraStates &states)
|
---|
| 291 | {
|
---|
| 292 | applyStyle(p, states);
|
---|
[561] | 293 | qreal oldOpacity = p->opacity();
|
---|
| 294 | p->setOpacity(oldOpacity * states.fillOpacity);
|
---|
[2] | 295 |
|
---|
[561] | 296 | // Force the font to have a size of 100 pixels to avoid truncation problems
|
---|
| 297 | // when the font is very small.
|
---|
| 298 | qreal scale = 100.0 / p->font().pointSizeF();
|
---|
| 299 | Qt::Alignment alignment = states.textAnchor;
|
---|
[2] | 300 |
|
---|
| 301 | QTransform oldTransform = p->worldTransform();
|
---|
[561] | 302 | p->scale(1 / scale, 1 / scale);
|
---|
[2] | 303 |
|
---|
| 304 | qreal y = 0;
|
---|
| 305 | bool initial = true;
|
---|
[561] | 306 | qreal px = m_coord.x() * scale;
|
---|
| 307 | qreal py = m_coord.y() * scale;
|
---|
| 308 | QSizeF scaledSize = m_size * scale;
|
---|
[2] | 309 |
|
---|
| 310 | if (m_type == TEXTAREA) {
|
---|
[561] | 311 | if (alignment == Qt::AlignHCenter)
|
---|
[2] | 312 | px += scaledSize.width() / 2;
|
---|
[561] | 313 | else if (alignment == Qt::AlignRight)
|
---|
[2] | 314 | px += scaledSize.width();
|
---|
| 315 | }
|
---|
| 316 |
|
---|
| 317 | QRectF bounds;
|
---|
| 318 | if (m_size.height() != 0)
|
---|
[561] | 319 | bounds = QRectF(0, py, 1, scaledSize.height()); // x and width are not used.
|
---|
[2] | 320 |
|
---|
[561] | 321 | bool appendSpace = false;
|
---|
| 322 | QVector<QString> paragraphs;
|
---|
| 323 | QStack<QTextCharFormat> formats;
|
---|
| 324 | QVector<QList<QTextLayout::FormatRange> > formatRanges;
|
---|
| 325 | paragraphs.push_back(QString());
|
---|
| 326 | formatRanges.push_back(QList<QTextLayout::FormatRange>());
|
---|
[2] | 327 |
|
---|
[561] | 328 | for (int i = 0; i < m_tspans.size(); ++i) {
|
---|
| 329 | if (m_tspans[i] == LINEBREAK) {
|
---|
| 330 | if (m_type == TEXTAREA) {
|
---|
| 331 | if (paragraphs.back().isEmpty()) {
|
---|
| 332 | QFont font = p->font();
|
---|
| 333 | font.setPixelSize(font.pointSizeF() * scale);
|
---|
[2] | 334 |
|
---|
[561] | 335 | QTextLayout::FormatRange range;
|
---|
| 336 | range.start = 0;
|
---|
| 337 | range.length = 1;
|
---|
| 338 | range.format.setFont(font);
|
---|
| 339 | formatRanges.back().append(range);
|
---|
[2] | 340 |
|
---|
[561] | 341 | paragraphs.back().append(QLatin1Char(' '));;
|
---|
| 342 | }
|
---|
| 343 | appendSpace = false;
|
---|
| 344 | paragraphs.push_back(QString());
|
---|
| 345 | formatRanges.push_back(QList<QTextLayout::FormatRange>());
|
---|
| 346 | }
|
---|
| 347 | } else {
|
---|
| 348 | WhitespaceMode mode = m_tspans[i]->whitespaceMode();
|
---|
| 349 | m_tspans[i]->applyStyle(p, states);
|
---|
[2] | 350 |
|
---|
[561] | 351 | QFont font = p->font();
|
---|
| 352 | font.setPixelSize(font.pointSizeF() * scale);
|
---|
[2] | 353 |
|
---|
[561] | 354 | QString newText(m_tspans[i]->text());
|
---|
| 355 | newText.replace(QLatin1Char('\t'), QLatin1Char(' '));
|
---|
| 356 | newText.replace(QLatin1Char('\n'), QLatin1Char(' '));
|
---|
[2] | 357 |
|
---|
[561] | 358 | bool prependSpace = !appendSpace && !m_tspans[i]->isTspan() && (mode == Default) && !paragraphs.back().isEmpty() && newText.startsWith(QLatin1Char(' '));
|
---|
| 359 | if (appendSpace || prependSpace)
|
---|
| 360 | paragraphs.back().append(QLatin1Char(' '));
|
---|
[2] | 361 |
|
---|
[561] | 362 | bool appendSpaceNext = (!m_tspans[i]->isTspan() && (mode == Default) && newText.endsWith(QLatin1Char(' ')));
|
---|
[2] | 363 |
|
---|
[561] | 364 | if (mode == Default) {
|
---|
| 365 | newText = newText.simplified();
|
---|
| 366 | if (newText.isEmpty())
|
---|
| 367 | appendSpaceNext = false;
|
---|
| 368 | }
|
---|
[2] | 369 |
|
---|
[561] | 370 | QTextLayout::FormatRange range;
|
---|
| 371 | range.start = paragraphs.back().length();
|
---|
| 372 | range.length = newText.length();
|
---|
| 373 | range.format.setFont(font);
|
---|
| 374 | range.format.setTextOutline(p->pen());
|
---|
| 375 | range.format.setForeground(p->brush());
|
---|
[2] | 376 |
|
---|
[561] | 377 | if (appendSpace) {
|
---|
| 378 | Q_ASSERT(!formatRanges.back().isEmpty());
|
---|
| 379 | ++formatRanges.back().back().length;
|
---|
| 380 | } else if (prependSpace) {
|
---|
| 381 | --range.start;
|
---|
| 382 | ++range.length;
|
---|
| 383 | }
|
---|
| 384 | formatRanges.back().append(range);
|
---|
[2] | 385 |
|
---|
[561] | 386 | appendSpace = appendSpaceNext;
|
---|
| 387 | paragraphs.back() += newText;
|
---|
[2] | 388 |
|
---|
[561] | 389 | m_tspans[i]->revertStyle(p, states);
|
---|
| 390 | }
|
---|
[2] | 391 | }
|
---|
| 392 |
|
---|
[561] | 393 | if (states.svgFont) {
|
---|
| 394 | // SVG fonts not fully supported...
|
---|
| 395 | QString text = paragraphs.front();
|
---|
| 396 | for (int i = 1; i < paragraphs.size(); ++i) {
|
---|
| 397 | text.append(QLatin1Char('\n'));
|
---|
| 398 | text.append(paragraphs[i]);
|
---|
[2] | 399 | }
|
---|
[561] | 400 | states.svgFont->draw(p, m_coord * scale, text, p->font().pointSizeF() * scale, states.textAnchor);
|
---|
| 401 | } else {
|
---|
| 402 | for (int i = 0; i < paragraphs.size(); ++i) {
|
---|
| 403 | QTextLayout tl(paragraphs[i]);
|
---|
| 404 | QTextOption op = tl.textOption();
|
---|
| 405 | op.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
|
---|
| 406 | tl.setTextOption(op);
|
---|
| 407 | tl.setAdditionalFormats(formatRanges[i]);
|
---|
| 408 | tl.beginLayout();
|
---|
[2] | 409 |
|
---|
[561] | 410 | forever {
|
---|
| 411 | QTextLine line = tl.createLine();
|
---|
| 412 | if (!line.isValid())
|
---|
| 413 | break;
|
---|
| 414 | if (m_size.width() != 0)
|
---|
| 415 | line.setLineWidth(scaledSize.width());
|
---|
| 416 | }
|
---|
| 417 | tl.endLayout();
|
---|
[2] | 418 |
|
---|
[561] | 419 | bool endOfBoundsReached = false;
|
---|
| 420 | for (int i = 0; i < tl.lineCount(); ++i) {
|
---|
| 421 | QTextLine line = tl.lineAt(i);
|
---|
[2] | 422 |
|
---|
[561] | 423 | qreal x = 0;
|
---|
| 424 | if (alignment == Qt::AlignHCenter)
|
---|
| 425 | x -= 0.5 * line.naturalTextWidth();
|
---|
| 426 | else if (alignment == Qt::AlignRight)
|
---|
| 427 | x -= line.naturalTextWidth();
|
---|
[2] | 428 |
|
---|
[561] | 429 | if (initial && m_type == TEXT)
|
---|
| 430 | y -= line.ascent();
|
---|
| 431 | initial = false;
|
---|
[2] | 432 |
|
---|
[561] | 433 | line.setPosition(QPointF(x, y));
|
---|
[2] | 434 |
|
---|
[561] | 435 | // Check if the current line fits into the bounding rectangle.
|
---|
| 436 | if ((m_size.width() != 0 && line.naturalTextWidth() > scaledSize.width())
|
---|
| 437 | || (m_size.height() != 0 && y + line.height() > scaledSize.height())) {
|
---|
| 438 | // I need to set the bounds height to 'y-epsilon' to avoid drawing the current
|
---|
| 439 | // line. Since the font is scaled to 100 units, 1 should be a safe epsilon.
|
---|
| 440 | bounds.setHeight(y - 1);
|
---|
| 441 | endOfBoundsReached = true;
|
---|
| 442 | break;
|
---|
| 443 | }
|
---|
[2] | 444 |
|
---|
[561] | 445 | y += 1.1 * line.height();
|
---|
| 446 | }
|
---|
| 447 | tl.draw(p, QPointF(px, py), QVector<QTextLayout::FormatRange>(), bounds);
|
---|
| 448 |
|
---|
| 449 | if (endOfBoundsReached)
|
---|
| 450 | break;
|
---|
| 451 | }
|
---|
| 452 | }
|
---|
| 453 |
|
---|
| 454 | p->setWorldTransform(oldTransform, false);
|
---|
| 455 | p->setOpacity(oldOpacity);
|
---|
| 456 | revertStyle(p, states);
|
---|
[2] | 457 | }
|
---|
| 458 |
|
---|
[561] | 459 | void QSvgText::addText(const QString &text)
|
---|
[2] | 460 | {
|
---|
[561] | 461 | m_tspans.append(new QSvgTspan(this, false));
|
---|
| 462 | m_tspans.back()->setWhitespaceMode(m_mode);
|
---|
| 463 | m_tspans.back()->addText(text);
|
---|
[2] | 464 | }
|
---|
| 465 |
|
---|
| 466 | QSvgUse::QSvgUse(const QPointF &start, QSvgNode *parent, QSvgNode *node)
|
---|
| 467 | : QSvgNode(parent), m_link(node), m_start(start)
|
---|
| 468 | {
|
---|
| 469 |
|
---|
| 470 | }
|
---|
| 471 |
|
---|
| 472 | void QSvgUse::draw(QPainter *p, QSvgExtraStates &states)
|
---|
| 473 | {
|
---|
| 474 | applyStyle(p, states);
|
---|
| 475 |
|
---|
| 476 | if (!m_start.isNull()) {
|
---|
| 477 | p->translate(m_start);
|
---|
| 478 | }
|
---|
| 479 | m_link->draw(p, states);
|
---|
| 480 | if (!m_start.isNull()) {
|
---|
| 481 | p->translate(-m_start);
|
---|
| 482 | }
|
---|
| 483 |
|
---|
| 484 | revertStyle(p, states);
|
---|
| 485 | }
|
---|
| 486 |
|
---|
| 487 | void QSvgVideo::draw(QPainter *p, QSvgExtraStates &states)
|
---|
| 488 | {
|
---|
| 489 | applyStyle(p, states);
|
---|
| 490 |
|
---|
| 491 | revertStyle(p, states);
|
---|
| 492 | }
|
---|
| 493 |
|
---|
| 494 | QSvgNode::Type QSvgAnimation::type() const
|
---|
| 495 | {
|
---|
| 496 | return ANIMATION;
|
---|
| 497 | }
|
---|
| 498 |
|
---|
| 499 | QSvgNode::Type QSvgArc::type() const
|
---|
| 500 | {
|
---|
| 501 | return ARC;
|
---|
| 502 | }
|
---|
| 503 |
|
---|
| 504 | QSvgNode::Type QSvgCircle::type() const
|
---|
| 505 | {
|
---|
| 506 | return CIRCLE;
|
---|
| 507 | }
|
---|
| 508 |
|
---|
| 509 | QSvgNode::Type QSvgEllipse::type() const
|
---|
| 510 | {
|
---|
| 511 | return ELLIPSE;
|
---|
| 512 | }
|
---|
| 513 |
|
---|
| 514 | QSvgNode::Type QSvgImage::type() const
|
---|
| 515 | {
|
---|
| 516 | return IMAGE;
|
---|
| 517 | }
|
---|
| 518 |
|
---|
| 519 | QSvgNode::Type QSvgLine::type() const
|
---|
| 520 | {
|
---|
| 521 | return LINE;
|
---|
| 522 | }
|
---|
| 523 |
|
---|
| 524 | QSvgNode::Type QSvgPath::type() const
|
---|
| 525 | {
|
---|
| 526 | return PATH;
|
---|
| 527 | }
|
---|
| 528 |
|
---|
| 529 | QSvgNode::Type QSvgPolygon::type() const
|
---|
| 530 | {
|
---|
| 531 | return POLYGON;
|
---|
| 532 | }
|
---|
| 533 |
|
---|
| 534 | QSvgNode::Type QSvgPolyline::type() const
|
---|
| 535 | {
|
---|
| 536 | return POLYLINE;
|
---|
| 537 | }
|
---|
| 538 |
|
---|
| 539 | QSvgNode::Type QSvgRect::type() const
|
---|
| 540 | {
|
---|
| 541 | return RECT;
|
---|
| 542 | }
|
---|
| 543 |
|
---|
| 544 | QSvgNode::Type QSvgText::type() const
|
---|
| 545 | {
|
---|
| 546 | return m_type;
|
---|
| 547 | }
|
---|
| 548 |
|
---|
| 549 | QSvgNode::Type QSvgUse::type() const
|
---|
| 550 | {
|
---|
| 551 | return USE;
|
---|
| 552 | }
|
---|
| 553 |
|
---|
| 554 | QSvgNode::Type QSvgVideo::type() const
|
---|
| 555 | {
|
---|
| 556 | return VIDEO;
|
---|
| 557 | }
|
---|
| 558 |
|
---|
[846] | 559 | QRectF QSvgUse::bounds(QPainter *p, QSvgExtraStates &states) const
|
---|
[2] | 560 | {
|
---|
| 561 | QRectF bounds;
|
---|
[846] | 562 | if (m_link) {
|
---|
| 563 | p->translate(m_start);
|
---|
| 564 | bounds = m_link->transformedBounds(p, states);
|
---|
| 565 | p->translate(-m_start);
|
---|
[2] | 566 | }
|
---|
| 567 | return bounds;
|
---|
| 568 | }
|
---|
| 569 |
|
---|
[846] | 570 | QRectF QSvgPolyline::bounds(QPainter *p, QSvgExtraStates &) const
|
---|
[2] | 571 | {
|
---|
[846] | 572 | qreal sw = strokeWidth(p);
|
---|
| 573 | if (qFuzzyIsNull(sw)) {
|
---|
| 574 | return p->transform().map(m_poly).boundingRect();
|
---|
| 575 | } else {
|
---|
[2] | 576 | QPainterPath path;
|
---|
| 577 | path.addPolygon(m_poly);
|
---|
[846] | 578 | return boundsOnStroke(p, path, sw);
|
---|
[2] | 579 | }
|
---|
| 580 | }
|
---|
| 581 |
|
---|
[846] | 582 | QRectF QSvgArc::bounds(QPainter *p, QSvgExtraStates &) const
|
---|
[2] | 583 | {
|
---|
[846] | 584 | qreal sw = strokeWidth(p);
|
---|
| 585 | return qFuzzyIsNull(sw) ? p->transform().map(m_path).boundingRect()
|
---|
| 586 | : boundsOnStroke(p, m_path, sw);
|
---|
[2] | 587 | }
|
---|
| 588 |
|
---|
[846] | 589 | QRectF QSvgImage::bounds(QPainter *p, QSvgExtraStates &) const
|
---|
[2] | 590 | {
|
---|
[846] | 591 | return p->transform().mapRect(m_bounds);
|
---|
[2] | 592 | }
|
---|
| 593 |
|
---|
[846] | 594 | QRectF QSvgLine::bounds(QPainter *p, QSvgExtraStates &) const
|
---|
[2] | 595 | {
|
---|
[846] | 596 | qreal sw = strokeWidth(p);
|
---|
[561] | 597 | if (qFuzzyIsNull(sw)) {
|
---|
[846] | 598 | QPointF p1 = p->transform().map(m_line.p1());
|
---|
| 599 | QPointF p2 = p->transform().map(m_line.p2());
|
---|
| 600 | qreal minX = qMin(p1.x(), p2.x());
|
---|
| 601 | qreal minY = qMin(p1.y(), p2.y());
|
---|
| 602 | qreal maxX = qMax(p1.x(), p2.x());
|
---|
| 603 | qreal maxY = qMax(p1.y(), p2.y());
|
---|
| 604 | return QRectF(minX, minY, maxX - minX, maxY - minY);
|
---|
[2] | 605 | } else {
|
---|
| 606 | QPainterPath path;
|
---|
[846] | 607 | path.moveTo(m_line.p1());
|
---|
| 608 | path.lineTo(m_line.p2());
|
---|
| 609 | return boundsOnStroke(p, path, sw);
|
---|
[2] | 610 | }
|
---|
| 611 | }
|
---|
| 612 |
|
---|
| 613 | QT_END_NAMESPACE
|
---|
| 614 |
|
---|
| 615 | #endif // QT_NO_SVG
|
---|