133 QString underlinePos;
134 for (
int i=0; i < PSDStyleSheet.keys().size(); i++) {
135 const QString key = PSDStyleSheet.keys().at(i);
136 if (key ==
"/Font") {
137 KoCSSFontInfo fontInfo = fontNames.value(PSDStyleSheet.value(key).toInt());
139 italic = italic?
true: fontInfo.
slantMode != QFont::StyleNormal;
140 styles.append(
"font-family:"+fontInfo.
families.join(
","));
141 if (fontInfo.
width != 100) {
142 styles.append(
"font-width:"+QString::number(fontInfo.
width));
145 }
else if (key ==
"/FontSize") {
146 double val = PSDStyleSheet.value(key).toDouble();
147 val = scale.map(QPointF(val, val)).y();
148 styles.append(
"font-size:"+QString::number(val));
150 }
else if (key ==
"/AutoKerning" || key ==
"/AutoKern") {
151 if (!PSDStyleSheet.value(key).toBool()) {
152 styles.append(
"font-kerning: none");
155 }
else if (key ==
"/Kerning") {
157 unsupportedStyles << key;
159 }
else if (key ==
"/FauxBold") {
160 if (PSDStyleSheet.value(key).toBool()) {
164 }
else if (key ==
"/FauxItalic") {
165 if (PSDStyleSheet.value(key).toBool()) {
170 }
else if (key ==
"/Leading") {
171 bool autoleading =
true;
172 if (PSDStyleSheet.keys().contains(
"AutoLeading")) {
173 autoleading = PSDStyleSheet.value(
"AutoLeading").toBool();
176 double fontSize = PSDStyleSheet.value(
"FontSize").toDouble();
177 double val = PSDStyleSheet.value(key).toDouble();
178 styles.append(
"line-height:"+QString::number(val/fontSize));
182 }
else if (key ==
"/HorizontalScale" || key ==
"/VerticalScale") {
184 unsupportedStyles << key;
186 }
else if (key ==
"/Tracking") {
188 double letterSpacing = (0.001 * PSDStyleSheet.value(key).toDouble());
189 styles.append(
"letter-spacing:"+QString::number(letterSpacing)+
"em");
191 }
else if (key ==
"/BaselineShift") {
192 if (PSDStyleSheet.value(key).toDouble() > 0) {
193 double val = PSDStyleSheet.value(key).toDouble();
194 val = scale.map(QPointF(val, val)).y();
195 baselineShift.append(QString::number(val));
198 }
else if (key ==
"/FontCaps") {
199 switch (PSDStyleSheet.value(key).toInt()) {
203 fontVariantCaps.append(
"all-small-caps");
206 styles.append(
"text-transform:uppercase");
209 d->warnings << QString(
"Unknown value for %1: %2").arg(key).arg(PSDStyleSheet.value(key).toString());
212 }
else if (key ==
"/FontBaseline") {
216 switch (PSDStyleSheet.value(key).toInt()) {
220 baselineShift.append(
"super");
223 baselineShift.append(
"sub");
226 d->warnings << QString(
"Unknown value for %1: %2").arg(key).arg(PSDStyleSheet.value(key).toString());
229 }
else if (key ==
"/FontOTPosition") {
232 switch (PSDStyleSheet.value(key).toInt()) {
236 styles.append(
"font-variant-position:super");
239 styles.append(
"font-variant-position:sub");
242 fontFeatureSettings.append(
"'numr' 1");
245 fontFeatureSettings.append(
"'dnum' 1");
248 d->warnings << QString(
"Unknown value for %1: %2").arg(key).arg(PSDStyleSheet.value(key).toString());
251 }
else if (key ==
"/Underline") {
252 if (PSDStyleSheet.value(key).toBool()) {
253 textDecor.append(
"underline");
256 }
else if (key ==
"/UnderlinePosition") {
257 switch (PSDStyleSheet.value(key).toInt()) {
261 textDecor.append(
"underline");
262 underlinePos =
"auto left";
265 textDecor.append(
"underline");
266 underlinePos =
"auto right";
269 d->warnings << QString(
"Unknown value for %1: %1").arg(key).arg(PSDStyleSheet.value(key).toString());
272 }
else if (key ==
"/YUnderline") {
274 if (PSDStyleSheet.value(key).toInt() == 1) {
275 underlinePos =
"auto left";
276 }
else if (PSDStyleSheet.value(key).toInt() == 0) {
277 underlinePos =
"auto right";
280 }
else if (key ==
"/Strikethrough" || key ==
"/StrikethroughPosition") {
281 if (PSDStyleSheet.value(key).toBool()) {
282 textDecor.append(
"line-through");
285 }
else if (key ==
"/Ligatures") {
286 if (!PSDStyleSheet.value(key).toBool()) {
287 fontVariantLigatures.append(
"no-common-ligatures");
290 }
else if (key ==
"/DLigatures" || key ==
"/DiscretionaryLigatures" || key ==
"/AlternateLigatures") {
291 if (PSDStyleSheet.value(key).toBool()) {
292 fontVariantLigatures.append(
"discretionary-ligatures");
295 }
else if (key ==
"/ContextualLigatures") {
296 if (PSDStyleSheet.value(key).toBool()) {
297 fontVariantLigatures.append(
"contextual");
300 }
else if (key ==
"/Fractions") {
301 if (PSDStyleSheet.value(key).toBool()) {
302 fontVariantNumeric.append(
"diagonal-fractions");
305 }
else if (key ==
"/Ordinals") {
306 if (PSDStyleSheet.value(key).toBool()) {
307 fontVariantNumeric.append(
"ordinal");
310 }
else if (key ==
"/Swash") {
311 if (PSDStyleSheet.value(key).toBool()) {
312 fontFeatureSettings.append(
"'swsh' 1");
315 }
else if (key ==
"/Titling") {
316 if (PSDStyleSheet.value(key).toBool()) {
317 fontVariantCaps.append(
"titling-caps");
320 }
else if (key ==
"/StylisticAlternates") {
321 if (PSDStyleSheet.value(key).toBool()) {
322 fontFeatureSettings.append(
"'salt' 1");
325 }
else if (key ==
"/Ornaments") {
326 if (PSDStyleSheet.value(key).toBool()) {
327 fontFeatureSettings.append(
"'ornm' 1");
330 }
else if (key ==
"/OldStyle") {
331 if (PSDStyleSheet.value(key).toBool() && !fontVariantNumeric.contains(
"oldstyle-nums")) {
332 fontVariantNumeric.append(
"oldstyle-nums");
335 }
else if (key ==
"/FigureStyle") {
336 switch (PSDStyleSheet.value(key).toInt()) {
340 fontVariantNumeric.append(
"tabular-nums");
341 fontVariantNumeric.append(
"lining-nums");
344 fontVariantNumeric.append(
"proportional-nums");
345 fontVariantNumeric.append(
"oldstyle-nums");
348 fontVariantNumeric.append(
"proportional-nums");
349 fontVariantNumeric.append(
"lining-nums");
352 fontVariantNumeric.append(
"tabular-nums");
353 fontVariantNumeric.append(
"oldstyle-nums");
356 d->warnings << QString(
"Unknown value for %1: %2").arg(key).arg(PSDStyleSheet.value(key).toString());
359 }
else if (key ==
"/Italics") {
361 if (PSDStyleSheet.value(key).toBool()) {
362 fontFeatureSettings.append(
"'ital' 1");
365 }
else if (key ==
"/BaselineDirection") {
366 int val = PSDStyleSheet.value(key).toInt();
368 styles.append(
"text-orientation: upright");
369 }
else if (val == 2) {
370 styles.append(
"text-orientation: mixed");
371 }
else if (val == 3) {
372 styles.append(
"text-combine-upright: all");
374 d->warnings << QString(
"Unknown value for %1: %2").arg(key).arg(PSDStyleSheet.value(key).toString());
377 }
else if (key ==
"/Tsume" || key ==
"/LeftAki" || key ==
"/RightAki" || key ==
"/JiDori") {
383 unsupportedStyles << key;
385 }
else if (key ==
"/StyleRunAlignment") {
389 QString dominantBaseline;
390 switch(PSDStyleSheet.value(key).toInt()) {
392 dominantBaseline =
"alphabetic";
395 dominantBaseline =
"center";
398 dominantBaseline =
"ideographic";
401 dominantBaseline =
"text-top";
404 dominantBaseline =
"text-bottom";
407 d->warnings << QString(
"Unknown value for %1: %2").arg(key).arg(PSDStyleSheet.value(key).toString());
408 dominantBaseline = QString();
410 if (!dominantBaseline.isEmpty()) {
411 styles.append(
"dominant-baseline: "+dominantBaseline);
412 styles.append(
"alignment-baseline: "+dominantBaseline);
415 }
else if (key ==
"/Language") {
416 int val = PSDStyleSheet.value(key).toInt();
420 d->warnings << QString(
"Unknown value for %1: %2").arg(key).arg(PSDStyleSheet.value(key).toString());
423 }
else if (key ==
"/ProportionalMetrics") {
424 if (PSDStyleSheet.value(key).toBool()) {
425 fontFeatureSettings.append(
"'palt' 1");
428 }
else if (key ==
"/Kana") {
429 if (PSDStyleSheet.value(key).toBool()) {
430 fontFeatureSettings.append(
"'hkna' 1");
433 }
else if (key ==
"/Ruby") {
434 if (PSDStyleSheet.value(key).toBool()) {
435 fontVariantEastAsian.append(
"ruby");
437 }
else if (key ==
"/JapaneseAlternateFeature") {
444 int val = PSDStyleSheet.value(key).toInt();
447 }
else if (val == 1) {
448 fontVariantEastAsian.append(
"traditional");
449 }
else if (val == 2) {
450 fontFeatureSettings.append(
"'expt' 1");
451 }
else if (val == 3) {
452 fontVariantEastAsian.append(
"jis78");
454 d->warnings << QString(
"Unknown value for %1: %2").arg(key).arg(PSDStyleSheet.value(key).toString());
457 }
else if (key ==
"/NoBreak") {
459 if (PSDStyleSheet.value(key).toBool()) {
460 styles.append(
"word-break: keep-all");
463 }
else if (key ==
"/DirOverride") {
464 QString dir = PSDStyleSheet.value(key).toBool()?
"rtl":
"ltr";
465 if (PSDStyleSheet.value(key).toBool()) {
466 styles.append(
"direction: "+dir);
467 styles.append(
"unicode-bidi: isolate");
470 }
else if (key ==
"/FillColor") {
472 if (PSDStyleSheet.keys().contains(
"/FillFlag")) {
473 fill = PSDStyleSheet.value(
"/FillFlag").toBool();
476 QVariantHash color = PSDStyleSheet.value(key).toHash();
479 styles.append(
"fill:none");
481 }
else if (key ==
"/StrokeColor") {
483 if (PSDStyleSheet.keys().contains(
"/StrokeFlag")) {
484 fill = PSDStyleSheet.value(
"/StrokeFlag").toBool();
487 QVariantHash color = PSDStyleSheet.value(key).toHash();
490 styles.append(
"stroke:none");
493 }
else if (key ==
"/OutlineWidth" || key ==
"/LineWidth") {
494 double val = PSDStyleSheet.value(key).toDouble();
495 val = scale.map(QPointF(val, val)).y();
496 styles.append(
"stroke-width:"+QString::number(val));
497 }
else if (key ==
"/FillFirst") {
499 if (PSDStyleSheet.value(key).toBool()) {
500 styles.append(
"paint-order: fill");
503 }
else if (key ==
"/HindiNumbers") {
506 unsupportedStyles << key;
508 }
else if (key ==
"/Kashida") {
511 unsupportedStyles << key;
513 }
else if (key ==
"/DiacriticPos") {
516 unsupportedStyles << key;
518 }
else if (key ==
"/SlashedZero") {
520 if (PSDStyleSheet.value(key).toBool()) {
521 fontVariantNumeric.append(
"slashed-zero");
524 }
else if (key ==
"/StylisticSets") {
525 int flags = PSDStyleSheet.value(key).toInt();
526 for (
int i = 1; i <= 20; i++) {
527 const int bit = 2^(i-1);
528 const QString tag = i > 9? QString(
"ss%1").arg(i):QString(
"ss0%1").arg(i);
530 fontFeatureSettings.append(QString(
"\'%1\' 1").arg(tag));
534 }
else if (key ==
"/LineCap") {
535 switch (PSDStyleSheet.value(key).toInt()) {
537 styles.append(
"stroke-linecap: butt");
540 styles.append(
"stroke-linecap: round");
543 styles.append(
"stroke-linecap: square");
546 styles.append(
"stroke-linecap: butt");
548 }
else if (key ==
"/LineJoin") {
549 switch (PSDStyleSheet.value(key).toInt()) {
551 styles.append(
"stroke-linejoin: miter");
554 styles.append(
"stroke-linejoin: round");
557 styles.append(
"stroke-linejoin: bevel");
560 styles.append(
"stroke-linejoin: miter");
562 }
else if (key ==
"/MiterLimit") {
563 styles.append(
"stroke-miterlimit: "+PSDStyleSheet.value(key).toString());
566 }
else if (key ==
"/LineDashOffset") {
567 styles.append(
"stroke-dashoffset: "+PSDStyleSheet.value(key).toString());
568 }
else if (key ==
"/EnableWariChu" || key ==
"/WariChuWidowAmount" || key ==
"/WariChuLineGap" || key ==
"/WariChuJustification"
569 || key ==
"/WariChuOrphanAmount" || key ==
"/WariChuLineCount" || key ==
"/WariChuSubLineAmount") {
571 unsupportedStyles << key;
573 }
else if (key ==
"/TCYUpDownAdjustment" || key ==
"/TCYLeftRightAdjustment") {
575 unsupportedStyles << key;
577 }
else if (key ==
"/Type1EncodingNames" || key ==
"/ConnectionForms") {
579 unsupportedStyles << key;
581 }
else if (key ==
"/FillOverPrint" || key ==
"/StrokeOverPrint" || key ==
"/Blend") {
583 unsupportedStyles << key;
585 }
else if (key ==
"/UnderlineOffset") {
587 unsupportedStyles << key;
590 if (key !=
"/FillFlag" && key !=
"/StrokeFlag" && key !=
"/AutoLeading") {
591 d->warnings << QString(
"Unknown PSD character stylesheet style key, %1: %2").arg(key).arg(PSDStyleSheet.value(key).toString());
596 styles.append(
"font-weight:"+QString::number(weight));
599 styles.append(
"font-style:italic");
601 if (!textDecor.isEmpty()) {
602 styles.append(
"text-decoration:"+textDecor.join(
" "));
604 if (!baselineShift.isEmpty()) {
605 styles.append(
"baseline-shift:"+baselineShift.join(
" "));
607 if (!fontVariantLigatures.isEmpty()) {
608 styles.append(
"font-variant-ligatures:"+fontVariantLigatures.join(
" "));
610 if (!fontVariantNumeric.isEmpty()) {
611 styles.append(
"font-variant-numeric:"+fontVariantNumeric.join(
" "));
613 if (!fontVariantCaps.isEmpty()) {
614 styles.append(
"font-variant-caps:"+fontVariantCaps.join(
" "));
616 if (!fontVariantEastAsian.isEmpty()) {
617 styles.append(
"font-variant-east-asian:"+fontVariantEastAsian.join(
" "));
619 if (!fontFeatureSettings.isEmpty()) {
620 styles.append(
"font-feature-settings:"+fontFeatureSettings.join(
", "));
622 if (!underlinePos.isEmpty()) {
623 styles.append(
"text-decoration-position:"+underlinePos);
625 d->warnings << QString(
"Unsupported styles: %1").arg(unsupportedStyles.join(
","));
626 return styles.join(
"; ");
633 for (
int i=0; i < PSDParagraphSheet.keys().size(); i++) {
634 const QString key = PSDParagraphSheet.keys().at(i);
635 double val = PSDParagraphSheet.value(key).toDouble();
636 if (key ==
"/Justification") {
637 QString textAlign =
"start";
638 QString textAnchor =
"start";
639 switch (PSDParagraphSheet.value(key).toInt()) {
642 textAnchor =
"start";
649 textAlign =
"center";
650 textAnchor =
"middle";
653 textAlign =
"justify start";
654 textAnchor =
"start";
657 textAlign =
"justify end";
661 textAlign =
"justify center";
662 textAnchor =
"middle";
665 textAlign =
"justify";
666 textAnchor =
"middle";
672 styles.append(
"text-align:"+textAlign);
673 styles.append(
"text-anchor:"+textAnchor);
674 }
else if (key ==
"/FirstLineIndent") {
675 val = scaleToPt.map(QPointF(val, val)).x();
676 styles.append(
"text-indent:"+QString::number(val));
678 }
else if (key ==
"/StartIndent") {
680 unsupportedStyles << key;
682 }
else if (key ==
"/EndIndent") {
684 unsupportedStyles << key;
686 }
else if (key ==
"/SpaceBefore") {
688 unsupportedStyles << key;
690 }
else if (key ==
"/SpaceAfter") {
692 unsupportedStyles << key;
694 }
else if (key ==
"/AutoHyphenate") {
696 unsupportedStyles << key;
698 }
else if (key ==
"/HyphenatedWordSize") {
700 unsupportedStyles << key;
702 }
else if (key ==
"/PreHyphen") {
705 unsupportedStyles << key;
707 }
else if (key ==
"/PostHyphen") {
710 unsupportedStyles << key;
712 }
else if (key ==
"/ConsecutiveHyphens") {
715 unsupportedStyles << key;
717 }
else if (key ==
"/HyphenateCapitalized") {
718 unsupportedStyles << key;
720 }
else if (key ==
"/HyphenationPreference") {
721 unsupportedStyles << key;
723 }
else if (key ==
"/SingleWordJustification") {
724 unsupportedStyles << key;
726 }
else if (key ==
"/Zone") {
729 unsupportedStyles << key;
731 }
else if (key ==
"/WordSpacing") {
734 unsupportedStyles << key;
736 }
else if (key ==
"/LetterSpacing") {
739 unsupportedStyles << key;
741 }
else if (key ==
"/GlyphSpacing") {
743 unsupportedStyles << key;
745 }
else if (key ==
"/AutoLeading") {
746 styles.append(
"line-height:"+QString::number(val));
748 }
else if (key ==
"/LeadingType") {
751 unsupportedStyles << key;
753 }
else if (key ==
"/Hanging") {
756 }
else if (key ==
"/Burasagari" || key ==
"/BurasagariType") {
759 if (PSDParagraphSheet.value(key).toBool()) {
760 styles.append(
"hanging-punctuation:allow-end");
763 }
else if (key ==
"/Kinsoku") {
765 unsupportedStyles << key;
767 }
else if (key ==
"/KinsokuOrder") {
769 unsupportedStyles << key;
771 }
else if (key ==
"/EveryLineComposer") {
775 unsupportedStyles << key;
777 }
else if (key ==
"/ComposerEngine") {
778 unsupportedStyles << key;
780 }
else if (key ==
"/KurikaeshiMojiShori") {
781 unsupportedStyles << key;
783 }
else if (key ==
"/MojiKumiTable") {
784 unsupportedStyles << key;
786 }
else if (key ==
"/DropCaps") {
787 unsupportedStyles << key;
789 }
else if (key ==
"/TabStops" || key ==
"/AutoTCY" || key ==
"/KeepTogether" ) {
790 unsupportedStyles << key;
792 }
else if (key ==
"/ParagraphDirection") {
793 switch (PSDParagraphSheet.value(key).toInt()) {
795 styles.append(
"direction:rtl");
798 styles.append(
"direction:ltr");
803 }
else if (key ==
"/DefaultTabWidth") {
804 unsupportedStyles << key;
806 }
else if (key ==
"/DefaultStyle") {
807 styles.append(
stylesForPSDStyleSheet(lang, PSDParagraphSheet.value(key).toHash(), fontNames, scaleToPt, imageCs));
809 d->warnings << QString(
"Unknown PSD character stylesheet style key, %1: %2").arg(key).arg(PSDParagraphSheet.value(key).toString());
812 d->warnings << QString(
"Unsupported paragraph styles: %1").arg(unsupportedStyles.join(
","));
814 return styles.join(
"; ");
818 const QVariantHash txt2,
824 bool &offsetByAscent,
826 QTransform scaleToPt)
830 QVariantHash root = tySh;
831 bool loadFallback = txt2.isEmpty();
832 const QVariantHash docObjects = txt2.value(
"/DocumentObjects").toHash();
834 QVariantHash textObject = docObjects.value(
"/TextObjects").toList().value(textIndex).toHash();
835 if (textObject.isEmpty() || loadFallback) {
836 textObject = root[
"/EngineDict"].toHash();
839 if (textObject.isEmpty()) {
840 d->errors <<
"No engine dict found in PSD engine data";
844 QMap<int, KoCSSFontInfo> fontNames;
845 QVariantHash resourceDict = loadFallback? root.value(
"/DocumentResources").toHash(): txt2.value(
"/DocumentResources").toHash();
846 if (resourceDict.isEmpty()) {
847 d->errors <<
"No engine dict found in PSD engine data";
851 QVariantList fonts = loadFallback? resourceDict.value(
"/FontSet").toList()
852 : resourceDict.value(
"/FontSet").toHash().value(
"/Resources").toList();
853 for (
int i = 0; i < fonts.size(); i++) {
854 QVariantHash font = loadFallback? fonts.value(i).toHash()
855 : fonts.value(i).toHash().value(
"/Resource").toHash().value(
"/Identifier").toHash();
856 QString postScriptName = font.value(
"/Name").toString();
857 QString foundPostScriptName;
859 &foundPostScriptName);
861 if (postScriptName != foundPostScriptName) {
863 d->errors << QString(
"Font %1 not found, substituting %2").arg(postScriptName).arg(fontInfo.
families.join(
","));
865 fontNames.insert(i, fontInfo);
869 QString inlineSizeString;
873 QScopedPointer<KoPathShape> textShape;
874 double textPathStartOffset = -3;
875 double shapePadding = 0.0;
877 bool reversed =
false;
879 QVariantHash rendered = textObject.value(
"/Rendered").toHash();
881 if (!rendered.isEmpty()) {
882 QVariantHash shapeChild = rendered.value(
"/Shapes").toHash().value(
"/Children").toList()[0].toHash();
883 textType = shapeChild.value(
"/ShapeType").toInt();
885 QVariantList BoxBounds = shapeChild.value(
"/Cookie").toHash().value(
"/Photoshop").toHash().value(
"/BoxBounds").toList();
886 if (BoxBounds.size() == 4) {
890 inlineSizeString =
" inline-size:"+QString::number(
bounds.width())+
";";
892 inlineSizeString =
" inline-size:"+QString::number(
bounds.height())+
";";
898 QVariantHash view = textObject.value(
"/View").toHash();
900 QVariantList frames = view.value(
"/Frames").toList();
901 if (!frames.isEmpty()) {
902 int textFrameIndex = view.value(
"/Frames").toList().value(0).toHash().value(
"/Resource").toInt();
903 QVariantList textFrameSet = resourceDict.value(
"/TextFrameSet").toHash().value(
"/Resources").toList();
904 QVariantHash textFrame = textFrameSet.at(textFrameIndex).toHash().value(
"/Resource").toHash();
907 if (!textFrame.isEmpty()) {
908 textType = textFrame[
"/Data"].toHash()[
"/Type"].toInt();
911 QScopedPointer<KoPathShape> textCurve(
new KoPathShape());
912 QVariantHash data = textFrame.value(
"/Data").toHash();
913 QVariantList points = textFrame.value(
"/Bezier").toHash().value(
"/Points").toList();
914 QVariantList range = data.value(
"/TextOnPathTRange").toList();
915 QVariantList fm = data.value(
"/FrameMatrix").toList();
916 shapePadding = data.value(
"/Spacing").toDouble();
917 QVariantHash pathData = data.value(
"/PathData").toHash();
918 reversed = pathData.value(
"/Flip").toBool();
920 QVariant lineOrientation = data.value(
"/LineOrientation");
921 if (!lineOrientation.isNull()) {
922 if (lineOrientation.toInt() == 2) {
923 isHorizontal =
false;
926 QTransform frameMatrix = scaleToPt;
927 if (fm.size() == 6) {
929 frameMatrix = frameMatrix * scaleToPt;
932 int length = points.size()/8;
936 for (
int i = 0; i <
length; i++) {
943 if (i == 0 || endPoint != frameMatrix.map(
p1)) {
944 if (endPoint == startPoint && i > 0) {
945 textCurve->closeMerge();
947 textCurve->moveTo(frameMatrix.map(
p1));
948 startPoint = frameMatrix.map(
p1);
951 textCurve->lineTo(frameMatrix.map(p4));
953 textCurve->curveTo(frameMatrix.map(
p2), frameMatrix.map(
p3), frameMatrix.map(p4));
955 endPoint = frameMatrix.map(p4);
957 if (points.size() > 8) {
958 if (endPoint == startPoint) {
959 textCurve->closeMerge();
961 textShape.reset(textCurve.data());
963 if (!range.isEmpty()) {
964 textPathStartOffset = range[0].toDouble();
965 int segment = qFloor(textPathStartOffset);
966 double t = textPathStartOffset - segment;
968 double totalLength = 0;
969 for (
int i=0; i<textShape->subpathPointCount(0); i++) {
974 }
else if (i == segment) {
978 textPathStartOffset = (
length/totalLength) * 100.0;
984 QString paragraphStyle = isHorizontal?
"writing-mode: horizontal-tb;":
"writing-mode: vertical-rl;";
985 paragraphStyle +=
" white-space: pre-wrap;";
989 svgBuffer.open(QIODevice::WriteOnly);
990 styleBuffer.open(QIODevice::WriteOnly);
992 QXmlStreamWriter svgWriter(&svgBuffer);
993 QXmlStreamWriter stylesWriter(&styleBuffer);
994 stylesWriter.writeStartElement(
"defs");
996 stylesWriter.writeStartElement(
"rect");
997 stylesWriter.writeAttribute(
"id",
"bounds");
998 stylesWriter.writeAttribute(
"x", QString::number(
bounds.x()));
999 stylesWriter.writeAttribute(
"y", QString::number(
bounds.y()));
1000 stylesWriter.writeAttribute(
"width", QString::number(
bounds.width()));
1001 stylesWriter.writeAttribute(
"height", QString::number(
bounds.height()));
1002 stylesWriter.writeEndElement();
1005 stylesWriter.writeStartElement(
"path");
1006 stylesWriter.writeAttribute(
"id",
"textShape");
1007 stylesWriter.writeAttribute(
"d", textShape->toString());
1008 stylesWriter.writeAttribute(
"sodipodi:nodetypes", textShape->nodeTypes());
1009 stylesWriter.writeEndElement();
1014 svgWriter.setAutoFormatting(
false);
1016 svgWriter.writeStartElement(
"text");
1018 QVariantHash editor = loadFallback? textObject.value(
"/Editor").toHash() : textObject.value(
"/Model").toHash();
1020 if (editor.isEmpty()) {
1021 d->errors <<
"No editor dict found in PSD engine data";
1024 text = editor.value(
"/Text").toString();
1025 text.replace(
"\r",
"\n");
1026 text.replace(QChar(0x03),
"\n");
1029 int antiAliasing = 0;
1030 antiAliasing = loadFallback? textObject.value(
"/AntiAlias").toInt()
1031 : textObject.value(
"/StorySheet").toHash().value(
"/AntiAlias").toInt();
1033 if (antiAliasing == 3) {
1034 svgWriter.writeAttribute(
"text-rendering",
"auto");
1035 }
else if (antiAliasing == 0) {
1036 svgWriter.writeAttribute(
"text-rendering",
"OptimizeSpeed");
1039 QVariantHash paragraphRun = loadFallback? textObject.value(
"/ParagraphRun").toHash() : editor.value(
"/ParagraphRun").toHash();
1040 if (!paragraphRun.isEmpty()) {
1042 QVariantList runArray = paragraphRun.value(
"/RunArray").toList();
1043 QString features = loadFallback?
"/Properties":
"/Features";
1044 QVariantHash style = loadFallback? runArray.value(0).toHash() : runArray.value(0).toHash().value(
"/RunData").toHash();
1045 QVariantHash parasheet = loadFallback? runArray.value(0).toHash()[
"/ParagraphSheet"].toHash():
1046 runArray.at(0).toHash()[
"/RunData"].toHash()[
"/ParagraphSheet"].toHash();
1047 QVariantHash styleSheet = parasheet[features].toHash();
1051 if (!lang.isEmpty()) {
1052 svgWriter.writeAttribute(
"xml:lang", lang);
1056 offsetByAscent =
false;
1057 paragraphStyle +=
" shape-inside:url(#textShape);";
1058 if (shapePadding > 0) {
1059 QPointF sPadding = scaleToPt.map(QPointF(shapePadding, shapePadding));
1060 paragraphStyle +=
" shape-padding:"+QString::number(sPadding.x())+
";";
1062 }
else if (styleString.contains(
"text-align:justify") &&
bounds.isValid()) {
1063 offsetByAscent =
false;
1064 paragraphStyle +=
" shape-inside:url(#bounds);";
1065 }
else if (
bounds.isValid()){
1066 offsetByAscent =
true;
1067 offset = isHorizontal?
bounds.topLeft():
bounds.topRight();
1068 if (styleString.contains(
"text-anchor:middle")) {
1069 offset = isHorizontal? QPointF(
bounds.center().x(), offset.y()):
1070 QPointF(offset.x(),
bounds.center().y());
1071 }
else if (styleString.contains(
"text-anchor:end")) {
1072 offset = isHorizontal? QPointF(
bounds.right(), offset.y()):
1073 QPointF(offset.x(),
bounds.bottom());
1075 paragraphStyle += inlineSizeString;
1076 svgWriter.writeAttribute(
"transform", QString(
"translate(%1, %2)").arg(offset.x()).arg(offset.y()));
1079 paragraphStyle += styleString;
1080 svgWriter.writeAttribute(
"style", paragraphStyle);
1084 bool textPathCreated =
false;
1085 if (textShape && textType == 2) {
1086 svgWriter.writeStartElement(
"textPath");
1087 textPathCreated =
true;
1088 svgWriter.writeAttribute(
"path", textShape->toString());
1090 svgWriter.writeAttribute(
"side",
"right");
1092 svgWriter.writeAttribute(
"startOffset", QString::number(textPathStartOffset)+
"%");
1095 QVariantHash styleRun = loadFallback? textObject.value(
"/StyleRun").toHash(): editor.value(
"/StyleRun").toHash();
1096 if (styleRun.isEmpty()) {
1097 d->errors <<
"No styleRun dict found in PSD engine data";
1100 QString features = loadFallback?
"/StyleSheetData":
"/Features";
1101 QVariantList runLengthArray = styleRun.value(
"/RunLengthArray").toList();
1102 QVariantList runArray = styleRun.value(
"/RunArray").toList();
1103 if (runArray.isEmpty()) {
1104 d->errors <<
"No styleRun dict found in PSD engine data";
1107 QVariantHash style = loadFallback? runArray.at(0).toHash() : runArray.at(0).toHash()[
"/RunData"].toHash();
1108 QVariantHash styleSheet = style.value(
"/StyleSheet").toHash().value(features).toHash();
1111 for (
int i = 0; i < runArray.size(); i++) {
1112 style = loadFallback? runArray.at(i).toHash() : runArray.at(i).toHash()[
"/RunData"].toHash();
1113 int l = loadFallback? runLengthArray.at(i).toInt(): runArray.at(i).toHash().value(
"/Length").toInt();
1115 QVariantHash newStyleSheet = style.value(
"/StyleSheet").toHash().value(features).toHash();
1116 if (newStyleSheet == styleSheet) {
1119 svgWriter.writeStartElement(
"tspan");
1121 svgWriter.writeAttribute(
"style",
stylesForPSDStyleSheet(lang, styleSheet, fontNames, scaleToPt, imageCs));
1122 if (!lang.isEmpty()) {
1123 svgWriter.writeAttribute(
"xml:lang", lang);
1125 svgWriter.writeCharacters(text.mid(pos,
length));
1126 svgWriter.writeEndElement();
1127 styleSheet = newStyleSheet;
1132 svgWriter.writeStartElement(
"tspan");
1134 svgWriter.writeAttribute(
"style",
stylesForPSDStyleSheet(lang, styleSheet, fontNames, scaleToPt, imageCs));
1135 if (!lang.isEmpty()) {
1136 svgWriter.writeAttribute(
"xml:lang", lang);
1138 svgWriter.writeCharacters(text.mid(pos));
1139 svgWriter.writeEndElement();
1143 if (textPathCreated) {
1144 svgWriter.writeEndElement();
1147 svgWriter.writeEndElement();
1148 stylesWriter.writeEndElement();
1150 if (svgWriter.hasError() || stylesWriter.hasError()) {
1151 d->errors << i18n(
"Unknown error writing SVG text element");
1154 *svgText = QString::fromUtf8(svgBuffer.data()).trimmed();
1155 *svgStyles = QString::fromUtf8(styleBuffer.data()).trimmed();
1403 QVariantHash parentStyle,
1404 QMap<QString, QString> parentCssStyles,
1405 QVariantList &styles,
1406 QVariantList &fontSet, QTransform scaleToPx) {
1407 QMap<QString, QString> cssStyles = parentCssStyles;
1408 if (el.hasAttribute(
"style")) {
1409 QString style = el.attribute(
"style");
1412 Q_FOREACH(QString style, dummy) {
1413 QString key = style.split(
":").first().trimmed();
1414 QString val = style.split(
":").last().trimmed();
1415 cssStyles.insert(key, val);
1418 if (el.hasAttribute(attribute)) {
1419 cssStyles.insert(attribute, el.attribute(attribute));
1424 if (el.firstChild().isText()) {
1425 QDomText textNode = el.firstChild().toText();
1426 QString currentText = textNode.data();
1427 text += currentText;
1434 gatherFonts(cssStyles, currentText, fontSet, lengths, fontIndices);
1435 for (
int i = 0; i< fontIndices.size(); i++) {
1436 QVariantHash curDict = styleDict;
1437 curDict[
"/Font"] = fontIndices.at(i);
1438 QVariantHash fDict = {
1439 {
"/StyleSheet", QVariantHash({{
"/Name",
""}, {
"/Parent", 0}, {
"/Features", curDict}})}
1441 styles.append(QVariantHash({
1442 {
"/Length", lengths.at(i)},
1443 {
"/RunData", fDict},
1447 }
else if (el.childNodes().size()>0) {
1450 QDomElement childEl = el.firstChildElement();
1451 while(!childEl.isNull()) {
1452 gatherStyles(childEl, text, styleDict, cssStyles, styles, fontSet, scaleToPx);
1453 childEl = childEl.nextSiblingElement();
1522 QTransform scaleToPx)
1530 QVariantList styles;
1531 QVariantList fontSet;
1533 QVariantList textObjects = txt2.value(
"/DocumentObjects").toHash().value(
"/TextObjects").toList();
1534 QVariantHash defaultParagraphProps = txt2.value(
"/DocumentObjects").toHash().value(
"/OriginalNormalParagraphFeatures").toHash();
1536 const int tIndex = textObjects.size();
1537 QVariantHash docResources = txt2.value(
"/DocumentResources").toHash();
1538 QVariantList textFrames = docResources.value(
"/TextFrameSet").toHash().value(
"/Resources").toList();
1539 const QVariantList resFontSet = docResources.value(
"/FontSet").toHash().value(
"/Resources").toList();
1541 Q_FOREACH(
const QVariant entry, resFontSet) {
1542 const QVariantHash docFont = entry.toHash().value(
"/Resource").toHash();
1543 QVariantHash font = docFont.value(
"/Identifier").toHash();
1544 fontSet.append(font);
1553 doc.setContent(svgText);
1554 gatherStyles(doc.documentElement(), text, QVariantHash(), QMap<QString, QString>(), styles, fontSet, scaleToPx);
1558 defaultParagraphProps,
1559 isHorizontal, &inlineSize,
1563 model.insert(
"/Text", text);
1565 QVariantHash paragraphSet;
1566 paragraphSet.insert(
"/Length", QVariant(text.length()));
1567 paragraphSet.insert(
"/RunData", QVariantHash{{
"/ParagraphSheet", paragraphStyle}});
1569 model.insert(
"/ParagraphRun", QVariantHash{{
"/RunArray", QVariantList({paragraphSet})}});
1571 QVariantHash styleRun;
1572 QVariantList properStyleRun;
1573 Q_FOREACH(QVariant entry, styles) {
1574 properStyleRun.append(entry);
1576 styleRun.insert(
"/RunArray", properStyleRun);
1578 model.insert(
"/StyleRun", styleRun);
1580 QVariantHash storySheet;
1581 storySheet.insert(
"/UseFractionalGlyphWidths",
true);
1582 storySheet.insert(
"/AntiAlias", 1);
1583 model.insert(
"/StorySheet", storySheet);
1586 if (!(inlineSize.isEmpty() || inlineSize ==
"auto")) {
1589 double inlineSizeVal = inlineSize.toDouble(&ok);
1592 bounds.setWidth(inlineSizeVal);
1594 bounds.setHeight(inlineSizeVal);
1601 int shapeType =
bounds.isEmpty()? 0: 1;
1602 int writingDirection = isHorizontal? 0: 2;
1605 const int textFrameIndex = textFrames.size();
1606 QVariantHash newTextFrame;
1607 QVariantHash newTextFrameData;
1608 newTextFrameData.insert(
"/LineOrientation", writingDirection);
1613 QScopedPointer<KoPathShape> textShape;
1614 Q_FOREACH(
KoShape *shape, shapesInside) {
1622 for (
int i = 0; i<textShape->subpathPointCount(0); i++) {
1629 }
else if (!
bounds.isEmpty()) {
1630 points.append(
bounds.topLeft());
1631 points.append(
bounds.topLeft());
1632 points.append(
bounds.topRight());
1633 points.append(
bounds.topRight());
1634 points.append(
bounds.topRight());
1635 points.append(
bounds.topRight());
1636 points.append(
bounds.bottomRight());
1637 points.append(
bounds.bottomRight());
1638 points.append(
bounds.bottomRight());
1639 points.append(
bounds.bottomRight());
1640 points.append(
bounds.bottomLeft());
1641 points.append(
bounds.bottomLeft());
1642 points.append(
bounds.bottomLeft());
1643 points.append(
bounds.bottomLeft());
1644 points.append(
bounds.topLeft());
1645 points.append(
bounds.topLeft());
1647 if (!points.isEmpty()) {
1649 for(
int i = 0; i < points.size(); i++) {
1650 QPointF
p2 = scaleToPx.map(points.at(i));
1654 newTextFrame.insert(
"/Bezier", QVariantHash({{
"/Points",
p}}));
1657 newTextFrameData.insert(
"/Type", shapeType);
1658 newTextFrame.insert(
"/Data", newTextFrameData);
1660 view.insert(
"/Frames", QVariantList({QVariantHash({{
"/Resource", textFrameIndex}})}));
1662 QVariantList bbox = {0.0, 0.0,
bounds.width(),
bounds.height()};
1695 QVariantHash rendered {
1696 {
"/RunData", QVariantHash({{
"/LineCount", 1}})},
1697 {
"/Length", textTotal.length()}
1699 view.insert(
"/RenderedData", QVariantHash({{
"/RunArray", QVariantList({rendered})}}));
1702 textFrames.append(QVariantHash({{
"/Resource", newTextFrame}}));
1703 textObjects.append(QVariantHash({{
"/Model", model}, {
"/View", view}}));
1709 QVariantList newFontSet;
1711 Q_FOREACH(
const QVariant entry, fontSet) {
1712 newFontSet.append(QVariantHash({{
"/Resource", QVariantHash({{
"/StreamTag",
"/CoolTypeFont"}, {
"/Identifier", entry}})}}));
1715 QVariantHash docObjects = txt2.value(
"/DocumentObjects").toHash();
1716 docObjects.insert(
"/TextObjects", textObjects);
1717 txt2.insert(
"/DocumentObjects", docObjects);
1718 docResources.insert(
"/TextFrameSet", QVariantHash({{
"/Resources", textFrames}}));
1719 docResources.insert(
"/FontSet", QVariantHash({{
"/Resources", newFontSet}}));
1720 txt2.insert(
"/DocumentResources", docResources);