BUG:517717, some vertical fonts have nonsense values for the caret. We can assume a multi-master font is relatively modern and doesn't have this issue.
There's 3 different definitions of the so-called vertical metrics, that is, the ascender and descender for horizontally laid out script. WinAsc & Desc, HHAE asc&desc, and OS/2... we need the last one, but harfbuzz doesn't return it unless there's a flag set in the font, which is missing in a lot of fonts that were from the transitional period, like Deja Vu Sans. Hence we need to get the OS/2 table and calculate the values manually (and fall back in various ways).
844{
846 hb_direction_t dir = isHorizontal? HB_DIRECTION_LTR: HB_DIRECTION_TTB;
847 QLatin1String scriptLatin1(script.toLatin1());
848 hb_script_t scriptTag = hb_script_from_string(scriptLatin1.data(), scriptLatin1.size());
849 hb_tag_t otScriptTag = HB_OT_TAG_DEFAULT_SCRIPT;
850 hb_tag_t otLangTag = HB_OT_TAG_DEFAULT_LANGUAGE;
852 hb_ot_tags_from_script_and_language(scriptTag, nullptr, &maxCount, &otScriptTag, &maxCount, &otLangTag);
855
857
859
860 metrics.
fontSize = isHorizontal? face.
data()->size->metrics.y_ppem*64.0: face.
data()->size->metrics.x_ppem*64.0;
861
862 uint charIndex = FT_Get_Char_Index(face.
data(), 0x20);
863 if(charIndex > 0) {
864 if (FT_Load_Glyph(face.
data(), charIndex, faceLoadFlags) == FT_Err_Ok) {
865 metrics.
spaceAdvance = isHorizontal? face.
data()->glyph->advance.x: face.
data()->glyph->advance.y;
866 } else {
868 }
869 } else {
871 }
872
873 charIndex = FT_Get_Char_Index(face.
data(),
'0');
874 if(charIndex > 0) {
875 if(FT_Load_Glyph(face.
data(), charIndex, faceLoadFlags) == FT_Err_Ok) {
876 metrics.
zeroAdvance = isHorizontal? face.
data()->glyph->advance.x: face.
data()->glyph->advance.y;
877 } else {
879 }
880 } else {
882 }
883
884 bool isIdeographic = false;
885 charIndex = FT_Get_Char_Index(face.
data(), 0x6C34);
886 if(charIndex > 0) {
887 if (FT_Load_Glyph(face.
data(), charIndex, faceLoadFlags) == FT_Err_Ok) {
889 } else {
891 }
892 isIdeographic = true;
893 } else {
895 }
896
897 const bool useFallback = hb_version_atleast(4, 0, 0);
898
899
900
902 HB_OT_METRICS_TAG_X_HEIGHT,
903 HB_OT_METRICS_TAG_CAP_HEIGHT,
904 HB_OT_METRICS_TAG_SUPERSCRIPT_EM_X_OFFSET,
905 HB_OT_METRICS_TAG_SUPERSCRIPT_EM_Y_OFFSET,
906 HB_OT_METRICS_TAG_SUBSCRIPT_EM_X_OFFSET,
907 HB_OT_METRICS_TAG_SUBSCRIPT_EM_Y_OFFSET,
908 HB_OT_METRICS_TAG_UNDERLINE_OFFSET,
909 HB_OT_METRICS_TAG_UNDERLINE_SIZE,
910 HB_OT_METRICS_TAG_STRIKEOUT_OFFSET,
911 HB_OT_METRICS_TAG_STRIKEOUT_SIZE,
912 });
913
914 if (isHorizontal) {
915 metricTags.append(HB_OT_METRICS_TAG_HORIZONTAL_CARET_RISE);
916 metricTags.append(HB_OT_METRICS_TAG_HORIZONTAL_CARET_RUN);
917 metricTags.append(HB_OT_METRICS_TAG_HORIZONTAL_CARET_OFFSET);
920 } else {
921 metricTags.append(HB_OT_METRICS_TAG_VERTICAL_CARET_RISE);
922 metricTags.append(HB_OT_METRICS_TAG_VERTICAL_CARET_RUN);
923 metricTags.append(HB_OT_METRICS_TAG_VERTICAL_CARET_OFFSET);
926 }
927
928 for (auto it = metricTags.begin(); it!= metricTags.end(); it++) {
929 char c[4];
930 hb_tag_to_string(*it, c);
931 const QLatin1String tagName(c, 4);
932 hb_position_t origin = 0;
933 if (useFallback) {
934 hb_ot_metrics_get_position_with_fallback(font.data(), *it, &origin);
936 } else if (hb_ot_metrics_get_position(font.data(), *it, &origin)) {
938 }
939 }
940
943 if (!FT_HAS_MULTIPLE_MASTERS(face) && !isHorizontal) {
946 }
947
948
949
951 HB_OT_LAYOUT_BASELINE_TAG_ROMAN,
952 HB_OT_LAYOUT_BASELINE_TAG_HANGING,
953 HB_OT_LAYOUT_BASELINE_TAG_IDEO_FACE_BOTTOM_OR_LEFT,
954 HB_OT_LAYOUT_BASELINE_TAG_IDEO_FACE_TOP_OR_RIGHT,
955 HB_OT_LAYOUT_BASELINE_TAG_IDEO_EMBOX_BOTTOM_OR_LEFT,
956 HB_OT_LAYOUT_BASELINE_TAG_IDEO_EMBOX_TOP_OR_RIGHT,
957 HB_OT_LAYOUT_BASELINE_TAG_MATH
958 });
959
960 QMap<QString, qint32> baselineVals;
961
962 for (auto it = baselines.begin(); it!= baselines.end(); it++) {
963 hb_position_t origin = 0;
964 if (hb_ot_layout_get_baseline(font.data(), *it, dir, otScriptTag, otLangTag, &origin)) {
965 std::vector<char> c(4);
966 hb_tag_to_string(*it, c.data());
967 baselineVals.insert(QString::fromLatin1(c.data(), 4), origin);
968 }
969 }
970
971
972
973 hb_position_t ascender = 0;
974 hb_position_t descender = 0;
975 hb_position_t lineGap = 0;
976
991 TT_OS2 *os2Table = nullptr;
992 os2Table = (TT_OS2*)FT_Get_Sfnt_Table(face.
data(), FT_SFNT_OS2);
993 if (os2Table) {
994 int yscale = face.
data()->size->metrics.y_scale;
995
996 ascender = FT_MulFix(os2Table->sTypoAscender, yscale);
997 descender = FT_MulFix(os2Table->sTypoDescender, yscale);
998 lineGap = FT_MulFix(os2Table->sTypoLineGap, yscale);
999 }
1000
1001 constexpr unsigned USE_TYPO_METRICS = 1u << 7;
1002 if (!os2Table || os2Table->version == 0xFFFFU || !(os2Table->fsSelection & USE_TYPO_METRICS)) {
1003 hb_position_t altAscender = 0;
1004 hb_position_t altDescender = 0;
1005 hb_position_t altLineGap = 0;
1006 if (!hb_ot_metrics_get_position(font.data(), HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER, &altAscender)) {
1007 altAscender = face.
data()->ascender;
1008 }
1009 if (!hb_ot_metrics_get_position(font.data(), HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER, &altDescender)) {
1010 altDescender = face.
data()->descender;
1011 }
1012 if (!hb_ot_metrics_get_position(font.data(), HB_OT_METRICS_TAG_HORIZONTAL_LINE_GAP, &altLineGap)) {
1013 altLineGap = face.
data()->height - (altAscender-altDescender);
1014 }
1015
1016
1017
1018
1019
1020 if (!os2Table || (altAscender - altDescender + altLineGap) > (ascender - descender + lineGap)) {
1021 ascender = altAscender;
1022 descender = altDescender;
1023 lineGap = altLineGap;
1024 }
1025 }
1026
1027
1028
1029
1030 const QString ideoBottom("ideo");
1031 const QString ideoTop("idtp");
1032 const QString alphabetic("romn");
1033 const QString ideoCenter("Idce");
1034 const QString hang("hang");
1035 const QString math("math");
1036
1037 if (baselineVals.keys().contains(ideoBottom) && !baselineVals.keys().contains(ideoTop)) {
1038 baselineVals.insert(ideoTop, baselineVals.value(ideoBottom)+metrics.
fontSize);
1039 } else if (!baselineVals.keys().contains(ideoBottom) && baselineVals.keys().contains(ideoTop)) {
1040 baselineVals.insert(ideoBottom, baselineVals.value(ideoTop)-metrics.
fontSize);
1041 } else if (!baselineVals.keys().contains(ideoBottom) && !baselineVals.keys().contains(ideoTop)){
1042
1043 if (!isIdeographic && isHorizontal) {
1044 hb_blob_t_sp dLang(hb_ot_meta_reference_entry( hbFace.data() , HB_OT_META_TAG_DESIGN_LANGUAGES));
1045 uint length = hb_blob_get_length(dLang.data());
1046 QByteArray ba(hb_blob_get_data(dLang.data(), &
length),
length);
1047
1048 const QString designLang = QString::fromLatin1(ba).trimmed();
1049
1050 if (!designLang.isEmpty()) {
1051
1052
1063 };
1064 Q_FOREACH (const QString cjk, cjkScripts) {
1065 if (cjk.isEmpty()) continue;
1066 if (designLang.contains(cjk.trimmed())) {
1067 isIdeographic = true;
1068 break;
1069 }
1070 }
1071 }
1072 }
1073
1074 if (isIdeographic && isHorizontal) {
1075 baselineVals.insert(ideoTop, ascender);
1076 baselineVals.insert(ideoBottom, descender);
1077 if (!baselineVals.keys().contains(alphabetic)) {
1078 baselineVals.insert(alphabetic, 0);
1079 }
1080 if (!baselineVals.keys().contains(hang)) {
1081 baselineVals.insert(hang, baselineVals.value(alphabetic)+metrics.
fontSize*0.6);
1082 }
1083 } else if (isHorizontal) {
1084 const qreal alphabeticMultiplier = qreal(ascender+descender)/metrics.
fontSize;
1085 qint32 top = qMax(baselineVals.value(hang, metrics.
capHeight), qint32(qRound(ascender*alphabeticMultiplier)));
1086 baselineVals.insert(ideoTop, top);
1087 baselineVals.insert(ideoBottom, top-metrics.
fontSize);
1088 } else {
1089 baselineVals.insert(ideoTop, metrics.
fontSize);
1090 baselineVals.insert(ideoBottom, 0);
1091 if (!baselineVals.keys().contains(alphabetic)) {
1092 const qreal alphabeticMultiplier = qreal(ascender+descender)/metrics.
fontSize;
1093 baselineVals.insert(alphabetic, (-descender)*alphabeticMultiplier);
1094 }
1095 if (!baselineVals.keys().contains(hang)) {
1096 baselineVals.insert(hang, baselineVals.value(alphabetic)+metrics.
fontSize*0.6);
1097 }
1098 }
1099 }
1100 baselineVals.insert(ideoCenter, (baselineVals.value(ideoTop) + baselineVals.value(ideoBottom))/2);
1101 if (!isHorizontal && !baselineVals.keys().contains(math)) {
1102 baselineVals.insert(math, baselineVals.value(ideoCenter));
1103 }
1104
1105 if (useFallback) {
1106 for (auto it = baselines.begin(); it!= baselines.end(); it++) {
1107 char c[4];
1108 hb_tag_to_string(*it, c);
1109 const QString tagName = QString::fromLatin1(c, 4);
1110 if (!baselineVals.keys().contains(tagName)) {
1111 hb_position_t origin = 0;
1112 hb_ot_layout_get_baseline_with_fallback(font.data(), *it, dir, otScriptTag, otLangTag, &origin);
1113 baselineVals.insert(tagName, origin);
1114 }
1115 }
1116 }
1117
1118 Q_FOREACH(const QString key, baselineVals.keys()) {
1120 }
1121
1122 if (!isHorizontal) {
1123
1126
1127
1128
1129 qreal height = ascender - descender;
1130 ascender = height*0.5;
1131 descender = -ascender;
1132 }
1133 if (ascender == 0 && descender == 0) {
1134 ascender = face->size->metrics.ascender;
1135 descender = face->size->metrics.descender;
1136 qreal height = ascender - descender;
1137 lineGap = face->size->metrics.height - height;
1138 if (!isHorizontal) {
1139 ascender = height * 0.5;
1140 descender = -ascender;
1141 }
1142 }
1146
1147 if (isHorizontal) {
1149 } else {
1150
1154 }
1155
1156 return metrics;
1157}
static int32_t loadFlagsForFace(FT_Face face, bool isHorizontal=true, int32_t loadFlags=0, const KoSvgText::TextRendering rendering=KoSvgText::RenderingAuto)
@ BaselineAlphabetic
Use 'romn' or the baseline for LCG scripts.
@ BaselineCentral
Use the center between the ideographic over and under.
qint32 ideographicCenterBaseline
default baseline for vertical, centered between over and under.
qint32 ideographicUnderBaseline
location of ideographic under baseline from origin, may fall back to descender.
void setBaselineValueByTag(const QString &tag, int32_t value)
qint32 lineGap
additional linegap between consecutive lines.
qint32 zeroAdvance
Advance of the character '0', CSS Unit 'ch', defaults to 0.5 em in horizontal and 1....
qint32 ideographicAdvance
Advance of the character 'æ°´' (U+6C34), CSS Unit ic, defaults to 1 em.
qint32 ideographicOverBaseline
location of ideographic over baseline from origin.
void setMetricsValueByTag(const QLatin1String &tag, int32_t value)
qint32 fontSize
Currently set size, CSS unit 'em'.
qint32 caretRun
These are only used to determine the caret slant proportion.
qint32 descender
distance for origin to bottom.
qint32 ascender
distance from origin to top.
qint32 capHeight
Height of capital letters, defaults to ascender.
void offsetMetricsToNewOrigin(const Baseline baseline)
bool isVertical
Different fontMetrics count between vertical and horizontal.
qint32 spaceAdvance
Advance of the character ' ', used by tabs.