Skip to content

Commit faffe13

Browse files
committed
Bind ot-metrics APIs
Fixes #132
1 parent 4fb0b0d commit faffe13

4 files changed

Lines changed: 205 additions & 0 deletions

File tree

‎config-override.h‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#undef HB_NO_CUBIC_GLYF
1111
#undef HB_NO_VAR_COMPOSITES
1212
#undef HB_NO_NAME
13+
#undef HB_NO_METRICS
1314
#undef HB_NO_LAYOUT_FEATURE_PARAMS
1415
#undef HB_NO_LAYOUT_RARELY_USED
1516
#undef HB_NO_LAYOUT_UNUSED

‎harfbuzz.symbols‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,11 @@ _hb_ot_layout_lookup_get_optical_bound
8585
_hb_ot_layout_get_glyph_class
8686
_hb_ot_layout_get_ligature_carets
8787
_hb_ot_layout_feature_get_name_ids
88+
_hb_ot_metrics_get_position
89+
_hb_ot_metrics_get_position_with_fallback
90+
_hb_ot_metrics_get_variation
91+
_hb_ot_metrics_get_x_variation
92+
_hb_ot_metrics_get_y_variation
8893
_hb_ot_name_list_names
8994
_hb_ot_name_get_utf16
9095
_hb_ot_tag_to_language

‎src/font.ts‎

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,81 @@ import {
33
exports,
44
registry,
55
STATIC_ARRAY_SIZE,
6+
hb_tag,
67
utf8_ptr_to_string,
78
string_to_utf8_ptr,
9+
type ValueOf,
810
} from "./helpers";
911
import type { FontExtents, GlyphExtents, SvgPathCommand } from "./types";
1012
import type { Direction } from "./buffer";
1113
import { Face } from "./face";
1214
import type { FontFuncs } from "./font-funcs";
1315
import type { Variation } from "./variation";
1416

