oasis-editor 0.0.97 → 0.0.99

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.
@@ -51,7 +51,12 @@ export interface ParsedFontProgram {
51
51
  getRawTableData(tag: string): Uint8Array | null;
52
52
  }
53
53
  export interface TextLayouter {
54
- layout(text: string): GlyphRun;
54
+ /**
55
+ * Lays out `text` into positioned glyphs. `features` lists OpenType GSUB
56
+ * feature tags to apply (e.g. `["liga", "onum"]`); layouters that don't shape
57
+ * ignore it and produce a 1:1 codepoint→glyph run.
58
+ */
59
+ layout(text: string, features?: readonly string[]): GlyphRun;
55
60
  }
56
61
  export interface FontSubset {
57
62
  readonly fontFile: Uint8Array;
@@ -0,0 +1,21 @@
1
+ import { GlyphRun, ParsedFontProgram, TextLayouter } from '../core/types.js';
2
+
3
+ /**
4
+ * A {@link TextLayouter} that applies OpenType GSUB substitution for the editor's
5
+ * Latin font features (ligatures, figure style, stylistic sets, contextual
6
+ * alternates) when the font carries a GSUB table and the caller requests them.
7
+ *
8
+ * With no requested features — or a font without GSUB — it falls straight back to
9
+ * the same 1:1 codepoint→glyph mapping as `SimpleTextLayouter`, so ordinary text
10
+ * is byte-for-byte unchanged. GSUB only substitutes glyphs; advances come from
11
+ * `hmtx` (GPOS positioning is out of scope), so `positions[].xAdvance` equals each
12
+ * resulting glyph's advance width.
13
+ */
14
+ export declare class OpenTypeLayouter implements TextLayouter {
15
+ private readonly font;
16
+ private readonly gsub;
17
+ constructor(font: ParsedFontProgram);
18
+ /** True when the font exposes a GSUB table the shaper could use. */
19
+ get hasGsub(): boolean;
20
+ layout(text: string, features?: readonly string[]): GlyphRun;
21
+ }
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Minimal OpenType GSUB (glyph substitution) parser + apply engine, scoped to the
3
+ * Latin font features the editor exposes: ligatures (liga/dlig/hlig), figure
4
+ * style (lnum/onum/pnum/tnum), stylistic sets (ss01–ss20), and contextual
5
+ * alternates (calt).
6
+ *
7
+ * It parses ScriptList/FeatureList/LookupList and the substitution lookup types
8
+ * those features use — single (1), multiple (2), alternate (3), ligature (4),
9
+ * chaining contextual (6), and extension (7). Positioning (GPOS), complex-script
10
+ * shaping, and lookup-flag glyph skipping are intentionally out of scope.
11
+ *
12
+ * Parsing is defensive: any malformed offset or unsupported construct is skipped
13
+ * (the feature simply doesn't apply) rather than throwing, so a broken GSUB can
14
+ * never break PDF export. All offsets are relative to the start of the GSUB table
15
+ * bytes passed to {@link GsubTable.parse}.
16
+ */
17
+ /** A glyph in the shaping buffer, carrying the source code points for ToUnicode. */
18
+ export interface ShapingGlyph {
19
+ id: number;
20
+ codePoints: number[];
21
+ }
22
+ export declare class GsubTable {
23
+ private readonly lookups;
24
+ private readonly featureToLookups;
25
+ private constructor();
26
+ /** Parses GSUB bytes (table-relative). Returns null on any structural failure. */
27
+ static parse(bytes: Uint8Array): GsubTable | null;
28
+ /** True when at least one of the requested feature tags exists in this font. */
29
+ hasAnyFeature(tags: readonly string[]): boolean;
30
+ /** Ordered (ascending) unique lookup indices referenced by the given tags. */
31
+ collectLookupIndices(tags: readonly string[]): number[];
32
+ /**
33
+ * Applies one lookup (by index) at a single position. Tries each subtable in
34
+ * order; the first that matches wins. Returns the advance (output glyph count)
35
+ * or null when nothing matched. Used both by the shaping walk and by nested
36
+ * chaining-contextual substitution records.
37
+ */
38
+ applyLookupIndexAt(lookupIndex: number, glyphs: ShapingGlyph[], pos: number): number | null;
39
+ /** Applies the lookups for the given features across the whole buffer. */
40
+ shape(glyphs: ShapingGlyph[], tags: readonly string[]): void;
41
+ }
@@ -8,3 +8,31 @@ export declare function resolveCanvasTextRenderMetrics(styles: {
8
8
  fontSize: number;
9
9
  baselineOffset: number;
10
10
  };
11
+ /**
12
+ * Applies the subset of OpenType-feature/typography intent that Canvas 2D can
13
+ * actually express, keeping painting consistent with the metric-only slot
14
+ * measurement (see `measureCharacterWidth`).
15
+ *
16
+ * - **Kerning (`w:kern`)** → `ctx.fontKerning`. `w:kern/@w:val` is a *minimum
17
+ * font size threshold* (stored in pt as `kerningThreshold`); kerning is active
18
+ * only when the run's size meets it. Defaulting to `"none"` keeps the painter
19
+ * aligned with the no-kerning measurement, avoiding caret/slot drift.
20
+ * - **Ligatures (`w14:ligatures`)** → `ctx.textRendering` as a *coarse, lossy
21
+ * hint* (`optimizeSpeed` suppresses, `optimizeLegibility` enables). It cannot
22
+ * distinguish standard/contextual/historical and is engine-dependent.
23
+ *
24
+ * Canvas 2D has **no** API for `w14:numForm`, `w14:numSpacing`,
25
+ * `w14:stylisticSets`, or `w14:cntxtAlts` (no `fontVariantNumeric` /
26
+ * `fontVariantLigatures` / `fontFeatureSettings`), so those remain a documented
27
+ * canvas limitation — HTML/CSS honors them via `styleCss.ts`; PDF is separate.
28
+ *
29
+ * `fontVariantCaps`, `letterSpacing`, and `fontStretch` are intentionally left
30
+ * untouched: small caps, character spacing, and character scale are already
31
+ * realized by the render-metric/slot/scale path and would double-apply here.
32
+ *
33
+ * @param fontSizePx the run's effective (unscaled) font size in px.
34
+ */
35
+ export declare function applyCanvasTextFeatureHints(ctx: CanvasRenderingContext2D, styles: {
36
+ kerningThreshold?: number | null;
37
+ ligatures?: "none" | "standard" | "contextual" | "historical" | "standardContextual" | null;
38
+ } | undefined, fontSizePx: number): void;
@@ -1,7 +1,7 @@
1
1
  import { EditorLayoutLine, EditorPageSettings, EditorParagraphNode, EditorState } from '../../core/model.js';
2
2
  import { CanvasBlockPainters } from './canvasBlockPainters.js';
3
3
 
4
- export { resolveCanvasFontFamily, resolveCanvasTextRenderMetrics, } from './canvasFontResolution.js';
4
+ export { applyCanvasTextFeatureHints, resolveCanvasFontFamily, resolveCanvasTextRenderMetrics, } from './canvasFontResolution.js';
5
5
  /**
6
6
  * Paints the floating images anchored within a paragraph, mirroring
7
7
  * `drawFloatingTextBoxesForParagraph`. Split into `behind`/`front` layers so a
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oasis-editor",
3
- "version": "0.0.97",
3
+ "version": "0.0.99",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",