@flighthq/textshaper 0.5.0-next.1944.cd6b6e7 → 0.5.1-edge.638.d4dbd4e
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/textShaper.d.ts +3 -1
- package/dist/textShaper.d.ts.map +1 -1
- package/dist/textShaper.js +15 -0
- package/dist/textShaper.js.map +1 -1
- package/package.json +4 -4
- package/src/textShaper.test.ts +69 -2
package/README.md
CHANGED
|
@@ -13,5 +13,5 @@ Import the supported application-facing API from `@flighthq/textshaper`. The `@f
|
|
|
13
13
|
This package is part of the locked-version Flight SDK graph. Applications may instead install and import `@flighthq/sdk` when package-level tree shaking is sufficient.
|
|
14
14
|
|
|
15
15
|
- [Flight project](https://github.com/flighthq/flight)
|
|
16
|
-
- [Source for @flighthq/textshaper@0.5.
|
|
17
|
-
- [License](https://github.com/flighthq/flight/blob/
|
|
16
|
+
- [Source for @flighthq/textshaper@0.5.1-edge.638.d4dbd4e](https://github.com/flighthq/flight/tree/d4dbd4e347dc8be10b180c85a766ab50333dcb51/packages/textshaper)
|
|
17
|
+
- [License](https://github.com/flighthq/flight/blob/d4dbd4e347dc8be10b180c85a766ab50333dcb51/LICENSE.md)
|
package/dist/textShaper.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import type { TextFormat, TextShaperBackend } from '@flighthq/types/contract';
|
|
1
|
+
import type { BackendOperationExplanation, TextFormat, TextShaperBackend, TextShaperOperation } from '@flighthq/types/contract';
|
|
2
|
+
export declare function explainTextShaperOperation(operation: TextShaperOperation): BackendOperationExplanation;
|
|
2
3
|
export declare function getTextShaperBackend(): TextShaperBackend | null;
|
|
4
|
+
export declare function hasTextShaperOperation(operation: TextShaperOperation): boolean;
|
|
3
5
|
export declare function measureText(text: string, format: Readonly<TextFormat>): number;
|
|
4
6
|
export declare function setTextShaperBackend(backend: TextShaperBackend | null): void;
|
|
5
7
|
//# sourceMappingURL=textShaper.d.ts.map
|
package/dist/textShaper.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"textShaper.d.ts","sourceRoot":"","sources":["../src/textShaper.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"textShaper.d.ts","sourceRoot":"","sources":["../src/textShaper.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,2BAA2B,EAC3B,UAAU,EACV,iBAAiB,EACjB,mBAAmB,EACpB,MAAM,0BAA0B,CAAC;AAelC,wBAAgB,0BAA0B,CAAC,SAAS,EAAE,mBAAmB,GAAG,2BAA2B,CAKtG;AAED,wBAAgB,oBAAoB,IAAI,iBAAiB,GAAG,IAAI,CAE/D;AAID,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,mBAAmB,GAAG,OAAO,CAE9E;AAKD,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,UAAU,CAAC,GAAG,MAAM,CAG9E;AAKD,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,iBAAiB,GAAG,IAAI,GAAG,IAAI,CAG5E"}
|
package/dist/textShaper.js
CHANGED
|
@@ -6,9 +6,24 @@ import { _textShaperBackendHook } from './_textShaperHooks';
|
|
|
6
6
|
// historical measure-provider, which was null until a renderer registered one — callers fall back to
|
|
7
7
|
// leaving text unmeasured until a backend exists. A future @flighthq/textshaper-harfbuzz registers
|
|
8
8
|
// the same way.
|
|
9
|
+
// Which layer implements `operation`. This capability is a SINGLE NULLABLE SLOT — there is no host layer
|
|
10
|
+
// and no sentinel — so the two honest answers are `'custom'` (a backend is installed and provides it) and
|
|
11
|
+
// `'none'` (no backend is installed at all). Reporting `'sentinel'` here would name a fall-through object
|
|
12
|
+
// that does not exist, and inventing a host layer would describe a precedence this package does not have.
|
|
13
|
+
export function explainTextShaperOperation(operation) {
|
|
14
|
+
if (_backend !== null && typeof _backend[operation] === 'function') {
|
|
15
|
+
return { implemented: true, layer: 'custom', operation };
|
|
16
|
+
}
|
|
17
|
+
return { implemented: false, layer: 'none', operation };
|
|
18
|
+
}
|
|
9
19
|
export function getTextShaperBackend() {
|
|
10
20
|
return _backend;
|
|
11
21
|
}
|
|
22
|
+
// Whether an installed backend implements `operation`. False when nothing is installed, which for this
|
|
23
|
+
// capability is also what the getter reports by returning null.
|
|
24
|
+
export function hasTextShaperOperation(operation) {
|
|
25
|
+
return explainTextShaperOperation(operation).implemented;
|
|
26
|
+
}
|
|
12
27
|
// Measures `text` in `format` to its horizontal advance, in pixels, via the active backend. Returns
|
|
13
28
|
// the sentinel -1 when no backend is registered (expected before setup), so callers can distinguish
|
|
14
29
|
// "unmeasurable" from a real zero-width advance.
|
package/dist/textShaper.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"textShaper.js","sourceRoot":"","sources":["../src/textShaper.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"textShaper.js","sourceRoot":"","sources":["../src/textShaper.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAE5D,4FAA4F;AAC5F,oGAAoG;AACpG,+EAA+E;AAC/E,oGAAoG;AACpG,qGAAqG;AACrG,mGAAmG;AACnG,gBAAgB;AAChB,yGAAyG;AACzG,0GAA0G;AAC1G,0GAA0G;AAC1G,0GAA0G;AAC1G,MAAM,UAAU,0BAA0B,CAAC,SAA8B;IACvE,IAAI,QAAQ,KAAK,IAAI,IAAI,OAAO,QAAQ,CAAC,SAAS,CAAC,KAAK,UAAU,EAAE,CAAC;QACnE,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;IAC3D,CAAC;IACD,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;AAC1D,CAAC;AAED,MAAM,UAAU,oBAAoB;IAClC,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,uGAAuG;AACvG,gEAAgE;AAChE,MAAM,UAAU,sBAAsB,CAAC,SAA8B;IACnE,OAAO,0BAA0B,CAAC,SAAS,CAAC,CAAC,WAAW,CAAC;AAC3D,CAAC;AAED,oGAAoG;AACpG,oGAAoG;AACpG,iDAAiD;AACjD,MAAM,UAAU,WAAW,CAAC,IAAY,EAAE,MAA4B;IACpE,IAAI,QAAQ,KAAK,IAAI;QAAE,OAAO,CAAC,CAAC,CAAC;IACjC,OAAO,QAAQ,CAAC,WAAW,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;AAC5C,CAAC;AAED,+FAA+F;AAC/F,iGAAiG;AACjG,6BAA6B;AAC7B,MAAM,UAAU,oBAAoB,CAAC,OAAiC;IACpE,QAAQ,GAAG,OAAO,CAAC;IACnB,sBAAsB,EAAE,CAAC,OAAO,CAAC,CAAC;AACpC,CAAC;AAED,IAAI,QAAQ,GAA6B,IAAI,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flighthq/textshaper",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.1-edge.638.d4dbd4e",
|
|
4
4
|
"author": "Joshua Granick and other contributors",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -38,9 +38,9 @@
|
|
|
38
38
|
"clean:dist": "tsx ../../scripts/clean-package-dist.ts"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@flighthq/log": "0.5.
|
|
42
|
-
"@flighthq/signals": "0.5.
|
|
43
|
-
"@flighthq/types": "0.5.
|
|
41
|
+
"@flighthq/log": "0.5.1-edge.638.d4dbd4e",
|
|
42
|
+
"@flighthq/signals": "0.5.1-edge.638.d4dbd4e",
|
|
43
|
+
"@flighthq/types": "0.5.1-edge.638.d4dbd4e"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"typescript": "^5.3.0"
|
package/src/textShaper.test.ts
CHANGED
|
@@ -1,17 +1,77 @@
|
|
|
1
|
-
import type { TextShaperBackend } from '@flighthq/types/contract';
|
|
1
|
+
import type { TextShaperBackend, TextShaperOperation } from '@flighthq/types/contract';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
explainTextShaperOperation,
|
|
5
|
+
getTextShaperBackend,
|
|
6
|
+
hasTextShaperOperation,
|
|
7
|
+
measureText,
|
|
8
|
+
setTextShaperBackend,
|
|
9
|
+
} from './textShaper';
|
|
4
10
|
|
|
5
11
|
afterEach(() => {
|
|
6
12
|
setTextShaperBackend(null);
|
|
7
13
|
});
|
|
8
14
|
|
|
15
|
+
describe('explainTextShaperOperation', () => {
|
|
16
|
+
afterEach(() => {
|
|
17
|
+
setTextShaperBackend(null);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
// ★ With nothing installed, the getter returns null,
|
|
21
|
+
// so a query resolving through the getter would report every operation implemented. It must not.
|
|
22
|
+
it('reports none and no implementation when nothing is installed', () => {
|
|
23
|
+
setTextShaperBackend(null);
|
|
24
|
+
for (const operation of OPTIONAL_OPERATIONS) {
|
|
25
|
+
expect(explainTextShaperOperation(operation)).toEqual({ implemented: false, layer: 'none', operation });
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('reports a custom backend as implementing only what it provides', () => {
|
|
30
|
+
setTextShaperBackend(partialBackend());
|
|
31
|
+
for (const operation of OPTIONAL_OPERATIONS) {
|
|
32
|
+
expect(hasTextShaperOperation(operation)).toBe(false);
|
|
33
|
+
}
|
|
34
|
+
expect(explainTextShaperOperation(OPTIONAL_OPERATIONS[0]).layer).toBe('none');
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('reports an operation the backend does provide', () => {
|
|
38
|
+
const operation = OPTIONAL_OPERATIONS[0];
|
|
39
|
+
setTextShaperBackend({ ...partialBackend(), [operation]: () => undefined } as TextShaperBackend);
|
|
40
|
+
expect(explainTextShaperOperation(operation)).toEqual({ implemented: true, layer: 'custom', operation });
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
|
|
9
44
|
describe('getTextShaperBackend', () => {
|
|
10
45
|
it('returns null before a backend is set', () => {
|
|
11
46
|
expect(getTextShaperBackend()).toBeNull();
|
|
12
47
|
});
|
|
13
48
|
});
|
|
14
49
|
|
|
50
|
+
describe('hasTextShaperOperation', () => {
|
|
51
|
+
afterEach(() => {
|
|
52
|
+
setTextShaperBackend(null);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('agrees with explainTextShaperOperation for every optional operation', () => {
|
|
56
|
+
setTextShaperBackend(partialBackend());
|
|
57
|
+
for (const operation of OPTIONAL_OPERATIONS) {
|
|
58
|
+
expect(hasTextShaperOperation(operation)).toBe(explainTextShaperOperation(operation).implemented);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// Per-operation availability for TextShaperBackend. The operations below are the ones the interface declares
|
|
64
|
+
// OPTIONAL, so a host that omits them is compliant rather than broken — that is the absence-of-an-export
|
|
65
|
+
// ruling, and this is the query that makes it observable.
|
|
66
|
+
const OPTIONAL_OPERATIONS: readonly TextShaperOperation[] = [
|
|
67
|
+
'getCodePointForGlyph',
|
|
68
|
+
'getFontMetrics',
|
|
69
|
+
'getGlyphExtents',
|
|
70
|
+
'getGlyphIndexForCodePoint',
|
|
71
|
+
'getGlyphName',
|
|
72
|
+
'shapeRun',
|
|
73
|
+
];
|
|
74
|
+
|
|
15
75
|
describe('measureText', () => {
|
|
16
76
|
it('returns -1 when no backend is registered', () => {
|
|
17
77
|
expect(measureText('hello', {})).toBe(-1);
|
|
@@ -40,3 +100,10 @@ describe('setTextShaperBackend', () => {
|
|
|
40
100
|
expect(getTextShaperBackend()).toBe(second);
|
|
41
101
|
});
|
|
42
102
|
});
|
|
103
|
+
|
|
104
|
+
// A host implementing only the REQUIRED members — partial support declared by absence.
|
|
105
|
+
function partialBackend(): TextShaperBackend {
|
|
106
|
+
return {
|
|
107
|
+
measureText: (() => undefined) as never,
|
|
108
|
+
} as TextShaperBackend;
|
|
109
|
+
}
|