17+
/**
18+
* Metric tags corresponding to [MVAR Value
19+
* Tags](https://docs.microsoft.com/en-us/typography/opentype/spec/mvar#value-tags).
20+
*/
21+
export const MetricsTag = {
22+
/** horizontal ascender. */
23+
HORIZONTAL_ASCENDER: hb_tag("hasc"),
24+
/** horizontal descender. */
25+
HORIZONTAL_DESCENDER: hb_tag("hdsc"),
26+
/** horizontal line gap. */
27+
HORIZONTAL_LINE_GAP: hb_tag("hlgp"),
28+
/** horizontal clipping ascent. */
29+
HORIZONTAL_CLIPPING_ASCENT: hb_tag("hcla"),
30+
/** horizontal clipping descent. */
31+
HORIZONTAL_CLIPPING_DESCENT: hb_tag("hcld"),
32+
/** vertical ascender. */
33+
VERTICAL_ASCENDER: hb_tag("vasc"),
34+
/** vertical descender. */
35+
VERTICAL_DESCENDER: hb_tag("vdsc"),
36+
/** vertical line gap. */
37+
VERTICAL_LINE_GAP: hb_tag("vlgp"),
38+
/** horizontal caret rise. */
39+
HORIZONTAL_CARET_RISE: hb_tag("hcrs"),
40+
/** horizontal caret run. */
41+
HORIZONTAL_CARET_RUN: hb_tag("hcrn"),
42+
/** horizontal caret offset. */
43+
HORIZONTAL_CARET_OFFSET: hb_tag("hcof"),
44+
/** vertical caret rise. */
45+
VERTICAL_CARET_RISE: hb_tag("vcrs"),
46+
/** vertical caret run. */
47+
VERTICAL_CARET_RUN: hb_tag("vcrn"),
48+
/** vertical caret offset. */
49+
VERTICAL_CARET_OFFSET: hb_tag("vcof"),
50+
/** x height. */
51+
X_HEIGHT: hb_tag("xhgt"),
52+
/** cap height. */
53+
CAP_HEIGHT: hb_tag("cpht"),
54+
/** subscript em x size. */
55+
SUBSCRIPT_EM_X_SIZE: hb_tag("sbxs"),
56+
/** subscript em y size. */
57+
SUBSCRIPT_EM_Y_SIZE: hb_tag("sbys"),
58+
/** subscript em x offset. */
59+
SUBSCRIPT_EM_X_OFFSET: hb_tag("sbxo"),
60+
/** subscript em y offset. */
61+
SUBSCRIPT_EM_Y_OFFSET: hb_tag("sbyo"),
62+
/** superscript em x size. */
63+
SUPERSCRIPT_EM_X_SIZE: hb_tag("spxs"),
64+
/** superscript em y size. */
65+
SUPERSCRIPT_EM_Y_SIZE: hb_tag("spys"),
66+
/** superscript em x offset. */
67+
SUPERSCRIPT_EM_X_OFFSET: hb_tag("spxo"),
68+
/** superscript em y offset. */
69+
SUPERSCRIPT_EM_Y_OFFSET: hb_tag("spyo"),
70+
/** strikeout size. */
71+
STRIKEOUT_SIZE: hb_tag("strs"),
72+
/** strikeout offset. */
73+
STRIKEOUT_OFFSET: hb_tag("stro"),
74+
/** underline size. */
75+
UNDERLINE_SIZE: hb_tag("unds"),
76+
/** underline offset. */
77+
UNDERLINE_OFFSET: hb_tag("undo"),
78+
} as const;
79+
export type MetricsTag = ValueOf<typeof MetricsTag>;
80+
1581
interface DrawPtrs {
1682
drawFuncsPtr?: number;
1783
moveToPtr?: number;
@@ -530,4 +596,74 @@ export class Font {
530596
Module.stackRestore(sp);
531597
return carets;
532598
}
599+
600+
/**
601+
* Fetches metrics value corresponding to `metricsTag` from the font.
602+
*
603+
* @param metricsTag {@link MetricsTag} of metrics value you like to fetch.
604+
* @returns The metrics value, or undefined if not found in the font.
605+
*/
606+
getMetricPosition(metricsTag: MetricsTag): number | undefined {
607+
const sp = Module.stackSave();
608+
const positionPtr = Module.stackAlloc(4);
609+
let position: number | undefined;
610+
if (exports.hb_ot_metrics_get_position(this.ptr, metricsTag, positionPtr)) {
611+
position = Module.HEAP32[positionPtr / 4];
612+
}
613+
Module.stackRestore(sp);
614+
return position;
615+
}
616+
617+
/**
618+
* Fetches metrics value corresponding to `metricsTag` from the font, and
619+
* synthesizes a value if the value is missing in the font.
620+
*
621+
* @param metricsTag {@link MetricsTag} of metrics value you like to fetch.
622+
* @returns The metrics value.
623+
*/
624+
getMetricPositionWithFallback(metricsTag: MetricsTag): number {
625+
const sp = Module.stackSave();
626+
const positionPtr = Module.stackAlloc(4);
627+
exports.hb_ot_metrics_get_position_with_fallback(
628+
this.ptr,
629+
metricsTag,
630+
positionPtr,
631+
);
632+
const position = Module.HEAP32[positionPtr / 4];
633+
Module.stackRestore(sp);
634+
return position;
635+
}
636+
637+
/**
638+
* Fetches metrics value corresponding to `metricsTag` from the font with the
639+
* current font variation settings applied.
640+
*
641+
* @param metricsTag {@link MetricsTag} of metrics value you like to fetch.
642+
* @returns The requested metric value.
643+
*/
644+
getMetricVariation(metricsTag: MetricsTag): number {
645+
return exports.hb_ot_metrics_get_variation(this.ptr, metricsTag);
646+
}
647+
648+
/**
649+
* Fetches horizontal metrics value corresponding to `metricsTag` from the
650+
* font with the current font variation settings applied.
651+
*
652+
* @param metricsTag {@link MetricsTag} of metrics value you like to fetch.
653+
* @returns The requested metric value.
654+
*/
655+
getMetricXVariation(metricsTag: MetricsTag): number {
656+
return exports.hb_ot_metrics_get_x_variation(this.ptr, metricsTag);
657+
}
658+
659+
/**
660+
* Fetches vertical metrics value corresponding to `metricsTag` from the font
661+
* with the current font variation settings applied.
662+
*
663+
* @param metricsTag {@link MetricsTag} of metrics value you like to fetch.
664+
* @returns The requested metric value.
665+
*/
666+
getMetricYVariation(metricsTag: MetricsTag): number {
667+
return exports.hb_ot_metrics_get_y_variation(this.ptr, metricsTag);
668+
}
533669
}

