summaryrefslogtreecommitdiffstats
path: root/src/svg/qsvggraphics.cpp
diff options
context:
space:
mode:
authorKim Motoyoshi Kalland <kim.kalland@nokia.com>2009-07-30 12:33:41 (GMT)
committerKim Motoyoshi Kalland <kim.kalland@nokia.com>2009-07-30 13:46:03 (GMT)
commit54ed2a3db855aab8219f9588e241d3110bdddfb1 (patch)
treecf87f833bcf872f9677fcc0740d78d3e285e64d9 /src/svg/qsvggraphics.cpp
parent5aa46b1052b05d34cbfef175caaf941928520964 (diff)
downloadQt-54ed2a3db855aab8219f9588e241d3110bdddfb1.zip
Qt-54ed2a3db855aab8219f9588e241d3110bdddfb1.tar.gz
Qt-54ed2a3db855aab8219f9588e241d3110bdddfb1.tar.bz2
Fixed font attribute inheritence, text and textArea elements in QtSvg.
Text used to be formatted during the parsing of the SVG file, but because the text can be referenced by a 'use' element, the text formatting is not known at this point in time. Now, font attributes can be inherited from 'use' elements, and the text is formatted each time it is drawn. Reviewed-by: Tor Arne
Diffstat (limited to 'src/svg/qsvggraphics.cpp')
-rw-r--r--src/svg/qsvggraphics.cpp286
1 files changed, 143 insertions, 143 deletions
diff --git a/src/svg/qsvggraphics.cpp b/src/svg/qsvggraphics.cpp
index e09f382..2be7559 100644
--- a/src/svg/qsvggraphics.cpp
+++ b/src/svg/qsvggraphics.cpp
@@ -266,21 +266,23 @@ void QSvgRect::draw(QPainter *p, QSvgExtraStates &states)
}
}
+QSvgTspan * const QSvgText::LINEBREAK = 0;
+
QSvgText::QSvgText(QSvgNode *parent, const QPointF &coord)
: QSvgNode(parent)
, m_coord(coord)
- , m_textAlignment(Qt::AlignLeft)
- , m_scale(1)
- , m_appendSpace(false)
, m_type(TEXT)
, m_size(0, 0)
+ , m_mode(Default)
{
- m_paragraphs.push_back(QString());
- m_formatRanges.push_back(QList<QTextLayout::FormatRange>());
}
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)
@@ -295,175 +297,173 @@ void QSvgText::draw(QPainter *p, QSvgExtraStates &states)
{
applyStyle(p, states);
- QSvgFontStyle *fontStyle = static_cast<QSvgFontStyle*>(
- styleProperty(QSvgStyleProperty::FONT));
- if (fontStyle && fontStyle->svgFont()) {
- // SVG fonts not fully supported...
- QString text = m_paragraphs.front();
- for (int i = 1; i < m_paragraphs.size(); ++i) {
- text.append(QLatin1Char('\n'));
- text.append(m_paragraphs[i]);
- }
- fontStyle->svgFont()->draw(p, m_coord, text, fontStyle->pointSize(), m_textAlignment);
- revertStyle(p, states);
- return;
- }
+ // 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;
- // Scale the font to its correct size.
QTransform oldTransform = p->worldTransform();
- p->scale(1 / m_scale, 1 / m_scale);
+ p->scale(1 / scale, 1 / scale);
qreal y = 0;
bool initial = true;
- qreal px = m_coord.x() * m_scale;
- qreal py = m_coord.y() * m_scale;
- QSizeF scaledSize = m_size * m_scale;
+ qreal px = m_coord.x() * scale;
+ qreal py = m_coord.y() * scale;
+ QSizeF scaledSize = m_size * scale;
if (m_type == TEXTAREA) {
- if (m_textAlignment == Qt::AlignHCenter)
+ if (alignment == Qt::AlignHCenter)
px += scaledSize.width() / 2;
- else if (m_textAlignment == Qt::AlignRight)
+ else if (alignment == Qt::AlignRight)
px += scaledSize.width();
}
QRectF bounds;
if (m_size.height() != 0)
- bounds = QRectF(0, 0, 1, scaledSize.height());
-
- for (int i = 0; i < m_paragraphs.size(); ++i) {
- QTextLayout tl(m_paragraphs[i]);
- QTextOption op = tl.textOption();
- op.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
- tl.setTextOption(op);
- tl.setAdditionalFormats(m_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 (m_textAlignment == Qt::AlignHCenter)
- x -= line.naturalTextWidth() / 2;
- else if (m_textAlignment == Qt::AlignRight)
- x -= line.naturalTextWidth();
-
- if (initial && m_type == TEXT)
- y -= line.ascent();
- initial = false;
-
- line.setPosition(QPointF(x, y));
- if ((m_size.width() != 0 && line.naturalTextWidth() > scaledSize.width())
- || (m_size.height() != 0 && y + line.height() > scaledSize.height())) {
- bounds.setHeight(y);
- endOfBoundsReached = true;
- break;
+ bounds = QRectF(0, py, 1, scaledSize.height()); // x and width are not used.
+
+ bool appendSpace = false;
+ QVector<QString> paragraphs;
+ QStack<QTextCharFormat> formats;
+ QVector<QList<QTextLayout::FormatRange> > formatRanges;
+ paragraphs.push_back(QString());
+ formatRanges.push_back(QList<QTextLayout::FormatRange>());
+
+ 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<QTextLayout::FormatRange>());
}
+ } else {
+ WhitespaceMode mode = m_tspans[i]->whitespaceMode();
+ m_tspans[i]->applyStyle(p, states);
- y += 1.1 * line.height();
- }
- tl.draw(p, QPointF(px, py), QVector<QTextLayout::FormatRange>(), bounds);
+ QFont font = p->font();
+ font.setPixelSize(font.pointSizeF() * scale);
- if (endOfBoundsReached)
- break;
- }
+ QString newText(m_tspans[i]->text());
+ newText.replace(QLatin1Char('\t'), QLatin1Char(' '));
+ newText.replace(QLatin1Char('\n'), QLatin1Char(' '));
- p->setWorldTransform(oldTransform, false);
- revertStyle(p, states);
-}
+ bool prependSpace = !appendSpace && !m_tspans[i]->isTspan() && (mode == Default) && !paragraphs.back().isEmpty() && newText.startsWith(QLatin1Char(' '));
+ if (appendSpace || prependSpace)
+ paragraphs.back().append(QLatin1Char(' '));
-void QSvgText::insertText(const QString &text, WhitespaceMode mode)
-{
- bool isTSpan = (m_formats.count() == 2);
- QString newText(text);
- newText.replace(QLatin1Char('\t'), QLatin1Char(' '));
- newText.replace(QLatin1Char('\n'), QLatin1Char(' '));
+ bool appendSpaceNext = (!m_tspans[i]->isTspan() && (mode == Default) && newText.endsWith(QLatin1Char(' ')));
- bool prependSpace = !m_appendSpace && !isTSpan && (mode == Default) && !m_paragraphs.back().isEmpty() && newText.startsWith(QLatin1Char(' '));
- if (m_appendSpace || prependSpace)
- m_paragraphs.back().append(QLatin1Char(' '));
+ if (mode == Default) {
+ newText = newText.simplified();
+ if (newText.isEmpty())
+ appendSpaceNext = false;
+ }
- bool appendSpaceNext = (!isTSpan && (mode == Default) && newText.endsWith(QLatin1Char(' ')));
+ 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);
- if (mode == Default) {
- newText = newText.simplified();
- if (newText.isEmpty())
- appendSpaceNext = false;
- }
+ appendSpace = appendSpaceNext;
+ paragraphs.back() += newText;
- if (!m_formats.isEmpty()) {
- QTextLayout::FormatRange range;
- range.start = m_paragraphs.back().length();
- range.length = newText.length();
- range.format = m_formats.top();
- if (m_appendSpace) {
- Q_ASSERT(!m_formatRanges.back().isEmpty());
- ++m_formatRanges.back().back().length;
- } else if (prependSpace) {
- --range.start;
- ++range.length;
+ m_tspans[i]->revertStyle(p, states);
}
- m_formatRanges.back().append(range);
}
- m_appendSpace = appendSpaceNext;
- m_paragraphs.back() += newText;
-}
-
-void QSvgText::insertFormat(const QTextCharFormat &format)
-{
- QTextCharFormat mergedFormat = format;
- if (!m_formats.isEmpty()) {
- mergedFormat = m_formats.top();
- mergedFormat.merge(format);
- }
- m_formats.push(mergedFormat);
-}
+ 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, 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.setAdditionalFormats(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<QTextLayout::FormatRange>(), bounds);
-void QSvgText::insertLineBreak()
-{
- if (m_type == TEXTAREA) {
- if (m_paragraphs.back().isEmpty())
- insertText(QLatin1String(" "), Preserve);
- m_appendSpace = false;
- m_paragraphs.push_back(QString());
- m_formatRanges.push_back(QList<QTextLayout::FormatRange>());
+ if (endOfBoundsReached)
+ break;
+ }
}
-}
-void QSvgText::popFormat()
-{
- if (m_formats.count() > 1)
- m_formats.pop();
-}
-
-qreal QSvgText::scale() const
-{
- return m_scale;
-}
-
-void QSvgText::setScale(qreal scale)
-{
- m_scale = scale;
-}
-
-const QTextCharFormat &QSvgText::topFormat() const
-{
- return m_formats.top();
+ p->setWorldTransform(oldTransform, false);
+ revertStyle(p, states);
}
-void QSvgText::setTextAlignment(const Qt::Alignment &alignment)
+void QSvgText::addText(const QString &text)
{
- m_textAlignment = alignment;
+ 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)