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).
786{
788 hb_direction_t dir = isHorizontal? HB_DIRECTION_LTR: HB_DIRECTION_TTB;
789 QLatin1String scriptLatin1(script.toLatin1());
790 hb_script_t scriptTag = hb_script_from_string(scriptLatin1.data(), scriptLatin1.size());
791 hb_tag_t otScriptTag = HB_OT_TAG_DEFAULT_SCRIPT;
792 hb_tag_t otLangTag = HB_OT_TAG_DEFAULT_LANGUAGE;
794 hb_ot_tags_from_script_and_language(scriptTag, nullptr, &maxCount, &otScriptTag, &maxCount, &otLangTag);
797
799
801
802 metrics.
fontSize = isHorizontal? face.
data()->size->metrics.y_ppem*64.0: face.
data()->size->metrics.x_ppem*64.0;
803
804 uint charIndex = FT_Get_Char_Index(face.
data(), 0x20);
805 if(charIndex > 0) {
806 if (FT_Load_Glyph(face.
data(), charIndex, faceLoadFlags) == FT_Err_Ok) {
807 metrics.
spaceAdvance = isHorizontal? face.
data()->glyph->advance.x: face.
data()->glyph->advance.y;
808 } else {
810 }
811 } else {
813 }
814
815 charIndex = FT_Get_Char_Index(face.
data(),
'0');
816 if(charIndex > 0) {
817 if(FT_Load_Glyph(face.
data(), charIndex, faceLoadFlags) == FT_Err_Ok) {
818 metrics.
zeroAdvance = isHorizontal? face.
data()->glyph->advance.x: face.
data()->glyph->advance.y;
819 } else {
821 }
822 } else {
824 }
825
826 bool isIdeographic = false;
827 charIndex = FT_Get_Char_Index(face.
data(), 0x6C34);
828 if(charIndex > 0) {
829 if (FT_Load_Glyph(face.
data(), charIndex, faceLoadFlags) == FT_Err_Ok) {
831 } else {
833 }
834 isIdeographic = true;
835 } else {
837 }
838
839 const bool useFallback = hb_version_atleast(4, 0, 0);
840
841
842
844 HB_OT_METRICS_TAG_X_HEIGHT,
845 HB_OT_METRICS_TAG_CAP_HEIGHT,
846 HB_OT_METRICS_TAG_SUPERSCRIPT_EM_X_OFFSET,
847 HB_OT_METRICS_TAG_SUPERSCRIPT_EM_Y_OFFSET,
848 HB_OT_METRICS_TAG_SUBSCRIPT_EM_X_OFFSET,
849 HB_OT_METRICS_TAG_SUBSCRIPT_EM_Y_OFFSET,
850 HB_OT_METRICS_TAG_UNDERLINE_OFFSET,
851 HB_OT_METRICS_TAG_UNDERLINE_SIZE,
852 HB_OT_METRICS_TAG_STRIKEOUT_OFFSET,
853 HB_OT_METRICS_TAG_STRIKEOUT_SIZE,
854 });
855
856 if (isHorizontal) {
857 metricTags.append(HB_OT_METRICS_TAG_HORIZONTAL_CARET_RISE);
858 metricTags.append(HB_OT_METRICS_TAG_HORIZONTAL_CARET_RUN);
859 metricTags.append(HB_OT_METRICS_TAG_HORIZONTAL_CARET_OFFSET);
862 } else {
863 metricTags.append(HB_OT_METRICS_TAG_VERTICAL_CARET_RISE);
864 metricTags.append(HB_OT_METRICS_TAG_VERTICAL_CARET_RUN);
865 metricTags.append(HB_OT_METRICS_TAG_VERTICAL_CARET_OFFSET);
868 }
869
870 for (auto it = metricTags.begin(); it!= metricTags.end(); it++) {
871 char c[4];
872 hb_tag_to_string(*it, c);
873 const QLatin1String tagName(c, 4);
874 hb_position_t origin = 0;
875 if (useFallback) {
876 hb_ot_metrics_get_position_with_fallback(font.data(), *it, &origin);
878 } else if (hb_ot_metrics_get_position(font.data(), *it, &origin)) {
880 }
881 }
882
883
884
886 HB_OT_LAYOUT_BASELINE_TAG_ROMAN,
887 HB_OT_LAYOUT_BASELINE_TAG_HANGING,
888 HB_OT_LAYOUT_BASELINE_TAG_IDEO_FACE_BOTTOM_OR_LEFT,
889 HB_OT_LAYOUT_BASELINE_TAG_IDEO_FACE_TOP_OR_RIGHT,
890 HB_OT_LAYOUT_BASELINE_TAG_IDEO_EMBOX_BOTTOM_OR_LEFT,
891 HB_OT_LAYOUT_BASELINE_TAG_IDEO_EMBOX_TOP_OR_RIGHT,
892 HB_OT_LAYOUT_BASELINE_TAG_MATH
893 });
894
895 QMap<QString, qint32> baselineVals;
896
897 for (auto it = baselines.begin(); it!= baselines.end(); it++) {
898 hb_position_t origin = 0;
899 if (hb_ot_layout_get_baseline(font.data(), *it, dir, otScriptTag, otLangTag, &origin)) {
900 std::vector<char> c(4);
901 hb_tag_to_string(*it, c.data());
902 baselineVals.insert(QString::fromLatin1(c.data(), 4), origin);
903 }
904 }
905
906
907
908 hb_position_t ascender = 0;
909 hb_position_t descender = 0;
910 hb_position_t lineGap = 0;
911
926 TT_OS2 *os2Table = nullptr;
927 os2Table = (TT_OS2*)FT_Get_Sfnt_Table(face.
data(), FT_SFNT_OS2);
928 if (os2Table) {
929 int yscale = face.
data()->size->metrics.y_scale;
930
931 ascender = FT_MulFix(os2Table->sTypoAscender, yscale);
932 descender = FT_MulFix(os2Table->sTypoDescender, yscale);
933 lineGap = FT_MulFix(os2Table->sTypoLineGap, yscale);
934 }
935
936 constexpr unsigned USE_TYPO_METRICS = 1u << 7;
937 if (!os2Table || os2Table->version == 0xFFFFU || !(os2Table->fsSelection & USE_TYPO_METRICS)) {
938 hb_position_t altAscender = 0;
939 hb_position_t altDescender = 0;
940 hb_position_t altLineGap = 0;
941 if (!hb_ot_metrics_get_position(font.data(), HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER, &altAscender)) {
942 altAscender = face.
data()->ascender;
943 }
944 if (!hb_ot_metrics_get_position(font.data(), HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER, &altDescender)) {
945 altDescender = face.
data()->descender;
946 }
947 if (!hb_ot_metrics_get_position(font.data(), HB_OT_METRICS_TAG_HORIZONTAL_LINE_GAP, &altLineGap)) {
948 altLineGap = face.
data()->height - (altAscender-altDescender);
949 }
950
951
952
953
954
955 if (!os2Table || (altAscender - altDescender + altLineGap) > (ascender - descender + lineGap)) {
956 ascender = altAscender;
957 descender = altDescender;
958 lineGap = altLineGap;
959 }
960 }
961
962
963
964
965 const QString ideoBottom("ideo");
966 const QString ideoTop("idtp");
967 const QString alphabetic("romn");
968 const QString ideoCenter("Idce");
969 const QString hang("hang");
970 const QString math("math");
971
972 if (baselineVals.keys().contains(ideoBottom) && !baselineVals.keys().contains(ideoTop)) {
973 baselineVals.insert(ideoTop, baselineVals.value(ideoBottom)+metrics.
fontSize);
974 } else if (!baselineVals.keys().contains(ideoBottom) && baselineVals.keys().contains(ideoTop)) {
975 baselineVals.insert(ideoBottom, baselineVals.value(ideoTop)-metrics.
fontSize);
976 } else if (!baselineVals.keys().contains(ideoBottom) && !baselineVals.keys().contains(ideoTop)){
977
978 if (!isIdeographic && isHorizontal) {
979 hb_blob_t_sp dLang(hb_ot_meta_reference_entry( hbFace.data() , HB_OT_META_TAG_DESIGN_LANGUAGES));
980 uint length = hb_blob_get_length(dLang.data());
981 QByteArray ba(hb_blob_get_data(dLang.data(), &
length),
length);
982
983 const QString designLang = QString::fromLatin1(ba).trimmed();
984
985 if (!designLang.isEmpty()) {
986
987
998 };
999 Q_FOREACH (const QString cjk, cjkScripts) {
1000 if (cjk.isEmpty()) continue;
1001 if (designLang.contains(cjk.trimmed())) {
1002 isIdeographic = true;
1003 break;
1004 }
1005 }
1006 }
1007 }
1008
1009 if (isIdeographic && isHorizontal) {
1010 baselineVals.insert(ideoTop, ascender);
1011 baselineVals.insert(ideoBottom, descender);
1012 if (!baselineVals.keys().contains(alphabetic)) {
1013 baselineVals.insert(alphabetic, 0);
1014 }
1015 if (!baselineVals.keys().contains(hang)) {
1016 baselineVals.insert(hang, baselineVals.value(alphabetic)+metrics.
fontSize*0.6);
1017 }
1018 } else if (isHorizontal) {
1019 const qreal alphabeticMultiplier = qreal(ascender+descender)/metrics.
fontSize;
1020 qint32 top = qMax(baselineVals.value(hang, metrics.
capHeight), qint32(qRound(ascender*alphabeticMultiplier)));
1021 baselineVals.insert(ideoTop, top);
1022 baselineVals.insert(ideoBottom, top-metrics.
fontSize);
1023 } else {
1024 baselineVals.insert(ideoTop, metrics.
fontSize);
1025 baselineVals.insert(ideoBottom, 0);
1026 if (!baselineVals.keys().contains(alphabetic)) {
1027 const qreal alphabeticMultiplier = qreal(ascender+descender)/metrics.
fontSize;
1028 baselineVals.insert(alphabetic, (-descender)*alphabeticMultiplier);
1029 }
1030 if (!baselineVals.keys().contains(hang)) {
1031 baselineVals.insert(hang, baselineVals.value(alphabetic)+metrics.
fontSize*0.6);
1032 }
1033 }
1034 }
1035 baselineVals.insert(ideoCenter, (baselineVals.value(ideoTop) + baselineVals.value(ideoBottom))/2);
1036 if (!isHorizontal && !baselineVals.keys().contains(math)) {
1037 baselineVals.insert(math, baselineVals.value(ideoCenter));
1038 }
1039
1040 if (useFallback) {
1041 for (auto it = baselines.begin(); it!= baselines.end(); it++) {
1042 char c[4];
1043 hb_tag_to_string(*it, c);
1044 const QString tagName = QString::fromLatin1(c, 4);
1045 if (!baselineVals.keys().contains(tagName)) {
1046 hb_position_t origin = 0;
1047 hb_ot_layout_get_baseline_with_fallback(font.data(), *it, dir, otScriptTag, otLangTag, &origin);
1048 baselineVals.insert(tagName, origin);
1049 }
1050 }
1051 }
1052
1053 Q_FOREACH(const QString key, baselineVals.keys()) {
1055 }
1056
1057 if (!isHorizontal) {
1058
1061
1062
1063
1064 qreal height = ascender - descender;
1065 ascender = height*0.5;
1066 descender = -ascender;
1067 }
1068 if (ascender == 0 && descender == 0) {
1069 ascender = face->size->metrics.ascender;
1070 descender = face->size->metrics.descender;
1071 qreal height = ascender - descender;
1072 lineGap = face->size->metrics.height - height;
1073 if (!isHorizontal) {
1074 ascender = height * 0.5;
1075 descender = -ascender;
1076 }
1077 }
1081
1082 if (isHorizontal) {
1084 } else {
1085
1089 }
1090
1091 return metrics;
1092}
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.