File indexing completed on 2024-05-12 16:33:18

0001 /* This file is part of the KDE project
0002  * Copyright (C) 2011 Jan Hambrecht <jaham@gmx.net>
0003  *
0004  * This library is free software; you can redistribute it and/or
0005  * modify it under the terms of the GNU Library General Public
0006  * License as published by the Free Software Foundation; either
0007  * version 2 of the License, or (at your option) any later version.
0008  *
0009  * This library is distributed in the hope that it will be useful,
0010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0012  * Library General Public License for more details.
0013  *
0014  * You should have received a copy of the GNU Library General Public License
0015  * along with this library; see the file COPYING.LIB.  If not, write to
0016  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0017  * Boston, MA 02110-1301, USA.
0018  */
0019 
0020 #include "ArtisticTextLoadingContext.h"
0021 #include "SvgUtil.h"
0022 #include "SvgGraphicContext.h"
0023 
0024 #include <KoXmlReader.h>
0025 #include <QDebug>
0026 
0027 #include <math.h>
0028 
0029 ArtisticTextLoadingContext::ArtisticTextLoadingContext()
0030     : m_textPosition(HUGE_VAL, HUGE_VAL)
0031 {
0032 }
0033 
0034 QString ArtisticTextLoadingContext::simplifyText(const QString &text, bool preserveWhiteSpace)
0035 {
0036     // simplifies text according ot the svg specification
0037     QString simple = text;
0038     simple.remove('\n');
0039     simple.replace('\t', ' ');
0040     if (preserveWhiteSpace)
0041         return simple;
0042 
0043     QString stripped = simple.simplified();
0044     // preserve last whitespace character
0045     if (simple.endsWith(' '))
0046         stripped += QChar(' ');
0047 
0048     return stripped;
0049 }
0050 
0051 ArtisticTextLoadingContext::OffsetType ArtisticTextLoadingContext::xOffsetType() const
0052 {
0053     if(m_currentAbsolutePosX.data.count())
0054         return Absolute;
0055     else if(m_currentRelativePosY.data.count())
0056         return Relative;
0057     else {
0058         if (m_absolutePosX.count() &&  m_absolutePosX.last().data.count())
0059             return Absolute;
0060         else if(m_relativePosX.count() && m_relativePosX.last().data.count())
0061             return Relative;
0062     }
0063     return None;
0064 }
0065 
0066 ArtisticTextLoadingContext::OffsetType ArtisticTextLoadingContext::yOffsetType() const
0067 {
0068     if(m_currentAbsolutePosY.data.count())
0069         return Absolute;
0070     else if(m_currentRelativePosY.data.count())
0071         return Relative;
0072     else {
0073         if (m_absolutePosY.count() &&  m_absolutePosY.last().data.count())
0074             return Absolute;
0075         else if(m_relativePosY.count() && m_relativePosY.last().data.count())
0076             return Relative;
0077     }
0078     return None;
0079 }
0080 
0081 CharTransforms ArtisticTextLoadingContext::xOffsets(int count)
0082 {
0083     switch(xOffsetType()) {
0084     case Absolute: {
0085         const QPointF origin = textPosition();
0086         CharTransforms offsets = collectValues(count, m_currentAbsolutePosX, m_absolutePosX);
0087         const int offsetCount = offsets.count();
0088         for (int i = 0; i < offsetCount; ++i) {
0089             offsets[i] -= origin.x();
0090         }
0091         return offsets;
0092     }
0093     case Relative:
0094         return collectValues(count, m_currentRelativePosX, m_relativePosX);
0095     default:
0096         return CharTransforms();
0097     }
0098 }
0099 
0100 CharTransforms ArtisticTextLoadingContext::yOffsets(int count)
0101 {
0102     switch(yOffsetType()) {
0103     case Absolute: {
0104         const QPointF origin = textPosition();
0105         CharTransforms offsets = collectValues(count, m_currentAbsolutePosY, m_absolutePosY);
0106         const int offsetCount = offsets.count();
0107         for (int i = 0; i < offsetCount; ++i) {
0108             offsets[i] -= origin.y();
0109         }
0110         return offsets;
0111     }
0112     case Relative:
0113         return collectValues(count, m_currentRelativePosY, m_relativePosY);
0114     default:
0115         return CharTransforms();
0116     }
0117 }
0118 
0119 CharTransforms ArtisticTextLoadingContext::rotations(int count)
0120 {
0121     return collectValues(count, m_currentRotations, m_rotations);
0122 }
0123 
0124 QPointF ArtisticTextLoadingContext::textPosition() const
0125 {
0126     qreal x = 0.0, y = 0.0;
0127     if (m_textPosition.x() != HUGE_VAL)
0128         x = m_textPosition.x();
0129     if (m_textPosition.y() != HUGE_VAL)
0130         y = m_textPosition.y();
0131 
0132     return QPointF(x, y);
0133 }
0134 
0135 /// Parses current character transforms (x,y,dx,dy,rotate)
0136 void ArtisticTextLoadingContext::parseCharacterTransforms(const KoXmlElement &element, SvgGraphicsContext *gc)
0137 {
0138     m_currentAbsolutePosX = parseList(element.attribute("x"), gc, XLength);
0139     m_currentAbsolutePosY = parseList(element.attribute("y"), gc, YLength);
0140     m_currentRelativePosX = parseList(element.attribute("dx"), gc, XLength);
0141     m_currentRelativePosY = parseList(element.attribute("dy"), gc, YLength);
0142     m_currentRotations = parseList(element.attribute("rotate"), gc, Number);
0143 
0144     if (m_textPosition.x() == HUGE_VAL && m_currentAbsolutePosX.data.count()) {
0145         m_textPosition.setX(m_currentAbsolutePosX.data.first());
0146     }
0147     if (m_textPosition.y() == HUGE_VAL && m_currentAbsolutePosY.data.count()) {
0148         m_textPosition.setY(m_currentAbsolutePosY.data.first());
0149     }
0150 }
0151 
0152 void ArtisticTextLoadingContext::pushCharacterTransforms()
0153 {
0154     m_absolutePosX.append(m_currentAbsolutePosX);
0155     m_currentAbsolutePosX = CharTransformState();
0156     m_absolutePosY.append(m_currentAbsolutePosY);
0157     m_currentAbsolutePosY = CharTransformState();
0158     m_relativePosX.append(m_currentRelativePosX);
0159     m_currentRelativePosX = CharTransformState();
0160     m_relativePosY.append(m_currentRelativePosY);
0161     m_currentRelativePosY = CharTransformState();
0162     m_rotations.append(m_currentRotations);
0163     m_currentRotations = CharTransformState();
0164 }
0165 
0166 void ArtisticTextLoadingContext::popCharacterTransforms()
0167 {
0168     m_currentAbsolutePosX = m_absolutePosX.last();
0169     m_absolutePosX.pop_back();
0170     m_currentAbsolutePosY = m_absolutePosY.last();
0171     m_absolutePosY.pop_back();
0172     m_currentRelativePosX = m_relativePosX.last();
0173     m_relativePosX.pop_back();
0174     m_currentRelativePosY = m_relativePosY.last();
0175     m_relativePosY.pop_back();
0176     m_currentRotations = m_rotations.last();
0177     m_rotations.pop_back();
0178 }
0179 
0180 CharTransforms ArtisticTextLoadingContext::parseList(const QString &listString, SvgGraphicsContext *gc, ValueType type)
0181 {
0182     if (listString.isEmpty()) {
0183         return CharTransforms();
0184     } else {
0185         CharTransforms values;
0186         QStringList offsets = QString(listString).replace(',', ' ').simplified().split(' ');
0187         foreach(const QString &offset, offsets) {
0188             switch(type) {
0189             case Number:
0190                 values.append(offset.toDouble());
0191                 break;
0192             case XLength:
0193                 values.append(SvgUtil::parseUnitX(gc, offset));
0194                 break;
0195             case YLength:
0196                 values.append(SvgUtil::parseUnitY(gc, offset));
0197                 break;
0198             }
0199         }
0200         return values;
0201     }
0202 }
0203 
0204 CharTransforms ArtisticTextLoadingContext::collectValues(int count, CharTransformState &current, CharTransformStack &stack)
0205 {
0206     CharTransforms collected;
0207 
0208     if (current.hasData) {
0209         // use value from current data if that has initial values
0210         collected = current.extract(count);
0211     } else {
0212         collected = current.extract(count);
0213         // collect values from ancestors
0214         const int stackCount = stack.count();
0215         for(int i = stackCount-1; i >= 0; --i) {
0216             CharTransformState &state = stack[i];
0217             // determine the number of values we need / can get from this ancestor
0218             const int copyCount = qMin(count - collected.count(), state.data.count());
0219             // extract values so they are not consumed more than once
0220             collected.append(state.extract(copyCount));
0221             // ok this ancestor had initial data, so we stop collecting values here
0222             if(state.hasData) {
0223                 if(collected.isEmpty())
0224                     collected.append(state.lastTransform);
0225                 break;
0226             }
0227             if(copyCount == 0)
0228                 break;
0229         }
0230     }
0231     return collected;
0232 }
0233 
0234 void ArtisticTextLoadingContext::printDebug()
0235 {
0236     QString indent;
0237     foreach(const CharTransformState &state, CharTransformStack(m_absolutePosX) << m_currentAbsolutePosX) {
0238         qDebug() << indent << state.data << state.hasData << state.lastTransform;
0239         indent.append("  ");
0240     }
0241     indent.clear();
0242     foreach(const CharTransformState &state, CharTransformStack(m_absolutePosY) << m_currentAbsolutePosY) {
0243         qDebug() << indent << state.data << state.hasData << state.lastTransform;
0244         indent.append("  ");
0245     }
0246     indent.clear();
0247     foreach(const CharTransformState &state, CharTransformStack(m_relativePosX) << m_currentRelativePosX) {
0248         qDebug() << indent << state.data << state.hasData << state.lastTransform;
0249         indent.append("  ");
0250     }
0251     indent.clear();
0252     foreach(const CharTransformState &state, CharTransformStack(m_relativePosY) << m_currentRelativePosY) {
0253         qDebug() << indent << state.data << state.hasData << state.lastTransform;
0254         indent.append("  ");
0255     }
0256     indent.clear();
0257     foreach(const CharTransformState &state, CharTransformStack(m_rotations) << m_currentRotations) {
0258         qDebug() << indent << state.data << state.hasData << state.lastTransform;
0259         indent.append("  ");
0260     }
0261     indent.clear();
0262 }