Krita Source Code Documentation
Loading...
Searching...
No Matches
KoFFWWSConverter.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2024 Wolthera van Hövell tot Westerflier <griffinvalley@gmail.com>
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 */
6#include "KoFFWWSConverter.h"
7
8#include <KisForest.h>
10#include <kis_assert.h>
11#include <hb.h>
12#include <hb-ft.h>
13#include FT_TRUETYPE_TABLES_H
14
15#include <QFileInfo>
16
17
32 bool isSet = false;
33 bool os2table = false;
34 int subFamilyID = 0;
35
36 qreal low = -1;
37 qreal high = -1;
38 qreal designSize = 0;
39 QHash<QLocale, QString> localizedLabels;
40 QString debugInfo() const {
41 QString label;
42 if (!localizedLabels.isEmpty()) {
43 label = localizedLabels.value(QLocale(QLocale::English), localizedLabels.values().first());
44 }
45 return QString("Optical Size Info: OS2=%1, label: %2, min: %3, max: %4, designSize: %5").arg(os2table? "true": "false").arg(label).arg(low).arg(high).arg(designSize);
46 }
47
48 bool compare(const FontFamilySizeInfo &other) {
49 if (isSet != other.isSet) return false;
50 if (os2table != other.os2table) {
51 return false;
52 } else if (os2table) {
53 return qFuzzyCompare(low, other.low) && qFuzzyCompare(high, other.high);
54 } else {
55 return qFuzzyCompare(designSize, other.designSize);
56 }
57 }
58};
59
60QDebug operator<<(QDebug dbg, const FontFamilySizeInfo &info) {
61 dbg.nospace() << info.debugInfo();
62 return dbg.space();
63}
64
66
68
69 QString fontFamily;
70 QString fontStyle;
71 QString fileName;
72 int fileIndex = 0;
73
74 QHash<QString, QString> sampleStrings;
76
78 QDateTime lastModified;
79
80 // The localized font-families. This should be the name associated with the current node,
81 // and thus is the typographic, wws or ribbi name depending on the depth.
82 QHash<QLocale, QString> localizedFontFamilies;
83
84 // Style name can depend on depth, and when returning the representation, we need to select the correct name.
85 QHash<QLocale, QString> localizedFontStyle;
86 QHash<QLocale, QString> localizedTypographicStyle;
87 QHash<QLocale, QString> localizedWWSStyle;
88
89 // The full proper name as used by Windows to identify unique names.
90 QHash<QLocale, QString> localizedFullName;
91
99 QHash<QString, KoSvgText::FontFamilyAxis> axes;
100
108
118 QHash<int, QStringList> pixelSizes;
119
126
127 // Data from the osTable fsSelection flags. Older fonts use these to identify italic/oblique in ways that are hard
128 // to coalesce with the axes information, though it'd be good if we could figure out how.
129 bool isItalic = false;
130 bool isOblique = false;
131
132 bool compareAxes(QHash<QString, KoSvgText::FontFamilyAxis> otherAxes) {
133 if (axes.keys() != otherAxes.keys()) {
134 return false;
135 }
136 for (int k = 0; k < axes.keys().size(); k++) {
137 KoSvgText::FontFamilyAxis a = axes.value(axes.keys().at(k));
138 KoSvgText::FontFamilyAxis b = otherAxes.value(axes.keys().at(k));
139 if (!qFuzzyCompare(a.value, b.value)) {
140 return false;
141 }
142 }
143 return true;
144 }
145
146 static FontFamilyNode createWWSFamilyNode(const FontFamilyNode &child, const FontFamilyNode &typographic, QStringList existingWWSNames) {
147 FontFamilyNode wwsFamily;
148 if (child.type != KoSvgText::OpenTypeFontType) {
149 if (child.fontStyle.toLower() == "regular") {
150 wwsFamily.fontFamily = child.fontFamily;
151 } else {
152 wwsFamily.fontFamily = child.fontFamily + " " + child.fontStyle;
153 }
154 wwsFamily.fontStyle = child.fontStyle;
155 } else {
156 wwsFamily.fontFamily = typographic.fontFamily;
157 if (existingWWSNames.contains(typographic.fontFamily)) {
158 wwsFamily.fontFamily = child.fontFamily;
159 if (existingWWSNames.contains(child.fontFamily)) {
160 wwsFamily.fontFamily = child.fontFamily + " " + child.fontStyle;
161 }
162 }
163 }
166 wwsFamily.isVariable = child.isVariable;
167 wwsFamily.colorBitMap = child.colorBitMap;
168 wwsFamily.colorSVG = child.colorSVG;
169 wwsFamily.colorClrV0 = child.colorClrV0;
170 wwsFamily.colorClrV1 = child.colorClrV1;
171 wwsFamily.type = child.type;
172 return wwsFamily;
173 }
174
176 bool isVariable = false;
177 bool colorClrV0 = false;
178 bool colorClrV1 = false;
179 bool colorSVG = false;
180 bool colorBitMap = false;
181
182 bool hasAnyColor() const {
183 return (colorClrV0 || colorClrV1 || colorSVG || colorBitMap);
184 }
185
186 QStringList debugInfo() const;
187};
188
190{
191 const QString style = isItalic? isOblique? "Oblique": "Italic": "Roman";
192 const QString fullname = localizedFullName.empty()? "": localizedFullName.values().first();
193 QStringList debug = {QString("\'%1\' \'%2\', style: %3, type:%4, full name: %5").arg(fontFamily, fontStyle, style).arg(type).arg(fullname)};
194 debug.append(QString("Index: %1, File: %2").arg(fileIndex).arg(fileName));
195 for (int i=0; i< axes.size(); i++) {
196 KoSvgText::FontFamilyAxis axis = axes.value(axes.keys().at(i));
197 debug.append(axis.debugInfo());
198 }
199 for (int i=0; i< styleInfo.size(); i++) {
200 debug.append(styleInfo.at(i).debugInfo());
201 }
202 if (sizeInfo.low >= 0 && sizeInfo.high >= 0) {
203 debug.append(sizeInfo.debugInfo());
204 }
205 if (!pixelSizes.isEmpty()) {
206 QStringList pix;
207 for (int i=0; i< pixelSizes.size(); i++) {
208 pix.append(QString::number(pixelSizes.keys().at(i)));
209 }
210 debug.append("PixelSizes: "+pix.join(", "));
211 } else if (!otherFiles.isEmpty()) {
212 debug.append("Other files: "+otherFiles.join(", "));
213 }
214 return debug;
215}
216
217QDebug operator<<(QDebug dbg, const FontFamilyNode &node) {
218 dbg.nospace() << node.debugInfo();
219 return dbg.space();
220}
221
258
260 : d(new Private())
261{
262
263}
264
268
269// OS2 fsSelection bitflags. See https://learn.microsoft.com/en-us/typography/opentype/spec/os2#fsselection
270constexpr unsigned OS2_ITALIC = 1u << 0;
271constexpr unsigned OS2_BOLD = 1u << 5;
272constexpr unsigned OS2_REGULAR = 1u << 6;
273constexpr unsigned OS2_WWS = 1u << 8;
274constexpr unsigned OS2_OBLIQUE = 1u << 9; // Is an oblique instead of an italic.
275constexpr unsigned OS2_USE_TYPO_METRICS = 1u << 7;
276
277const QString WEIGHT_TAG = "wght";
278const QString WIDTH_TAG = "wdth";
279const QString SLANT_TAG = "slnt";
280const QString ITALIC_TAG = "ital";
281const QString OPTICAL_TAG = "opsz";
282
283bool KoFFWWSConverter::addFontFromPattern(const FcPattern *pattern, FT_LibrarySP freeTypeLibrary)
284{
285 if (!freeTypeLibrary.data()) {
286 return false;
287 }
288
289 bool getFile = false;
290 FcChar8 *fileValue{};
291 if (FcPatternGetString(pattern, FC_FILE, 0, &fileValue) != FcResultMatch) {
292 qWarning() << "Failed to get font file for" << pattern;
293 } else {
294 getFile = true;
295 }
296 QString filename = QString::fromUtf8(reinterpret_cast<char *>(fileValue));
297
298 int indexValue{};
299 if (FcPatternGetInteger(pattern, FC_INDEX, 0, &indexValue) != FcResultMatch) {
300 qWarning() << "Failed to get font index for" << pattern << "(file:" << filename << ")";
301 getFile = false;
302 }
303
304 if (getFile == false) {
305 return getFile;
306 }
307
308 if (indexValue > 0xffff) { // this indicates the font is a variable font instance, so we don't try to load it.
309 return false;
310 }
311
312 bool success = addFontFromFile(filename, indexValue, freeTypeLibrary);
313 if (success) {
314 FcLangSet *set;
315 if (FcPatternGetLangSet(pattern, FC_LANG, 0, &set) != FcResultMatch) {
316 qWarning() << "Failed to get font index for" << pattern << "(file:" << filename << ")";
317 return success;
318 }
319 FcStrList *list = FcStrListCreate(FcLangSetGetLangs(set));
320 FcStrListFirst(list);
321 FcChar8 *langString = FcStrListNext(list);
322 QString lang = QString::fromUtf8(reinterpret_cast<char *>(langString));
323 QList<QLocale> languages;
324 while (!lang.isEmpty()) {
325 languages.append(QLocale(lang));
326
327 langString = FcStrListNext(list);
328 lang = QString::fromUtf8(reinterpret_cast<char *>(langString));
329 }
330 FcStrListDone(list);
331 FcCharSet *charSet = nullptr;
332 if (FcPatternGetCharSet(pattern, FC_CHARSET, 0, &charSet) != FcResultMatch) {
333 return success;
334 }
335 addSupportedLanguagesByFile(filename, indexValue, languages, charSet);
336
337 }
338 return success;
339}
340
341bool KoFFWWSConverter::addFontFromFile(const QString &filename, const int index, FT_LibrarySP freeTypeLibrary) {
342
343 FontFamilyNode fontFamily;
344 fontFamily.fileName = filename;
345 fontFamily.fileIndex = index;
346 for (auto it = d->fontFamilyCollection.begin(); it != d->fontFamilyCollection.end(); it++) {
347 if (it->fileName == fontFamily.fileName && it->fileIndex == fontFamily.fileIndex) {
348 return true;
349 }
350 }
351
352 FT_Face f = nullptr;
353 FT_FaceSP face;
354 QByteArray utfData = fontFamily.fileName.toUtf8();
355 if (FT_New_Face(freeTypeLibrary.data(), utfData.data(), fontFamily.fileIndex, &f) == 0) {
356 face.reset(f);
357 } else {
358 return false;
359 }
360
361 fontFamily.fontFamily = face->family_name;
362 fontFamily.fontStyle = face->style_name;
363 fontFamily.lastModified = QFileInfo(fontFamily.fileName).lastModified();
364 FontFamilyNode typographicFamily;
365 FontFamilyNode wwsFamily;
366 bool isWWSFamilyWithoutName = false;
367
368 if (!FT_IS_SFNT(face.data())) {
369 fontFamily.type = FT_IS_SCALABLE(face.data())? KoSvgText::Type1FontType: KoSvgText::BDFFontType;
370
371 fontFamily.isItalic = face->style_flags & FT_STYLE_FLAG_ITALIC;
372 if (face->style_flags & FT_STYLE_FLAG_BOLD) {
374 } else {
376 }
377
378 for (int i=0; i< face->num_fixed_sizes; i++) {
379 // 64 = Freetype pixel
380 fontFamily.pixelSizes.insert((face->available_sizes[i].size / 64.0), {fontFamily.fileName});
381 }
382 } else {
384 hb_face_t_sp hbFace(hb_ft_face_create_referenced(face.data()));
385 hb_font_t_sp hbFont(hb_ft_font_create_referenced(face.data()));
386
387 // Retrieve width, weight and slant data.
388
389 fontFamily.axes.insert(WEIGHT_TAG, KoSvgText::FontFamilyAxis::weightAxis(hb_style_get_value(hbFont.data(), HB_STYLE_TAG_WEIGHT)));
390 fontFamily.axes.insert(WIDTH_TAG, KoSvgText::FontFamilyAxis::widthAxis(hb_style_get_value(hbFont.data(), HB_STYLE_TAG_WIDTH)));
391 fontFamily.isItalic = hb_style_get_value(hbFont.data(), HB_STYLE_TAG_ITALIC) > 0;
392 //fontFamily.isOblique = hb_style_get_value(hbFont.data(), HB_STYLE_TAG_SLANT_ANGLE) != 0;
393
394 TT_OS2 *os2Table = nullptr;
395 os2Table = (TT_OS2*)FT_Get_Sfnt_Table(face.data(), FT_SFNT_OS2);
396 if (os2Table) {
397
398 fontFamily.isOblique = os2Table->fsSelection & OS2_OBLIQUE;
399
400 if (os2Table->fsSelection & OS2_REGULAR) {
401 fontFamily.isItalic = false;
402 fontFamily.isOblique = false;
403 }
404
405 if (os2Table->version >= 5) {
406 FontFamilySizeInfo sizeInfo;
407 const qreal twip = 0.05;
408 sizeInfo.high = os2Table->usUpperOpticalPointSize * twip;
409 sizeInfo.low = os2Table->usLowerOpticalPointSize * twip;
410 sizeInfo.os2table = true;
411 sizeInfo.isSet = true;
412 fontFamily.sizeInfo = sizeInfo;
413 }
414 isWWSFamilyWithoutName = (os2Table->fsSelection & OS2_WWS);
415 }
416
417
418 // retrieve gpos size data...
419 uint designSize;
420 uint subFamilyId;
421 uint rangeStart;
422 uint rangeEnd;
423 hb_ot_name_id_t sizeNameId;
424 if (hb_ot_layout_get_size_params(hbFace.data(), &designSize, &subFamilyId, &sizeNameId, &rangeStart, &rangeEnd)) {
425 FontFamilySizeInfo sizeInfo;
426 qreal tenth = 0.1;
427 sizeInfo.low = rangeStart * tenth;
428 sizeInfo.high = rangeEnd * tenth;
429 sizeInfo.subFamilyID = subFamilyId;
430 sizeInfo.designSize = designSize * tenth;
431 sizeInfo.isSet = true;
432 fontFamily.sizeInfo = sizeInfo;
433 }
434
435 // retrieve axis data...
436 // Would also be good if we could read the STAT table for more info, but we cannot as there's no API for that in harfbuzz.
437
438 QHash<hb_ot_name_id_t, QString> axisNameIDs;
439 QVector<hb_ot_name_id_t> instanceNameIDs;
440 if (hb_ot_var_has_data(hbFace.data())) {
441 fontFamily.isVariable = true;
442 uint count = hb_ot_var_get_axis_count(hbFace.data());
443 uint maxInfos = 1;
444 QStringList axesTags;
445 for (uint i = 0; i < count; i++) {
447 hb_ot_var_axis_info_t axis;
448 hb_ot_var_get_axis_infos(hbFace.data(), i, &maxInfos, &axis);
449 axisInfo.min = axis.min_value;
450 axisInfo.max = axis.max_value;
451 axisInfo.value = axis.default_value;
452 axisInfo.axisHidden = axis.flags & HB_OT_VAR_AXIS_FLAG_HIDDEN;
453 char buff[4];
454 hb_tag_to_string(axis.tag, buff);
455 axisInfo.tag = QString::fromLatin1(buff, 4);
456 axisNameIDs.insert(axis.name_id, axisInfo.tag);
457 axisInfo.variableAxis = true;
458 fontFamily.axes.insert(axisInfo.tag, axisInfo);
459 axesTags.append(axisInfo.tag);
460 }
461 count = hb_ot_var_get_named_instance_count (hbFace.data());
462 for (uint i = 0; i < count; i++) {
463 QHash<QString, float> instanceCoords;
464 uint coordLength = axesTags.size();
465 std::vector<float> coordinate(coordLength);
466 hb_ot_var_named_instance_get_design_coords (hbFace.data(), i, &coordLength, coordinate.data());
467 for (uint j =0; j < coordLength; j++ ){
468 instanceCoords.insert(axesTags.value(j), coordinate[j]);
469 }
471 style.instanceCoords = instanceCoords;
472 instanceNameIDs.append(hb_ot_var_named_instance_get_subfamily_name_id(hbFace.data(), i));
473 fontFamily.styleInfo.append(style);
474 }
475 }
476
477 // Get some basic color data.
478 fontFamily.colorBitMap = hb_ot_color_has_png(hbFace.data());
479 fontFamily.colorSVG = hb_ot_color_has_svg(hbFace.data());
480 fontFamily.colorClrV0 = hb_ot_color_has_layers(hbFace.data());
481 //fontFamily.colorClrV1 = hb_ot_color_has_paint(hbFace);
482 wwsFamily.colorBitMap = fontFamily.colorBitMap;
483 wwsFamily.colorSVG = fontFamily.colorSVG;
484 wwsFamily.colorClrV0 = fontFamily.colorClrV0;
485
486 uint numEntries = 0;
487 const hb_ot_name_entry_t *entries = hb_ot_name_list_names(hbFace.data(), &numEntries);
488
489 QHash<QLocale, QString> ribbiFamilyNames;
490 QHash<QLocale, QString> ribbiStyleNames;
491 QHash<QLocale, QString> WWSFamilyNames;
492 QHash<QLocale, QString> WWSStyleNames;
493 QHash<QLocale, QString> typographicFamilyNames;
494 QHash<QLocale, QString> typographicStyleNames;
495 QHash<QLocale, QString> fullNames;
496 for (uint i = 0; i < numEntries; i++) {
497 hb_ot_name_entry_t entry = entries[i];
498 QString lang(hb_language_to_string(entry.language));
499 QLocale locale(lang);
500 uint length = hb_ot_name_get_utf8(hbFace.data(), entry.name_id, entry.language, nullptr, nullptr)+1;
501 std::vector<char> buff(length);
502 hb_ot_name_get_utf8(hbFace.data(), entry.name_id, entry.language, &length, buff.data());
503 QString name = QString::fromUtf8(buff.data(), length);
504 if (name.isEmpty()) continue;
505
506 if (entry.name_id == HB_OT_NAME_ID_FONT_FAMILY) {
507 ribbiFamilyNames.insert(locale, name);
508 } else if (entry.name_id == HB_OT_NAME_ID_TYPOGRAPHIC_FAMILY) {
509 typographicFamilyNames.insert(locale, name);
510 } else if (entry.name_id == HB_OT_NAME_ID_WWS_FAMILY) {
511 WWSFamilyNames.insert(locale, name);
512 } else if (entry.name_id == HB_OT_NAME_ID_FONT_SUBFAMILY) {
513 ribbiStyleNames.insert(locale, name);
514 } else if (entry.name_id == HB_OT_NAME_ID_TYPOGRAPHIC_SUBFAMILY) {
515 typographicStyleNames.insert(locale, name);
516 } else if (entry.name_id == HB_OT_NAME_ID_WWS_SUBFAMILY) {
517 WWSStyleNames.insert(locale, name);
518 } else if (entry.name_id == HB_OT_NAME_ID_FULL_NAME) {
519 fullNames.insert(locale, name);
520 } else if (entry.name_id > 0) { // Fonts made by Adobe seem to use the copyright id (0) as the input when the given value is empty.
521 if (axisNameIDs.keys().contains(entry.name_id)) {
522 fontFamily.axes[axisNameIDs.value(entry.name_id)].localizedLabels.insert(locale, name);
523 } else if (entry.name_id == sizeNameId) {
524 fontFamily.sizeInfo.localizedLabels.insert(locale, name);
525 } else if (instanceNameIDs.contains(entry.name_id)) {
526 int idx = instanceNameIDs.indexOf(entry.name_id);
527 fontFamily.styleInfo[idx].localizedLabels.insert(locale, name);
528 }
529 }
530 }
531 QLocale english(QLocale::English);
532 if (!typographicFamilyNames.isEmpty()) {
533 typographicFamily.fontFamily = typographicFamilyNames.value(english, typographicFamilyNames.values().first());
534 typographicFamily.localizedFontFamilies = typographicFamilyNames;
535 }
536 fontFamily.localizedTypographicStyle = typographicStyleNames;
537 if (!ribbiFamilyNames.isEmpty()) {
538 fontFamily.fontFamily = ribbiFamilyNames.value(english, ribbiFamilyNames.values().first());
539 fontFamily.localizedFontFamilies = ribbiFamilyNames;
540 }
541 // Second check is a hack to avoid issues with the css test fonts. Why they are configure this way, beats me.
542 // Either way, if the fullname, which has 100% priority is the name as typographic name (which is tested last)
543 // the font search will only select the node with this fullname, which we don't want.
544 if (!fullNames.isEmpty() && fullNames != typographicFamilyNames) {
545 fontFamily.localizedFullName = fullNames;
546 }
547 if (!ribbiStyleNames.isEmpty()) {
548 fontFamily.fontStyle = ribbiStyleNames.value(english, ribbiStyleNames.values().first());
549 fontFamily.localizedFontStyle = ribbiStyleNames;
550 }
551 if (!WWSFamilyNames.isEmpty()) {
552 wwsFamily.fontFamily = WWSFamilyNames.value(english, WWSFamilyNames.values().first());
553 wwsFamily.localizedFontFamilies = WWSFamilyNames;
554 }
555 fontFamily.localizedWWSStyle = WWSStyleNames;
556 }
557
558 if (fontFamily.fontFamily.isEmpty()) {
559 fontFamily.fontFamily = QFileInfo(fontFamily.fileName).baseName();
560 }
561 if (typographicFamily.fontFamily.isEmpty()) {
562 typographicFamily.fontFamily = fontFamily.fontFamily;
563 }
564 wwsFamily.isVariable = fontFamily.isVariable;
565 wwsFamily.type = fontFamily.type;
566 typographicFamily.type = typographicFamily.type;
567
568 if (typographicFamily.fontFamily.isEmpty() && fontFamily.fontFamily.isEmpty()) {
569 d->fontFamilyCollection.insert(d->fontFamilyCollection.childEnd(), fontFamily);
570 } else {
571 // find potential typographic family
572 auto it = d->fontFamilyCollection.childBegin();
573 for (; it != d->fontFamilyCollection.childEnd(); it++) {
574 if (!typographicFamily.fontFamily.isEmpty() && it->fontFamily == typographicFamily.fontFamily) {
575 break;
576 } else if (it->fontFamily == fontFamily.fontFamily) {
577 break;
578 }
579 }
580 if (it != d->fontFamilyCollection.childEnd()) {
581
582 if (isWWSFamilyWithoutName) {
583 wwsFamily.fontFamily = fontFamily.fontFamily;
584 }
585 if (!wwsFamily.fontFamily.isEmpty()) {
586 // sort into wws family
587 auto wws = childBegin(it);
588 for (; wws != childEnd(it); wws++) {
589 if (wws->fontFamily == wwsFamily.fontFamily) {
590 break;
591 }
592 }
593 if (wws != childEnd(it)) {
594 d->fontFamilyCollection.insert(childEnd(wws), fontFamily);
595 } else {
596 auto wwsNew = d->fontFamilyCollection.insert(childEnd(it), wwsFamily);
597 d->fontFamilyCollection.insert(childEnd(wwsNew), fontFamily);
598 }
599 } else if (!fontFamily.pixelSizes.isEmpty()) {
600 // sort any pixel sizes into the appropriate family.
601 auto pixel = childBegin(it);
602 for (; pixel != childEnd(it); pixel++) {
603 if (pixel->fontFamily == fontFamily.fontFamily && pixel->fontStyle == fontFamily.fontStyle && !pixel->pixelSizes.isEmpty()) {
604 for (int pxSize = 0; pxSize < fontFamily.pixelSizes.keys().size(); pxSize++) {
605 int px = fontFamily.pixelSizes.keys().at(pxSize);
606 QStringList files = pixel->pixelSizes.value(px, QStringList());
607 files.append(fontFamily.pixelSizes.value(px));
608 pixel->pixelSizes.insert(px, files);
609 }
610 break;
611 }
612 }
613 if (pixel == childEnd(it)) {
614 d->fontFamilyCollection.insert(childEnd(it), fontFamily);
615 }
616
617 } else {
618 d->fontFamilyCollection.insert(childEnd(it), fontFamily);
619 }
620 } else {
621 auto typographic = d->fontFamilyCollection.insert(d->fontFamilyCollection.childEnd(), typographicFamily);
622 if (isWWSFamilyWithoutName) {
623 wwsFamily.fontFamily = fontFamily.fontFamily;
624 }
625 if (!wwsFamily.fontFamily.isEmpty()) {
626 auto wwsNew = d->fontFamilyCollection.insert(childEnd(typographic), wwsFamily);
627 d->fontFamilyCollection.insert(childEnd(wwsNew), fontFamily);
628 } else {
629 d->fontFamilyCollection.insert(childEnd(typographic), fontFamily);
630 }
631 }
632 }
633 return true;
634}
635
636#include <KoWritingSystemUtils.h>
637void KoFFWWSConverter::addSupportedLanguagesByFile(const QString &filename, const int index, const QList<QLocale> &supportedLanguages, FcCharSet *set)
638{
639 auto it = d->fontFamilyCollection.depthFirstTailBegin();
640 for (; it!= d->fontFamilyCollection.depthFirstTailEnd(); it++) {
641 if (it->fileName == filename && it->fileIndex == index) {
642 break;
643 }
644 }
645 if (it != d->fontFamilyCollection.depthFirstTailEnd()) {
646 it->supportedLanguages = supportedLanguages;
647
648 QMap<QString, QString> samples = KoWritingSystemUtils::samples();
649
650 for (int i = 0; i < samples.size(); i++) {
651 QString sample = samples.keys().at(i);
652 bool matching = true;
653 Q_FOREACH (uint unicode, sample.toUcs4()) {
654 if (!FcCharSetHasChar(set, unicode)) {
655 matching = false;
656 break;
657 }
658 }
659 if (matching) {
660 it->sampleStrings.insert(samples.value(sample), sample);
661 }
662 }
663 }
664}
665
667{
668 QStringList wwsNames;
669 // Some font families have predefined wws families, others don't. This function sorts out everything so that each font file has
670 // a wws family in between the typographic and font-file nodes, this is important, because the wws family will be the one presented
671 // as the font-family resource.
672 for (auto typographic = d->fontFamilyCollection.childBegin(); typographic != d->fontFamilyCollection.childEnd(); typographic++) {
674
675 QVector<qreal> weights;
676 QVector<qreal> widths;
677 QVector<int> fileIndices;
679
680 // This takes all of the current children that aren't inside a wws-node already and puts them into a temp list,
681 // as well as tallying the current widths and weights. We need these to find the most regular value.
683 for (auto child = childBegin(typographic); child != childEnd(typographic); child++) {
684 if (childBegin(child) != childEnd(child)) {
685 wwsNames.append(child->fontFamily);
686 continue;
687 }
688 tempList.insert(tempList.childEnd(), *child);
689 qreal wght = child->axes.value(WEIGHT_TAG, KoSvgText::FontFamilyAxis::weightAxis(400)).value;
690 if (!weights.contains(wght)) weights.append(wght);
691 qreal wdth = child->axes.value(WIDTH_TAG, KoSvgText::FontFamilyAxis::widthAxis(100)).value;
692 if (!widths.contains(wdth)) widths.append(wdth);
693 if (!fileIndices.contains(child->fileIndex)) fileIndices.append(child->fileIndex);
694 types.append(child->type);
695 deleteList.append(child);
696 }
697 while (!deleteList.isEmpty()) {
698 auto child = deleteList.takeFirst();
699 KIS_ASSERT_RECOVER_NOOP(childBegin(child) == childEnd(child));
700 d->fontFamilyCollection.erase(child);
701 }
702 if (KisForestDetail::size(tempList) > 0) {
703 //Do most regular first...
706 for (auto font = tempList.childBegin(); font != tempList.childEnd(); font++) {
707 const qreal testWeight = weights.contains(400)? 400: weights.first();
708 const qreal testWidth = widths.contains(100)? 100: widths.first();
709 // We want to select the first file index if possible.
710 const int testFileIndex = fileIndices.contains(0)? 0: fileIndices.first();
711
712 bool widthTested = !font->axes.keys().contains(WIDTH_TAG);
713 widthTested = widthTested? true: qFuzzyCompare(font->axes.value(WIDTH_TAG).value, testWidth);
714
715 QPair<QString, QString> fontStyle(font->fontFamily, font->fontStyle);
716 if (qFuzzyCompare(font->axes.value(WEIGHT_TAG).value, testWeight) && widthTested
717 && !font->isItalic
718 && !font->isOblique
719 && ( ( fileIndices.size() > 1 && font->fileIndex == testFileIndex ) || ( fileIndices.size()<=1 ))
720 && font->type == testType
721 && !existing.contains(fontStyle)) {
722 FontFamilyNode wwsFamily = FontFamilyNode::createWWSFamilyNode(*font, *typographic, wwsNames);
723 wwsNames.append(wwsFamily.fontFamily);
724 existing.append(fontStyle);
725
726 auto newWWS = d->fontFamilyCollection.insert(childEnd(typographic), wwsFamily);
727 d->fontFamilyCollection.insert(childEnd(newWWS), *font);
728 deleteList.append(font);
729 }
730 }
731 while (!deleteList.isEmpty()) {
732 auto child = deleteList.takeFirst();
733 KIS_ASSERT_RECOVER_NOOP(childBegin(child) == childEnd(child));
734 tempList.erase(child);
735 }
736 // Then sort the rest of the family nodes into wws families.
737 for (auto font = tempList.childBegin(); font != tempList.childEnd(); font++) {
738 auto wws = childBegin(typographic);
739 auto wwsCandidate = childEnd(typographic);
740 for (; wws != childEnd(typographic); wws++) {
741 auto wwsChild = childBegin(wws);
742 if (font->type != KoSvgText::OpenTypeFontType && font->type != KoSvgText::Type1FontType) {
743 // Hack for really old fonts.
744 // It's questionable whether this is wise, given that it is not the family name,
745 // but it seems CSS is explicitly vague about what the family name is, because
746 // of the varying ways a font can be assigned a family name.
747 // Like, it is technically correct, because it allows us to select fonts with CSS
748 // that would otherwise be unselectable, but this does mean that other applications
749 // would need to have our exact idea of a CSS family name.
750 if (wws->fontStyle.toLower() != "regular"
751 && !font->fontStyle.contains(wws->fontStyle)) {
752 continue;
753 }
754 }
755 // In a previous version of the code, variable and non-variable were not mixed, but after reconsidering,
756 // they probably should be sorted together. The code will prioritize variable fonts in any case.
757
758 if (!wwsChild->sizeInfo.compare(font->sizeInfo)) {
759 // Skip sorting if the WWS family has size info that is incompatible with the sorted font.
760 continue;
761 }
762 for (; wwsChild != childEnd(wws); wwsChild++) {
763 if (wwsChild->isItalic == font->isItalic
764 && wwsChild->isOblique == font->isOblique
765 && wwsChild->compareAxes(font->axes)
766 && wwsChild->hasAnyColor() == font->hasAnyColor()) {
767 break;
768 }
769 }
770 if (wwsChild != childEnd(wws)) {
771 if (wwsChild->fontFamily == font->fontFamily && wwsChild->fontStyle == font->fontStyle) {
772 // If, for all intends and purposes, the font seems to be the same, merge nodes.
773 // This sometimes happens with installations where the same font is installed in
774 // a variety of formats. We want to prefer the opentype version in any case.
775 if (wwsChild->type != KoSvgText::OpenTypeFontType && font->type == KoSvgText::OpenTypeFontType) {
776 wwsChild->otherFiles.append(wwsChild->fileName);
777 wwsChild->otherFiles.append(font->otherFiles);
778 wwsChild->fileName = font->fileName;
779 wwsChild->type = KoSvgText::OpenTypeFontType;
780 } else {
781 wwsChild->otherFiles.append(font->fileName);
782 wwsChild->otherFiles.append(font->otherFiles);
783 }
784 if (wwsChild->lastModified < font->lastModified) {
785 wwsChild->lastModified = font->lastModified;
786 }
787 break;
788 } else {
789 continue;
790 }
791 } else {
792 /*
793 * Try to match the wws family with the same fontfamily, otherwise select the first candidate.
794 */
795 if (wwsCandidate == childEnd(typographic)) {
796 wwsCandidate = wws;
797 }
798 if (wws->fontFamily == font->fontFamily) {
799 wwsCandidate = wws;
800 break;
801 }
802 }
803 }
804 if (wwsCandidate != childEnd(typographic)) {
805 d->fontFamilyCollection.insert(childEnd(wwsCandidate), *font);
806 } else if (wws == childEnd(typographic)) {
807 FontFamilyNode wwsFamily = FontFamilyNode::createWWSFamilyNode(*font, *typographic, wwsNames);
808 wwsNames.append(wwsFamily.fontFamily);
809 auto newWWS = d->fontFamilyCollection.insert(childEnd(typographic), wwsFamily);
810 if (wwsFamily.fontFamily != typographic->fontFamily) {
811 font->localizedTypographicStyle.clear();
812 }
813 d->fontFamilyCollection.insert(childEnd(newWWS), *font);
814 }
815 }
816 // This only triggers when the first wws family was created with the typographic name,
817 // yet more wws families have followed after sorting was finished, and this name might be not the most precise.
818 if (wwsNames.contains(typographic->fontFamily) && std::distance(childBegin(typographic), childEnd(typographic)) > 1) {
819 for (auto wws = childBegin(typographic); wws != childEnd(typographic); wws++) {
820 if (wws->fontFamily == typographic->fontFamily) {
821 if (wwsNames.contains(childBegin(wws)->fontFamily)) {
822 const QString otherWWSName = childBegin(wws)->fontFamily;
823 // Also rename any other wwsfamilies that has been using the second style.
824 for (auto otherwws = childBegin(typographic); otherwws != childEnd(typographic); otherwws++) {
825 if (otherwws->fontFamily == otherWWSName) {
826 otherwws->fontFamily = childBegin(otherwws)->fontFamily + " " + childBegin(otherwws)->fontStyle;
827 QHash<QLocale, QString> families;
828 const QHash<QLocale, QString> styles = childBegin(otherwws)->localizedFontStyle;
829 Q_FOREACH (const QLocale l, otherwws->localizedFontFamilies.keys()) {
830 families.insert(l, otherwws->localizedFontFamilies.value(l)+" "+styles.value(l, childBegin(otherwws)->fontStyle));
831 }
832 otherwws->localizedFontFamilies = families;
833 break;
834 }
835 }
836 }
837 wws->fontFamily = childBegin(wws)->fontFamily;
838 childBegin(wws)->localizedTypographicStyle.clear();
839 break;
840 }
841 }
842 }
843 }
844 }
845}
846
847void KoFFWWSConverter::addGenericFamily(const QString &name)
848{
849 FontFamilyNode typographicFamily;
850 FontFamilyNode fontFamily;
851
852 QHash<QLocale, QString> familyNames = {{QLocale(QLocale::English), name}};
853 // TODO: can and should we translate this?
854 QHash<QLocale, QString> styleNames = {{QLocale(QLocale::English), "Regular"}};
855
856 fontFamily.fontFamily = name;
857 fontFamily.localizedFontFamilies = familyNames;
858
860
861 typographicFamily = fontFamily;
863 fontFamily.fontStyle = styleNames.values().first();
864 fontFamily.localizedFontStyle = styleNames;
865 QString tag = KoWritingSystemUtils::sampleTagForQLocale(QLocale(QLocale::English));
866 fontFamily.sampleStrings.insert(tag, KoWritingSystemUtils::samples().key(tag));
867
868 auto typographic = d->fontFamilyCollection.insert(d->fontFamilyCollection.childEnd(), typographicFamily);
869 d->fontFamilyCollection.insert(childEnd(typographic), fontFamily);
870}
871
873 KoFontFamilyWWSRepresentation representation;
874 representation.fontFamilyName = wws->fontFamily;
875 representation.localizedFontFamilyNames = wws->localizedFontFamilies;
876 if (!singleFamily) {
877 // This funnels the typographic family to the resource, so that resources may potentially be sorted by their typographic family.
878 representation.typographicFamilyName = typographic->fontFamily;
879 representation.localizedTypographicFamily = typographic->localizedFontFamilies;
880 representation.localizedTypographicStyles = wws->localizedTypographicStyle;
881 }
882 representation.isVariable = wws->isVariable;
883 representation.colorBitMap = wws->colorBitMap;
884 representation.colorClrV0 = wws->colorClrV0;
885 representation.colorClrV1 = wws->colorClrV1;
886 representation.colorSVG = wws->colorSVG;
887 representation.type = wws->type;
888
889 for (auto subFamily = childBegin(wws); subFamily != childEnd(wws); subFamily++) {
891 if (subFamily == childBegin(wws)
892 || representation.lastModified < subFamily->lastModified) {
893 representation.lastModified = subFamily->lastModified;
894 }
895 representation.sampleStrings.insert(subFamily->sampleStrings);
896 Q_FOREACH(const QLocale &locale, subFamily->supportedLanguages) {
897 if (!representation.supportedLanguages.contains(locale)) {
898 representation.supportedLanguages.append(locale);
899 }
900 }
901
902 for (int a = 0; a < subFamily->axes.size(); a++) {
903 QString key = subFamily->axes.keys().at(a);
904 KoSvgText::FontFamilyAxis axis = subFamily->axes.value(key);
905 KoSvgText::FontFamilyAxis mainAxis = representation.axes.value(key);
906 mainAxis.min = qMin(mainAxis.min, axis.min);
907 mainAxis.defaultValue = axis.defaultValue;
908 mainAxis.max = qMax(mainAxis.max, axis.max);
909 mainAxis.localizedLabels.insert(axis.localizedLabels);
910 mainAxis.tag = axis.tag;
911 mainAxis.axisHidden = axis.axisHidden;
912 representation.axes.insert(mainAxis.tag, mainAxis);
913
914 if (!subFamily->isVariable) {
915 style.instanceCoords.insert(key, axis.value);
916 }
917 }
918 if (!subFamily->isVariable) {
919 if (!subFamily->localizedWWSStyle.isEmpty()) {
920 style.localizedLabels = subFamily->localizedWWSStyle;
921 } else if (!subFamily->localizedTypographicStyle.isEmpty()) {
922 style.localizedLabels = subFamily->localizedTypographicStyle;
923 } else if (!subFamily->localizedFontStyle.isEmpty()) {
924 style.localizedLabels = subFamily->localizedFontStyle;
925 } else {
926 style.localizedLabels.insert(QLocale(QLocale::English), subFamily->fontStyle);
927 }
928 style.isItalic = subFamily->isItalic;
929 style.isOblique = subFamily->isOblique;
930 representation.styles.append(style);
931 } else {
932 for (int i = 0; i < subFamily->styleInfo.size(); i++) {
933 KoSvgText::FontFamilyStyleInfo styleInfo = subFamily->styleInfo.at(i);
934 if (!styleInfo.localizedLabels.isEmpty()) {
935 styleInfo.isItalic = subFamily->isItalic;
936 styleInfo.isOblique = subFamily->isOblique;
937 representation.styles.append(styleInfo);
938 }
939 }
940 }
941 }
942 return representation;
943}
944
946{
948 for (auto typographic = d->fontFamilyCollection.childBegin(); typographic != d->fontFamilyCollection.childEnd(); typographic++) {
949 auto counter = childBegin(typographic);
950 counter++;
951 bool singleFamily = childBegin(typographic) != childEnd(typographic) && std::next(childBegin(typographic)) == childEnd(typographic);
952
953 for (auto wws = childBegin(typographic); wws != childEnd(typographic); wws++) {
954
955 collection.append(createRepresentation(wws, typographic, singleFamily));
956 }
957 }
958 return collection;
959}
960
962 QString familySimplified = family;
963 QString familyLower = family.toLower();
964 // Qt's fontdatabase would add the vendor in [] behind the font name, when there were duplicates,
965 // though sometimes there was no such explanation, so we should check against that...
966 if (family.endsWith("]") && family.contains("[")) {
967 familySimplified = family.split("[", Qt::SkipEmptyParts).first().trimmed().toLower();
968 }
969 for (; it != endIt; it++) {
970 if (it.state() == KisForestDetail::Enter) {
971 continue;
972 }
973
974 if (childBegin(it) == childEnd(it)) {
975 // For the lowest nodes, we only want to test the full name.
976 QStringList local = it->localizedFullName.values();
977 if (local.contains(familySimplified, Qt::CaseInsensitive)
978 || local.contains(familyLower, Qt::CaseInsensitive)) {
979 break;
980 } else {
981 continue;
982 }
983 }
984
985 QStringList local = it->localizedFontFamilies.values();
986 QString itFamilyLower = QString(it->fontFamily).toLower();
987 if (itFamilyLower == familySimplified
988 || itFamilyLower == familyLower
989 || local.contains(familySimplified, Qt::CaseInsensitive)
990 || local.contains(familyLower, Qt::CaseInsensitive)) {
991 break;
992 }
993
994 }
995 return it;
996}
997
998std::optional<KoFontFamilyWWSRepresentation> KoFFWWSConverter::representationByFamilyName(const QString &familyName) const
999{
1000 for (auto typographic = d->fontFamilyCollection.childBegin(); typographic != d->fontFamilyCollection.childEnd(); typographic++) {
1001 auto counter = childBegin(typographic);
1002 counter++;
1003 bool singleFamily = counter == childEnd(typographic);
1004
1005 for (auto wws = childBegin(typographic); wws != childEnd(typographic); wws++) {
1006 if (wws->fontFamily == familyName) {
1007 return std::make_optional(createRepresentation(wws, typographic, singleFamily));
1008 }
1009 }
1010 }
1011 return std::nullopt;
1012}
1013
1014std::optional<QString> KoFFWWSConverter::wwsNameByFamilyName(const QString familyName) const
1015{
1016 auto it = d->fontFamilyCollection.compositionBegin();
1017 it = searchNodes(it, d->fontFamilyCollection.compositionEnd(), familyName);
1018 if (it != d->fontFamilyCollection.compositionEnd()) {
1019
1020 bool isChild = childBegin(it) == childEnd(it);
1021 auto wws = siblingCurrent(it);
1022
1023 // check if we're in a typographic family by testing the hierarchy.
1024 // if so, select the wws family.
1025 auto hierarchy = hierarchyBegin(wws);
1026 hierarchy++;
1027 if (isChild) {
1028 wws = siblingCurrent(hierarchy);
1029 } else if (hierarchy == hierarchyEnd(wws)) {
1030 wws = childBegin(it);
1031 }
1032 return std::make_optional(wws->fontFamily);
1033 }
1034 return std::nullopt;
1035}
1036#include <KoCssTextUtils.h>
1037QVector<FontFamilyNode> findNodesByAxis(const QVector<FontFamilyNode> &nodes, const QString axisTag, const qreal &value, const qreal &defaultValue, const qreal &defaultValueUpper) {
1038 QVector<FontFamilyNode> candidates;
1039 QVector<qreal> values;
1040 Q_FOREACH (const FontFamilyNode &node, nodes) {
1041 qreal selectingVal = defaultValue;
1042
1043 if (node.axes.keys().contains(axisTag)) {
1044 KoSvgText::FontFamilyAxis axis = node.axes.value(axisTag);
1045 selectingVal = axis.value;
1046 if (axis.variableAxis) {
1047 if (value >= axis.min && value <= axis.max) {
1048 candidates.append(node);
1049 selectingVal = value;
1050 } else {
1051 values.append(axis.min);
1052 values.append(axis.max);
1053 }
1054 continue;
1055 }
1056 values.append(selectingVal);
1057 }
1058 }
1059 // We found some variable fonts already, so lets return early.
1060 if (!candidates.isEmpty()) {
1061 return candidates;
1062 }
1063
1064 // follow the CSS Fonts selection mechanism.
1065 bool shouldNotReturnDefault = ((axisTag == ITALIC_TAG || axisTag == SLANT_TAG) && value != defaultValue);
1066 qreal selectedValue = KoCssTextUtils::cssSelectFontStyleValue(values, value, defaultValue, defaultValueUpper, shouldNotReturnDefault);
1067
1068 Q_FOREACH (const FontFamilyNode &node, nodes) {
1069 if (node.axes.keys().contains(axisTag)) {
1070 KoSvgText::FontFamilyAxis axis = node.axes.value(axisTag);
1071 if (axis.value == selectedValue) {
1072 candidates.append(node);
1073 }
1074 } else if (value == defaultValue && !shouldNotReturnDefault) {
1075 candidates.append(node);
1076 }
1077 }
1078 return candidates;
1079}
1080
1082 quint32 xRes, quint32 yRes) const
1083{
1084 QVector<FontFileEntry> candidateFileNames;
1085
1086 int pixelSize = info.size * (qMin(xRes, yRes) / 72.0);
1087
1088 Q_FOREACH(const QString &family, info.families) {
1089 auto it = d->fontFamilyCollection.compositionBegin();
1090 it = searchNodes(it, d->fontFamilyCollection.compositionEnd(), family);
1091 if (it != d->fontFamilyCollection.compositionEnd()) {
1092 auto wws = siblingCurrent(it);
1093
1094 // check if we're in a typographic family by testing the hierarchy.
1095 // if so, select all subnodes.
1096 // Because we test subtree depth-first for finding the nodes, wws
1097 // and full names are tested before typographic.
1098 auto hierarchy = hierarchyBegin(wws);
1099 hierarchy++;
1100 QVector<FontFamilyNode> candidates;
1101 if (hierarchy == hierarchyEnd(wws)) {
1102 auto nodes = subtreeBegin(wws);
1103 auto endNodes = subtreeEnd(wws);
1104 for (;nodes != endNodes; nodes++) {
1105 if (childBegin(nodes) == childEnd(nodes)) {
1106 candidates.append(*nodes);
1107 }
1108 }
1109 } else if (childBegin(wws) == childEnd(wws)) {
1110 candidates.append(*wws);
1111 } else {
1112 auto style = childBegin(wws);
1113 auto styleEnd = childEnd(wws);
1114 for (;style != styleEnd; style++) {
1115 candidates.append(*style);
1116 }
1117 }
1118
1119 if (candidates.size() > 1) {
1120 // first find width
1121 candidates = findNodesByAxis(candidates, WIDTH_TAG, info.width, 100.0, 100.0);
1122 }
1123
1124 if (candidates.size() > 1) {
1125 // then find weight
1126 candidates = findNodesByAxis(candidates, WEIGHT_TAG, info.weight, 400.0, 500.0);
1127 }
1128 // then match italic
1129 if (candidates.size() > 1) {
1131 QVector<FontFamilyNode> obliques;
1132
1133 if (wws->isVariable) {
1134 qreal slantValue = info.slantMode == QFont::StyleItalic? 11: info.autoSlant? 14: info.slantValue;
1135 italics = findNodesByAxis(candidates, ITALIC_TAG, 1.0, 0.0, 0.0);
1136 obliques = findNodesByAxis(candidates, SLANT_TAG, -slantValue, 0.0, 0.0);
1137 }
1138 if (italics.isEmpty() && obliques.isEmpty()) {
1139 Q_FOREACH(const FontFamilyNode &node, candidates) {
1140 if (node.isItalic) {
1141 if (!node.isOblique) {
1142 italics.append(node);
1143 } else {
1144 obliques.append(node);
1145 }
1146 }
1147 }
1148 }
1149
1150 if (info.slantMode == QFont::StyleItalic) {
1151 if (!italics.isEmpty()) {
1152 candidates = italics;
1153 } else if (!obliques.isEmpty()) {
1154 candidates = obliques;
1155 }
1156 } else if (info.slantMode == QFont::StyleOblique) {
1157 if (!obliques.isEmpty()) {
1158 candidates = obliques;
1159 } else if (!italics.isEmpty()) {
1160 candidates = italics;
1161 }
1162 } else {
1163 QStringList slantedFontFiles;
1165 Q_FOREACH(const FontFamilyNode &italic, italics) {
1166 slantedFontFiles.append(italic.fileName);
1167 }
1168 Q_FOREACH(const FontFamilyNode &oblique, obliques) {
1169 slantedFontFiles.append(oblique.fileName);
1170 }
1171 Q_FOREACH(const FontFamilyNode &node, candidates) {
1172 if (!slantedFontFiles.contains(node.fileName)) {
1173 regular.append(node);
1174 }
1175 }
1176 if (!regular.isEmpty()) {
1177 candidates = regular;
1178 }
1179 }
1180 }
1181
1182 // prefer opentype
1183 if (candidates.size() > 1) {
1184 QVector<FontFamilyNode> openType;
1185 Q_FOREACH(const FontFamilyNode &node, candidates) {
1186 if (node.type == KoSvgText::OpenTypeFontType) {
1187 openType.append(node);
1188 }
1189 }
1190 if (!openType.isEmpty()) {
1191 candidates = openType;
1192 }
1193 }
1194
1195 // finally, match size.
1196 Q_FOREACH(const FontFamilyNode &node, candidates) {
1197 QStringList fileNames = node.otherFiles;
1198 fileNames.append(node.fileName);
1199 fileNames = node.pixelSizes.value(pixelSize, fileNames);
1200 Q_FOREACH(const QString &fileName, fileNames) {
1201 if (fileName.isEmpty()) continue;
1202 FontFileEntry entry;
1203 entry.fileName = fileName;
1204 entry.fontIndex = node.fileIndex;
1205 candidateFileNames.append(entry);
1206 }
1207 }
1208 }
1209 }
1210 return candidateFileNames;
1211}
1212
1214{
1215 qDebug() << "Debug for font family collection" << KisForestDetail::size(d->fontFamilyCollection);
1216 QString spaces;
1217 for (auto it = compositionBegin(d->fontFamilyCollection); it != compositionEnd(d->fontFamilyCollection); it++) {
1218 if (it.state() == KisForestDetail::Enter) {
1219 QStringList debugInfo = it->debugInfo();
1220 for (int i = 0; i< debugInfo.size(); i++) {
1221 if (i==0) {
1222 qDebug().noquote() << QString(spaces + "+") << debugInfo.at(i);
1223 } else {
1224 qDebug().noquote() << QString(spaces + "| ") << debugInfo.at(i);
1225 }
1226 }
1227 spaces.append(" ");
1228 }
1229
1230 if (it.state() == KisForestDetail::Leave) {
1231 spaces.chop(4);
1232 }
1233 }
1234}
1235
qreal length(const QPointF &vec)
Definition Ellipse.cc:82
float value(const T *src, size_t ch)
QList< QString > QStringList
const QString SLANT_TAG
QDebug operator<<(QDebug dbg, const FontFamilySizeInfo &info)
KoFontFamilyWWSRepresentation createRepresentation(KisForest< FontFamilyNode >::child_iterator wws, KisForest< FontFamilyNode >::child_iterator typographic, bool singleFamily)
constexpr unsigned OS2_OBLIQUE
Indicates that the given font is primarily a WWS family and requires no further processing.
const QString WEIGHT_TAG
QVector< FontFamilyNode > findNodesByAxis(const QVector< FontFamilyNode > &nodes, const QString axisTag, const qreal &value, const qreal &defaultValue, const qreal &defaultValueUpper)
constexpr unsigned OS2_ITALIC
const QString ITALIC_TAG
const QString WIDTH_TAG
KisForest< FontFamilyNode >::composition_iterator searchNodes(KisForest< FontFamilyNode >::composition_iterator it, KisForest< FontFamilyNode >::composition_iterator endIt, const QString family)
constexpr unsigned OS2_USE_TYPO_METRICS
const QString OPTICAL_TAG
constexpr unsigned OS2_WWS
Is truly regular (instead of italic or oblique)
constexpr unsigned OS2_BOLD
Is italic.
constexpr unsigned OS2_REGULAR
Is bold.
unsigned int uint
traversal_state state() const
Definition KisForest.h:516
child_iterator childEnd()
Definition KisForest.h:880
child_iterator insert(child_iterator pos, X &&value)
Inserts element value into position pos. value becomes the child of the same parent as pos and is pla...
Definition KisForest.h:943
child_iterator childBegin()
Definition KisForest.h:876
child_iterator erase(child_iterator pos)
Removes element at position pos. If pos is 'end', then result is undefined.
Definition KisForest.h:956
static qreal cssSelectFontStyleValue(const QVector< qreal > &values, const qreal targetValue, const qreal defaultValue, const qreal defaultValueUpper, const bool shouldNotReturnDefault)
cssSelectFontStyleValue Select the closest font style value from the list, following the CSS Fonts se...
std::optional< KoFontFamilyWWSRepresentation > representationByFamilyName(const QString &familyName) const
Gets a single WWSFamily representation for a given CSS Family Name, used by KoFontStorage.
QVector< FontFileEntry > candidatesForCssValues(const KoCSSFontInfo info, quint32 xRes=72, quint32 yRes=72) const
candidatesForCssValues Search the nodes for the most appropriate font for the given css values....
bool addFontFromPattern(const FcPattern *pattern, FT_LibrarySP freeTypeLibrary)
Add a font from a fontconfig pattern.
bool addFontFromFile(const QString &filename, const int index, FT_LibrarySP freeTypeLibrary)
QScopedPointer< Private > d
void debugInfo() const
Print out the font family hierarchy into the debug output.
void sortIntoWWSFamilies()
Sort any straggling fonts into WWSFamilies.
void addSupportedLanguagesByFile(const QString &filename, const int index, const QList< QLocale > &supportedLanguages, FcCharSet *set)
std::optional< QString > wwsNameByFamilyName(const QString familyName) const
Used to find the closest corresponding resource when the family name doesn't match.
void addGenericFamily(const QString &name)
This adds a CSS generic family. Call this before sortIntoWWSFamilies.
QList< KoFontFamilyWWSRepresentation > collectFamilies() const
Collects all WWSFamilies (that is, CSS compatible groupings of font files) and return them.
static QString sampleTagForQLocale(const QLocale &locale)
static QMap< QString, QString > samples()
static bool qFuzzyCompare(half p1, half p2)
#define KIS_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:97
int size(const Forest< T > &forest)
Definition KisForest.h:1232
@ OpenTypeFontType
Definition KoSvgText.h:810
@ Type1FontType
Definition KoSvgText.h:809
@ UnknownFontType
Definition KoSvgText.h:807
static FontFamilyNode createWWSFamilyNode(const FontFamilyNode &child, const FontFamilyNode &typographic, QStringList existingWWSNames)
QStringList debugInfo() const
bool compareAxes(QHash< QString, KoSvgText::FontFamilyAxis > otherAxes)
QHash< int, QStringList > pixelSizes
pixelSizes This is only used for bitmap fonts, when searching we try to return the files associated w...
QHash< QLocale, QString > localizedTypographicStyle
QDateTime lastModified
Other files that seem related. These might be duplicate font files, or fonts where only the tech diff...
QHash< QString, QString > sampleStrings
Truetype collections have indices that need to be checked against.
QList< KoSvgText::FontFamilyStyleInfo > styleInfo
styleInfo This abstracts both font families that consist of many separate font-files and variable fon...
QHash< QLocale, QString > localizedFontStyle
KoSvgText::FontFormatType type
QHash< QString, KoSvgText::FontFamilyAxis > axes
axes While typical font-files within the same family are defined by having a single weight or width,...
QHash< QLocale, QString > localizedFullName
QHash< QLocale, QString > localizedFontFamilies
Last time the file was modified.
QHash< QLocale, QString > localizedWWSStyle
QList< QLocale > supportedLanguages
sample string used to generate the preview;
bool hasAnyColor() const
QStringList otherFiles
Languages supported, according to fontconfig.
FontFamilySizeInfo sizeInfo
sizeInfo This is only really used to ensure that sizes get sorted into different WWS families,...
The FontFamilySizeInfo class Some font-families have different designs for different sizes....
int subFamilyID
Whether this is using the OS2 table or the GPOS Size feature.
QHash< QLocale, QString > localizedLabels
bool compare(const FontFamilySizeInfo &other)
QString debugInfo() const
bool os2table
Whether the size info is set.
The KoCSSFontInfo class Convenience struct to make it easier to use KoFontRegistry....
QStringList families
QFont::Style slantMode
KisForest< FontFamilyNode > fontFamilyCollection
fontFamilyCollection
QList< KoSvgText::FontFamilyStyleInfo > styles
QHash< QString, KoSvgText::FontFamilyAxis > axes
KoSvgText::FontFormatType type
QDateTime lastModified
Value of the most recently modified font family. Used for updates.
QHash< QLocale, QString > localizedTypographicStyles
QList< QLocale > supportedLanguages
sample string used to generate the preview;
QHash< QString, QString > sampleStrings
QHash< QLocale, QString > localizedTypographicFamily
QHash< QLocale, QString > localizedFontFamilyNames
static FontFamilyAxis weightAxis(qreal val)
Definition KoSvgText.h:754
QString debugInfo() const
Some variable fonts have axes that are not really supposed to be shown to the user.
Definition KoSvgText.h:785
QHash< QLocale, QString > localizedLabels
Definition KoSvgText.h:777
static FontFamilyAxis widthAxis(qreal val)
Definition KoSvgText.h:761
QHash< QString, float > instanceCoords
Definition KoSvgText.h:816
QHash< QLocale, QString > localizedLabels
Definition KoSvgText.h:815