‎test/index.test.js‎

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -582,6 +582,69 @@ describe("Font", function () {
582582
expect(carets).to.deep.equal([562, 1199]);
583583
});
584584

585+
it("getMetricPosition returns metrics values from the font", function () {
586+
let blob = new hb.Blob(
587+
fs.readFileSync(path.join(__dirname, "fonts/noto/NotoSans-Regular.ttf")),
588+
);
589+
let face = new hb.Face(blob);
590+
let font = new hb.Font(face);
591+
let metric;
592+
metric = font.getMetricPosition(hb.MetricsTag.X_HEIGHT);
593+
expect(metric).to.equal(536);
594+
metric = font.getMetricPosition(hb.MetricsTag.CAP_HEIGHT);
595+
expect(metric).to.equal(714);
596+
metric = font.getMetricPosition(hb.MetricsTag.UNDERLINE_OFFSET);
597+
expect(metric).to.equal(-100);
598+
metric = font.getMetricPosition(hb.MetricsTag.STRIKEOUT_SIZE);
599+
expect(metric).to.equal(50);
600+
// Vertical metrics are not available in this build.
601+
metric = font.getMetricPosition(hb.MetricsTag.VERTICAL_ASCENDER);
602+
expect(metric).to.equal(undefined);
603+
});
604+
605+
it("getMetricPositionWithFallback synthesizes missing metrics", function () {
606+
let blob = new hb.Blob(
607+
fs.readFileSync(path.join(__dirname, "fonts/noto/NotoSans-Regular.ttf")),
608+
);
609+
let face = new hb.Face(blob);
610+
let font = new hb.Font(face);
611+
let metric;
612+
// Present in the font: same value as getMetricPosition.
613+
metric = font.getMetricPositionWithFallback(hb.MetricsTag.CAP_HEIGHT);
614+
expect(metric).to.equal(714);
615+
// Missing from the font: a value is synthesized.
616+
metric = font.getMetricPosition(hb.MetricsTag.VERTICAL_ASCENDER);
617+
expect(metric).to.equal(undefined);
618+
metric = font.getMetricPositionWithFallback(
619+
hb.MetricsTag.VERTICAL_ASCENDER,
620+
);
621+
expect(metric).to.equal(500);
622+
});
623+
624+
it("getMetricVariation reflects variation settings", function () {
625+
let blob = new hb.Blob(
626+
fs.readFileSync(
627+
path.join(__dirname, "fonts/noto/NotoSansArabic-Variable.ttf"),
628+
),
629+
);
630+
let face = new hb.Face(blob);
631+
let font = new hb.Font(face);
632+
let metric;
633+
metric = font.getMetricPosition(hb.MetricsTag.CAP_HEIGHT);
634+
expect(metric).to.equal(416);
635+
metric = font.getMetricVariation(hb.MetricsTag.CAP_HEIGHT);
636+
expect(metric).to.equal(0);
637+
font.setVariations([new hb.Variation("wght", 900)]);
638+
metric = font.getMetricPosition(hb.MetricsTag.CAP_HEIGHT);
639+
expect(metric).to.equal(550);
640+
metric = font.getMetricVariation(hb.MetricsTag.CAP_HEIGHT);
641+
expect(metric).to.equal(134);
642+
metric = font.getMetricXVariation(hb.MetricsTag.CAP_HEIGHT);
643+
expect(metric).to.equal(134);
644+
metric = font.getMetricYVariation(hb.MetricsTag.CAP_HEIGHT);
645+
expect(metric).to.equal(134);
646+
});
647+
585648
it("setVariations affects advances", function () {
586649
let blob = new hb.Blob(
587650
fs.readFileSync(

0 commit comments

Comments
 (0)