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 ¤t, 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 }