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 debug.append(QString("Last Modified: %1").arg(lastModified.toString(Qt::ISODateWithMs)));
196 for (int i=0; i< axes.size(); i++) {
197 KoSvgText::FontFamilyAxis axis = axes.value(axes.keys().at(i));
198 debug.append(axis.debugInfo());
199 }
200 for (int i=0; i< styleInfo.size(); i++) {
201 debug.append(styleInfo.at(i).debugInfo());
202 }
203 if (sizeInfo.low >= 0 && sizeInfo.high >= 0) {
204 debug.append(sizeInfo.debugInfo());
205 }
206 if (!pixelSizes.isEmpty()) {
207 QStringList pix;
208 for (int i=0; i< pixelSizes.size(); i++) {
209 pix.append(QString::number(pixelSizes.keys().at(i)));
210 }
211 debug.append("PixelSizes: "+pix.join(", "));
212 } else if (!otherFiles.isEmpty()) {
213 debug.append("Other files: "+otherFiles.join(", "));
214 }
215 return debug;
216}
217
218QDebug operator<<(QDebug dbg, const FontFamilyNode &node) {
219 dbg.nospace() << node.debugInfo();
220 return dbg.space();
221}
222
259
261 : d(new Private())
262{
263
264}
265
269
270// OS2 fsSelection bitflags. See https://learn.microsoft.com/en-us/typography/opentype/spec/os2#fsselection
271constexpr unsigned OS2_ITALIC = 1u << 0;
272constexpr unsigned OS2_BOLD = 1u << 5;
273constexpr unsigned OS2_REGULAR = 1u << 6;
274constexpr unsigned OS2_WWS = 1u << 8;
275constexpr unsigned OS2_OBLIQUE = 1u << 9; // Is an oblique instead of an italic.
276constexpr unsigned OS2_USE_TYPO_METRICS = 1u << 7;
277
278const QString WEIGHT_TAG = "wght";
279const QString WIDTH_TAG = "wdth";
280const QString SLANT_TAG = "slnt";
281const QString ITALIC_TAG = "ital";
282const QString OPTICAL_TAG = "opsz";
283
284bool KoFFWWSConverter::addFontFromPattern(const FcPattern *pattern, FT_LibrarySP freeTypeLibrary)
285{
286 if (!freeTypeLibrary.data()) {
287 return false;
288 }
289
290 bool getFile = false;
291 FcChar8 *fileValue{};
292 if (FcPatternGetString(pattern, FC_FILE, 0, &fileValue) != FcResultMatch) {
293 qWarning() << "Failed to get font file for" << pattern;
294 } else {
295 getFile = true;
296 }
297 QString filename = QString::fromUtf8(reinterpret_cast<char *>(fileValue));
298
299 int indexValue{};
300 if (FcPatternGetInteger(pattern, FC_INDEX, 0, &indexValue) != FcResultMatch) {
301 qWarning() << "Failed to get font index for" << pattern << "(file:" << filename << ")";
302 getFile = false;
303 }
304
305 if (getFile == false) {
306 return getFile;
307 }
308
309 if (indexValue > 0xffff) { // this indicates the font is a variable font instance, so we don't try to load it.
310 return false;
311 }
312
313 bool success = addFontFromFile(filename, indexValue, freeTypeLibrary);
314 if (success) {
315 FcLangSet *set;
316 if (FcPatternGetLangSet(pattern, FC_LANG, 0, &set) != FcResultMatch) {
317 qWarning() << "Failed to get font index for" << pattern << "(file:" << filename << ")";
318 return success;
319 }
320 FcStrList *list = FcStrListCreate(FcLangSetGetLangs(set));
321 FcStrListFirst(list);
322 FcChar8 *langString = FcStrListNext(list);
323 QString lang = QString::fromUtf8(reinterpret_cast<char *>(langString));
324 QList<QLocale> languages;
325 while (!lang.isEmpty()) {
326 languages.append(QLocale(lang));
327
328 langString = FcStrListNext(list);
329 lang = QString::fromUtf8(reinterpret_cast<char *>(langString));
330 }
331 FcStrListDone(list);
332 FcCharSet *charSet = nullptr;
333 if (FcPatternGetCharSet(pattern, FC_CHARSET, 0, &charSet) != FcResultMatch) {
334 return success;
335 }
336 addSupportedLanguagesByFile(filename, indexValue, languages, charSet);
337
338 }
339 return success;
340}
341
342bool KoFFWWSConverter::addFontFromFile(const QString &filename, const int index, FT_LibrarySP freeTypeLibrary) {
343
344 FontFamilyNode fontFamily;
345 fontFamily.fileName = filename;
346 fontFamily.fileIndex = index;
347 for (auto it = d->fontFamilyCollection.begin(); it != d->fontFamilyCollection.end(); it++) {
348 if (it->fileName == fontFamily.fileName && it->fileIndex == fontFamily.fileIndex) {
349 return true;
350 }
351 }
352
353 FT_Face f = nullptr;
354 FT_FaceSP face;
355 QByteArray utfData = fontFamily.fileName.toUtf8();
356 if (FT_New_Face(freeTypeLibrary.data(), utfData.data(), fontFamily.fileIndex, &f) == 0) {
357 face.reset(f);
358 } else {
359 return false;
360 }
361
362 fontFamily.fontFamily = face->family_name;
363 fontFamily.fontStyle = face->style_name;
364 fontFamily.lastModified = QFileInfo(fontFamily.fileName).lastModified();
365 if (!fontFamily.lastModified.isValid()) {
366 QDateTime time = QFileInfo(fontFamily.fileName).birthTime();
367 if (time.isValid()) {
368 fontFamily.lastModified = time;
369 } else {
370 fontFamily.lastModified = QDateTime::fromMSecsSinceEpoch(0);
371 }
372 }
373 FontFamilyNode typographicFamily;
374 FontFamilyNode wwsFamily;
375 bool isWWSFamilyWithoutName = false;
376
377 if (!FT_IS_SFNT(face.data())) {
378 fontFamily.type = FT_IS_SCALABLE(face.data())? KoSvgText::Type1FontType: KoSvgText::BDFFontType;
379
380 fontFamily.isItalic = face->style_flags & FT_STYLE_FLAG_ITALIC;
381 if (face->style_flags & FT_STYLE_FLAG_BOLD) {
383 } else {
385 }
386
387 for (int i=0; i< face->num_fixed_sizes; i++) {
388 // 64 = Freetype pixel
389 fontFamily.pixelSizes.insert((face->available_sizes[i].size / 64.0), {fontFamily.fileName});
390 }
391 } else {
393 hb_face_t_sp hbFace(hb_ft_face_create_referenced(face.data()));
394 hb_font_t_sp hbFont(hb_ft_font_create_referenced(face.data()));
395
396 // Retrieve width, weight and slant data.
397
398 fontFamily.axes.insert(WEIGHT_TAG, KoSvgText::FontFamilyAxis::weightAxis(hb_style_get_value(hbFont.data(), HB_STYLE_TAG_WEIGHT)));
399 fontFamily.axes.insert(WIDTH_TAG, KoSvgText::FontFamilyAxis::widthAxis(hb_style_get_value(hbFont.data(), HB_STYLE_TAG_WIDTH)));
400 fontFamily.isItalic = hb_style_get_value(hbFont.data(), HB_STYLE_TAG_ITALIC) > 0;
401 //fontFamily.isOblique = hb_style_get_value(hbFont.data(), HB_STYLE_TAG_SLANT_ANGLE) != 0;
402
403 TT_OS2 *os2Table = nullptr;
404 os2Table = (TT_OS2*)FT_Get_Sfnt_Table(face.data(), FT_SFNT_OS2);
405 if (os2Table) {
406
407 fontFamily.isOblique = os2Table->fsSelection & OS2_OBLIQUE;
408
409 if (os2Table->fsSelection & OS2_REGULAR) {
410 fontFamily.isItalic = false;
411 fontFamily.isOblique = false;
412 }
413
414 if (os2Table->version >= 5) {
415 FontFamilySizeInfo sizeInfo;
416 const qreal twip = 0.05;
417 sizeInfo.high = os2Table->usUpperOpticalPointSize * twip;
418 sizeInfo.low = os2Table->usLowerOpticalPointSize * twip;
419 sizeInfo.os2table = true;
420 sizeInfo.isSet = true;
421 fontFamily.sizeInfo = sizeInfo;
422 }
423 isWWSFamilyWithoutName = (os2Table->fsSelection & OS2_WWS);
424 }
425
426
427 // retrieve gpos size data...
428 uint designSize;
429 uint subFamilyId;
430 uint rangeStart;
431 uint rangeEnd;
432 hb_ot_name_id_t sizeNameId;
433 if (hb_ot_layout_get_size_params(hbFace.data(), &designSize, &subFamilyId, &sizeNameId, &rangeStart, &rangeEnd)) {
434 FontFamilySizeInfo sizeInfo;
435 qreal tenth = 0.1;
436 sizeInfo.low = rangeStart * tenth;
437 sizeInfo.high = rangeEnd * tenth;
438 sizeInfo.subFamilyID = subFamilyId;
439 sizeInfo.designSize = designSize * tenth;
440 sizeInfo.isSet = true;
441 fontFamily.sizeInfo = sizeInfo;
442 }
443
444 // retrieve axis data...
445 // 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.
446
447 QHash<hb_ot_name_id_t, QString> axisNameIDs;
448 QVector<hb_ot_name_id_t> instanceNameIDs;
449 if (hb_ot_var_has_data(hbFace.data())) {
450 fontFamily.isVariable = true;
451 uint count = hb_ot_var_get_axis_count(hbFace.data());
452 uint maxInfos = 1;
453 QStringList axesTags;
454 for (uint i = 0; i < count; i++) {
456 hb_ot_var_axis_info_t axis;
457 hb_ot_var_get_axis_infos(hbFace.data(), i, &maxInfos, &axis);
458 axisInfo.min = axis.min_value;
459 axisInfo.max = axis.max_value;
460 axisInfo.value = axis.default_value;
461 axisInfo.axisHidden = axis.flags & HB_OT_VAR_AXIS_FLAG_HIDDEN;
462 char buff[4];
463 hb_tag_to_string(axis.tag, buff);
464 axisInfo.tag = QString::fromLatin1(buff, 4);
465 axisNameIDs.insert(axis.name_id, axisInfo.tag);
466 axisInfo.variableAxis = true;
467 fontFamily.axes.insert(axisInfo.tag, axisInfo);
468 axesTags.append(axisInfo.tag);
469 }
470 count = hb_ot_var_get_named_instance_count (hbFace.data());
471 for (uint i = 0; i < count; i++) {
472 QHash<QString, float> instanceCoords;
473 uint coordLength = axesTags.size();
474 std::vector<float> coordinate(coordLength);
475 hb_ot_var_named_instance_get_design_coords (hbFace.data(), i, &coordLength, coordinate.data());
476 for (uint j =0; j < coordLength; j++ ){
477 instanceCoords.insert(axesTags.value(j), coordinate[j]);
478 }
480 style.instanceCoords = instanceCoords;
481 instanceNameIDs.append(hb_ot_var_named_instance_get_subfamily_name_id(hbFace.data(), i));
482 fontFamily.styleInfo.append(style);
483 }
484 }
485
486 // Get some basic color data.
487 fontFamily.colorBitMap = hb_ot_color_has_png(hbFace.data());
488 fontFamily.colorSVG = hb_ot_color_has_svg(hbFace.data());
489 fontFamily.colorClrV0 = hb_ot_color_has_layers(hbFace.data());
490 //fontFamily.colorClrV1 = hb_ot_color_has_paint(hbFace);
491 wwsFamily.colorBitMap = fontFamily.colorBitMap;
492 wwsFamily.colorSVG = fontFamily.colorSVG;
493 wwsFamily.colorClrV0 = fontFamily.colorClrV0;
494
495 uint numEntries = 0;
496 const hb_ot_name_entry_t *entries = hb_ot_name_list_names(hbFace.data(), &numEntries);
497
498 QHash<QLocale, QString> ribbiFamilyNames;
499 QHash<QLocale, QString> ribbiStyleNames;
500 QHash<QLocale, QString> WWSFamilyNames;
501 QHash<QLocale, QString> WWSStyleNames;
502 QHash<QLocale, QString> typographicFamilyNames;
503 QHash<QLocale, QString> typographicStyleNames;
504 QHash<QLocale, QString> fullNames;
505 for (uint i = 0; i < numEntries; i++) {
506 hb_ot_name_entry_t entry = entries[i];
507 QString lang(hb_language_to_string(entry.language));
508 QLocale locale(lang);
509 uint length = hb_ot_name_get_utf8(hbFace.data(), entry.name_id, entry.language, nullptr, nullptr)+1;
510 std::vector<char> buff(length);
511 hb_ot_name_get_utf8(hbFace.data(), entry.name_id, entry.language, &length, buff.data());
512 QString name = QString::fromUtf8(buff.data(), length);
513 if (name.isEmpty()) continue;
514
515 if (entry.name_id == HB_OT_NAME_ID_FONT_FAMILY) {
516 ribbiFamilyNames.insert(locale, name);
517 } else if (entry.name_id == HB_OT_NAME_ID_TYPOGRAPHIC_FAMILY) {
518 typographicFamilyNames.insert(locale, name);
519 } else if (entry.name_id == HB_OT_NAME_ID_WWS_FAMILY) {
520 WWSFamilyNames.insert(locale, name);
521 } else if (entry.name_id == HB_OT_NAME_ID_FONT_SUBFAMILY) {
522 ribbiStyleNames.insert(locale, name);
523 } else if (entry.name_id == HB_OT_NAME_ID_TYPOGRAPHIC_SUBFAMILY) {
524 typographicStyleNames.insert(locale, name);
525 } else if (entry.name_id == HB_OT_NAME_ID_WWS_SUBFAMILY) {
526 WWSStyleNames.insert(locale, name);
527 } else if (entry.name_id == HB_OT_NAME_ID_FULL_NAME) {
528 fullNames.insert(locale, name);
529 } 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.
530 if (axisNameIDs.keys().contains(entry.name_id)) {
531 fontFamily.axes[axisNameIDs.value(entry.name_id)].localizedLabels.insert(locale, name);
532 } else if (entry.name_id == sizeNameId) {
533 fontFamily.sizeInfo.localizedLabels.insert(locale, name);
534 } else if (instanceNameIDs.contains(entry.name_id)) {
535 int idx = instanceNameIDs.indexOf(entry.name_id);
536 fontFamily.styleInfo[idx].localizedLabels.insert(locale, name);
537 }
538 }
539 }
540 QLocale english(QLocale::English);
541 if (!typographicFamilyNames.isEmpty()) {
542 typographicFamily.fontFamily = typographicFamilyNames.value(english, typographicFamilyNames.values().first());
543 typographicFamily.localizedFontFamilies = typographicFamilyNames;
544 }
545 fontFamily.localizedTypographicStyle = typographicStyleNames;
546 if (!ribbiFamilyNames.isEmpty()) {
547 fontFamily.fontFamily = ribbiFamilyNames.value(english, ribbiFamilyNames.values().first());
548 fontFamily.localizedFontFamilies = ribbiFamilyNames;
549 }
550 // Second check is a hack to avoid issues with the css test fonts. Why they are configure this way, beats me.
551 // Either way, if the fullname, which has 100% priority is the name as typographic name (which is tested last)
552 // the font search will only select the node with this fullname, which we don't want.
553 if (!fullNames.isEmpty() && fullNames != typographicFamilyNames) {
554 fontFamily.localizedFullName = fullNames;
555 }
556 if (!ribbiStyleNames.isEmpty()) {
557 fontFamily.fontStyle = ribbiStyleNames.value(english, ribbiStyleNames.values().first());
558 fontFamily.localizedFontStyle = ribbiStyleNames;
559 }
560 if (!WWSFamilyNames.isEmpty()) {
561 wwsFamily.fontFamily = WWSFamilyNames.value(english, WWSFamilyNames.values().first());
562 wwsFamily.localizedFontFamilies = WWSFamilyNames;
563 }
564 fontFamily.localizedWWSStyle = WWSStyleNames;
565 }
566
567 if (fontFamily.fontFamily.isEmpty()) {
568 fontFamily.fontFamily = QFileInfo(fontFamily.fileName).baseName();
569 }
570 if (typographicFamily.fontFamily.isEmpty()) {
571 typographicFamily.fontFamily = fontFamily.fontFamily;
572 }
573 wwsFamily.isVariable = fontFamily.isVariable;
574 wwsFamily.type = fontFamily.type;
575 typographicFamily.type = typographicFamily.type;
576
577 if (typographicFamily.fontFamily.isEmpty() && fontFamily.fontFamily.isEmpty()) {
578 d->fontFamilyCollection.insert(d->fontFamilyCollection.childEnd(), fontFamily);
579 } else {
580 // find potential typographic family
581 auto it = d->fontFamilyCollection.childBegin();
582 for (; it != d->fontFamilyCollection.childEnd(); it++) {
583 if (!typographicFamily.fontFamily.isEmpty() && it->fontFamily == typographicFamily.fontFamily) {
584 break;
585 } else if (it->fontFamily == fontFamily.fontFamily) {
586 break;
587 }
588 }
589 if (it != d->fontFamilyCollection.childEnd()) {
590
591 if (isWWSFamilyWithoutName) {
601 if (!typographicFamily.fontFamily.isEmpty() && typographicFamily.fontFamily != fontFamily.fontFamily) {
602 wwsFamily.fontFamily = typographicFamily.fontFamily;
603 } else {
604 wwsFamily.fontFamily = fontFamily.fontFamily;
605 }
606 }
607 if (!wwsFamily.fontFamily.isEmpty()) {
608 // sort into wws family
609 auto wws = childBegin(it);
610 for (; wws != childEnd(it); wws++) {
611 if (wws->fontFamily == wwsFamily.fontFamily) {
612 break;
613 }
614 }
615 if (wws != childEnd(it)) {
616 d->fontFamilyCollection.insert(childEnd(wws), fontFamily);
617 } else {
618 auto wwsNew = d->fontFamilyCollection.insert(childEnd(it), wwsFamily);
619 d->fontFamilyCollection.insert(childEnd(wwsNew), fontFamily);
620 }
621 } else if (!fontFamily.pixelSizes.isEmpty()) {
622 // sort any pixel sizes into the appropriate family.
623 auto pixel = childBegin(it);
624 for (; pixel != childEnd(it); pixel++) {
625 if (pixel->fontFamily == fontFamily.fontFamily && pixel->fontStyle == fontFamily.fontStyle && !pixel->pixelSizes.isEmpty()) {
626 for (int pxSize = 0; pxSize < fontFamily.pixelSizes.keys().size(); pxSize++) {
627 int px = fontFamily.pixelSizes.keys().at(pxSize);
628 QStringList files = pixel->pixelSizes.value(px, QStringList());
629 files.append(fontFamily.pixelSizes.value(px));
630 pixel->pixelSizes.insert(px, files);
631 }
632 break;
633 }
634 }
635 if (pixel == childEnd(it)) {
636 d->fontFamilyCollection.insert(childEnd(it), fontFamily);
637 }
638
639 } else {
640 d->fontFamilyCollection.insert(childEnd(it), fontFamily);
641 }
642 } else {
643 auto typographic = d->fontFamilyCollection.insert(d->fontFamilyCollection.childEnd(), typographicFamily);
644 if (isWWSFamilyWithoutName) {
645 if (!typographicFamily.fontFamily.isEmpty() && typographicFamily.fontFamily != fontFamily.fontFamily) {
646 wwsFamily.fontFamily = typographicFamily.fontFamily;
647 } else {
648 wwsFamily.fontFamily = fontFamily.fontFamily;
649 }
650 }
651 if (!wwsFamily.fontFamily.isEmpty()) {
652 auto wwsNew = d->fontFamilyCollection.insert(childEnd(typographic), wwsFamily);
653 d->fontFamilyCollection.insert(childEnd(wwsNew), fontFamily);
654 } else {
655 d->fontFamilyCollection.insert(childEnd(typographic), fontFamily);
656 }
657 }
658 }
659 return true;
660}
661
662#include <KoWritingSystemUtils.h>
663void KoFFWWSConverter::addSupportedLanguagesByFile(const QString &filename, const int index, const QList<QLocale> &supportedLanguages, FcCharSet *set)
664{
665 auto it = d->fontFamilyCollection.depthFirstTailBegin();
666 for (; it!= d->fontFamilyCollection.depthFirstTailEnd(); it++) {
667 if (it->fileName == filename && it->fileIndex == index) {
668 break;
669 }
670 }
671 if (it != d->fontFamilyCollection.depthFirstTailEnd()) {
672 it->supportedLanguages = supportedLanguages;
673
674 QMap<QString, QString> samples = KoWritingSystemUtils::samples();
675
676 for (int i = 0; i < samples.size(); i++) {
677 QString sample = samples.keys().at(i);
678 bool matching = true;
679 Q_FOREACH (uint unicode, sample.toUcs4()) {
680 if (!FcCharSetHasChar(set, unicode)) {
681 matching = false;
682 break;
683 }
684 }
685 if (matching) {
686 it->sampleStrings.insert(samples.value(sample), sample);
687 }
688 }
689 }
690}
691
693{
694 QStringList wwsNames;
695 // Some font families have predefined wws families, others don't. This function sorts out everything so that each font file has
696 // a wws family in between the typographic and font-file nodes, this is important, because the wws family will be the one presented
697 // as the font-family resource.
698 for (auto typographic = d->fontFamilyCollection.childBegin(); typographic != d->fontFamilyCollection.childEnd(); typographic++) {
700
701 QVector<qreal> weights;
702 QVector<qreal> widths;
703 QVector<int> fileIndices;
705
706 // This takes all of the current children that aren't inside a wws-node already and puts them into a temp list,
707 // as well as tallying the current widths and weights. We need these to find the most regular value.
709 for (auto child = childBegin(typographic); child != childEnd(typographic); child++) {
710 if (childBegin(child) != childEnd(child)) {
711 wwsNames.append(child->fontFamily);
712 continue;
713 }
714 tempList.insert(tempList.childEnd(), *child);
715 qreal wght = child->axes.value(WEIGHT_TAG, KoSvgText::FontFamilyAxis::weightAxis(400)).value;
716 if (!weights.contains(wght)) weights.append(wght);
717 qreal wdth = child->axes.value(WIDTH_TAG, KoSvgText::FontFamilyAxis::widthAxis(100)).value;
718 if (!widths.contains(wdth)) widths.append(wdth);
719 if (!fileIndices.contains(child->fileIndex)) fileIndices.append(child->fileIndex);
720 types.append(child->type);
721 deleteList.append(child);
722 }
723 while (!deleteList.isEmpty()) {
724 auto child = deleteList.takeFirst();
725 KIS_ASSERT_RECOVER_NOOP(childBegin(child) == childEnd(child));
726 d->fontFamilyCollection.erase(child);
727 }
728 if (KisForestDetail::size(tempList) > 0) {
729 //Do most regular first...
732 for (auto font = tempList.childBegin(); font != tempList.childEnd(); font++) {
733 const qreal testWeight = weights.contains(400)? 400: weights.first();
734 const qreal testWidth = widths.contains(100)? 100: widths.first();
735 // We want to select the first file index if possible.
736 const int testFileIndex = fileIndices.contains(0)? 0: fileIndices.first();
737
738 bool widthTested = !font->axes.keys().contains(WIDTH_TAG);
739 widthTested = widthTested? true: qFuzzyCompare(font->axes.value(WIDTH_TAG).value, testWidth);
740
741 QPair<QString, QString> fontStyle(font->fontFamily, font->fontStyle);
742 if (qFuzzyCompare(font->axes.value(WEIGHT_TAG).value, testWeight) && widthTested
743 && !font->isItalic
744 && !font->isOblique
745 && ( ( fileIndices.size() > 1 && font->fileIndex == testFileIndex ) || ( fileIndices.size()<=1 ))
746 && font->type == testType
747 && !existing.contains(fontStyle)) {
748 FontFamilyNode wwsFamily = FontFamilyNode::createWWSFamilyNode(*font, *typographic, wwsNames);
749 wwsNames.append(wwsFamily.fontFamily);
750 existing.append(fontStyle);
751
752 auto newWWS = d->fontFamilyCollection.insert(childEnd(typographic), wwsFamily);
753 d->fontFamilyCollection.insert(childEnd(newWWS), *font);
754 deleteList.append(font);
755 }
756 }
757 while (!deleteList.isEmpty()) {
758 auto child = deleteList.takeFirst();
759 KIS_ASSERT_RECOVER_NOOP(childBegin(child) == childEnd(child));
760 tempList.erase(child);
761 }
762 // Then sort the rest of the family nodes into wws families.
763 for (auto font = tempList.childBegin(); font != tempList.childEnd(); font++) {
764 auto wws = childBegin(typographic);
765 auto wwsCandidate = childEnd(typographic);
766 for (; wws != childEnd(typographic); wws++) {
767 auto wwsChild = childBegin(wws);
768 if (font->type != KoSvgText::OpenTypeFontType && font->type != KoSvgText::Type1FontType) {
769 // Hack for really old fonts.
770 // It's questionable whether this is wise, given that it is not the family name,
771 // but it seems CSS is explicitly vague about what the family name is, because
772 // of the varying ways a font can be assigned a family name.
773 // Like, it is technically correct, because it allows us to select fonts with CSS
774 // that would otherwise be unselectable, but this does mean that other applications
775 // would need to have our exact idea of a CSS family name.
776 if (wws->fontStyle.toLower() != "regular"
777 && !font->fontStyle.contains(wws->fontStyle)) {
778 continue;
779 }
780 }
781 // In a previous version of the code, variable and non-variable were not mixed, but after reconsidering,
782 // they probably should be sorted together. The code will prioritize variable fonts in any case.
783
784 if (!wwsChild->sizeInfo.compare(font->sizeInfo)) {
785 // Skip sorting if the WWS family has size info that is incompatible with the sorted font.
786 continue;
787 }
788 for (; wwsChild != childEnd(wws); wwsChild++) {
789 if (wwsChild->isItalic == font->isItalic
790 && wwsChild->isOblique == font->isOblique
791 && wwsChild->compareAxes(font->axes)
792 && wwsChild->hasAnyColor() == font->hasAnyColor()) {
793 break;
794 }
795 }
796 if (wwsChild != childEnd(wws)) {
797 if (wwsChild->fontFamily == font->fontFamily && wwsChild->fontStyle == font->fontStyle) {
798 // If, for all intends and purposes, the font seems to be the same, merge nodes.
799 // This sometimes happens with installations where the same font is installed in
800 // a variety of formats. We want to prefer the opentype version in any case.
801 if (wwsChild->type != KoSvgText::OpenTypeFontType && font->type == KoSvgText::OpenTypeFontType) {
802 wwsChild->otherFiles.append(wwsChild->fileName);
803 wwsChild->otherFiles.append(font->otherFiles);
804 wwsChild->fileName = font->fileName;
805 wwsChild->type = KoSvgText::OpenTypeFontType;
806 } else {
807 wwsChild->otherFiles.append(font->fileName);
808 wwsChild->otherFiles.append(font->otherFiles);
809 }
810 if (wwsChild->lastModified < font->lastModified) {
811 wwsChild->lastModified = font->lastModified;
812 }
813 break;
814 } else {
815 continue;
816 }
817 } else {
818 /*
819 * Try to match the wws family with the same fontfamily, otherwise select the first candidate.
820 */
821 if (wwsCandidate == childEnd(typographic)) {
822 wwsCandidate = wws;
823 }
824 if (wws->fontFamily == font->fontFamily) {
825 wwsCandidate = wws;
826 break;
827 }
828 }
829 }
830 if (wwsCandidate != childEnd(typographic)) {
831 d->fontFamilyCollection.insert(childEnd(wwsCandidate), *font);
832 } else if (wws == childEnd(typographic)) {
833 FontFamilyNode wwsFamily = FontFamilyNode::createWWSFamilyNode(*font, *typographic, wwsNames);
834 wwsNames.append(wwsFamily.fontFamily);
835 auto newWWS = d->fontFamilyCollection.insert(childEnd(typographic), wwsFamily);
836 if (wwsFamily.fontFamily != typographic->fontFamily) {
837 font->localizedTypographicStyle.clear();
838 }
839 d->fontFamilyCollection.insert(childEnd(newWWS), *font);
840 }
841 }
842 // This only triggers when the first wws family was created with the typographic name,
843 // yet more wws families have followed after sorting was finished, and this name might be not the most precise.
844 if (wwsNames.contains(typographic->fontFamily) && std::distance(childBegin(typographic), childEnd(typographic)) > 1) {
845 for (auto wws = childBegin(typographic); wws != childEnd(typographic); wws++) {
846 if (wws->fontFamily == typographic->fontFamily) {
847 if (wwsNames.contains(childBegin(wws)->fontFamily)) {
848 const QString otherWWSName = childBegin(wws)->fontFamily;
849 // Also rename any other wwsfamilies that has been using the second style.
850 for (auto otherwws = childBegin(typographic); otherwws != childEnd(typographic); otherwws++) {
851 if (otherwws->fontFamily == otherWWSName) {
852 otherwws->fontFamily = childBegin(otherwws)->fontFamily + " " + childBegin(otherwws)->fontStyle;
853 QHash<QLocale, QString> families;
854 const QHash<QLocale, QString> styles = childBegin(otherwws)->localizedFontStyle;
855 Q_FOREACH (const QLocale l, otherwws->localizedFontFamilies.keys()) {
856 families.insert(l, otherwws->localizedFontFamilies.value(l)+" "+styles.value(l, childBegin(otherwws)->fontStyle));
857 }
858 otherwws->localizedFontFamilies = families;
859 break;
860 }
861 }
862 }
863 wws->fontFamily = childBegin(wws)->fontFamily;
864 childBegin(wws)->localizedTypographicStyle.clear();
865 break;
866 }
867 }
868 }
869 }
870 }
871}
872
873void KoFFWWSConverter::addGenericFamily(const QString &name)
874{
875 FontFamilyNode typographicFamily;
876 FontFamilyNode fontFamily;
877
878 QHash<QLocale, QString> familyNames = {{QLocale(QLocale::English), name}};
879 // TODO: can and should we translate this?
880 QHash<QLocale, QString> styleNames = {{QLocale(QLocale::English), "Regular"}};
881
882 fontFamily.fontFamily = name;
883 fontFamily.localizedFontFamilies = familyNames;
884
886
887 typographicFamily = fontFamily;
889 fontFamily.fontStyle = styleNames.values().first();
890 fontFamily.localizedFontStyle = styleNames;
891 QString tag = KoWritingSystemUtils::sampleTagForQLocale(QLocale(QLocale::English));
892 fontFamily.sampleStrings.insert(tag, KoWritingSystemUtils::samples().key(tag));
893
894 auto typographic = d->fontFamilyCollection.insert(d->fontFamilyCollection.childEnd(), typographicFamily);
895 d->fontFamilyCollection.insert(childEnd(typographic), fontFamily);
896}
897
899 KoFontFamilyWWSRepresentation representation;
900 representation.fontFamilyName = wws->fontFamily;
901 representation.localizedFontFamilyNames = wws->localizedFontFamilies;
902 if (!singleFamily) {
903 // This funnels the typographic family to the resource, so that resources may potentially be sorted by their typographic family.
904 representation.typographicFamilyName = typographic->fontFamily;
905 representation.localizedTypographicFamily = typographic->localizedFontFamilies;
906 representation.localizedTypographicStyles = wws->localizedTypographicStyle;
907 }
908 representation.isVariable = wws->isVariable;
909 representation.colorBitMap = wws->colorBitMap;
910 representation.colorClrV0 = wws->colorClrV0;
911 representation.colorClrV1 = wws->colorClrV1;
912 representation.colorSVG = wws->colorSVG;
913 representation.type = wws->type;
914
915 for (auto subFamily = childBegin(wws); subFamily != childEnd(wws); subFamily++) {
917 if (subFamily == childBegin(wws)
918 || representation.lastModified < subFamily->lastModified) {
919 representation.lastModified = subFamily->lastModified;
920 }
921 representation.sampleStrings.insert(subFamily->sampleStrings);
922 Q_FOREACH(const QLocale &locale, subFamily->supportedLanguages) {
923 if (!representation.supportedLanguages.contains(locale)) {
924 representation.supportedLanguages.append(locale);
925 }
926 }
927
928 for (int a = 0; a < subFamily->axes.size(); a++) {
929 QString key = subFamily->axes.keys().at(a);
930 KoSvgText::FontFamilyAxis axis = subFamily->axes.value(key);
931 KoSvgText::FontFamilyAxis mainAxis = representation.axes.value(key);
932 mainAxis.min = qMin(mainAxis.min, axis.min);
933 mainAxis.defaultValue = axis.defaultValue;
934 mainAxis.max = qMax(mainAxis.max, axis.max);
935 mainAxis.localizedLabels.insert(axis.localizedLabels);
936 mainAxis.tag = axis.tag;
937 mainAxis.axisHidden = axis.axisHidden;
938 representation.axes.insert(mainAxis.tag, mainAxis);
939
940 if (!subFamily->isVariable) {
941 style.instanceCoords.insert(key, axis.value);
942 }
943 }
944 if (!subFamily->isVariable) {
945 if (!subFamily->localizedWWSStyle.isEmpty()) {
946 style.localizedLabels = subFamily->localizedWWSStyle;
947 } else if (!subFamily->localizedTypographicStyle.isEmpty()) {
948 style.localizedLabels = subFamily->localizedTypographicStyle;
949 } else if (!subFamily->localizedFontStyle.isEmpty()) {
950 style.localizedLabels = subFamily->localizedFontStyle;
951 } else {
952 style.localizedLabels.insert(QLocale(QLocale::English), subFamily->fontStyle);
953 }
954 style.isItalic = subFamily->isItalic;
955 style.isOblique = subFamily->isOblique;
956 representation.styles.append(style);
957 } else {
958 for (int i = 0; i < subFamily->styleInfo.size(); i++) {
959 KoSvgText::FontFamilyStyleInfo styleInfo = subFamily->styleInfo.at(i);
960 if (!styleInfo.localizedLabels.isEmpty()) {
961 styleInfo.isItalic = subFamily->isItalic;
962 styleInfo.isOblique = subFamily->isOblique;
963 representation.styles.append(styleInfo);
964 }
965 }
966 }
967 }
968 return representation;
969}
970
972{
974 for (auto typographic = d->fontFamilyCollection.childBegin(); typographic != d->fontFamilyCollection.childEnd(); typographic++) {
975 auto counter = childBegin(typographic);
976 counter++;
977 bool singleFamily = childBegin(typographic) != childEnd(typographic) && std::next(childBegin(typographic)) == childEnd(typographic);
978
979 for (auto wws = childBegin(typographic); wws != childEnd(typographic); wws++) {
980
981 collection.append(createRepresentation(wws, typographic, singleFamily));
982 }
983 }
984 return collection;
985}
986
988 QString familySimplified = family;
989 QString familyLower = family.toLower();
990 // Qt's fontdatabase would add the vendor in [] behind the font name, when there were duplicates,
991 // though sometimes there was no such explanation, so we should check against that...
992 if (family.endsWith("]") && family.contains("[")) {
993 familySimplified = family.split("[", Qt::SkipEmptyParts).first().trimmed().toLower();
994 }
995 for (; it != endIt; it++) {
996 if (it.state() == KisForestDetail::Enter) {
997 continue;
998 }
999
1000 if (childBegin(it) == childEnd(it)) {
1001 // For the lowest nodes, we only want to test the full name.
1002 QStringList local = it->localizedFullName.values();
1003
1004 // For some fonts, the full name is the exact same as the typographic family name,
1005 // in which case, we want to ignore the full name.
1006 QStringList localFamily = it->localizedFontFamilies.values();
1007 bool inLocalFamilyToo = (localFamily.contains(familySimplified, Qt::CaseInsensitive)
1008 || localFamily.contains(familyLower, Qt::CaseInsensitive));
1009
1010 if (local.contains(familySimplified, Qt::CaseInsensitive)
1011 || local.contains(familyLower, Qt::CaseInsensitive)) {
1012 if (inLocalFamilyToo) continue;
1013 break;
1014 } else {
1015 continue;
1016 }
1017 }
1018
1019 QStringList local = it->localizedFontFamilies.values();
1020 QString itFamilyLower = QString(it->fontFamily).toLower();
1021 if (itFamilyLower == familySimplified
1022 || itFamilyLower == familyLower
1023 || local.contains(familySimplified, Qt::CaseInsensitive)
1024 || local.contains(familyLower, Qt::CaseInsensitive)) {
1025 break;
1026 }
1027
1028 }
1029 return it;
1030}
1031
1032std::optional<KoFontFamilyWWSRepresentation> KoFFWWSConverter::representationByFamilyName(const QString &familyName) const
1033{
1034 for (auto typographic = d->fontFamilyCollection.childBegin(); typographic != d->fontFamilyCollection.childEnd(); typographic++) {
1035 auto counter = childBegin(typographic);
1036 counter++;
1037 bool singleFamily = counter == childEnd(typographic);
1038
1039 for (auto wws = childBegin(typographic); wws != childEnd(typographic); wws++) {
1040 if (wws->fontFamily == familyName) {
1041 return std::make_optional(createRepresentation(wws, typographic, singleFamily));
1042 }
1043 }
1044 }
1045 return std::nullopt;
1046}
1047
1048std::optional<QString> KoFFWWSConverter::wwsNameByFamilyName(const QString familyName) const
1049{
1050 auto it = d->fontFamilyCollection.compositionBegin();
1051 it = searchNodes(it, d->fontFamilyCollection.compositionEnd(), familyName);
1052 if (it != d->fontFamilyCollection.compositionEnd()) {
1053
1054 bool isChild = childBegin(it) == childEnd(it);
1055 auto wws = siblingCurrent(it);
1056
1057 // check if we're in a typographic family by testing the hierarchy.
1058 // if so, select the wws family.
1059 auto hierarchy = hierarchyBegin(wws);
1060 hierarchy++;
1061 if (isChild) {
1062 wws = siblingCurrent(hierarchy);
1063 } else if (hierarchy == hierarchyEnd(wws)) {
1064 wws = childBegin(it);
1065 }
1066 return std::make_optional(wws->fontFamily);
1067 }
1068 return std::nullopt;
1069}
1070#include <KoCssTextUtils.h>
1071QVector<FontFamilyNode> findNodesByAxis(const QVector<FontFamilyNode> &nodes, const QString axisTag, const qreal &value, const qreal &defaultValue, const qreal &defaultValueUpper) {
1072 QVector<FontFamilyNode> candidates;
1073 QVector<qreal> values;
1074 Q_FOREACH (const FontFamilyNode &node, nodes) {
1075 qreal selectingVal = defaultValue;
1076
1077 if (node.axes.keys().contains(axisTag)) {
1078 KoSvgText::FontFamilyAxis axis = node.axes.value(axisTag);
1079 selectingVal = axis.value;
1080 if (axis.variableAxis) {
1081 if (value >= axis.min && value <= axis.max) {
1082 candidates.append(node);
1083 selectingVal = value;
1084 } else {
1085 values.append(axis.min);
1086 values.append(axis.max);
1087 }
1088 continue;
1089 }
1090 values.append(selectingVal);
1091 }
1092 }
1093 // We found some variable fonts already, so lets return early.
1094 if (!candidates.isEmpty()) {
1095 return candidates;
1096 }
1097
1098 // follow the CSS Fonts selection mechanism.
1099 bool shouldNotReturnDefault = ((axisTag == ITALIC_TAG || axisTag == SLANT_TAG) && value != defaultValue);
1100 qreal selectedValue = KoCssTextUtils::cssSelectFontStyleValue(values, value, defaultValue, defaultValueUpper, shouldNotReturnDefault);
1101
1102 Q_FOREACH (const FontFamilyNode &node, nodes) {
1103 if (node.axes.keys().contains(axisTag)) {
1104 KoSvgText::FontFamilyAxis axis = node.axes.value(axisTag);
1105 if (axis.value == selectedValue) {
1106 candidates.append(node);
1107 }
1108 } else if (value == defaultValue && !shouldNotReturnDefault) {
1109 candidates.append(node);
1110 }
1111 }
1112 return candidates;
1113}
1114
1116 quint32 xRes, quint32 yRes) const
1117{
1118 QVector<FontFileEntry> candidateFileNames;
1119
1120 int pixelSize = info.size * (qMin(xRes, yRes) / 72.0);
1121
1122 Q_FOREACH(const QString &family, info.families) {
1123 auto it = d->fontFamilyCollection.compositionBegin();
1124 it = searchNodes(it, d->fontFamilyCollection.compositionEnd(), family);
1125 if (it != d->fontFamilyCollection.compositionEnd()) {
1126 auto wws = siblingCurrent(it);
1127
1128 // check if we're in a typographic family by testing the hierarchy.
1129 // if so, select all subnodes.
1130 // Because we test subtree depth-first for finding the nodes, wws
1131 // and full names are tested before typographic.
1132 auto hierarchy = hierarchyBegin(wws);
1133 hierarchy++;
1134 QVector<FontFamilyNode> candidates;
1135 if (hierarchy == hierarchyEnd(wws)) {
1136 auto nodes = subtreeBegin(wws);
1137 auto endNodes = subtreeEnd(wws);
1138 for (;nodes != endNodes; nodes++) {
1139 if (childBegin(nodes) == childEnd(nodes)) {
1140 candidates.append(*nodes);
1141 }
1142 }
1143 } else if (childBegin(wws) == childEnd(wws)) {
1144 candidates.append(*wws);
1145 } else {
1146 auto style = childBegin(wws);
1147 auto styleEnd = childEnd(wws);
1148 for (;style != styleEnd; style++) {
1149 candidates.append(*style);
1150 }
1151 }
1152
1153 if (candidates.size() > 1) {
1154 // first find width
1155 candidates = findNodesByAxis(candidates, WIDTH_TAG, info.width, 100.0, 100.0);
1156 }
1157
1158 if (candidates.size() > 1) {
1159 // then find weight
1160 candidates = findNodesByAxis(candidates, WEIGHT_TAG, info.weight, 400.0, 500.0);
1161 }
1162 // then match italic
1163 if (candidates.size() > 1) {
1165 QVector<FontFamilyNode> obliques;
1166
1167 if (wws->isVariable) {
1168 qreal slantValue = info.slantMode == QFont::StyleItalic? 11: info.autoSlant? 14: info.slantValue;
1169 italics = findNodesByAxis(candidates, ITALIC_TAG, 1.0, 0.0, 0.0);
1170 obliques = findNodesByAxis(candidates, SLANT_TAG, -slantValue, 0.0, 0.0);
1171 }
1172 if (italics.isEmpty() && obliques.isEmpty()) {
1173 Q_FOREACH(const FontFamilyNode &node, candidates) {
1174 if (node.isItalic) {
1175 if (!node.isOblique) {
1176 italics.append(node);
1177 } else {
1178 obliques.append(node);
1179 }
1180 }
1181 }
1182 }
1183
1184 if (info.slantMode == QFont::StyleItalic) {
1185 if (!italics.isEmpty()) {
1186 candidates = italics;
1187 } else if (!obliques.isEmpty()) {
1188 candidates = obliques;
1189 }
1190 } else if (info.slantMode == QFont::StyleOblique) {
1191 if (!obliques.isEmpty()) {
1192 candidates = obliques;
1193 } else if (!italics.isEmpty()) {
1194 candidates = italics;
1195 }
1196 } else {
1197 QStringList slantedFontFiles;
1199 Q_FOREACH(const FontFamilyNode &italic, italics) {
1200 slantedFontFiles.append(italic.fileName);
1201 }
1202 Q_FOREACH(const FontFamilyNode &oblique, obliques) {
1203 slantedFontFiles.append(oblique.fileName);
1204 }
1205 Q_FOREACH(const FontFamilyNode &node, candidates) {
1206 if (!slantedFontFiles.contains(node.fileName)) {
1207 regular.append(node);
1208 }
1209 }
1210 if (!regular.isEmpty()) {
1211 candidates = regular;
1212 }
1213 }
1214 }
1215
1216 // prefer opentype
1217 if (candidates.size() > 1) {
1218 QVector<FontFamilyNode> openType;
1219 Q_FOREACH(const FontFamilyNode &node, candidates) {
1220 if (node.type == KoSvgText::OpenTypeFontType) {
1221 openType.append(node);
1222 }
1223 }
1224 if (!openType.isEmpty()) {
1225 candidates = openType;
1226 }
1227 }
1228
1229 // finally, match size.
1230 Q_FOREACH(const FontFamilyNode &node, candidates) {
1231 QStringList fileNames = node.otherFiles;
1232 fileNames.append(node.fileName);
1233 fileNames = node.pixelSizes.value(pixelSize, fileNames);
1234 Q_FOREACH(const QString &fileName, fileNames) {
1235 if (fileName.isEmpty()) continue;
1236 FontFileEntry entry;
1237 entry.fileName = fileName;
1238 entry.fontIndex = node.fileIndex;
1239 candidateFileNames.append(entry);
1240 }
1241 }
1242 }
1243 }
1244 return candidateFileNames;
1245}
1246
1248{
1249 qDebug() << "Debug for font family collection" << KisForestDetail::size(d->fontFamilyCollection);
1250 QString spaces;
1251 for (auto it = compositionBegin(d->fontFamilyCollection); it != compositionEnd(d->fontFamilyCollection); it++) {
1252 if (it.state() == KisForestDetail::Enter) {
1253 QStringList debugInfo = it->debugInfo();
1254 for (int i = 0; i< debugInfo.size(); i++) {
1255 if (i==0) {
1256 qDebug().noquote() << QString(spaces + "+") << debugInfo.at(i);
1257 } else {
1258 qDebug().noquote() << QString(spaces + "| ") << debugInfo.at(i);
1259 }
1260 }
1261 spaces.append(" ");
1262 }
1263
1264 if (it.state() == KisForestDetail::Leave) {
1265 spaces.chop(4);
1266 }
1267 }
1268}
1269
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