Fixes a crash in QDoubleSpinBox
[qt-netbsd.git] / src / svg / qsvggraphics.cpp
bloba6606240a6be8541f561db58f882c3440b062269
1 /****************************************************************************
2 **
3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
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.
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.
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.
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.
36 ** If you have questions regarding the use of this file, please contact
37 ** Nokia at qt-info@nokia.com.
38 ** $QT_END_LICENSE$
40 ****************************************************************************/
42 #include "qsvggraphics_p.h"
44 #ifndef QT_NO_SVG
46 #include "qsvgfont_p.h"
48 #include "qpainter.h"
49 #include "qtextdocument.h"
50 #include "qabstracttextdocumentlayout.h"
51 #include "qtextcursor.h"
52 #include "qdebug.h"
54 #include <math.h>
55 #include <limits.h>
57 QT_BEGIN_NAMESPACE
59 #define QT_SVG_DRAW_SHAPE(command) \
60 applyStyle(p, states); \
61 qreal oldOpacity = p->opacity(); \
62 QBrush oldBrush = p->brush(); \
63 QPen oldPen = p->pen(); \
64 p->setPen(Qt::NoPen); \
65 p->setOpacity(oldOpacity * states.fillOpacity); \
66 command; \
67 p->setOpacity(oldOpacity); \
68 p->setPen(oldPen); \
69 p->setBrush(Qt::NoBrush); \
70 command; \
71 p->setBrush(oldBrush); \
72 revertStyle(p, states);
75 void QSvgAnimation::draw(QPainter *, QSvgExtraStates &)
77 qWarning("<animation> no implemented");
80 static inline QRectF boundsOnStroke(const QPainterPath &path, qreal width)
82 QPainterPathStroker stroker;
83 stroker.setWidth(width);
84 QPainterPath stroke = stroker.createStroke(path);
85 return stroke.boundingRect();
88 QSvgCircle::QSvgCircle(QSvgNode *parent, const QRectF &rect)
89 : QSvgNode(parent), m_bounds(rect)
94 QRectF QSvgCircle::bounds() const
96 qreal sw = strokeWidth();
97 if (qFuzzyCompare(sw + 1, 1))
98 return m_bounds;
99 else {
100 QPainterPath path;
101 path.addRect(m_bounds);
102 return boundsOnStroke(path, sw);
106 void QSvgCircle::draw(QPainter *p, QSvgExtraStates &states)
108 QT_SVG_DRAW_SHAPE(p->drawEllipse(m_bounds));
111 QSvgArc::QSvgArc(QSvgNode *parent, const QPainterPath &path)
112 : QSvgNode(parent), cubic(path)
114 m_cachedBounds = path.boundingRect();
117 void QSvgArc::draw(QPainter *p, QSvgExtraStates &states)
119 applyStyle(p, states);
120 p->drawPath(cubic);
121 revertStyle(p, states);
124 QSvgEllipse::QSvgEllipse(QSvgNode *parent, const QRectF &rect)
125 : QSvgNode(parent), m_bounds(rect)
129 QRectF QSvgEllipse::bounds() const
131 qreal sw = strokeWidth();
132 if (qFuzzyCompare(sw + 1, 1))
133 return m_bounds;
134 else {
135 QPainterPath path;
136 path.addEllipse(m_bounds);
137 return boundsOnStroke(path, sw);
141 void QSvgEllipse::draw(QPainter *p, QSvgExtraStates &states)
143 QT_SVG_DRAW_SHAPE(p->drawEllipse(m_bounds));
146 QSvgImage::QSvgImage(QSvgNode *parent, const QImage &image,
147 const QRect &bounds)
148 : QSvgNode(parent), m_image(image),
149 m_bounds(bounds)
151 if (m_bounds.width() == 0)
152 m_bounds.setWidth(m_image.width());
153 if (m_bounds.height() == 0)
154 m_bounds.setHeight(m_image.height());
157 void QSvgImage::draw(QPainter *p, QSvgExtraStates &states)
159 applyStyle(p, states);
160 p->drawImage(m_bounds, m_image);
161 revertStyle(p, states);
165 QSvgLine::QSvgLine(QSvgNode *parent, const QLineF &line)
166 : QSvgNode(parent), m_bounds(line)
171 void QSvgLine::draw(QPainter *p, QSvgExtraStates &states)
173 applyStyle(p, states);
174 p->drawLine(m_bounds);
175 revertStyle(p, states);
178 QSvgPath::QSvgPath(QSvgNode *parent, const QPainterPath &qpath)
179 : QSvgNode(parent), m_path(qpath)
181 //m_cachedBounds = m_path.controlPointRect();
182 m_cachedBounds = m_path.boundingRect();
185 void QSvgPath::draw(QPainter *p, QSvgExtraStates &states)
187 QT_SVG_DRAW_SHAPE(p->drawPath(m_path));
190 QRectF QSvgPath::bounds() const
192 qreal sw = strokeWidth();
193 if (qFuzzyCompare(sw + 1, 1))
194 return m_cachedBounds;
195 else {
196 return boundsOnStroke(m_path, sw);
200 QSvgPolygon::QSvgPolygon(QSvgNode *parent, const QPolygonF &poly)
201 : QSvgNode(parent), m_poly(poly)
206 QRectF QSvgPolygon::bounds() const
208 qreal sw = strokeWidth();
209 if (qFuzzyCompare(sw + 1, 1))
210 return m_poly.boundingRect();
211 else {
212 QPainterPath path;
213 path.addPolygon(m_poly);
214 return boundsOnStroke(path, sw);
218 void QSvgPolygon::draw(QPainter *p, QSvgExtraStates &states)
220 QT_SVG_DRAW_SHAPE(p->drawPolygon(m_poly));
224 QSvgPolyline::QSvgPolyline(QSvgNode *parent, const QPolygonF &poly)
225 : QSvgNode(parent), m_poly(poly)
230 void QSvgPolyline::draw(QPainter *p, QSvgExtraStates &states)
232 applyStyle(p, states);
233 if (p->brush().style() != Qt::NoBrush) {
234 QPen save = p->pen();
235 p->setPen(QPen(Qt::NoPen));
236 p->drawPolygon(m_poly);
237 p->setPen(save);
239 p->drawPolyline(m_poly);
240 revertStyle(p, states);
243 QSvgRect::QSvgRect(QSvgNode *node, const QRectF &rect, int rx, int ry)
244 : QSvgNode(node),
245 m_rect(rect), m_rx(rx), m_ry(ry)
249 QRectF QSvgRect::bounds() const
251 qreal sw = strokeWidth();
252 if (qFuzzyCompare(sw + 1, 1))
253 return m_rect;
254 else {
255 QPainterPath path;
256 path.addRect(m_rect);
257 return boundsOnStroke(path, sw);
261 void QSvgRect::draw(QPainter *p, QSvgExtraStates &states)
263 if (m_rx || m_ry) {
264 QT_SVG_DRAW_SHAPE(p->drawRoundedRect(m_rect, m_rx, m_ry, Qt::RelativeSize));
265 } else {
266 QT_SVG_DRAW_SHAPE(p->drawRect(m_rect));
270 QSvgText::QSvgText(QSvgNode *parent, const QPointF &coord)
271 : QSvgNode(parent)
272 , m_coord(coord)
273 , m_textAlignment(Qt::AlignLeft)
274 , m_scale(1)
275 , m_appendSpace(false)
276 , m_type(TEXT)
277 , m_size(0, 0)
279 m_paragraphs.push_back(QString());
280 m_formatRanges.push_back(QList<QTextLayout::FormatRange>());
283 QSvgText::~QSvgText()
287 void QSvgText::setTextArea(const QSizeF &size)
289 m_size = size;
290 m_type = TEXTAREA;
293 //QRectF QSvgText::bounds() const {}
295 void QSvgText::draw(QPainter *p, QSvgExtraStates &states)
297 applyStyle(p, states);
299 QSvgFontStyle *fontStyle = static_cast<QSvgFontStyle*>(
300 styleProperty(QSvgStyleProperty::FONT));
301 if (fontStyle && fontStyle->svgFont()) {
302 // SVG fonts not fully supported...
303 QString text = m_paragraphs.front();
304 for (int i = 1; i < m_paragraphs.size(); ++i) {
305 text.append(QLatin1Char('\n'));
306 text.append(m_paragraphs[i]);
308 fontStyle->svgFont()->draw(p, m_coord, text, fontStyle->pointSize(), m_textAlignment);
309 revertStyle(p, states);
310 return;
313 // Scale the font to its correct size.
314 QTransform oldTransform = p->worldTransform();
315 p->scale(1 / m_scale, 1 / m_scale);
317 qreal y = 0;
318 bool initial = true;
319 qreal px = m_coord.x() * m_scale;
320 qreal py = m_coord.y() * m_scale;
321 QSizeF scaledSize = m_size * m_scale;
323 if (m_type == TEXTAREA) {
324 if (m_textAlignment == Qt::AlignHCenter)
325 px += scaledSize.width() / 2;
326 else if (m_textAlignment == Qt::AlignRight)
327 px += scaledSize.width();
330 QRectF bounds;
331 if (m_size.height() != 0)
332 bounds = QRectF(0, 0, 1, scaledSize.height());
334 for (int i = 0; i < m_paragraphs.size(); ++i) {
335 QTextLayout tl(m_paragraphs[i]);
336 QTextOption op = tl.textOption();
337 op.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
338 tl.setTextOption(op);
339 tl.setAdditionalFormats(m_formatRanges[i]);
340 tl.beginLayout();
341 forever {
342 QTextLine line = tl.createLine();
343 if (!line.isValid())
344 break;
346 if (m_size.width() != 0)
347 line.setLineWidth(scaledSize.width());
349 tl.endLayout();
351 bool endOfBoundsReached = false;
352 for (int i = 0; i < tl.lineCount(); ++i) {
353 QTextLine line = tl.lineAt(i);
355 qreal x = 0;
356 if (m_textAlignment == Qt::AlignHCenter)
357 x -= line.naturalTextWidth() / 2;
358 else if (m_textAlignment == Qt::AlignRight)
359 x -= line.naturalTextWidth();
361 if (initial && m_type == TEXT)
362 y -= line.ascent();
363 initial = false;
365 line.setPosition(QPointF(x, y));
366 if ((m_size.width() != 0 && line.naturalTextWidth() > scaledSize.width())
367 || (m_size.height() != 0 && y + line.height() > scaledSize.height())) {
368 bounds.setHeight(y);
369 endOfBoundsReached = true;
370 break;
373 y += 1.1 * line.height();
375 tl.draw(p, QPointF(px, py), QVector<QTextLayout::FormatRange>(), bounds);
377 if (endOfBoundsReached)
378 break;
381 p->setWorldTransform(oldTransform, false);
382 revertStyle(p, states);
385 void QSvgText::insertText(const QString &text, WhitespaceMode mode)
387 bool isTSpan = (m_formats.count() == 2);
388 QString newText(text);
389 newText.replace(QLatin1Char('\t'), QLatin1Char(' '));
390 newText.replace(QLatin1Char('\n'), QLatin1Char(' '));
392 bool prependSpace = !m_appendSpace && !isTSpan && (mode == Default) && !m_paragraphs.back().isEmpty() && newText.startsWith(QLatin1Char(' '));
393 if (m_appendSpace || prependSpace)
394 m_paragraphs.back().append(QLatin1Char(' '));
396 bool appendSpaceNext = (!isTSpan && (mode == Default) && newText.endsWith(QLatin1Char(' ')));
398 if (mode == Default) {
399 newText = newText.simplified();
400 if (newText.isEmpty())
401 appendSpaceNext = false;
404 if (!m_formats.isEmpty()) {
405 QTextLayout::FormatRange range;
406 range.start = m_paragraphs.back().length();
407 range.length = newText.length();
408 range.format = m_formats.top();
409 if (m_appendSpace) {
410 Q_ASSERT(!m_formatRanges.back().isEmpty());
411 ++m_formatRanges.back().back().length;
412 } else if (prependSpace) {
413 --range.start;
414 ++range.length;
416 m_formatRanges.back().append(range);
419 m_appendSpace = appendSpaceNext;
420 m_paragraphs.back() += newText;
423 void QSvgText::insertFormat(const QTextCharFormat &format)
425 QTextCharFormat mergedFormat = format;
426 if (!m_formats.isEmpty()) {
427 mergedFormat = m_formats.top();
428 mergedFormat.merge(format);
430 m_formats.push(mergedFormat);
433 void QSvgText::insertLineBreak()
435 if (m_type == TEXTAREA) {
436 if (m_paragraphs.back().isEmpty())
437 insertText(QLatin1String(" "), Preserve);
438 m_appendSpace = false;
439 m_paragraphs.push_back(QString());
440 m_formatRanges.push_back(QList<QTextLayout::FormatRange>());
444 void QSvgText::popFormat()
446 if (m_formats.count() > 1)
447 m_formats.pop();
450 qreal QSvgText::scale() const
452 return m_scale;
455 void QSvgText::setScale(qreal scale)
457 m_scale = scale;
460 const QTextCharFormat &QSvgText::topFormat() const
462 return m_formats.top();
465 void QSvgText::setTextAlignment(const Qt::Alignment &alignment)
467 m_textAlignment = alignment;
470 QSvgUse::QSvgUse(const QPointF &start, QSvgNode *parent, QSvgNode *node)
471 : QSvgNode(parent), m_link(node), m_start(start)
476 void QSvgUse::draw(QPainter *p, QSvgExtraStates &states)
478 applyStyle(p, states);
480 if (!m_start.isNull()) {
481 p->translate(m_start);
483 m_link->draw(p, states);
484 if (!m_start.isNull()) {
485 p->translate(-m_start);
488 revertStyle(p, states);
491 void QSvgVideo::draw(QPainter *p, QSvgExtraStates &states)
493 applyStyle(p, states);
495 revertStyle(p, states);
498 QSvgNode::Type QSvgAnimation::type() const
500 return ANIMATION;
503 QSvgNode::Type QSvgArc::type() const
505 return ARC;
508 QSvgNode::Type QSvgCircle::type() const
510 return CIRCLE;
513 QSvgNode::Type QSvgEllipse::type() const
515 return ELLIPSE;
518 QSvgNode::Type QSvgImage::type() const
520 return IMAGE;
523 QSvgNode::Type QSvgLine::type() const
525 return LINE;
528 QSvgNode::Type QSvgPath::type() const
530 return PATH;
533 QSvgNode::Type QSvgPolygon::type() const
535 return POLYGON;
538 QSvgNode::Type QSvgPolyline::type() const
540 return POLYLINE;
543 QSvgNode::Type QSvgRect::type() const
545 return RECT;
548 QSvgNode::Type QSvgText::type() const
550 return m_type;
553 QSvgNode::Type QSvgUse::type() const
555 return USE;
558 QSvgNode::Type QSvgVideo::type() const
560 return VIDEO;
563 QRectF QSvgUse::bounds() const
565 if (m_link && m_bounds.isEmpty()) {
566 m_bounds = m_link->bounds();
567 m_bounds = QRectF(m_bounds.x()+m_start.x(),
568 m_bounds.y()+m_start.y(),
569 m_bounds.width(),
570 m_bounds.height());
572 return m_bounds;
574 return m_bounds;
577 QRectF QSvgUse::transformedBounds(const QTransform &transform) const
579 QRectF bounds;
580 QTransform t = transform;
582 if (m_link) {
583 QSvgTransformStyle *transStyle = m_style.transform;
584 if (transStyle) {
585 t = transStyle->qtransform() * t;
587 t.translate(m_start.x(), m_start.y());
589 bounds = m_link->transformedBounds(t);
591 return bounds;
593 return bounds;
596 QRectF QSvgPolyline::bounds() const
598 qreal sw = strokeWidth();
599 if (qFuzzyCompare(sw + 1, 1))
600 return m_poly.boundingRect();
601 else {
602 QPainterPath path;
603 path.addPolygon(m_poly);
604 return boundsOnStroke(path, sw);
608 QRectF QSvgArc::bounds() const
610 qreal sw = strokeWidth();
611 if (qFuzzyCompare(sw + 1, 1))
612 return m_cachedBounds;
613 else {
614 return boundsOnStroke(cubic, sw);
618 QRectF QSvgImage::bounds() const
620 return m_bounds;
623 QRectF QSvgLine::bounds() const
625 qreal sw = strokeWidth();
626 if (qFuzzyCompare(sw + 1, 1)) {
627 qreal minX = qMin(m_bounds.x1(), m_bounds.x2());
628 qreal minY = qMin(m_bounds.y1(), m_bounds.y2());
629 qreal maxX = qMax(m_bounds.x1(), m_bounds.x2());
630 qreal maxY = qMax(m_bounds.y1(), m_bounds.y2());
631 return QRectF(minX, minY, maxX-minX, maxY-minY);
632 } else {
633 QPainterPath path;
634 path.moveTo(m_bounds.x1(), m_bounds.y1());
635 path.lineTo(m_bounds.x2(), m_bounds.y2());
636 return boundsOnStroke(path, sw);
640 QT_END_NAMESPACE
642 #endif // QT_NO_SVG