attributes = ownProperties.convertToSvgTextAttributes();
if (attributes.size() > 0) {
QString styleString;
for (auto it = attributes.constBegin(); it != attributes.constEnd(); ++it) {
if (QString(it.key().toLatin1().data()).contains("text-anchor")) {
QString val = it.value();
if (it.value()=="middle") {
val = "center";
} else if (it.value()=="end") {
val = "right";
} else {
val = "left";
}
styleString.append("text-align")
.append(": ")
.append(val)
.append(";" );
} else if (QString(it.key().toLatin1().data()).contains("fill")){
styleString.append("color")
.append(": ")
.append(it.value())
.append(";" );
} else if (QString(it.key().toLatin1().data()).contains("font-size")){
QString val = it.value();
if (QRegExp ("\\d*").exactMatch(val)) {
val.append("pt");
}
styleString.append(it.key().toLatin1().data())
.append(": ")
.append(val)
.append(";" );
} else {
styleString.append(it.key().toLatin1().data())
.append(": ")
.append(it.value())
.append(";" );
}
}
context.shapeWriter().addAttribute("style", styleString);
}
if (layoutInterface()->isTextNode()) {
debugFlake << "saveHTML" << this << d->text << xPos << yPos << dxPos << dyPos;
// After adding all the styling to the element, add the text
context.shapeWriter().addTextNode(d->text);
}
else {
Q_FOREACH (KoShape *child, this->shapes()) {
KoSvgTextChunkShape *childText = dynamic_cast(child);
KIS_SAFE_ASSERT_RECOVER(childText) { continue; }
childText->saveHtml(context);
}
}
if (isRootTextNode() && layoutInterface()->isTextNode()) {
context.shapeWriter().endElement(); // body
}
context.shapeWriter().endElement(); // p or span
return true;
}
void writeTextListAttribute(const QString &attribute, const QVector &values, KoXmlWriter &writer)
{
const QString value = convertListAttribute(values);
if (!value.isEmpty()) {
writer.addAttribute(attribute.toLatin1().data(), value);
}
}
bool KoSvgTextChunkShape::saveSvg(SvgSavingContext &context)
{
Q_D(KoSvgTextChunkShape);
if (isRootTextNode()) {
context.shapeWriter().startElement("text", false);
if (!context.strippedTextMode()) {
context.shapeWriter().addAttribute("id", context.getID(this));
SvgUtil::writeTransformAttributeLazy("transform", transformation(), context.shapeWriter());
SvgStyleWriter::saveSvgStyle(this, context);
} else {
SvgStyleWriter::saveSvgFill(this, context);
SvgStyleWriter::saveSvgStroke(this, context);
}
} else {
context.shapeWriter().startElement("tspan", false);
if (!context.strippedTextMode()) {
SvgStyleWriter::saveSvgBasicStyle(this, context);
}
}
if (layoutInterface()->isTextNode()) {
QVector xPos;
QVector yPos;
QVector dxPos;
QVector dyPos;
QVector rotate;
fillTransforms(&xPos, &yPos, &dxPos, &dyPos, &rotate, d->localTransformations);
writeTextListAttribute("x", xPos, context.shapeWriter());
writeTextListAttribute("y", yPos, context.shapeWriter());
writeTextListAttribute("dx", dxPos, context.shapeWriter());
writeTextListAttribute("dy", dyPos, context.shapeWriter());
writeTextListAttribute("rotate", rotate, context.shapeWriter());
}
if (!d->textLength.isAuto) {
context.shapeWriter().addAttribute("textLength", KisDomUtils::toString(d->textLength.customValue));
if (d->lengthAdjust == KoSvgText::LengthAdjustSpacingAndGlyphs) {
context.shapeWriter().addAttribute("lengthAdjust", "spacingAndGlyphs");
}
}
KoSvgTextChunkShape *parent = !isRootTextNode() ? dynamic_cast(this->parent()) : 0;
KoSvgTextProperties parentProperties =
parent ? parent->textProperties() : KoSvgTextProperties::defaultProperties();
KoSvgTextProperties ownProperties = textProperties().ownProperties(parentProperties);
// we write down stroke/fill iff they are different from the parent's value
if (!isRootTextNode()) {
if (ownProperties.hasProperty(KoSvgTextProperties::FillId)) {
SvgStyleWriter::saveSvgFill(this, context);
}
if (ownProperties.hasProperty(KoSvgTextProperties::StrokeId)) {
SvgStyleWriter::saveSvgStroke(this, context);
}
}
QMap attributes = ownProperties.convertToSvgTextAttributes();
for (auto it = attributes.constBegin(); it != attributes.constEnd(); ++it) {
context.shapeWriter().addAttribute(it.key().toLatin1().data(), it.value());
}
if (layoutInterface()->isTextNode()) {
context.shapeWriter().addTextNode(d->text);
} else {
Q_FOREACH (KoShape *child, this->shapes()) {
KoSvgTextChunkShape *childText = dynamic_cast(child);
KIS_SAFE_ASSERT_RECOVER(childText) { continue; }
childText->saveSvg(context);
}
}
context.shapeWriter().endElement();
return true;
}
void KoSvgTextChunkShapePrivate::loadContextBasedProperties(SvgGraphicsContext *gc)
{
properties = gc->textProperties;
font = gc->font;
fontFamiliesList = gc->fontFamiliesList;
}
void KoSvgTextChunkShape::resetTextShape()
{
Q_D(KoSvgTextChunkShape);
using namespace KoSvgText;
d->properties = KoSvgTextProperties();
d->font = QFont();
d->fontFamiliesList = QStringList();
d->textLength = AutoValue();
d->lengthAdjust = LengthAdjustSpacing;
d->localTransformations.clear();
d->text.clear();
// all the subchunks are destroyed!
// (first detach, then destroy)
QList shapesToReset = shapes();
Q_FOREACH (KoShape *shape, shapesToReset) {
shape->setParent(0);
delete shape;
}
}
bool KoSvgTextChunkShape::loadSvg(const KoXmlElement &e, SvgLoadingContext &context)
{
Q_D(KoSvgTextChunkShape);
SvgGraphicsContext *gc = context.currentGC();
KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(gc, false);
d->loadContextBasedProperties(gc);
d->textLength = KoSvgText::parseAutoValueXY(e.attribute("textLength", ""), context, "");
d->lengthAdjust = KoSvgText::parseLengthAdjust(e.attribute("lengthAdjust", "spacing"));
QVector xPos = parseListAttributeX(e.attribute("x", ""), context);
QVector yPos = parseListAttributeY(e.attribute("y", ""), context);
QVector dxPos = parseListAttributeX(e.attribute("dx", ""), context);
QVector dyPos = parseListAttributeY(e.attribute("dy", ""), context);
QVector rotate = parseListAttributeAngular(e.attribute("rotate", ""), context);
const int numLocalTransformations =
std::max({xPos.size(), yPos.size(),
dxPos.size(), dyPos.size(),
rotate.size()});
d->localTransformations.resize(numLocalTransformations);
for (int i = 0; i < numLocalTransformations; i++) {
if (i < xPos.size()) {
d->localTransformations[i].xPos = xPos[i];
}
if (i < yPos.size()) {
d->localTransformations[i].yPos = yPos[i];
}
if (i < dxPos.size() && dxPos[i] != 0.0) {
d->localTransformations[i].dxPos = dxPos[i];
}
if (i < dyPos.size() && dyPos[i] != 0.0) {
d->localTransformations[i].dyPos = dyPos[i];
}
if (i < rotate.size()) {
d->localTransformations[i].rotate = rotate[i];
}
}
return true;
}
namespace {
bool hasNextSibling(const KoXmlNode &node)
{
if (!node.nextSibling().isNull()) return true;
KoXmlNode parentNode = node.parentNode();
if (!parentNode.isNull() &&
parentNode.isElement() &&
parentNode.toElement().tagName() == "tspan") {
return hasNextSibling(parentNode);
}
return false;
}
bool hasPreviousSibling(const KoXmlNode &node)
{
if (!node.previousSibling().isNull()) return true;
KoXmlNode parentNode = node.parentNode();
if (!parentNode.isNull() &&
parentNode.isElement() &&
parentNode.toElement().tagName() == "tspan") {
return hasPreviousSibling(parentNode);
}
return false;
}
}
bool KoSvgTextChunkShape::loadSvgTextNode(const KoXmlText &text, SvgLoadingContext &context)
{
Q_D(KoSvgTextChunkShape);
SvgGraphicsContext *gc = context.currentGC();
KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(gc, false);
d->loadContextBasedProperties(gc);
QString data = text.data();
data.replace(QRegExp("[\\r\\n]"), "");
data.replace(QRegExp("\\s{2,}"), " ");
if (data.startsWith(' ') && !hasPreviousSibling(text)) {
data.remove(0, 1);
}
if (data.endsWith(' ') && !hasNextSibling(text)) {
data.remove(data.size() - 1, 1);
}
if (data == " ") {
data = "";
}
//ENTER_FUNCTION() << text.data() << "-->" << data;
d->text = data;
return !data.isEmpty();
}
void KoSvgTextChunkShape::normalizeCharTransformations()
{
Q_D(KoSvgTextChunkShape);
d->applyParentCharTransformations(d->localTransformations);
}
void KoSvgTextChunkShape::simplifyFillStrokeInheritance()
{
- Q_D(KoSvgTextChunkShape);
-
if (!isRootTextNode()) {
KoShape *parentShape = parent();
KIS_SAFE_ASSERT_RECOVER_RETURN(parentShape);
QSharedPointer bg = background();
QSharedPointer parentBg = parentShape->background();
if (!inheritBackground() &&
((!bg && !parentBg) ||
(bg && parentBg &&
bg->compareTo(parentShape->background().data())))) {
setInheritBackground(true);
}
KoShapeStrokeModelSP stroke = this->stroke();
KoShapeStrokeModelSP parentStroke= parentShape->stroke();
if (!inheritStroke() &&
((!stroke && !parentStroke) ||
(stroke && parentStroke &&
stroke->compareFillTo(parentShape->stroke().data()) &&
stroke->compareStyleTo(parentShape->stroke().data())))) {
setInheritStroke(true);
}
}
Q_FOREACH (KoShape *shape, shapes()) {
KoSvgTextChunkShape *chunkShape = dynamic_cast(shape);
KIS_SAFE_ASSERT_RECOVER_RETURN(chunkShape);
chunkShape->simplifyFillStrokeInheritance();
}
}
KoSvgTextProperties KoSvgTextChunkShape::textProperties() const
{
Q_D(const KoSvgTextChunkShape);
KoSvgTextProperties properties = d->properties;
properties.setProperty(KoSvgTextProperties::FillId, QVariant::fromValue(KoSvgText::BackgroundProperty(background())));
properties.setProperty(KoSvgTextProperties::StrokeId, QVariant::fromValue(KoSvgText::StrokeProperty(stroke())));
return properties;
}
bool KoSvgTextChunkShape::isTextNode() const
{
Q_D(const KoSvgTextChunkShape);
return d->layoutInterface->isTextNode();
}
KoSvgTextChunkShapeLayoutInterface *KoSvgTextChunkShape::layoutInterface()
{
Q_D(KoSvgTextChunkShape);
return d->layoutInterface.data();
}
bool KoSvgTextChunkShape::isRootTextNode() const
{
return false;
}
/**************************************************************************************************/
/* KoSvgTextChunkShapePrivate */
/**************************************************************************************************/
#include "SimpleShapeContainerModel.h"
KoSvgTextChunkShapePrivate::KoSvgTextChunkShapePrivate(KoSvgTextChunkShape *_q)
: KoShapeContainerPrivate(_q)
{
}
KoSvgTextChunkShapePrivate::KoSvgTextChunkShapePrivate(const KoSvgTextChunkShapePrivate &rhs, KoSvgTextChunkShape *q)
: KoShapeContainerPrivate(rhs, q),
properties(rhs.properties),
font(rhs.font),
fontFamiliesList(rhs.fontFamiliesList),
localTransformations(rhs.localTransformations),
textLength(rhs.textLength),
lengthAdjust(rhs.lengthAdjust),
text(rhs.text)
{
if (rhs.model) {
SimpleShapeContainerModel *otherModel = dynamic_cast(rhs.model);
KIS_ASSERT_RECOVER_RETURN(otherModel);
model = new SimpleShapeContainerModel(*otherModel);
}
}
KoSvgTextChunkShapePrivate::~KoSvgTextChunkShapePrivate()
{
}
#include
#include
#include