Krita Source Code Documentation
Loading...
Searching...
No Matches
KoSvgTextProperties.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2017 Dmitry Kazakov <dimula73@gmail.com>
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
8
9#include <QFontMetrics>
10#include <QGlobalStatic>
11#include <QMap>
12#include <QRegExp>
13
14#include <fontconfig/fontconfig.h>
15
16#include <SvgGraphicContext.h>
17#include <SvgLoadingContext.h>
18#include <SvgUtil.h>
19#include <kis_dom_utils.h>
20#include <kis_global.h>
21
22#include "KoSvgText.h"
23#include "KoFontRegistry.h"
24
26{
27 QMap<PropertyId, QVariant> properties;
28
29 static bool isInheritable(PropertyId id);
30};
31
36
40
42 : m_d(new Private)
43{
44 m_d->properties = rhs.m_d->properties;
45}
46
48{
49 if (&rhs != this) {
50 m_d->properties = rhs.m_d->properties;
51 }
52 return *this;
53}
54
56 return m_d->properties == rhs.m_d->properties;
57}
58
60{
61 m_d->properties.insert(id, value);
62}
63
65{
66 return m_d->properties.contains(id);
67}
68
69QVariant KoSvgTextProperties::property(KoSvgTextProperties::PropertyId id, const QVariant &defaultValue) const
70{
71 return m_d->properties.value(id, defaultValue);
72}
73
78
80{
81 QVariant value = m_d->properties.value(id);
82 if (value.isNull()) {
84 }
85 return value;
86}
87
92
94{
95 return m_d->properties.isEmpty();
96}
97
98
103
105{
106 auto it = m_d->properties.begin();
107 for (; it != m_d->properties.end(); ++it) {
108 if (!m_d->isInheritable(it.key())) {
109 it.value() = defaultProperties().property(it.key());
110 }
111 }
112}
113
114void KoSvgTextProperties::inheritFrom(const KoSvgTextProperties &parentProperties, bool resolve)
115{
116 auto it = parentProperties.m_d->properties.constBegin();
117 for (; it != parentProperties.m_d->properties.constEnd(); ++it) {
118 if (!hasProperty(it.key()) && m_d->isInheritable(it.key())) {
119 setProperty(it.key(), it.value());
120 }
121 }
122
123 if (resolve) {
124 resolveRelativeValues(parentProperties.metrics(), parentProperties.fontSize().value);
125 }
126}
127
129{
130 // First resolve 'font-*' properties.
131 // See https://www.w3.org/TR/css-values-4/#font-relative-lengths
134 this->setFontSize(size);
135 const qreal usedSize = size.value;
137 if (!lineHeight.isNormal && !lineHeight.isNumber) {
140 } else {
141 lineHeight.length.convertToAbsolute(this->metrics(false), usedSize);
142 }
143 setProperty(LineHeightId, QVariant::fromValue(lineHeight));
144 }
145
146 const KoSvgText::FontMetrics usedMetrics = this->metrics();
147
148 for (auto it = this->m_d->properties.begin(); it != this->m_d->properties.end(); it++) {
149
152
153 if (it.key() == LineHeightId) continue;
154 if (it.value().canConvert<KoSvgText::CssLengthPercentage>() && it.key() != KoSvgTextProperties::FontSizeId) {
156 length.convertToAbsolute(usedMetrics, usedSize, percentageUnit);
157 it.value() = QVariant::fromValue(length);
158 } else if (it.value().canConvert<KoSvgText::AutoLengthPercentage>()) {
160
161 if (!val.isAuto) {
162 val.length.convertToAbsolute(usedMetrics, usedSize, percentageUnit);
163 it.value() = QVariant::fromValue(val);
164 }
165 } else if (it.key() == KoSvgTextProperties::TabSizeId) {
167 if (!tabSize.isNumber) {
168 tabSize.length.convertToAbsolute(usedMetrics, usedSize);
169 it.value() = QVariant::fromValue(tabSize);
170 }
171 } else if (it.key() == KoSvgTextProperties::TextIndentId) {
172 KoSvgText::TextIndentInfo indent = it.value().value<KoSvgText::TextIndentInfo>();
174 indent.length.convertToAbsolute(usedMetrics, usedSize);
175 }
176 it.value() = QVariant::fromValue(indent);
177 }
178 }
179}
180
182{
183 return !hasProperty(id) || parentProperties.property(id) == property(id);
184}
185
187{
188 for (auto it = m_d->properties.constBegin(); it != m_d->properties.constEnd(); ++it) {
189 if (!m_d->isInheritable(it.key())) {
190 return true;
191 }
192 }
193 return false;
194}
195
197{
198 auto it = properties.m_d->properties.constBegin();
199 for (; it != properties.m_d->properties.constEnd(); ++it) {
200 if (m_d->isInheritable(it.key())) {
201 setProperty(it.key(), it.value());
202 }
203 }
204}
205
206void KoSvgTextProperties::scaleAbsoluteValues(const double scaleInline, const double scaleBlock)
207{
209
210 for (auto it = this->m_d->properties.begin(); it != this->m_d->properties.end(); ++it) {
211 if (it.value().canConvert<KoSvgText::CssLengthPercentage>()) {
213 if (length.unit != absoluteUnit) continue;
214 if (it.key() == FontSizeId || it.key() == BaselineShiftValueId) {
215 length.value *= scaleBlock;
216 } else {
217 length.value *= scaleInline;
218 }
219 it.value() = QVariant::fromValue(length);
220 } else if (it.value().canConvert<KoSvgText::LineHeightInfo>()) {
222 if (info.length.unit != absoluteUnit) continue;
223 info.length.value *= scaleBlock;
224 it.value() = QVariant::fromValue(info);
225 } else if (it.value().canConvert<KoSvgText::TabSizeInfo>()) {
227 if (info.length.unit != absoluteUnit) continue;
228 info.length.value *= scaleInline;
229 it.value() = QVariant::fromValue(info);
230 } else if (it.value().canConvert<KoSvgText::TextIndentInfo>()) {
231 KoSvgText::TextIndentInfo info = it.value().value<KoSvgText::TextIndentInfo>();
232 if (info.length.unit != absoluteUnit) continue;
233 info.length.value *= scaleInline;
234 it.value() = QVariant::fromValue(info);
235 } else if (it.value().canConvert<KoSvgText::AutoValue>()) {
236 KoSvgText::AutoValue info = it.value().value<KoSvgText::AutoValue>();
237 if (info.isAuto) continue;
238 if (it.key() != InlineSizeId) continue;
239 info.customValue *= scaleInline;
240 it.value() = QVariant::fromValue(info);
241 }
242 // TODO: Check shape padding and margin when they become editable.
243 }
244}
245
246KoSvgTextProperties KoSvgTextProperties::ownProperties(const KoSvgTextProperties &parentProperties, bool keepFontSize) const
247{
248 KoSvgTextProperties result;
249
250 auto it = m_d->properties.constBegin();
251 for (; it != m_d->properties.constEnd(); ++it) {
252 if ((keepFontSize && it.key() == FontSizeId) || !parentProperties.hasProperty(it.key())
253 || parentProperties.property(it.key()) != it.value()) {
254 result.setProperty(it.key(), it.value());
255 }
256 }
257
258 return result;
259}
260
261inline qreal roundToStraightAngle(qreal value)
262{
263 return normalizeAngle(int((value + M_PI_4) / M_PI_2) * M_PI_2);
264}
265
266QPair<QString, QString> parseTag(QString taggedValue) {
267 QPair<QString, QString> tag;
268 tag.first = taggedValue.mid(1, 4);
269 if (taggedValue.length() > 6) {
270 tag.second = taggedValue.remove(0, 6).trimmed();
271 }
272 return tag;
273}
274
275QVariantMap parseVariantStringList(const QStringList features) {
276 QVariantMap settings;
277 for (int i = 0; i < features.size(); i++) {
278 QString feature = features.at(i).trimmed();
279 if ((!feature.startsWith('\'') && !feature.startsWith('\"')) || feature.isEmpty()) {
280 continue;
281 }
282 QPair<QString, QString> tag = parseTag(feature);
283 bool ok = false;
284 double featureVal = tag.second.toDouble(&ok);
285
286 if (ok && !tag.first.isEmpty()) {
287 settings.insert(tag.first, QVariant(featureVal));
288 }
289 }
290 return settings;
291}
292
293QVariantMap parseFeatureSettingsStringList(const QStringList features) {
294 QVariantMap settings;
295 for (int i = 0; i < features.size(); i++) {
296 QString feature = features.at(i).trimmed();
297 if ((!feature.startsWith('\'') && !feature.startsWith('\"')) || feature.isEmpty()) {
298 continue;
299 }
300 QPair<QString, QString> tag = parseTag(feature);
301 if (tag.second.isEmpty()) {
302 settings.insert(tag.first, QVariant(1));
303 } else {
304 bool ok = false;
305 int featureVal = tag.second.toInt(&ok);
306 if (tag.second.toLower() == "on") {
307 featureVal = 1;
308 ok = true;
309 } else if (tag.second.toLower() == "off") {
310 featureVal = 0;
311 ok = true;
312 }
313 if (ok && !tag.first.isEmpty()) {
314 settings.insert(tag.first, QVariant(featureVal));
315 }
316 }
317 }
318 return settings;
319}
320
321void KoSvgTextProperties::parseSvgTextAttribute(const SvgLoadingContext &context, const QString &command, const QString &value)
322{
323 if (command == "writing-mode") {
325 } else if (command == "glyph-orientation-vertical") {
327 // glyph-orientation-vertical should only be converted for the 'auto', '0' and '90' cases,
328 // and treated as invalid otherwise.
329 QStringList acceptedOrientations;
330 acceptedOrientations << "auto"
331 << "0"
332 << "0deg"
333 << "90"
334 << "90deg";
335 if (acceptedOrientations.contains(value.toLower())) {
336 if (!autoValue.isAuto) {
338 }
340 setProperty(TextOrientationId, orientation);
341 }
342 } else if (command == "text-orientation") {
344 } else if (command == "direction") {
346 } else if (command == "unicode-bidi") {
348 } else if (command == "text-anchor") {
350 } else if (command == "dominant-baseline") {
352 } else if (command == "alignment-baseline") {
354 } else if (command == "baseline-shift") {
359 setProperty(BaselineShiftValueId, QVariant::fromValue(shift));
360 }
361 } else if (command == "vertical-align") {
362 QRegExp digits = QRegExp("\\d");
363 Q_FOREACH (const QString &param, value.split(' ', Qt::SkipEmptyParts)) {
364#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
365 bool paramContains = param.contains(digits);
366#else
367 bool paramContains = (digits.indexIn(param) > 0);
368#endif
369
370
371 if (param == "sub" || param == "super" || param == "top" || param == "bottom" || paramContains) {
372 parseSvgTextAttribute(context, "baseline-shift", param);
373 } else {
374 parseSvgTextAttribute(context, "alignment-baseline", param);
375 }
376 }
377 } else if (command == "kerning" || command == "font-kerning") {
378 KoSvgText::AutoValue kerning;
379 if (value == "none") {
380 kerning.isAuto = false;
381 kerning.customValue = 0;
382 } else if (value == "normal") {
383 kerning.isAuto = false;
384 kerning.customValue = 1;
385 } else {
386 kerning = KoSvgText::parseAutoValueXY(value, context);
387 }
389 } else if (command == "letter-spacing") {
390 setProperty(LetterSpacingId, QVariant::fromValue(KoSvgText::parseAutoLengthPercentageXY(value, context, "normal", context.currentGC()->currentBoundingBox, true)));
391 } else if (command == "word-spacing") {
392 setProperty(WordSpacingId, QVariant::fromValue(KoSvgText::parseAutoLengthPercentageXY(value, context, "normal", context.currentGC()->currentBoundingBox, true)));
393 } else if (command == "font-family") {
394 QStringList familiesList;
395 Q_FOREACH (const QString &fam, value.split(',', Qt::SkipEmptyParts)) {
396 QString family = fam.trimmed();
397 if ((family.startsWith('\"') && family.endsWith('\"')) ||
398 (family.startsWith('\'') && family.endsWith('\''))) {
399
400 family = family.mid(1, family.size() - 2);
401 }
402 familiesList.append(family);
403 }
404 setProperty(FontFamiliesId, familiesList);
405
406 } else if (command == "font-style") {
408 } else if (command == "font-variant" || command == "font-variant-ligatures" || command == "font-variant-position" || command == "font-variant-caps"
409 || command == "font-variant-numeric" || command == "font-variant-east-asian" || command == "font-variant-alternates") {
410 const QStringList features = value.split(" ");
411 Q_FOREACH (const QString f, features) {
412 bool commandFontVariant = (command == "font-variant");
413 if (commandFontVariant || command == "font-variant-ligatures") {
416 setProperty(FontVariantLigatureId, QVariant::fromValue(liga));
417 }
418 if (commandFontVariant || command == "font-variant-position") {
421 setProperty(FontVariantPositionId, QVariant::fromValue(pos));
422 }
423 if (commandFontVariant || command == "font-variant-caps") {
425 caps = KoSvgText::parseFontFeatureCaps(f, caps);
426 setProperty(FontVariantCapsId, QVariant::fromValue(caps));
427 }
428 if (commandFontVariant || command == "font-variant-numeric") {
431 setProperty(FontVariantNumericId, QVariant::fromValue(num));
432 }
433 if (commandFontVariant || command == "font-variant-east-asian") {
436 setProperty(FontVariantEastAsianId, QVariant::fromValue(ea));
437 }
438 }
439
440 } else if (command == "font-feature-settings") {
442 } else if (command == "font-stretch") {
443 int newStretch = 100;
444
446
447 setProperty(FontStretchId, newStretch);
448
449 } else if (command == "font-weight") {
451
452 setProperty(FontWeightId, weight);
453
454 } else if (command == "font-size") {
456 if (pointSize.value > 0.0) {
457 setProperty(FontSizeId, QVariant::fromValue(pointSize));
458 }
459 } else if (command == "font-size-adjust") {
461
462 } else if (command == "font-optical-sizing") {
464 } else if (command == "font-variation-settings") {
466 } else if (command == "text-decoration" || command == "text-decoration-line" || command == "text-decoration-style" || command == "text-decoration-color"
467 || command == "text-decoration-position") {
468 using namespace KoSvgText;
469
470 TextDecorations deco = propertyOrDefault(TextDecorationLineId).value<KoSvgText::TextDecorations>();
471 if (command == "text-decoration" || command == "text-decoration-line") {
472 // reset deco when those values are being set..
474 }
475
478 QColor textDecorationColor = propertyOrDefault(TextDecorationStyleId).value<QColor>();
479 bool setPosition = false;
480
481 Q_FOREACH (const QString &param, value.split(' ', Qt::SkipEmptyParts)) {
482 if (param == "line-through") {
483 deco |= DecorationLineThrough;
484 } else if (param == "underline") {
485 deco |= DecorationUnderline;
486 } else if (param == "overline") {
487 deco |= DecorationOverline;
488 } else if (param == "solid") {
489 style = Solid;
490 } else if (param == "double") {
491 style = Double;
492 } else if (param == "dotted") {
493 style = Dotted;
494 } else if (param == "dashed") {
495 style = Dashed;
496 } else if (param == "wavy") {
497 style = Wavy;
498 } else if (param == "auto") {
499 underlinePos.horizontalPosition = UnderlineAuto;
500 setPosition = true;
501 } else if (param == "under") {
502 underlinePos.horizontalPosition = UnderlineUnder;
503 setPosition = true;
504 } else if (param == "left") {
505 underlinePos.verticalPosition = UnderlineLeft;
506 setPosition = true;
507 } else if (param == "right") {
508 underlinePos.verticalPosition = UnderlineRight;
509 setPosition = true;
510 } else if (QColor::isValidColor(param)) {
511 // TODO: Convert to KoColor::fromSvg11.
512 textDecorationColor = QColor(param);
513 }
514 }
515
516 if (command == "text-decoration" || command == "text-decoration-line") {
517 setProperty(TextDecorationLineId, QVariant::fromValue(deco));
518 }
519 if (command == "text-decoration" || command == "text-decoration-style") {
521 }
522 if (command == "text-decoration" || command == "text-decoration-color") {
523 setProperty(TextDecorationColorId, QVariant::fromValue(textDecorationColor));
524 }
525 if ((command == "text-decoration" || command == "text-decoration-position") && setPosition) {
526 setProperty(TextDecorationPositionId, QVariant::fromValue(underlinePos));
527 }
528
529 } else if (command == "xml:lang") {
531 } else if (command == "text-transform") {
533 } else if (command == "white-space") {
534 KoSvgText::TextSpaceTrims trims = propertyOrDefault(TextTrimId).value<KoSvgText::TextSpaceTrims>();
537
538 KoSvgText::whiteSpaceValueToLongHands(value, collapse, wrap, trims);
539
540 setProperty(TextTrimId, QVariant::fromValue(trims));
541 setProperty(TextWrapId, wrap);
542 setProperty(TextCollapseId, collapse);
543
544 } else if (command == "xml:space") {
547 setProperty(TextCollapseId, collapse);
548 } else if (command == "word-break") {
550 } else if (command == "line-break") {
552 } else if (command == "text-align" || command == "text-align-all" || command == "text-align-last") {
553 QStringList params = value.split(' ', Qt::SkipEmptyParts);
554 if (command == "text-align" || command == "text-align-all") {
556 if (value == "justify-all") {
558 }
559 }
560 if (command == "text-align" && params.size() > 1) {
562 }
563 if (command == "text-align-last") {
565 }
566 } else if (command == "line-height") {
567 setProperty(LineHeightId, QVariant::fromValue(KoSvgText::parseLineHeight(value, context)));
568 } else if (command == "text-indent") {
569 setProperty(TextIndentId, QVariant::fromValue(KoSvgText::parseTextIndent(value, context)));
570 } else if (command == "hanging-punctuation") {
571 KoSvgText::HangingPunctuations hang;
572 Q_FOREACH (const QString &param, value.split(' ', Qt::SkipEmptyParts)) {
573 if (param == "first") {
574 hang.setFlag(KoSvgText::HangFirst, true);
575 } else if (param == "last") {
576 hang.setFlag(KoSvgText::HangLast, true);
577 } else if (param == "allow-end") {
578 hang.setFlag(KoSvgText::HangEnd, true);
579 hang.setFlag(KoSvgText::HangForce, false);
580 } else if (param == "force-end") {
581 hang.setFlag(KoSvgText::HangEnd, true);
582 hang.setFlag(KoSvgText::HangForce, true);
583 }
584 }
585 setProperty(HangingPunctuationId, QVariant::fromValue(hang));
586 } else if (command == "inline-size") {
588 } else if (command == "overflow") {
590 } else if (command == "text-overflow") {
592 } else if (command == "overflow-wrap" || command == "word-wrap") {
595 : value == "anywhere" ? KoSvgText::OverflowWrapAnywhere
597 } else if (command == "tab-size") {
598 setProperty(TabSizeId, QVariant::fromValue(KoSvgText::parseTabSize(value, context)));
599 } else if (command == "shape-padding") {
601 } else if (command == "shape-margin") {
603 } else if (command == "font-synthesis") {
608 if (value != "none") {
609 QStringList params = value.split(" ");
610 if (params.contains("position")) {
612 }
613 if (params.contains("weight")) {
615 }
616 if (params.contains("style")) {
618 }
619 if (params.contains("small-caps")) {
621 }
622 }
623 } else if (command == "font-synthesis-weight") {
625 } else if (command == "font-synthesis-style") {
627 } else if (command == "font-synthesis-small-caps") {
629 } else if (command == "font-synthesis-position") {
631 } else if (command == "text-rendering") {
633 } else {
634 qFatal("FATAL: Unknown SVG property: %s = %s", command.toUtf8().data(), value.toUtf8().data());
635 }
636}
637
639{
640 using namespace KoSvgText;
641
642 QMap<QString, QString> result;
643
644 bool svg1_1 = false;
645
647 result.insert("writing-mode", writeWritingMode(WritingMode(property(WritingModeId).toInt()), svg1_1));
648 }
649
651 if (svg1_1) {
653 QString value = "auto";
654 if (orientation == OrientationUpright) {
655 value = "0";
656 } else if (orientation == OrientationSideWays) {
657 value = "90";
658 }
659 result.insert("glyph-orientation-vertical", value);
660 } else {
661 result.insert("text-orientation", writeTextOrientation(TextOrientation(property(TextOrientationId).toInt())));
662 }
663 }
664
666 result.insert("direction", writeDirection(Direction(property(DirectionId).toInt())));
667 }
668
670 result.insert("unicode-bidi", writeUnicodeBidi(UnicodeBidi(property(UnicodeBidiId).toInt())));
671 }
672
674 result.insert("text-anchor", writeTextAnchor(TextAnchor(property(TextAnchorId).toInt())));
675 }
676
678 result.insert("dominant-baseline", writeDominantBaseline(Baseline(property(DominantBaselineId).toInt())));
679 }
680
681 bool writeSeparate = true;
682 if (hasProperty(BaselineShiftModeId) && !svg1_1) {
684 if (mode == ShiftLineTop || mode == ShiftLineBottom) {
685 writeSeparate = false;
686 }
687 }
688
689 if (writeSeparate) {
691 result.insert("alignment-baseline", writeAlignmentBaseline(Baseline(property(AlignmentBaselineId).toInt())));
692 }
693
696 result.insert("baseline-shift",
697 writeBaselineShiftMode(BaselineShiftMode(property(BaselineShiftModeId).toInt()), shift));
698 }
699 } else {
700 QStringList verticalAlign;
702 verticalAlign.append(writeAlignmentBaseline(Baseline(property(AlignmentBaselineId).toInt())));
703 }
704
707 verticalAlign.append(writeBaselineShiftMode(BaselineShiftMode(property(BaselineShiftModeId).toInt()), shift));
708 }
709 if (!verticalAlign.isEmpty()) {
710 result.insert("vertical-align", verticalAlign.join(" "));
711 }
712 }
713
714 if (hasProperty(KerningId)) {
715 if (svg1_1) {
716 result.insert("kerning", writeAutoValue(property(KerningId).value<AutoValue>()));
717 } else {
718 AutoValue kerning = property(KerningId).value<AutoValue>();
719 if (kerning.isAuto) {
720 result.insert("font-kerning", "auto");
721 } else if (kerning.customValue == 0) {
722 result.insert("font-kerning", "none");
723 } else {
724 result.insert("font-kerning", "normal");
725 }
726 }
727 }
728
729 // Word-spacing and letter-spacing don't support % until css-text-4, and in svg 1.1, % were viewport, so save % as em for now.
731 result.insert("letter-spacing", writeAutoLengthPercentage(property(LetterSpacingId).value<AutoLengthPercentage>(), "normal", true));
732 }
733
735 result.insert("word-spacing", writeAutoLengthPercentage(property(WordSpacingId).value<AutoLengthPercentage>(), "normal", true));
736 }
737
739 result.insert("font-family", property(FontFamiliesId).toStringList().join(','));
740 }
741
744 result.insert("font-style", KoSvgText::writeFontStyle(style));
745 }
746
749 result.insert("font-variant-ligatures", writeFontFeatureLigatures(feat));
750 }
753 result.insert("font-variant-position", writeFontFeaturePosition(feat));
754 }
757 result.insert("font-variant-caps", writeFontFeatureCaps(feat));
758 }
761 result.insert("font-variant-numeric", writeFontFeatureNumeric(feat));
762 }
765 result.insert("font-variant-east-asian", writeFontFeatureEastAsian(feat));
766 }
768 QStringList settings;
769 QVariantMap vals = property(FontFeatureSettingsId).toMap();
770 for(auto it = vals.begin(); it != vals.end(); it++) {
771 settings.append(QString("'%1' %2").arg(it.key()).arg(it.value().toDouble()));
772 }
773 result.insert("font-feature-settings", settings.join(", "));
774 }
775
777 if (!property(FontOpticalSizingId).toBool()) {
778 result.insert("font-optical-sizing", "none");
779 }
780 }
782 QStringList settings;
783 QVariantMap vals = property(FontVariationSettingsId).toMap();
784 for(auto it = vals.begin(); it != vals.end(); it++) {
785 settings.append(QString("'%1' %2").arg(it.key()).arg(it.value().toDouble()));
786 }
787 result.insert("font-variation-settings", settings.join(", "));
788 }
789
791 const int stretch = property(FontStretchId).toInt();
792 static constexpr std::array<int, 9> fontStretches = {50, 62, 75, 87, 100, 112, 125, 150, 200};
793 if (svg1_1 || std::find(fontStretches.begin(), fontStretches.end(), stretch) != fontStretches.end()) {
794 const auto it = std::lower_bound(fontStretches.begin(), fontStretches.end(), stretch);
795 if (it != fontStretches.end()) {
796 const auto index = std::distance(fontStretches.begin(), it);
797 KIS_ASSERT(index >= 0);
798 result.insert("font-stretch", KoSvgText::fontStretchNames.at(static_cast<size_t>(index)));
799 }
800 } else {
801 result.insert("font-stretch", KisDomUtils::toString(stretch));
802 }
803 }
804
806 result.insert("font-weight", KisDomUtils::toString(property(FontWeightId).toInt()));
807 }
808
809 if (hasProperty(FontSizeId)) {
810 result.insert("font-size", writeLengthPercentage(fontSize()));
811 }
812
814 result.insert("font-size-adjust", writeAutoValue(property(FontSizeAdjustId).value<AutoValue>(), "none"));
815 }
816
817 QStringList decoStrings;
819 TextDecorations deco = property(TextDecorationLineId).value<TextDecorations>();
820
821 if (deco.testFlag(DecorationUnderline)) {
822 decoStrings.append("underline");
823 }
824
825 if (deco.testFlag(DecorationOverline)) {
826 decoStrings.append("overline");
827 }
828
829 if (deco.testFlag(DecorationLineThrough)) {
830 decoStrings.append("line-through");
831 }
832
833 if (deco != DecorationNone) {
836
837 if (style == Solid) {
838 decoStrings.append("solid");
839 } else if (style == Double) {
840 decoStrings.append("double");
841 } else if (style == Dotted) {
842 decoStrings.append("dotted");
843 } else if (style == Dashed) {
844 decoStrings.append("dashed");
845 } else if (style == Wavy) {
846 decoStrings.append("wavy");
847 }
848 }
850 QColor color = property(TextDecorationColorId).value<QColor>();
851 if (color.isValid()) {
852 decoStrings.append(color.name());
853 }
854 }
855 }
856 if (!decoStrings.isEmpty()) {
857 result.insert("text-decoration", decoStrings.join(' '));
858 }
859 }
860
862 QStringList decoPositionStrings;
864 if (pos.horizontalPosition == UnderlineUnder) {
865 decoPositionStrings.append("under");
866 } else {
867 decoPositionStrings.append("auto");
868 }
869 if (pos.verticalPosition == UnderlineRight) {
870 decoPositionStrings.append("right");
871 } else {
872 decoPositionStrings.append("left");
873 }
874 if (!decoPositionStrings.isEmpty()) {
875 result.insert("text-decoration-position", decoPositionStrings.join(' '));
876 }
877 }
878
880 result.insert("xml:lang", property(TextLanguage).toString());
881 }
882
884 result.insert("text-transform", writeTextTransform(property(TextTransformId).value<TextTransformInfo>()));
885 }
887 result.insert("word-break", writeWordBreak(WordBreak(property(WordBreakId).toInt())));
888 }
890 result.insert("line-break", writeLineBreak(LineBreak(property(LineBreakId).toInt())));
891 }
893 KoSvgText::TextSpaceTrims trims = propertyOrDefault(TextTrimId).value<KoSvgText::TextSpaceTrims>();
896 if (collapse == KoSvgText::PreserveSpaces || svg1_1) {
897 result.insert("xml:space", writeXmlSpace(collapse));
898 } else {
899 result.insert("white-space", writeWhiteSpaceValue(collapse, wrap, trims));
900 }
901 }
904 result.insert("line-height", KoSvgText::writeLineHeight(lineHeight));
905 }
906 if (hasProperty(TabSizeId)) {
907 result.insert("tab-size", writeTabSize(propertyOrDefault(TabSizeId).value<TabSizeInfo>()));
908 }
910 HangingPunctuations hang = property(HangingPunctuationId).value<HangingPunctuations>();
912
913 if (hang.testFlag(HangFirst)) {
914 value.append("first");
915 }
916 if (hang.testFlag(HangLast)) {
917 value.append("last");
918 }
919 if (hang.testFlag(HangEnd)) {
920 if (hang.testFlag(HangForce)) {
921 value.append("force-end");
922 } else {
923 value.append("allow-end");
924 }
925 }
926
927 if (!value.isEmpty()) {
928 result.insert("hanging-punctuation", value.join(" "));
929 }
930 }
931
934 if (overflow == OverflowWrapAnywhere) {
935 result.insert("overflow-wrap", "anywhere");
936 } else if (overflow == OverflowWrapBreakWord) {
937 result.insert("overflow-wrap", "break-word");
938 }
939 }
942 if (overflow == OverFlowClip) {
943 result.insert("overflow", "clip");
944 result.insert("text-overflow", "clip");
945 } else if (overflow == OverFlowEllipse) {
946 result.insert("overflow", "visible");
947 result.insert("text-overflow", "ellipse");
948 } else {
949 result.insert("overflow", "visible");
950 result.insert("text-overflow", "clip");
951 }
952 }
953
956 bool weight = property(FontSynthesisBoldId).toBool();
957 bool italic = property(FontSynthesisItalicId).toBool();
958 bool caps = property(FontSynthesisSmallCapsId).toBool();
959 bool super = property(FontSynthesisSuperSubId).toBool();
960
961 if (!weight && !italic && !caps && !super) {
962 result.insert("font-synthesis", "none");
963 } else {
964 QStringList params;
965 if (weight) params.append("weight");
966 if (italic) params.append("style");
967 if (caps) params.append("small-caps");
968 if (super) params.append("position");
969 result.insert("font-synthesis", params.join(" "));
970 }
971 } else {
973 result.insert("font-synthesis-weight", property(FontSynthesisBoldId).toBool()? "auto": "none");
974 }
976 result.insert("font-synthesis-style", property(FontSynthesisItalicId).toBool()? "auto": "none");
977 }
979 result.insert("font-synthesis-small-caps", property(FontSynthesisSmallCapsId).toBool()? "auto": "none");
980 }
982 result.insert("font-synthesis-position", property(FontSynthesisSuperSubId).toBool()? "auto": "none");
983 }
984 }
985
987 result.insert("text-rendering", KoSvgText::writeTextRendering(TextRendering(property(TextRenderingId).toInt())));
988 }
989
990 return result;
991}
992
994{
995 using namespace KoSvgText;
996 QMap<QString, QString> result;
998 result.insert("inline-size", writeAutoValue(property(InlineSizeId).value<AutoValue>(), "auto"));
999 }
1001 result.insert("text-indent", writeTextIndent(propertyOrDefault(TextIndentId).value<TextIndentInfo>()));
1002 }
1005 result.insert("text-align", writeTextAlign(all));
1007 if (last != all || last != AlignLastAuto) {
1008 result.insert("text-align-last", writeTextAlign(last));
1009 }
1010 }
1012 result.insert("shape-padding", QString::number(property(ShapePaddingId).toReal()));
1013 }
1015 result.insert("shape-margin", QString::number(property(ShapeMarginId).toReal()));
1016 }
1017 return result;
1018}
1019
1021{
1022 QString fontFamily;
1023
1024 QStringList familiesList =
1026 if (!familiesList.isEmpty()) {
1027 fontFamily = familiesList.first();
1028 }
1029 const QFont::Style style =
1031
1033
1034 // for rounding see a comment below!
1035 QFont font(fontFamily
1036 , qMax(qRound(fontSize.value), 1)
1038 , style != QFont::StyleNormal);
1039 font.setStyle(style);
1040
1045 font.setPointSizeF(fontSize.value);
1046
1047 font.setStretch(propertyOrDefault(KoSvgTextProperties::FontStretchId).toInt());
1048
1049 using namespace KoSvgText;
1050
1051 TextDecorations deco = propertyOrDefault(KoSvgTextProperties::TextDecorationLineId).value<KoSvgText::TextDecorations>();
1052
1053 font.setStrikeOut(deco & DecorationLineThrough);
1054 font.setUnderline(deco & DecorationUnderline);
1055 font.setOverline(deco & DecorationOverline);
1056
1057 struct FakePaintDevice : public QPaintDevice
1058 {
1059 QPaintEngine *paintEngine() const override {
1060 return nullptr;
1061 }
1062
1063 int metric(QPaintDevice::PaintDeviceMetric metric) const override {
1064
1065 if (metric == QPaintDevice::PdmDpiX || metric == QPaintDevice::PdmDpiY) {
1066 return 72;
1067 }
1068
1069
1070 return QPaintDevice::metric(metric);
1071 }
1072 };
1073
1074 // paint device is used only to initialize DPI, so
1075 // we can delete it right after creation of the font
1076 FakePaintDevice fake72DpiPaintDevice;
1077 return QFont(font, &fake72DpiPaintDevice);
1078}
1079
1081{
1082 const KoSvgText::FontMetrics metrics = this->metrics(false);
1083
1084 const qreal fontSizeVal = fontSize().value;
1085 if (metrics.xHeight > 0 && metrics.fontSize > 0) {
1086 return metrics.xHeight * fontSizeVal / metrics.fontSize;
1087 }
1088 return fontSizeVal * 0.5;
1089}
1090
1091KoSvgText::FontMetrics KoSvgTextProperties::metrics(const bool withResolvedLineHeight) const
1092{
1093 const KoCSSFontInfo info = cssFontInfo();
1094
1095 const bool isHorizontal = propertyOrDefault(WritingModeId).toInt() == KoSvgText::HorizontalTB;
1098
1099 return withResolvedLineHeight? applyLineHeight(metrics): metrics;
1100}
1101
1103{
1104 const qreal res = metrics.fontSize / this->fontSize().value;
1105
1107 if (!lineHeight.isNormal) {
1108 if (lineHeight.isNumber) {
1109 metrics.lineGap = (metrics.fontSize)*lineHeight.value;
1110 } else {
1111 metrics.lineGap = lineHeight.length.value * res;
1112 }
1114 }
1115 return metrics;
1116}
1117
1119{
1120 using namespace KoSvgText;
1121 QStringList fontFeatures;
1122
1124 fontFeatures.append(liga.fontFeatures(start, start+length));
1126 fontFeatures.append(KoSvgText::fontFeaturesPosition(pos, start, start+length));
1128 fontFeatures.append(KoSvgText::fontFeaturesCaps(caps, start, start+length));
1130 fontFeatures.append(numeric.fontFeatures(start, start+length));
1132 fontFeatures.append(eastasian.fontFeatures(start, start+length));
1133
1134 if (!property(KerningId).value<AutoValue>().isAuto && property(KerningId).value<AutoValue>().customValue == 0) {
1135 QString openTypeTag = "kern";
1136 openTypeTag += QString("[%1:%2]").arg(start).arg(start + length);
1137 openTypeTag += "=0";
1138 fontFeatures.append(openTypeTag);
1139 openTypeTag = "vkrn";
1140 openTypeTag += QString("[%1:%2]").arg(start).arg(start + length);
1141 openTypeTag += "=0";
1142 fontFeatures.append(openTypeTag);
1143 }
1144
1146 QVariantMap features = property(FontFeatureSettingsId).toMap();
1147 for (int i = 0; i < features.keys().size(); i++) {
1148 const QString key = features.keys().at(i);
1149 QString openTypeTag = QString("%1[%2:%3]=%4").arg(key).arg(start).arg(start + length).arg(features.value(key).toInt());
1150 fontFeatures.append(openTypeTag);
1151 }
1152 }
1153
1154 return fontFeatures;
1155}
1156
1158{
1159 KoCSSFontInfo info;
1160 info.weight = propertyOrDefault(FontWeightId).toInt();
1161 info.width = propertyOrDefault(FontStretchId).toInt();
1164 info.families = propertyOrDefault(FontFamiliesId).toStringList();
1166 if (property(KraTextVersionId).toInt() >= 3) {
1167 info.fontSizeAdjust = fontSizeAdjust.isAuto? 0.0: fontSizeAdjust.customValue;
1168 }
1169
1171 info.slantMode = style.style;
1172 info.autoSlant = style.slantValue.isAuto;
1173 info.slantValue = style.slantValue.customValue;
1174
1175 QVariantMap features = property(FontVariationSettingsId).toMap();
1176 for (auto it = features.begin(); it != features.end(); it++) {
1177 info.axisSettings.insert(it.key(), it.value().toDouble());
1178 }
1179
1180 return info;
1181}
1182
1187
1192
1197
1202
1204{
1205 QStringList attributes;
1206 attributes << "writing-mode"
1207 << "glyph-orientation-vertical"
1208 << "glyph-orientation-horizontal"
1209 << "direction"
1210 << "unicode-bidi"
1211 << "text-anchor"
1212 << "dominant-baseline"
1213 << "alignment-baseline"
1214 << "baseline-shift"
1215 << "kerning"
1216 << "letter-spacing"
1217 << "word-spacing"
1218 << "xml:space"
1219 << "xml:lang"
1220 << "text-rendering";
1221 return attributes;
1222}
1223
1224namespace {
1225Q_GLOBAL_STATIC(KoSvgTextProperties, s_defaultProperties)
1226}
1227
1229{
1230 if (!s_defaultProperties.exists()) {
1231 using namespace KoSvgText;
1232
1233 s_defaultProperties->setProperty(WritingModeId, HorizontalTB);
1234 s_defaultProperties->setProperty(DirectionId, DirectionLeftToRight);
1235 s_defaultProperties->setProperty(UnicodeBidiId, BidiNormal);
1236 s_defaultProperties->setProperty(TextAnchorId, AnchorStart);
1237 s_defaultProperties->setProperty(DominantBaselineId, BaselineAuto);
1238 s_defaultProperties->setProperty(AlignmentBaselineId, BaselineDominant);
1239 s_defaultProperties->setProperty(BaselineShiftModeId, ShiftNone);
1240 s_defaultProperties->setProperty(BaselineShiftValueId, QVariant::fromValue(KoSvgText::CssLengthPercentage()));
1241 s_defaultProperties->setProperty(KerningId, fromAutoValue(AutoValue()));
1242 s_defaultProperties->setProperty(TextOrientationId, OrientationMixed);
1243 s_defaultProperties->setProperty(LetterSpacingId, QVariant::fromValue(AutoLengthPercentage()));
1244 s_defaultProperties->setProperty(WordSpacingId, QVariant::fromValue(AutoLengthPercentage()));
1245
1246 s_defaultProperties->setProperty(FontFamiliesId, QStringLiteral("sans-serif"));
1247 s_defaultProperties->setProperty(FontStyleId, QVariant::fromValue(KoSvgText::CssFontStyleData()));
1248 s_defaultProperties->setProperty(FontStretchId, 100);
1249 s_defaultProperties->setProperty(FontWeightId, 400);
1250 s_defaultProperties->setProperty(FontSizeId, QVariant::fromValue(KoSvgText::CssLengthPercentage(12.0)));
1251 s_defaultProperties->setProperty(FontSizeAdjustId, fromAutoValue(AutoValue()));
1252
1253 s_defaultProperties->setProperty(FontSynthesisBoldId, true);
1254 s_defaultProperties->setProperty(FontSynthesisItalicId, true);
1255 s_defaultProperties->setProperty(FontSynthesisSmallCapsId, true);
1256 s_defaultProperties->setProperty(FontSynthesisSuperSubId, true);
1257
1258 s_defaultProperties->setProperty(FontOpticalSizingId, true);
1259 s_defaultProperties->setProperty(TextRenderingId, RenderingAuto);
1260 {
1261 using namespace KoSvgText;
1262 TextDecorations deco = DecorationNone;
1263
1264 s_defaultProperties->setProperty(TextDecorationLineId, QVariant::fromValue(deco));
1265 s_defaultProperties->setProperty(TextDecorationPositionId, QVariant::fromValue(KoSvgText::TextUnderlinePosition()));
1266 s_defaultProperties->setProperty(TextDecorationColorId, QVariant::fromValue(Qt::transparent));
1267 s_defaultProperties->setProperty(TextDecorationStyleId, Solid);
1268
1269 s_defaultProperties->setProperty(TextCollapseId, Collapse);
1270 s_defaultProperties->setProperty(TextWrapId, Wrap);
1271 TextSpaceTrims trim = TrimNone;
1272 s_defaultProperties->setProperty(TextTrimId, QVariant::fromValue(trim));
1273 s_defaultProperties->setProperty(LineBreakId, LineBreakAuto);
1274 s_defaultProperties->setProperty(WordBreakId, WordBreakNormal);
1275 s_defaultProperties->setProperty(TextAlignAllId, AlignStart);
1276 s_defaultProperties->setProperty(TextAlignLastId, AlignLastAuto);
1277 s_defaultProperties->setProperty(TextTransformId, TextTransformNone);
1278 s_defaultProperties->setProperty(LineHeightId, QVariant::fromValue(KoSvgText::LineHeightInfo()));
1279 s_defaultProperties->setProperty(TabSizeId, QVariant::fromValue(KoSvgText::TabSizeInfo()));
1280 HangingPunctuations hang = HangNone;
1281 s_defaultProperties->setProperty(HangingPunctuationId, QVariant::fromValue(hang));
1282 }
1283 }
1284 return *s_defaultProperties;
1285}
1286
1288{
1289 return id == WritingModeId ||
1290 id == TextAlignAllId ||
1291 id == TextAlignLastId ||
1292 id == TextIndentId ||
1293 id == HangingPunctuationId ||
1294 id == TextRenderingId;
1295}
1296
1298{
1299 return m_d->isInheritable(id);
1300}
1301
qreal length(const QPointF &vec)
Definition Ellipse.cc:82
float value(const T *src, size_t ch)
Q_GLOBAL_STATIC(KisStoragePluginRegistry, s_instance)
QPair< QString, QString > parseTag(QString taggedValue)
qreal roundToStraightAngle(qreal value)
QVariantMap parseFeatureSettingsStringList(const QStringList features)
QVariantMap parseVariantStringList(const QStringList features)
@ Collapse
Collapse if first or last in line.
KoSvgText::FontMetrics fontMetricsForCSSValues(KoCSSFontInfo info=KoCSSFontInfo(), const bool isHorizontal=true, const KoSvgText::TextRendering rendering=KoSvgText::RenderingAuto, const QString &text="", quint32 xRes=72, quint32 yRes=72, bool disableFontMatching=false, const QString &language=QString())
static KoFontRegistry * instance()
bool hasNonInheritableProperties() const
Test whether it has non-inheritable properties set.
@ TextAnchorId
KoSvgText::TextAnchor.
@ InlineSizeId
KoSvgText::AutoValue.
@ UnicodeBidiId
KoSvgText::UnicodeBidi.
@ KraTextVersionId
Int, used for handling incorrectly saved files.
@ DominantBaselineId
KoSvgText::Baseline.
@ TextTrimId
Flags, KoSvgText::TextSpaceTrims.
@ AlignmentBaselineId
KoSvgText::Baseline.
@ LineHeightId
KoSvgText::AutoValue.
@ WordSpacingId
KoSvgText::AutoLengthPercentage.
@ LineBreakId
KoSvgText::LineBreak.
@ FontSizeAdjustId
KoSvgText::AutoValue.
@ LetterSpacingId
KoSvgText::AutoLengthPercentage.
@ TextOrientationId
KoSvgText::TextOrientation.
@ TextAlignAllId
KoSvgText::TextAlign.
@ TextCollapseId
KoSvgText::TextSpaceCollapse.
@ StrokeId
KoSvgText::StrokeProperty.
@ TextDecorationStyleId
KoSvgText::TextDecorationStyle.
@ TextOverFlowId
KoSvgText::WordBreak.
@ TextTransformId
KoSvgText::TextTransformInfo Struct.
@ FillId
KoSvgText::BackgroundProperty.
@ FontStyleId
KoSvgText::CssSlantData.
@ FontFeatureSettingsId
QStringList.
@ WritingModeId
KoSvgText::WritingMode.
@ DirectionId
KoSvgText::Direction.
@ TextWrapId
KoSvgText::TextWrap.
@ HangingPunctuationId
Flags, KoSvgText::HangingPunctuations.
@ BaselineShiftModeId
KoSvgText::BaselineShiftMode.
@ TextAlignLastId
KoSvgText::TextAlign.
@ FontVariantLigatureId
KoSvgText::FontVariantFeature.
@ TextDecorationPositionId
KoSvgText::TextDecorationUnderlinePosition.
@ TextIndentId
KoSvgText::TextIndentInfo Struct.
@ FontVariationSettingsId
QStringList.
@ WordBreakId
KoSvgText::WordBreak.
@ TextLanguage
a language string.
@ TextDecorationLineId
Flags, KoSvgText::TextDecorations.
@ KerningId
KoSvgText::AutoValue.
QSharedPointer< KoShapeBackground > background() const
void removeProperty(PropertyId id)
KoShapeStrokeModelSP stroke() const
bool propertyIsInheritable(KoSvgTextProperties::PropertyId id) const
QList< PropertyId > properties() const
static bool propertyIsBlockOnly(KoSvgTextProperties::PropertyId id)
QMap< QString, QString > convertToSvgTextAttributes() const
KoSvgTextProperties & operator=(const KoSvgTextProperties &rhs)
QVariant property(PropertyId id, const QVariant &defaultValue=QVariant()) const
QMap< QString, QString > convertParagraphProperties() const
convertParagraphProperties some properties only apply to the root shape, so we write those separately...
const QScopedPointer< Private > m_d
static const KoSvgTextProperties & defaultProperties()
QStringList fontFeaturesForText(int start, int length) const
fontFeaturesForText Returns a harfbuzz friendly list of opentype font-feature settings using the vari...
bool operator==(const KoSvgTextProperties &rhs) const
void scaleAbsoluteValues(const double scaleInline=1.0, const double scaleBlock=1.0)
scaleAbsoluteValues This scales all absolute values stored in these text properties....
bool inheritsProperty(PropertyId id, const KoSvgTextProperties &parentProperties) const
KoCSSFontInfo cssFontInfo() const
cssFontInfo
bool hasProperty(PropertyId id) const
void resolveRelativeValues(const KoSvgText::FontMetrics metrics=KoSvgText::FontMetrics(12.0, true), const qreal fontSize=12.0)
resolveRelativeValues resolve the font-relative values.
void setAllButNonInheritableProperties(const KoSvgTextProperties &properties)
Used to merge child properties into parent properties.
void setFontSize(const KoSvgText::CssLengthPercentage length)
void parseSvgTextAttribute(const SvgLoadingContext &context, const QString &command, const QString &value)
parseSvgTextAttribute add a property according to an XML attribute value.
void setProperty(PropertyId id, const QVariant &value)
KoSvgText::FontMetrics applyLineHeight(KoSvgText::FontMetrics metrics) const
applyLineHeight Calculate the linegap for the current linegap property.
KoSvgTextProperties ownProperties(const KoSvgTextProperties &parentProperties, bool keepFontSize=false) const
KoSvgText::FontMetrics metrics(const bool withResolvedLineHeight=true) const
metrics Return the metrics of the first available font.
static QStringList supportedXmlAttributes()
QVariant propertyOrDefault(PropertyId id) const
KoSvgText::CssLengthPercentage fontSize() const
void inheritFrom(const KoSvgTextProperties &parentProperties, bool resolve=false)
QRectF currentBoundingBox
the current bound box used for bounding box units
Contains data used for loading svg.
SvgGraphicsContext * currentGC() const
Returns the current graphics context.
KoSvgTextProperties resolvedProperties() const
These are the text properties, completely resolved, ensuring that everything is inherited and the siz...
static KoSvgText::CssLengthPercentage parseTextUnitStruct(SvgGraphicsContext *gc, QStringView unit)
Unit structs for text do not need the percentage to be resolved to viewport in most cases.
Definition SvgUtil.cpp:239
static qreal parseUnitXY(SvgGraphicsContext *gc, const KoSvgTextProperties &resolved, const QString &unit)
parses a length attribute in xy-direction
Definition SvgUtil.cpp:322
#define KIS_ASSERT(cond)
Definition kis_assert.h:33
T kisRadiansToDegrees(T radians)
Definition kis_global.h:181
std::enable_if< std::is_floating_point< T >::value, T >::type normalizeAngle(T a)
Definition kis_global.h:121
QString toString(const QString &value)
BaselineShiftMode parseBaselineShiftMode(const QString &value)
BaselineShiftMode
Mode of the baseline shift.
Definition KoSvgText.h:240
@ ShiftLengthPercentage
Css Length Percentage, percentage is lh.
Definition KoSvgText.h:244
FontFeatureCaps
Represents font-feature-caps.
Definition KoSvgText.h:897
bool xmlSpaceToLongHands(const QString &value, TextSpaceCollapse &collapseMethod)
xmlSpaceToLongHands This takes xml:space values and converts them to CSS-Text-4 properties.
AutoValue parseAutoValueY(const QString &value, const SvgLoadingContext &context, const QString &autoKeyword)
TextAnchor
Where the text is anchored for SVG 1.1 text and 'inline-size'.
Definition KoSvgText.h:79
FontFeaturePosition
The FontFeatureLigatures class This enum represents css font-variant-position.
Definition KoSvgText.h:886
TabSizeInfo parseTabSize(const QString &value, const SvgLoadingContext &context)
Baseline parseBaseline(const QString &value)
TextDecorationStyle
Style of the text-decoration.
Definition KoSvgText.h:265
FontFeatureEastAsian parseFontFeatureEastAsian(const QString &value, FontFeatureEastAsian features)
TextAnchor parseTextAnchor(const QString &value)
OverflowWrap
What to do with words that cannot be broken, but still overflow.
Definition KoSvgText.h:151
@ OverflowWrapBreakWord
Definition KoSvgText.h:155
@ OverflowWrapNormal
Definition KoSvgText.h:152
@ OverflowWrapAnywhere
Break anywhere as soon as overflow happens.
Definition KoSvgText.h:154
TextOrientation parseTextOrientation(const QString &value)
WordBreak
Whether to break words.
Definition KoSvgText.h:132
UnicodeBidi parseUnicodeBidi(const QString &value)
WritingMode parseWritingMode(const QString &value)
TextWrap
Part of "white-space", in practice we only support wrap and nowrap.
Definition KoSvgText.h:109
QString writeLineHeight(LineHeightInfo lineHeight)
@ DecorationNone
Definition KoSvgText.h:258
bool whiteSpaceValueToLongHands(const QString &value, TextSpaceCollapse &collapseMethod, TextWrap &wrapMethod, TextSpaceTrims &trimMethod)
whiteSpaceValueToLongHands CSS-Text-4 takes CSS-Text-3 whitespace values and treats them as a shortha...
CssFontStyleData parseFontStyle(const QString &value)
TextRendering parseTextRendering(const QString &value)
Baseline
Baseline values used by dominant-baseline and baseline-align.
Definition KoSvgText.h:213
AutoValue parseAutoValueAngular(const QString &value, const SvgLoadingContext &context, const QString &autoKeyword)
TextOverflow
How to handle overflow.
Definition KoSvgText.h:193
@ OverFlowVisible
Definition KoSvgText.h:194
@ OverFlowEllipse
Replace the last characters with "U+2026".
Definition KoSvgText.h:198
@ OverFlowClip
Clip the rendered content.
Definition KoSvgText.h:197
TextIndentInfo parseTextIndent(const QString &value, const SvgLoadingContext &context)
QString writeTextRendering(TextRendering value)
TextAlign parseTextAlign(const QString &value)
Direction parseDirection(const QString &value)
LineHeightInfo parseLineHeight(const QString &value, const SvgLoadingContext &context)
@ HorizontalTB
Definition KoSvgText.h:38
QVariant fromAutoValue(const KoSvgText::AutoValue &value)
Definition KoSvgText.h:493
AutoValue parseAutoValueXY(const QString &value, const SvgLoadingContext &context, const QString &autoKeyword)
FontFeaturePosition parseFontFeaturePosition(const QString &value, FontFeaturePosition feature)
QString writeFontStyle(CssFontStyleData value)
LineBreak parseLineBreak(const QString &value)
QStringList fontFeaturesPosition(const FontFeaturePosition &feature, const int start, const int end)
int parseCSSFontWeight(const QString &value, int currentWeight)
TextOrientation parseTextOrientationFromGlyphOrientation(AutoValue value)
static const std::array< const char *, 9 > fontStretchNames
Definition KoSvgText.h:543
AutoLengthPercentage parseAutoLengthPercentageXY(const QString &value, const SvgLoadingContext &context, const QString &autoKeyword, QRectF bbox, bool percentageIsViewPort)
FontFeatureCaps parseFontFeatureCaps(const QString &value, FontFeatureCaps feature)
FontFeatureLigatures parseFontFeatureLigatures(const QString &value, FontFeatureLigatures features)
int parseCSSFontStretch(const QString &value, int currentStretch)
parseCSSFontStretch For CSS3, the font-stretches were only given as keywords. In Css 4 and above,...
TextOrientation
Orientation of the glyphs, used for vertical writing modes.
Definition KoSvgText.h:70
TextTransformInfo parseTextTransform(const QString &value)
@ HangForce
Whether to force hanging stops or commas.
Definition KoSvgText.h:209
@ HangLast
Hang closing brackets and quotes.
Definition KoSvgText.h:207
@ HangFirst
Hang opening brackets and quotes.
Definition KoSvgText.h:206
@ HangEnd
Hang stops and commas. Force/Allow is a separate boolean.
Definition KoSvgText.h:208
TextSpaceCollapse
Definition KoSvgText.h:96
@ PreserveSpaces
required for 'xml:space="preserve"' emulation.
Definition KoSvgText.h:102
WordBreak parseWordBreak(const QString &value)
FontFeatureNumeric parseFontFeatureNumeric(const QString &value, FontFeatureNumeric features)
QStringList fontFeaturesCaps(const FontFeatureCaps &feature, const int start, const int end)
The KoCSSFontInfo class Convenience struct to make it easier to use KoFontRegistry....
QStringList families
double fontSizeAdjust
bool automaticOpticalSizing
< Size in Pt.
QMap< QString, double > axisSettings
QFont::Style slantMode
static bool isInheritable(PropertyId id)
QMap< PropertyId, QVariant > properties
CssLengthPercentage length
Definition KoSvgText.h:466
BackgroundProperty is a special wrapper around KoShapeBackground for managing it in KoSvgTextProperti...
Definition KoSvgText.h:714
When style is oblique, a custom slant value can be specified for variable fonts.
Definition KoSvgText.h:475
KoSvgText::AutoValue slantValue
Definition KoSvgText.h:479
@ Absolute
Pt, everything needs to be converted to pt for this to work.
Definition KoSvgText.h:408
@ Lh
multiply by width of "U+6C34", represents average full width script advance.
Definition KoSvgText.h:415
void convertToAbsolute(const KoSvgText::FontMetrics metrics, const qreal fontSize, const UnitType percentageUnit=Em)
QStringList fontFeatures(const int start, const int end)
Definition KoSvgText.h:1023
The FontFeatureLigatures class This struct represents css font-variant-ligatures.
Definition KoSvgText.h:847
QStringList fontFeatures(const int start, const int end)
Definition KoSvgText.h:860
The FontFeatureLigatures class This struct represents css font-variant-numeric.
Definition KoSvgText.h:935
QStringList fontFeatures(const int start, const int end)
Definition KoSvgText.h:951
The FontMetrics class A class to keep track of a variety of font metrics. Note that values are in Fre...
Definition KoSvgText.h:327
qint32 xHeight
height of X, defaults to 0.5 fontsize.
Definition KoSvgText.h:334
qint32 lineGap
additional linegap between consecutive lines.
Definition KoSvgText.h:341
qint32 fontSize
Currently set size, CSS unit 'em'.
Definition KoSvgText.h:329
qint32 descender
distance for origin to bottom.
Definition KoSvgText.h:340
qint32 ascender
distance from origin to top.
Definition KoSvgText.h:339
bool isNumber
Length or number.
Definition KoSvgText.h:695
CssLengthPercentage length
Definition KoSvgText.h:693
bool isNormal
It's a number indicating the lineHeight;.
Definition KoSvgText.h:696
StrokeProperty is a special wrapper around KoShapeStrokeModel for managing it in KoSvgTextProperties.
Definition KoSvgText.h:733
qreal value
A length or a number. Length is currently marked 'at-risk'.
Definition KoSvgText.h:675
CssLengthPercentage length
Definition KoSvgText.h:678
CssLengthPercentage length
Definition KoSvgText.h:658
TextDecorationUnderlinePosition verticalPosition
Definition KoSvgText.h:1070
TextDecorationUnderlinePosition horizontalPosition
Definition KoSvgText.h:1069