agentic-mermaid 0.1.1 → 0.1.2
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/CHANGELOG.md +144 -4
- package/LICENSES/Apache-2.0.txt +202 -0
- package/README.md +25 -32
- package/THIRD_PARTY_NOTICES.md +29 -0
- package/dist/agent-core.d.ts +463 -0
- package/dist/agent-core.js +397 -0
- package/dist/agent.d.ts +73 -1868
- package/dist/agent.js +336 -37325
- package/dist/agentic-mermaid-mcp.js +7 -38399
- package/dist/am.js +33632 -40445
- package/dist/chunk-4QECACIA.js +4702 -0
- package/dist/chunk-CDPKJ2BQ.js +37 -0
- package/dist/chunk-QKDLMOJJ.js +3487 -0
- package/dist/chunk-WI35L7IR.js +615 -0
- package/dist/chunk-WN3YDOBG.js +149 -0
- package/dist/chunk-ZYMMKQ3I.js +62637 -0
- package/dist/index-D54dqSdC.d.ts +6777 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +310 -33014
- package/dist/mcp-cli-SBQSTJFC.js +17 -0
- package/docs/README.md +14 -6
- package/docs/schemas/style-spec.schema.json +1221 -30
- package/examples/styles/catalog.json +54 -0
- package/examples/styles/cloudflare-workers-inspired-prototype.mmd +9 -0
- package/examples/styles/cloudflare-workers-inspired-prototype.style.json +110 -0
- package/examples/styles/cupertino-prototype.mmd +10 -0
- package/examples/styles/cupertino-prototype.style.json +63 -0
- package/examples/styles/mid-century-report.style.json +2 -2
- package/examples/styles/star-chart-atlas.style.json +2 -1
- package/examples/styles/transit-route-map.style.json +2 -2
- package/examples/styles/vercel-inspired-prototype.mmd +6 -0
- package/examples/styles/vercel-inspired-prototype.style.json +87 -0
- package/package.json +68 -43
- package/server.json +2 -2
- package/skills/agentic-mermaid-diagram-workflow/SKILL.md +17 -36
- package/skills/agentic-mermaid-diagram-workflow/references/cli.md +4 -5
- package/skills/agentic-mermaid-diagram-workflow/references/code-mode.md +15 -31
- package/skills/agentic-mermaid-diagram-workflow/references/sequence.md +10 -14
- package/skills/agentic-mermaid-live-editor/SKILL.md +100 -206
- package/AGENT_NATIVE.md +0 -607
- package/Instructions_for_agents.md +0 -89
- package/TODO.md +0 -684
- package/assets/hero.png +0 -0
- package/dist/index-wjRjbsP-.d.ts +0 -1102
- package/docs/agent-api-cookbook.md +0 -308
- package/docs/agent-mutation-policy.md +0 -104
- package/docs/agent-workflow-examples.md +0 -46
- package/docs/api.md +0 -211
- package/docs/ascii.md +0 -103
- package/docs/assets/improvements/box-start-connector.png +0 -0
- package/docs/assets/improvements/comment-dropped-lint.png +0 -0
- package/docs/assets/improvements/er-content-loss.png +0 -0
- package/docs/assets/improvements/fan-in-trunks.png +0 -0
- package/docs/assets/improvements/gantt-family.png +0 -0
- package/docs/assets/improvements/gantt-milestone-clamp.png +0 -0
- package/docs/assets/improvements/gantt-verify-seam.png +0 -0
- package/docs/assets/improvements/subgraph-container-edges.png +0 -0
- package/docs/assets/improvements/wrapper-frontmatter.png +0 -0
- package/docs/assets/improvements/wrapper-init-directive.png +0 -0
- package/docs/assets/improvements/wrapper-leading-comment.png +0 -0
- package/docs/assets/style-cookbook/mid-century-report.png +0 -0
- package/docs/assets/style-cookbook/star-chart-atlas.png +0 -0
- package/docs/assets/style-cookbook/transit-route-map.png +0 -0
- package/docs/comparison.md +0 -94
- package/docs/config.md +0 -105
- package/docs/contributing/adding-diagram-types.md +0 -144
- package/docs/contributing/diagram-family-citizenship.matrix.json +0 -2127
- package/docs/contributing/diagram-family-citizenship.md +0 -82
- package/docs/contributing/harvesting-upstream-tests.md +0 -126
- package/docs/contributing/lessons-learned.md +0 -276
- package/docs/contributing/releasing.md +0 -82
- package/docs/contributing/visual-review-evidence.md +0 -53
- package/docs/custom-style-cookbook.md +0 -154
- package/docs/design/families/README.md +0 -26
- package/docs/design/families/architecture-beta.md +0 -48
- package/docs/design/families/flowchart-parser-conformance.md +0 -22
- package/docs/design/families/gantt-research.md +0 -137
- package/docs/design/families/gantt.md +0 -329
- package/docs/design/families/journey-agentic-cards-before.png +0 -0
- package/docs/design/families/journey-experience-curve-implementation.png +0 -0
- package/docs/design/families/journey-mermaid-classic-implementation.png +0 -0
- package/docs/design/families/journey-mermaid-classic-prototype.png +0 -0
- package/docs/design/families/journey-mermaid-classic-prototype.svg +0 -163
- package/docs/design/families/journey-mermaid-classic-prototype.svg.png +0 -0
- package/docs/design/families/journey-migration-parity.md +0 -439
- package/docs/design/families/journey-section-overlap-after.png +0 -0
- package/docs/design/families/journey-section-overlap-before.png +0 -0
- package/docs/design/families/journey-usage-research.md +0 -423
- package/docs/design/families/journey.md +0 -153
- package/docs/design/families/xychart.md +0 -85
- package/docs/design/system/README.md +0 -45
- package/docs/design/system/abstraction-audit.md +0 -224
- package/docs/design/system/abstraction-recommendations.md +0 -655
- package/docs/design/system/architecture.mmd +0 -29
- package/docs/design/system/architecture.svg +0 -137
- package/docs/design/system/layout-guarantees-and-robustness.md +0 -351
- package/docs/design/system/layout-rubric.md +0 -138
- package/docs/design/system/route-contracts.md +0 -848
- package/docs/design/system/source-preservation-ladder.md +0 -52
- package/docs/design/system/styles-rollout.md +0 -216
- package/docs/design/system/ugly-layouts.md +0 -119
- package/docs/diagram-families.md +0 -193
- package/docs/features.md +0 -147
- package/docs/fork-differences.md +0 -130
- package/docs/getting-started.md +0 -175
- package/docs/issue-derived-test-cases.md +0 -156
- package/docs/layout-characterization/README.md +0 -245
- package/docs/layout-characterization/contact-sheet-families.md +0 -498
- package/docs/layout-characterization/contact-sheet.md +0 -638
- package/docs/layout-characterization/properties.md +0 -281
- package/docs/layout-characterization/visual-quality.md +0 -160
- package/docs/layout-characterization/visual-snapshots/architecture.svg +0 -79
- package/docs/layout-characterization/visual-snapshots/class.svg +0 -60
- package/docs/layout-characterization/visual-snapshots/er.svg +0 -61
- package/docs/layout-characterization/visual-snapshots/flowchart.svg +0 -58
- package/docs/layout-characterization/visual-snapshots/gantt.svg +0 -52
- package/docs/layout-characterization/visual-snapshots/journey.svg +0 -98
- package/docs/layout-characterization/visual-snapshots/pie.svg +0 -36
- package/docs/layout-characterization/visual-snapshots/quadrant.svg +0 -47
- package/docs/layout-characterization/visual-snapshots/sequence.svg +0 -59
- package/docs/layout-characterization/visual-snapshots/state.svg +0 -52
- package/docs/layout-characterization/visual-snapshots/timeline.svg +0 -64
- package/docs/layout-characterization/visual-snapshots/xychart.svg +0 -97
- package/docs/mcp-code-mode-rationale.md +0 -101
- package/docs/mcp-http-transport.md +0 -223
- package/docs/mermaid-layout-complaints.md +0 -837
- package/docs/mutation-testing.md +0 -206
- package/docs/project/divergences.md +0 -98
- package/docs/project/dogfooding-docs-strategy.md +0 -66
- package/docs/project/hosted-mcp-cloudflare-plan.md +0 -338
- package/docs/project/lessons-learned.md +0 -1019
- package/docs/project/remove-role-styling-plan.md +0 -176
- package/docs/quality.md +0 -215
- package/docs/react.md +0 -97
- package/docs/style-authoring.md +0 -133
- package/docs/testing-strategy.md +0 -335
- package/docs/theming.md +0 -162
- package/examples/agent-improve-auth-flow.ts +0 -206
- package/examples/agent-loop.ts +0 -53
- package/examples/mcp-vs-cli-complex-diagrams.ts +0 -133
- package/llms.txt +0 -104
- package/skill-evals/README.md +0 -77
- package/skill-evals/fixtures/architecture-cache/input.mmd +0 -5
- package/skill-evals/fixtures/class-payment/input.mmd +0 -4
- package/skill-evals/fixtures/er-orders/input.mmd +0 -10
- package/skill-evals/fixtures/flowchart-cache/input.mmd +0 -3
- package/skill-evals/fixtures/gantt-release/input.mmd +0 -7
- package/skill-evals/fixtures/journey-checkout/input.mmd +0 -6
- package/skill-evals/fixtures/pie-exports/input.mmd +0 -5
- package/skill-evals/fixtures/quadrant-priorities/input.mmd +0 -11
- package/skill-evals/fixtures/sequence-retry/input.mmd +0 -8
- package/skill-evals/fixtures/state-lifecycle/input.mmd +0 -5
- package/skill-evals/fixtures/timeline-release/input.mmd +0 -6
- package/skill-evals/fixtures/xychart-growth/input.mmd +0 -5
- package/skill-evals/shared-benchmark.json +0 -510
- package/src/agent/analyze.ts +0 -157
- package/src/agent/apply.ts +0 -146
- package/src/agent/architecture-body.ts +0 -458
- package/src/agent/certificates.ts +0 -110
- package/src/agent/class-body.ts +0 -319
- package/src/agent/core.ts +0 -207
- package/src/agent/create.ts +0 -106
- package/src/agent/describe.ts +0 -368
- package/src/agent/er-body.ts +0 -224
- package/src/agent/facts.ts +0 -360
- package/src/agent/families-builtin.ts +0 -920
- package/src/agent/families.ts +0 -232
- package/src/agent/family-layouts.ts +0 -832
- package/src/agent/flowchart-body.ts +0 -372
- package/src/agent/flowchart-unsupported.ts +0 -243
- package/src/agent/font-coverage.ts +0 -288
- package/src/agent/gantt-body.ts +0 -495
- package/src/agent/index.ts +0 -10
- package/src/agent/journey-body.ts +0 -374
- package/src/agent/label-metrics.ts +0 -28
- package/src/agent/layout-to-rendered.ts +0 -63
- package/src/agent/mutate.ts +0 -97
- package/src/agent/mutation-ops.ts +0 -51
- package/src/agent/op-schema.ts +0 -443
- package/src/agent/parse.ts +0 -223
- package/src/agent/pie-body.ts +0 -270
- package/src/agent/png.ts +0 -171
- package/src/agent/quadrant-body.ts +0 -336
- package/src/agent/quality.ts +0 -363
- package/src/agent/readability-audit.ts +0 -92
- package/src/agent/rendered-route-audit.ts +0 -61
- package/src/agent/sequence-body.ts +0 -345
- package/src/agent/serialize.ts +0 -214
- package/src/agent/state-body.ts +0 -589
- package/src/agent/structural-count.ts +0 -150
- package/src/agent/timeline-body.ts +0 -280
- package/src/agent/trace-log.ts +0 -43
- package/src/agent/types.ts +0 -995
- package/src/agent/verify.ts +0 -603
- package/src/agent/xychart-body.ts +0 -472
- package/src/architecture/config.ts +0 -282
- package/src/architecture/layout.ts +0 -496
- package/src/architecture/parser.ts +0 -375
- package/src/architecture/renderer.ts +0 -611
- package/src/architecture/types.ts +0 -113
- package/src/ascii/ansi.ts +0 -423
- package/src/ascii/architecture.ts +0 -210
- package/src/ascii/canvas.ts +0 -443
- package/src/ascii/class-diagram.ts +0 -697
- package/src/ascii/converter.ts +0 -475
- package/src/ascii/draw.ts +0 -1693
- package/src/ascii/edge-bundling.ts +0 -328
- package/src/ascii/edge-routing.ts +0 -330
- package/src/ascii/er-diagram.ts +0 -439
- package/src/ascii/gantt.ts +0 -276
- package/src/ascii/grid.ts +0 -747
- package/src/ascii/index.ts +0 -196
- package/src/ascii/journey.ts +0 -169
- package/src/ascii/meta.ts +0 -244
- package/src/ascii/multiline-utils.ts +0 -80
- package/src/ascii/pathfinder.ts +0 -301
- package/src/ascii/pie.ts +0 -67
- package/src/ascii/quadrant.ts +0 -158
- package/src/ascii/reverse.ts +0 -216
- package/src/ascii/sequence.ts +0 -499
- package/src/ascii/shapes/circle.ts +0 -27
- package/src/ascii/shapes/corners.ts +0 -143
- package/src/ascii/shapes/diamond.ts +0 -27
- package/src/ascii/shapes/hexagon.ts +0 -27
- package/src/ascii/shapes/index.ts +0 -106
- package/src/ascii/shapes/rectangle.ts +0 -168
- package/src/ascii/shapes/rounded.ts +0 -27
- package/src/ascii/shapes/special.ts +0 -332
- package/src/ascii/shapes/stadium.ts +0 -110
- package/src/ascii/shapes/state.ts +0 -192
- package/src/ascii/shapes/types.ts +0 -73
- package/src/ascii/timeline.ts +0 -141
- package/src/ascii/types.ts +0 -312
- package/src/ascii/validate.ts +0 -120
- package/src/ascii/width.ts +0 -81
- package/src/ascii/wrap.ts +0 -131
- package/src/ascii/xychart.ts +0 -919
- package/src/browser.ts +0 -29
- package/src/class/layout.ts +0 -240
- package/src/class/parser.ts +0 -332
- package/src/class/renderer.ts +0 -638
- package/src/class/types.ts +0 -129
- package/src/cli/agent-instructions.ts +0 -1
- package/src/cli/am-bin.ts +0 -6
- package/src/cli/exit-codes.ts +0 -18
- package/src/cli/index.ts +0 -1132
- package/src/cli/init-agent.ts +0 -147
- package/src/cli/run-entrypoint.ts +0 -10
- package/src/color-resolver.ts +0 -152
- package/src/elk-instance.ts +0 -114
- package/src/er/layout.ts +0 -187
- package/src/er/parser.ts +0 -226
- package/src/er/renderer.ts +0 -792
- package/src/er/types.ts +0 -99
- package/src/family-rubric.ts +0 -425
- package/src/font-manifest.ts +0 -31
- package/src/gantt/layout.ts +0 -365
- package/src/gantt/parser.ts +0 -282
- package/src/gantt/pipeline.ts +0 -27
- package/src/gantt/renderer.ts +0 -414
- package/src/gantt/schedule.ts +0 -513
- package/src/gantt/types.ts +0 -255
- package/src/index.ts +0 -368
- package/src/journey/layout.ts +0 -554
- package/src/journey/parse-core.ts +0 -251
- package/src/journey/parser.ts +0 -101
- package/src/journey/renderer.ts +0 -917
- package/src/journey/types.ts +0 -144
- package/src/layout/geometry.ts +0 -182
- package/src/layout/pass.ts +0 -142
- package/src/layout/passes/index.ts +0 -3916
- package/src/layout-engine.ts +0 -1565
- package/src/layout-rubric.ts +0 -659
- package/src/mcp/artifacts.ts +0 -137
- package/src/mcp/dynamic-harness.ts +0 -31
- package/src/mcp/dynamic-modules.d.ts +0 -7
- package/src/mcp/facade.ts +0 -608
- package/src/mcp/harness-runtime.ts +0 -203
- package/src/mcp/hosted-server.ts +0 -468
- package/src/mcp/mcp-bin.ts +0 -3
- package/src/mcp/mcp-cli.ts +0 -82
- package/src/mcp/protocol.ts +0 -13
- package/src/mcp/sandbox.ts +0 -195
- package/src/mcp/sdk-decl.ts +0 -391
- package/src/mcp/server.ts +0 -362
- package/src/mcp/tool-surface.ts +0 -146
- package/src/mermaid-source.ts +0 -655
- package/src/multiline-utils.ts +0 -228
- package/src/parser.ts +0 -1031
- package/src/pie/layout.ts +0 -168
- package/src/pie/parser.ts +0 -136
- package/src/pie/renderer.ts +0 -234
- package/src/pie/types.ts +0 -82
- package/src/quadrant/layout.ts +0 -191
- package/src/quadrant/parser.ts +0 -183
- package/src/quadrant/renderer.ts +0 -322
- package/src/quadrant/types.ts +0 -113
- package/src/render-family-hooks.ts +0 -244
- package/src/renderer.ts +0 -1099
- package/src/route-contracts.ts +0 -2723
- package/src/scene/backend.ts +0 -76
- package/src/scene/fidelity.ts +0 -183
- package/src/scene/hybrid-backend.ts +0 -174
- package/src/scene/ir.ts +0 -181
- package/src/scene/marks.ts +0 -127
- package/src/scene/rough-backend.ts +0 -378
- package/src/scene/seed.ts +0 -46
- package/src/scene/style-registry.ts +0 -556
- package/src/sequence/layout.ts +0 -456
- package/src/sequence/parser.ts +0 -255
- package/src/sequence/renderer.ts +0 -604
- package/src/sequence/types.ts +0 -154
- package/src/shape-clipping.ts +0 -351
- package/src/shared/accessibility-directives.ts +0 -22
- package/src/shared/batched.ts +0 -61
- package/src/shared/color-math.ts +0 -93
- package/src/shared/svg-a11y.ts +0 -29
- package/src/shared/syntax-error.ts +0 -17
- package/src/shared/unicode-ranges.ts +0 -135
- package/src/styles.ts +0 -318
- package/src/svg-paths.ts +0 -28
- package/src/text-metrics.ts +0 -248
- package/src/theme.ts +0 -710
- package/src/timeline/layout.ts +0 -381
- package/src/timeline/parser.ts +0 -215
- package/src/timeline/renderer.ts +0 -538
- package/src/timeline/types.ts +0 -105
- package/src/types.ts +0 -462
- package/src/website-worker-modules.d.ts +0 -18
- package/src/xychart/axis-utils.ts +0 -109
- package/src/xychart/colors.ts +0 -124
- package/src/xychart/config.ts +0 -88
- package/src/xychart/layout.ts +0 -656
- package/src/xychart/parser.ts +0 -504
- package/src/xychart/renderer.ts +0 -640
- package/src/xychart/types.ts +0 -282
|
@@ -1,288 +0,0 @@
|
|
|
1
|
-
// ============================================================================
|
|
2
|
-
// Glyph-coverage detection for the PNG raster path (helper owned by png.ts).
|
|
3
|
-
//
|
|
4
|
-
// renderMermaidPNG rasterizes with bundled fonts and loadSystemFonts: false
|
|
5
|
-
// (determinism), so characters no bundled font covers — CJK and most emoji —
|
|
6
|
-
// silently draw as tofu boxes. This module reads the cmap tables of the fonts
|
|
7
|
-
// resvg will actually see (bundled dir + caller fontDirs) and reports which
|
|
8
|
-
// characters of the rendered text have no glyph anywhere in that set, grouped
|
|
9
|
-
// by script, so png.ts can warn loudly instead.
|
|
10
|
-
//
|
|
11
|
-
// A compact cmap reader is deliberate: @resvg/resvg-js exposes no coverage
|
|
12
|
-
// API, and a full font library would be a heavy dependency for a yes/no
|
|
13
|
-
// lookup. Formats 4 and 12 cover every practically shipped Unicode cmap
|
|
14
|
-
// (formats 0 and 6 are handled for tiny legacy faces). TrueType collections
|
|
15
|
-
// (.ttc) are unioned across their member fonts.
|
|
16
|
-
//
|
|
17
|
-
// Determinism: results depend only on the font files and the text; scripts
|
|
18
|
-
// and characters are emitted in sorted order.
|
|
19
|
-
// ============================================================================
|
|
20
|
-
|
|
21
|
-
import { readdirSync, readFileSync, statSync } from 'node:fs'
|
|
22
|
-
import { join } from 'node:path'
|
|
23
|
-
import { decodeXML } from 'entities'
|
|
24
|
-
|
|
25
|
-
/** Per-font glyph lookup backed by the raw cmap bytes (exact, incl. glyph 0). */
|
|
26
|
-
interface CmapLookup { has(cp: number): boolean }
|
|
27
|
-
|
|
28
|
-
// ---------------------------------------------------------------------------
|
|
29
|
-
// sfnt / cmap parsing
|
|
30
|
-
// ---------------------------------------------------------------------------
|
|
31
|
-
|
|
32
|
-
const TAG_TTCF = 0x74746366 // 'ttcf'
|
|
33
|
-
const TAG_OTTO = 0x4f54544f // 'OTTO' (CFF-flavored OpenType)
|
|
34
|
-
const TAG_TRUE = 0x74727565 // 'true' (legacy Apple)
|
|
35
|
-
const TAG_CMAP = 0x636d6170 // 'cmap'
|
|
36
|
-
|
|
37
|
-
/** Parse one font file (ttf/otf/ttc) into cmap lookups; [] when unparsable. */
|
|
38
|
-
function parseFontFile(bytes: Uint8Array): CmapLookup[] {
|
|
39
|
-
const dv = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength)
|
|
40
|
-
try {
|
|
41
|
-
if (dv.getUint32(0) === TAG_TTCF) {
|
|
42
|
-
const numFonts = dv.getUint32(8)
|
|
43
|
-
const lookups: CmapLookup[] = []
|
|
44
|
-
for (let i = 0; i < numFonts; i++) lookups.push(...parseSfnt(dv, dv.getUint32(12 + i * 4)))
|
|
45
|
-
return lookups
|
|
46
|
-
}
|
|
47
|
-
return parseSfnt(dv, 0)
|
|
48
|
-
} catch {
|
|
49
|
-
return [] // torn/foreign file: treat as covering nothing
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function parseSfnt(dv: DataView, base: number): CmapLookup[] {
|
|
54
|
-
const version = dv.getUint32(base)
|
|
55
|
-
if (version !== 0x00010000 && version !== TAG_OTTO && version !== TAG_TRUE) return []
|
|
56
|
-
const numTables = dv.getUint16(base + 4)
|
|
57
|
-
for (let i = 0; i < numTables; i++) {
|
|
58
|
-
const rec = base + 12 + i * 16
|
|
59
|
-
// Table offsets are from the start of the file (also inside a .ttc).
|
|
60
|
-
if (dv.getUint32(rec) === TAG_CMAP) return parseCmapTable(dv, dv.getUint32(rec + 8))
|
|
61
|
-
}
|
|
62
|
-
return []
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
function parseCmapTable(dv: DataView, cmap: number): CmapLookup[] {
|
|
66
|
-
const lookups: CmapLookup[] = []
|
|
67
|
-
const numTables = dv.getUint16(cmap + 2)
|
|
68
|
-
for (let i = 0; i < numTables; i++) {
|
|
69
|
-
const rec = cmap + 4 + i * 8
|
|
70
|
-
const platform = dv.getUint16(rec)
|
|
71
|
-
const encoding = dv.getUint16(rec + 2)
|
|
72
|
-
// Unicode-capable subtables only: Unicode platform, or Windows BMP/full.
|
|
73
|
-
if (!(platform === 0 || (platform === 3 && (encoding === 1 || encoding === 10)))) continue
|
|
74
|
-
const sub = cmap + dv.getUint32(rec + 4)
|
|
75
|
-
const format = dv.getUint16(sub)
|
|
76
|
-
if (format === 4) lookups.push(format4Lookup(dv, sub))
|
|
77
|
-
else if (format === 12) lookups.push(format12Lookup(dv, sub))
|
|
78
|
-
else if (format === 6) lookups.push(format6Lookup(dv, sub))
|
|
79
|
-
else if (format === 0) lookups.push(format0Lookup(dv, sub))
|
|
80
|
-
}
|
|
81
|
-
return lookups
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/** Format 4 (segment mapping, BMP): exact glyph resolution incl. idRangeOffset. */
|
|
85
|
-
function format4Lookup(dv: DataView, sub: number): CmapLookup {
|
|
86
|
-
const segCount = dv.getUint16(sub + 6) / 2
|
|
87
|
-
const endsAt = sub + 14
|
|
88
|
-
const startsAt = endsAt + segCount * 2 + 2
|
|
89
|
-
const deltasAt = startsAt + segCount * 2
|
|
90
|
-
const rangeOffsetsAt = deltasAt + segCount * 2
|
|
91
|
-
return {
|
|
92
|
-
has(cp: number): boolean {
|
|
93
|
-
if (cp > 0xffff) return false
|
|
94
|
-
// Binary search the first segment with endCode >= cp.
|
|
95
|
-
let lo = 0
|
|
96
|
-
let hi = segCount - 1
|
|
97
|
-
while (lo < hi) {
|
|
98
|
-
const mid = (lo + hi) >> 1
|
|
99
|
-
if (dv.getUint16(endsAt + mid * 2) < cp) lo = mid + 1
|
|
100
|
-
else hi = mid
|
|
101
|
-
}
|
|
102
|
-
if (dv.getUint16(startsAt + lo * 2) > cp || cp === 0xffff) return false
|
|
103
|
-
const rangeOffset = dv.getUint16(rangeOffsetsAt + lo * 2)
|
|
104
|
-
if (rangeOffset === 0) return ((cp + dv.getInt16(deltasAt + lo * 2)) & 0xffff) !== 0
|
|
105
|
-
const glyphAt = rangeOffsetsAt + lo * 2 + rangeOffset + (cp - dv.getUint16(startsAt + lo * 2)) * 2
|
|
106
|
-
if (glyphAt + 2 > dv.byteLength) return false
|
|
107
|
-
const glyph = dv.getUint16(glyphAt)
|
|
108
|
-
return glyph !== 0 && ((glyph + dv.getInt16(deltasAt + lo * 2)) & 0xffff) !== 0
|
|
109
|
-
},
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/** Format 12 (segmented coverage, full Unicode): binary search the groups. */
|
|
114
|
-
function format12Lookup(dv: DataView, sub: number): CmapLookup {
|
|
115
|
-
const numGroups = dv.getUint32(sub + 12)
|
|
116
|
-
const groupsAt = sub + 16
|
|
117
|
-
return {
|
|
118
|
-
has(cp: number): boolean {
|
|
119
|
-
let lo = 0
|
|
120
|
-
let hi = numGroups - 1
|
|
121
|
-
while (lo <= hi) {
|
|
122
|
-
const mid = (lo + hi) >> 1
|
|
123
|
-
const at = groupsAt + mid * 12
|
|
124
|
-
if (cp < dv.getUint32(at)) hi = mid - 1
|
|
125
|
-
else if (cp > dv.getUint32(at + 4)) lo = mid + 1
|
|
126
|
-
else return dv.getUint32(at + 8) + (cp - dv.getUint32(at)) !== 0
|
|
127
|
-
}
|
|
128
|
-
return false
|
|
129
|
-
},
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/** Format 6 (trimmed table). */
|
|
134
|
-
function format6Lookup(dv: DataView, sub: number): CmapLookup {
|
|
135
|
-
const first = dv.getUint16(sub + 6)
|
|
136
|
-
const count = dv.getUint16(sub + 8)
|
|
137
|
-
return {
|
|
138
|
-
has: (cp: number) => cp >= first && cp < first + count && dv.getUint16(sub + 10 + (cp - first) * 2) !== 0,
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
/** Format 0 (byte encoding). */
|
|
143
|
-
function format0Lookup(dv: DataView, sub: number): CmapLookup {
|
|
144
|
-
return { has: (cp: number) => cp < 256 && dv.getUint8(sub + 6 + cp) !== 0 }
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// ---------------------------------------------------------------------------
|
|
148
|
-
// Directory scanning + caching
|
|
149
|
-
// ---------------------------------------------------------------------------
|
|
150
|
-
|
|
151
|
-
const FONT_EXT = /\.(ttf|otf|ttc|otc)$/i
|
|
152
|
-
const fileLookupCache = new Map<string, CmapLookup[]>()
|
|
153
|
-
|
|
154
|
-
/** Font files under `dir` (sorted, recursive, bounded), mirroring resvg's
|
|
155
|
-
* recursive fontDirs loading closely enough for a coverage answer. */
|
|
156
|
-
function fontFilesUnder(dir: string, depth = 4): string[] {
|
|
157
|
-
if (depth < 0) return []
|
|
158
|
-
let entries: string[]
|
|
159
|
-
try {
|
|
160
|
-
entries = readdirSync(dir).sort()
|
|
161
|
-
} catch {
|
|
162
|
-
return [] // missing/unreadable dir: resvg would load nothing from it too
|
|
163
|
-
}
|
|
164
|
-
const files: string[] = []
|
|
165
|
-
for (const entry of entries) {
|
|
166
|
-
const path = join(dir, entry)
|
|
167
|
-
try {
|
|
168
|
-
const stat = statSync(path)
|
|
169
|
-
if (stat.isDirectory()) files.push(...fontFilesUnder(path, depth - 1))
|
|
170
|
-
else if (FONT_EXT.test(entry)) files.push(path)
|
|
171
|
-
} catch {
|
|
172
|
-
// dangling symlink etc. — skip
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
return files
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
function lookupsForDirs(dirs: readonly string[]): CmapLookup[] {
|
|
179
|
-
const lookups: CmapLookup[] = []
|
|
180
|
-
for (const dir of dirs) {
|
|
181
|
-
for (const file of fontFilesUnder(dir)) {
|
|
182
|
-
let parsed = fileLookupCache.get(file)
|
|
183
|
-
if (!parsed) {
|
|
184
|
-
try {
|
|
185
|
-
parsed = parseFontFile(readFileSync(file))
|
|
186
|
-
} catch {
|
|
187
|
-
parsed = []
|
|
188
|
-
}
|
|
189
|
-
fileLookupCache.set(file, parsed)
|
|
190
|
-
}
|
|
191
|
-
lookups.push(...parsed)
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
return lookups
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
// ---------------------------------------------------------------------------
|
|
198
|
-
// SVG text extraction + script classification
|
|
199
|
-
// ---------------------------------------------------------------------------
|
|
200
|
-
|
|
201
|
-
/** Concatenated character content of every <text> element (tspans stripped,
|
|
202
|
-
* XML entities decoded) — exactly the characters resvg will shape. */
|
|
203
|
-
export function extractSvgTextContent(svg: string): string {
|
|
204
|
-
let out = ''
|
|
205
|
-
for (const m of svg.matchAll(/<text\b[^>]*>([\s\S]*?)<\/text>/g)) {
|
|
206
|
-
out += m[1]!.replace(/<[^>]*>/g, '')
|
|
207
|
-
}
|
|
208
|
-
return decodeXML(out)
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
/** Control/format characters that never need a glyph. */
|
|
212
|
-
function isIgnorable(cp: number): boolean {
|
|
213
|
-
return (
|
|
214
|
-
cp <= 0x20 ||
|
|
215
|
-
cp === 0x7f ||
|
|
216
|
-
(cp >= 0x200b && cp <= 0x200f) || // zero-width space/joiners, directional marks
|
|
217
|
-
cp === 0x2028 ||
|
|
218
|
-
cp === 0x2029 ||
|
|
219
|
-
(cp >= 0xfe00 && cp <= 0xfe0f) || // variation selectors (emoji presentation)
|
|
220
|
-
cp === 0xfeff
|
|
221
|
-
)
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
/** Coarse script bucket for a codepoint — only used to word the warning. */
|
|
225
|
-
function classifyScript(cp: number): string {
|
|
226
|
-
const ranges: ReadonlyArray<readonly [number, number, string]> = [
|
|
227
|
-
[0x0530, 0x058f, 'Armenian'],
|
|
228
|
-
[0x0590, 0x05ff, 'Hebrew'],
|
|
229
|
-
[0x0600, 0x077f, 'Arabic'],
|
|
230
|
-
[0x0900, 0x097f, 'Devanagari'],
|
|
231
|
-
[0x0980, 0x09ff, 'Bengali'],
|
|
232
|
-
[0x0e00, 0x0e7f, 'Thai'],
|
|
233
|
-
[0x10a0, 0x10ff, 'Georgian'],
|
|
234
|
-
[0x1100, 0x11ff, 'CJK'], // Hangul jamo
|
|
235
|
-
[0x2600, 0x27bf, 'emoji'], // misc symbols + dingbats
|
|
236
|
-
[0x2e80, 0x9fff, 'CJK'], // radicals, kana, CJK symbols, unified ideographs
|
|
237
|
-
[0xa960, 0xa97f, 'CJK'],
|
|
238
|
-
[0xac00, 0xd7ff, 'CJK'], // Hangul syllables
|
|
239
|
-
[0xf900, 0xfaff, 'CJK'], // compatibility ideographs
|
|
240
|
-
[0xfe30, 0xfe4f, 'CJK'],
|
|
241
|
-
[0xff00, 0xffef, 'CJK'], // fullwidth/halfwidth forms
|
|
242
|
-
[0x1f1e6, 0x1f1ff, 'emoji'], // regional indicators (flags)
|
|
243
|
-
[0x1f000, 0x1faff, 'emoji'],
|
|
244
|
-
[0x20000, 0x3ffff, 'CJK'], // ideograph extensions
|
|
245
|
-
]
|
|
246
|
-
for (const [lo, hi, script] of ranges) if (cp >= lo && cp <= hi) return script
|
|
247
|
-
return 'other'
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
// ---------------------------------------------------------------------------
|
|
251
|
-
// Public surface
|
|
252
|
-
// ---------------------------------------------------------------------------
|
|
253
|
-
|
|
254
|
-
export interface UncoveredScript {
|
|
255
|
-
/** Coarse script bucket ('CJK', 'emoji', 'Arabic', … or 'other'). */
|
|
256
|
-
script: string
|
|
257
|
-
/** Unique uncovered characters, ascending codepoint order. */
|
|
258
|
-
chars: string[]
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
/**
|
|
262
|
-
* Which characters of `svg`'s text have no glyph in any font under `fontDirs`?
|
|
263
|
-
* Returns one entry per script bucket, scripts sorted alphabetically —
|
|
264
|
-
* deterministic for identical inputs.
|
|
265
|
-
*/
|
|
266
|
-
export function findUncoveredScripts(svg: string, fontDirs: readonly string[]): UncoveredScript[] {
|
|
267
|
-
const text = extractSvgTextContent(svg)
|
|
268
|
-
if (!text) return []
|
|
269
|
-
const unique = new Set<number>()
|
|
270
|
-
for (const ch of text) unique.add(ch.codePointAt(0)!)
|
|
271
|
-
|
|
272
|
-
// Font files parse once per process (fileLookupCache); per render this is
|
|
273
|
-
// a handful of binary searches per unique codepoint.
|
|
274
|
-
const lookups = lookupsForDirs(fontDirs)
|
|
275
|
-
const byScript = new Map<string, number[]>()
|
|
276
|
-
for (const cp of unique) {
|
|
277
|
-
if (isIgnorable(cp)) continue
|
|
278
|
-
if (lookups.some(l => l.has(cp))) continue
|
|
279
|
-
const script = classifyScript(cp)
|
|
280
|
-
const bucket = byScript.get(script)
|
|
281
|
-
if (bucket) bucket.push(cp)
|
|
282
|
-
else byScript.set(script, [cp])
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
return [...byScript.entries()]
|
|
286
|
-
.sort(([a], [b]) => (a < b ? -1 : 1))
|
|
287
|
-
.map(([script, cps]) => ({ script, chars: cps.sort((a, b) => a - b).map(cp => String.fromCodePoint(cp)) }))
|
|
288
|
-
}
|