@json-to-office/shared 1.3.0 → 1.5.0
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/dist/{capabilities-DrHJ6_4G.d.ts → capabilities-DtPF3aBj.d.ts} +47 -4
- package/dist/{chunk-GPNPMKVZ.js → chunk-A4CY2MAM.js} +2 -2
- package/dist/{chunk-ZKD5BAMU.js → chunk-LLBCT7WL.js} +1 -1
- package/dist/chunk-LLBCT7WL.js.map +1 -0
- package/dist/chunk-MCUIFSUN.js +243 -0
- package/dist/chunk-MCUIFSUN.js.map +1 -0
- package/dist/{chunk-BJNBJSQG.js → chunk-QLZNOXT5.js} +50 -7
- package/dist/chunk-QLZNOXT5.js.map +1 -0
- package/dist/fonts/node.d.ts +7 -6
- package/dist/fonts/node.js +6 -4
- package/dist/fonts/node.js.map +1 -1
- package/dist/index.d.ts +19 -9
- package/dist/index.js +14 -187
- package/dist/index.js.map +1 -1
- package/dist/plugin/index.js +2 -2
- package/dist/rendering/index.d.ts +1 -1
- package/dist/rendering/index.js +3 -1
- package/dist/schemas/slide-content.d.ts +7 -3
- package/dist/schemas/slide-content.js +8 -3
- package/dist/schemas/slide-content.js.map +1 -1
- package/dist/validation/unified/index.js +1 -1
- package/package.json +6 -5
- package/dist/chunk-BJNBJSQG.js.map +0 -1
- package/dist/chunk-FDSJYZ5W.js +0 -41
- package/dist/chunk-FDSJYZ5W.js.map +0 -1
- package/dist/chunk-ZKD5BAMU.js.map +0 -1
- /package/dist/{chunk-GPNPMKVZ.js.map → chunk-A4CY2MAM.js.map} +0 -0
|
@@ -173,6 +173,25 @@ declare function diagnoseUnsupportedFeatures<TFeature extends string>(required:
|
|
|
173
173
|
* Call this after compiling to IR and before handing the IR to an adapter.
|
|
174
174
|
*/
|
|
175
175
|
declare function assertRendererSupports<TFeature extends string>(required: readonly FeatureRequirement<TFeature>[], renderer: Pick<OfficeRenderer<unknown, TFeature, string>, 'id' | 'format' | 'capabilities'>): void;
|
|
176
|
+
/**
|
|
177
|
+
* Whether a registered renderer can actually run on this host.
|
|
178
|
+
*
|
|
179
|
+
* Registration says a renderer exists; it says nothing about whether its
|
|
180
|
+
* backend is installed, because the factory is only invoked on selection. A
|
|
181
|
+
* discovery surface that reports the ids alone therefore advertises renderers
|
|
182
|
+
* that fail at the first render — which is what `jto_info` and `jto_discover`
|
|
183
|
+
* used to do for `office-open` on a host that never installed it.
|
|
184
|
+
*/
|
|
185
|
+
interface RendererStatus<TId extends string = string> {
|
|
186
|
+
id: TId;
|
|
187
|
+
/** The one used when a caller passes no id. */
|
|
188
|
+
default: boolean;
|
|
189
|
+
available: boolean;
|
|
190
|
+
/** Why not, when not — the message the load failure carried. */
|
|
191
|
+
reason?: string;
|
|
192
|
+
/** The command that would make it available. */
|
|
193
|
+
installHint?: string;
|
|
194
|
+
}
|
|
176
195
|
/**
|
|
177
196
|
* A registry of renderers for a single format.
|
|
178
197
|
*
|
|
@@ -187,8 +206,8 @@ declare class RendererRegistry<TIR, TFeature extends string, TId extends string>
|
|
|
187
206
|
/**
|
|
188
207
|
* Register a lazily-constructed renderer.
|
|
189
208
|
*
|
|
190
|
-
* The factory is async and only invoked on selection, so
|
|
191
|
-
*
|
|
209
|
+
* The factory is async and only invoked on selection, so choosing one
|
|
210
|
+
* renderer never imports another one's backend.
|
|
192
211
|
*/
|
|
193
212
|
register(id: TId, factory: () => Promise<OfficeRenderer<TIR, TFeature, TId>>): void;
|
|
194
213
|
/** Renderer ids registered for this format, in registration order. */
|
|
@@ -201,10 +220,34 @@ declare class RendererRegistry<TIR, TFeature extends string, TId extends string>
|
|
|
201
220
|
*
|
|
202
221
|
* An unknown id is `UnknownRendererError`, which carries the id asked for and
|
|
203
222
|
* the ones that exist, so a caller boundary can answer "bad request" rather
|
|
204
|
-
* than "the server broke". A
|
|
223
|
+
* than "the server broke". A backend that will not load is re-thrown with an
|
|
205
224
|
* actionable install hint.
|
|
206
225
|
*/
|
|
207
226
|
resolve(id?: TId): Promise<OfficeRenderer<TIR, TFeature, TId>>;
|
|
227
|
+
/**
|
|
228
|
+
* Every registered renderer, with whether it can actually be loaded here.
|
|
229
|
+
*
|
|
230
|
+
* Answers the question by loading each one, which is the only answer that
|
|
231
|
+
* cannot be wrong — a resolver check would still miss a backend that
|
|
232
|
+
* resolves and then throws on import.
|
|
233
|
+
*
|
|
234
|
+
* Memoized, and the promise rather than its value, so concurrent callers
|
|
235
|
+
* share one probe instead of racing several. Nothing installs a package into
|
|
236
|
+
* a running process, so the answer cannot go stale within one; without this
|
|
237
|
+
* `jto_validate` would pay a package import on every call, which is the tool
|
|
238
|
+
* an agent uses after every edit.
|
|
239
|
+
*/
|
|
240
|
+
statuses(): Promise<RendererStatus<TId>[]>;
|
|
241
|
+
private statusCache?;
|
|
242
|
+
private probeStatuses;
|
|
208
243
|
}
|
|
244
|
+
/**
|
|
245
|
+
* `Error.name` marking a renderer whose optional backend is not installed.
|
|
246
|
+
*
|
|
247
|
+
* A name rather than a subclass: the error crosses a package boundary and an
|
|
248
|
+
* `instanceof` there would depend on both sides loading the same copy of this
|
|
249
|
+
* module, which under a workspace layout is not something to rely on.
|
|
250
|
+
*/
|
|
251
|
+
declare const RENDERER_DEPENDENCY_MISSING = "RendererDependencyMissingError";
|
|
209
252
|
|
|
210
|
-
export { type FeatureRequirement as F, type OfficeFormat as O,
|
|
253
|
+
export { type FeatureRequirement as F, type OfficeFormat as O, RENDERER_DEPENDENCY_MISSING as R, UnknownRendererError as U, FeatureRequirementCollector as a, type OfficeRenderer as b, type RenderOptions as c, type RendererDiagnostic as d, type RendererDiagnosticSeverity as e, RendererRegistry as f, type RendererStatus as g, UnsupportedRendererFeatureError as h, type UnsupportedRendererFeatureErrorInit as i, assertNever as j, assertRendererSupports as k, diagnoseUnsupportedFeatures as l, rendererWarning as m, partitionDiagnostics as p, rendererError as r };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
formatErrorSummary,
|
|
3
3
|
transformValueErrors
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-LLBCT7WL.js";
|
|
5
5
|
import {
|
|
6
6
|
isValidSemver,
|
|
7
7
|
latestVersion
|
|
@@ -198,4 +198,4 @@ export {
|
|
|
198
198
|
isValidationSuccess,
|
|
199
199
|
getValidationSummary
|
|
200
200
|
};
|
|
201
|
-
//# sourceMappingURL=chunk-
|
|
201
|
+
//# sourceMappingURL=chunk-A4CY2MAM.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/validation/unified/schema-utils.ts","../src/validation/unified/error-formatter-config.ts","../src/validation/unified/error-transformer.ts"],"sourcesContent":["import type { TSchema, TUnion, TObject, TLiteral } from '@sinclair/typebox';\n\nconst componentNamesCache = new Map<unknown, string[]>();\n\nexport function isUnionSchema(schema: TSchema): schema is TUnion {\n return 'anyOf' in schema && Array.isArray(schema.anyOf);\n}\n\nexport function isObjectSchema(schema: TSchema): schema is TObject {\n return schema.type === 'object' && 'properties' in schema;\n}\n\nexport function isLiteralSchema(schema: TSchema): schema is TLiteral {\n return 'const' in schema;\n}\n\nexport function getObjectSchemaPropertyNames(schema: TSchema): string[] {\n if (isObjectSchema(schema)) {\n return Object.keys(schema.properties);\n }\n return [];\n}\n\nexport function getLiteralValue(schema: TSchema): unknown {\n if (isLiteralSchema(schema)) {\n return schema.const;\n }\n return undefined;\n}\n\nexport function extractStandardComponentNames(schema: TSchema): string[] {\n if (componentNamesCache.has(schema)) {\n return componentNamesCache.get(schema)!;\n }\n\n const names: string[] = [];\n\n if ('anyOf' in schema && Array.isArray(schema.anyOf)) {\n for (const componentSchema of schema.anyOf) {\n if (isObjectSchema(componentSchema)) {\n const nameProperty = componentSchema.properties?.name;\n if (nameProperty && isLiteralSchema(nameProperty)) {\n const nameValue = getLiteralValue(nameProperty);\n if (typeof nameValue === 'string') {\n names.push(nameValue);\n }\n }\n }\n }\n } else if (isObjectSchema(schema)) {\n const nameProperty = schema.properties?.name ?? schema.properties?.type;\n if (nameProperty && isLiteralSchema(nameProperty)) {\n const nameValue = getLiteralValue(nameProperty);\n if (typeof nameValue === 'string') {\n names.push(nameValue);\n }\n }\n }\n\n componentNamesCache.set(schema, names);\n return names;\n}\n\nexport function clearComponentNamesCache(): void {\n componentNamesCache.clear();\n}\n\nexport function getSchemaMetadata(schema: TSchema): {\n type?: string;\n properties?: string[];\n literal?: unknown;\n} {\n const metadata: {\n type?: string;\n properties?: string[];\n literal?: unknown;\n } = {};\n\n if ('type' in schema) {\n metadata.type = String(schema.type);\n }\n\n if (isObjectSchema(schema)) {\n metadata.properties = getObjectSchemaPropertyNames(schema);\n }\n\n if (isLiteralSchema(schema)) {\n metadata.literal = getLiteralValue(schema);\n }\n\n return metadata;\n}\n","export interface ErrorFormatterConfig {\n includeEmojis?: boolean;\n verbosity?: 'minimal' | 'normal' | 'detailed';\n includeSuggestions?: boolean;\n includePath?: boolean;\n maxMessageLength?: number;\n includeDocLinks?: boolean;\n colorSupport?: boolean;\n}\n\nfunction isCI(): boolean {\n if (typeof process === 'undefined' || !process.env) return false;\n return !!(\n process.env.CI ||\n process.env.GITHUB_ACTIONS ||\n process.env.GITLAB_CI\n );\n}\n\nfunction hasColorSupport(): boolean {\n if (typeof process === 'undefined') return false;\n if (process.env?.NO_COLOR) return false;\n if (process.env?.FORCE_COLOR) return true;\n if (process.stdout?.isTTY) return true;\n return !isCI();\n}\n\nexport const DEFAULT_ERROR_CONFIG: Required<ErrorFormatterConfig> = {\n includeEmojis: !isCI(),\n verbosity: 'normal',\n includeSuggestions: true,\n includePath: true,\n maxMessageLength: 0,\n includeDocLinks: false,\n colorSupport: hasColorSupport(),\n};\n\nexport function createErrorConfig(\n config?: ErrorFormatterConfig\n): Required<ErrorFormatterConfig> {\n return { ...DEFAULT_ERROR_CONFIG, ...config };\n}\n\nexport const ERROR_EMOJIS = {\n ERROR: '\\u274c',\n WARNING: '\\u26a0\\ufe0f',\n FIX: '\\ud83d\\udd27',\n INFO: '\\u2139\\ufe0f',\n SUCCESS: '\\u2705',\n};\n\nexport function formatErrorMessage(\n message: string,\n config: Required<ErrorFormatterConfig>\n): string {\n if (config.maxMessageLength > 0 && message.length > config.maxMessageLength) {\n return message.substring(0, config.maxMessageLength) + '...';\n }\n return message;\n}\n","import type { ValueError } from '@sinclair/typebox/value';\nimport type { ValidationError } from './types';\nimport {\n isObjectSchema,\n getObjectSchemaPropertyNames,\n getLiteralValue,\n} from './schema-utils';\nimport {\n ErrorFormatterConfig,\n createErrorConfig,\n formatErrorMessage,\n} from './error-formatter-config';\n\nexport type { TransformedError } from './types';\n\nfunction generateEnhancedMessage(\n error: ValueError,\n _config: Required<ErrorFormatterConfig>\n): string {\n const typeStr = String(error.type || '');\n const path = error.path || 'root';\n\n if (typeStr === '62' || typeStr === 'union') {\n return generateUnionErrorMessage(error);\n }\n\n if (error.message?.includes('additionalProperties')) {\n return generateAdditionalPropertiesMessage(error);\n }\n\n if (error.message?.includes('Required property')) {\n return generateRequiredPropertyMessage(error);\n }\n\n if (\n typeStr === 'string' ||\n typeStr === 'number' ||\n typeStr === 'boolean' ||\n typeStr === 'array' ||\n typeStr === 'object'\n ) {\n return generateTypeMismatchMessage(error);\n }\n\n if (typeStr === 'literal') {\n return generateLiteralErrorMessage(error);\n }\n\n if (typeStr === 'pattern' || typeStr === 'RegExp') {\n return generatePatternErrorMessage(error);\n }\n\n return `At ${path}: ${error.message}`;\n}\n\nfunction generateUnionErrorMessage(error: ValueError): string {\n const path = error.path || 'root';\n const value = error.value;\n\n if (path === 'root' || path === '/' || path === '/jsonDefinition') {\n if (value && typeof value === 'object') {\n if ('name' in value) {\n return `Invalid component configuration for '${(value as any).name}'. Check that all required fields are present.`;\n }\n if ('children' in value && Array.isArray((value as any).children)) {\n return \"Document is missing required 'name' field.\";\n }\n }\n return 'Invalid document structure. Check required fields.';\n }\n\n if (path.includes('/children/')) {\n if (value && typeof value === 'object' && 'name' in value) {\n const componentType = (value as any).name;\n return `Invalid component configuration for type '${componentType}'. Check that all required fields are present and correctly formatted.`;\n }\n return 'Invalid component structure. Each component must have a \"name\" field and valid configuration.';\n }\n\n return `Value at ${path} doesn't match any of the expected formats. Check the structure and required fields.`;\n}\n\nfunction generateAdditionalPropertiesMessage(error: ValueError): string {\n const path = error.path || 'root';\n const value = error.value;\n\n if (typeof value === 'object' && value !== null) {\n const schema = error.schema;\n if (schema && isObjectSchema(schema)) {\n const knownProps = getObjectSchemaPropertyNames(schema);\n const actualProps = Object.keys(value);\n const unknownProps = actualProps.filter((p) => !knownProps.includes(p));\n\n if (unknownProps.length > 0) {\n return (\n `Unknown properties at ${path}: ${unknownProps.join(', ')}. ` +\n `Allowed properties are: ${knownProps.join(', ')}`\n );\n }\n }\n }\n\n return `Additional properties not allowed at ${path}. Check for typos or unsupported fields.`;\n}\n\nfunction generateRequiredPropertyMessage(error: ValueError): string {\n const path = error.path || 'root';\n const match = error.message?.match(/Required property '([^']+)'/);\n\n if (match) {\n const propName = match[1];\n return `Missing required field '${propName}' at ${path}. This field is mandatory.`;\n }\n\n return `Missing required property at ${path}. Check that all mandatory fields are present.`;\n}\n\nfunction generateTypeMismatchMessage(error: ValueError): string {\n const path = error.path || 'root';\n const expectedType = String(error.type);\n const actualType = Array.isArray(error.value) ? 'array' : typeof error.value;\n\n if (path.includes('alignment')) {\n return `Invalid alignment value at ${path}. Expected one of: left, center, right, justify`;\n }\n\n if (path.includes('color')) {\n return `Invalid color value at ${path}. Use hex format (#RRGGBB), rgb(r,g,b), or a named color`;\n }\n\n if (path.includes('fontSize') || path.includes('size')) {\n return `Invalid size value at ${path}. Expected a number (in points)`;\n }\n\n if (\n path.includes('margin') ||\n path.includes('padding') ||\n path.includes('spacing')\n ) {\n return `Invalid spacing value at ${path}. Expected a number or spacing object with top/bottom/left/right`;\n }\n\n return `Type mismatch at ${path}: Expected ${expectedType} but got ${actualType}`;\n}\n\nfunction generateLiteralErrorMessage(error: ValueError): string {\n const path = error.path || 'root';\n const expected = error.schema\n ? JSON.stringify(getLiteralValue(error.schema))\n : 'specific value';\n const actual = JSON.stringify(error.value);\n\n return `Invalid value at ${path}: Expected exactly ${expected} but got ${actual}`;\n}\n\nfunction generatePatternErrorMessage(error: ValueError): string {\n const path = error.path || 'root';\n\n if (path.includes('email')) {\n return `Invalid email format at ${path}. Use format: user@example.com`;\n }\n if (path.includes('url') || path.includes('link')) {\n return `Invalid URL format at ${path}. Use format: https://example.com`;\n }\n if (path.includes('date')) {\n return `Invalid date format at ${path}. Use ISO format: YYYY-MM-DD`;\n }\n\n return `Value at ${path} doesn't match the required pattern`;\n}\n\nexport function transformValueError(\n error: ValueError,\n jsonString?: string,\n config?: ErrorFormatterConfig\n): ValidationError {\n const formatterConfig = createErrorConfig(config);\n\n const enhancedMessage = generateEnhancedMessage(error, formatterConfig);\n\n const baseError: ValidationError = {\n path: error.path || 'root',\n message: formatErrorMessage(\n enhancedMessage || error.message,\n formatterConfig\n ),\n code: String(error.type || 'validation_error'),\n value: error.value,\n };\n\n if (formatterConfig.includeSuggestions) {\n const suggestion = getSuggestion(error, formatterConfig);\n if (suggestion) {\n baseError.suggestion = formatErrorMessage(suggestion, formatterConfig);\n }\n }\n\n if (jsonString && error.path) {\n const position = calculatePosition(jsonString, error.path);\n if (position) {\n baseError.line = position.line;\n baseError.column = position.column;\n }\n }\n\n return baseError;\n}\n\nexport function transformValueErrors(\n errors: ValueError[],\n options?: {\n jsonString?: string;\n maxErrors?: number;\n }\n): ValidationError[] {\n const maxErrors = options?.maxErrors ?? Number.MAX_SAFE_INTEGER;\n const result: ValidationError[] = [];\n const seenPaths = new Set<string>();\n\n for (const error of errors) {\n if (result.length >= maxErrors) break;\n\n const errorKey = `${error.path}:${error.type}`;\n\n if (!seenPaths.has(errorKey)) {\n seenPaths.add(errorKey);\n result.push(transformValueError(error, options?.jsonString, undefined));\n }\n }\n\n return result;\n}\n\nexport function calculatePosition(\n jsonString: string,\n path: string\n): { line: number; column: number } | null {\n try {\n const pathParts = path.split('/').filter(Boolean);\n if (pathParts.length === 0) {\n return { line: 1, column: 1 };\n }\n\n const lastPart = pathParts[pathParts.length - 1];\n const searchPattern = `\"${lastPart}\"`;\n const index = jsonString.indexOf(searchPattern);\n\n if (index === -1) {\n return { line: 1, column: 1 };\n }\n\n const beforeError = jsonString.substring(0, index);\n const lines = beforeError.split('\\n');\n const line = lines.length;\n const column = lines[lines.length - 1].length + 1;\n\n return { line, column };\n } catch {\n return null;\n }\n}\n\nfunction getSuggestion(\n error: ValueError,\n _config: Required<ErrorFormatterConfig>\n): string | undefined {\n const { type, path } = error;\n const typeStr = String(type);\n\n if (typeStr === '62' || typeStr === 'union') {\n if (path === 'root' || path === '/') {\n return 'Ensure the document has proper structure with a root \"name\" field';\n }\n if (path?.includes('/children/')) {\n return 'Check that the component has a valid \"name\" and all required fields';\n }\n return 'Review the structure and ensure all required fields are present with correct types';\n }\n\n if (error.message?.includes('additionalProperties')) {\n return 'Remove any unknown or unsupported fields.';\n }\n\n if (error.message?.includes('Required property')) {\n return 'Add the missing required field to fix this error';\n }\n\n if (typeStr === 'string') {\n if (path?.includes('color')) {\n return 'Use a valid color format (hex: #RRGGBB, rgb: rgb(r,g,b), or named color)';\n }\n return 'Provide a text string value';\n }\n\n if (typeStr === 'number') {\n return 'Provide a numeric value';\n }\n\n if (typeStr === 'boolean') {\n return 'Use true or false (without quotes)';\n }\n\n if (typeStr === 'array') {\n return 'Provide an array of values using square brackets []';\n }\n\n if (typeStr === 'object') {\n return 'Provide an object with key-value pairs using curly braces {}';\n }\n\n if (typeStr === 'literal') {\n const expected = error.schema\n ? JSON.stringify(getLiteralValue(error.schema))\n : 'specific value';\n return `Use exactly this value: ${expected}`;\n }\n\n return undefined;\n}\n\nexport function formatErrorSummary(errors: ValidationError[]): string {\n if (errors.length === 0) return 'No errors';\n\n if (errors.length === 1) {\n return errors[0].message;\n }\n\n const summary = errors\n .slice(0, 3)\n .map((e) => `${e.path}: ${e.message}`)\n .join(', ');\n\n if (errors.length > 3) {\n return `${summary} and ${errors.length - 3} more...`;\n }\n\n return summary;\n}\n\nexport function groupErrorsByPath(\n errors: ValidationError[]\n): Map<string, ValidationError[]> {\n const grouped = new Map<string, ValidationError[]>();\n\n for (const error of errors) {\n const path = error.path || 'root';\n const group = grouped.get(path) || [];\n group.push(error);\n grouped.set(path, group);\n }\n\n return grouped;\n}\n\nexport function createJsonParseError(\n error: Error,\n jsonString: string\n): ValidationError {\n const match = error.message.match(/position (\\d+)/);\n const position = match ? parseInt(match[1], 10) : 0;\n\n let line = 1;\n let column = 1;\n\n if (position > 0) {\n const lines = jsonString.substring(0, position).split('\\n');\n line = lines.length;\n column = lines[lines.length - 1].length + 1;\n }\n\n return {\n path: 'root',\n message: `JSON Parse Error: ${error.message}`,\n code: 'json_parse_error',\n line,\n column,\n suggestion: 'Check for missing commas, quotes, or brackets',\n };\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
// src/fonts/sources/ttf-name.ts
|
|
2
|
+
var FAMILY_NAME_IDS = /* @__PURE__ */ new Set([
|
|
3
|
+
1,
|
|
4
|
+
// Font Family
|
|
5
|
+
4,
|
|
6
|
+
// Full Font Name
|
|
7
|
+
6,
|
|
8
|
+
// PostScript Name
|
|
9
|
+
16
|
|
10
|
+
// Typographic/Preferred Family
|
|
11
|
+
]);
|
|
12
|
+
var MAGIC_HEAD_CHECKSUM = 2981146554;
|
|
13
|
+
function sfntChecksum(buf) {
|
|
14
|
+
let sum = 0;
|
|
15
|
+
const end = buf.length;
|
|
16
|
+
const aligned = end - end % 4;
|
|
17
|
+
for (let i = 0; i < aligned; i += 4) {
|
|
18
|
+
sum = sum + buf.readUInt32BE(i) >>> 0;
|
|
19
|
+
}
|
|
20
|
+
if (aligned < end) {
|
|
21
|
+
let chunk = 0;
|
|
22
|
+
const remaining = end - aligned;
|
|
23
|
+
if (remaining >= 1) chunk |= buf[aligned] << 24;
|
|
24
|
+
if (remaining >= 2) chunk |= buf[aligned + 1] << 16;
|
|
25
|
+
if (remaining >= 3) chunk |= buf[aligned + 2] << 8;
|
|
26
|
+
sum = sum + chunk >>> 0;
|
|
27
|
+
}
|
|
28
|
+
return sum >>> 0;
|
|
29
|
+
}
|
|
30
|
+
function encodeString(record, value) {
|
|
31
|
+
if (record.platformID === 1) {
|
|
32
|
+
return Buffer.from(value, "ascii");
|
|
33
|
+
}
|
|
34
|
+
const out = Buffer.alloc(value.length * 2);
|
|
35
|
+
for (let i = 0; i < value.length; i += 1) {
|
|
36
|
+
out.writeUInt16BE(value.charCodeAt(i), i * 2);
|
|
37
|
+
}
|
|
38
|
+
return out;
|
|
39
|
+
}
|
|
40
|
+
function buildNameTable(records) {
|
|
41
|
+
const count = records.length;
|
|
42
|
+
const headerSize = 6 + count * 12;
|
|
43
|
+
let heapSize = 0;
|
|
44
|
+
for (const r of records) heapSize += r.bytes.length;
|
|
45
|
+
const raw = Buffer.alloc(headerSize + heapSize);
|
|
46
|
+
raw.writeUInt16BE(0, 0);
|
|
47
|
+
raw.writeUInt16BE(count, 2);
|
|
48
|
+
raw.writeUInt16BE(headerSize, 4);
|
|
49
|
+
let heapCursor = 0;
|
|
50
|
+
for (let i = 0; i < count; i += 1) {
|
|
51
|
+
const r = records[i];
|
|
52
|
+
const ro = 6 + i * 12;
|
|
53
|
+
raw.writeUInt16BE(r.platformID, ro);
|
|
54
|
+
raw.writeUInt16BE(r.encodingID, ro + 2);
|
|
55
|
+
raw.writeUInt16BE(r.languageID, ro + 4);
|
|
56
|
+
raw.writeUInt16BE(r.nameID, ro + 6);
|
|
57
|
+
raw.writeUInt16BE(r.bytes.length, ro + 8);
|
|
58
|
+
raw.writeUInt16BE(heapCursor, ro + 10);
|
|
59
|
+
r.bytes.copy(raw, headerSize + heapCursor);
|
|
60
|
+
heapCursor += r.bytes.length;
|
|
61
|
+
}
|
|
62
|
+
return raw;
|
|
63
|
+
}
|
|
64
|
+
function rewriteNameTable(input, decide) {
|
|
65
|
+
if (input.length < 12) return input;
|
|
66
|
+
const version = input.readUInt32BE(0);
|
|
67
|
+
const isSfnt = version === 65536 || version === 1330926671 || version === 1953658213 || version === 1954115633;
|
|
68
|
+
if (!isSfnt) return input;
|
|
69
|
+
const numTables = input.readUInt16BE(4);
|
|
70
|
+
if (numTables === 0 || input.length < 12 + numTables * 16) return input;
|
|
71
|
+
const tables = [];
|
|
72
|
+
for (let i = 0; i < numTables; i += 1) {
|
|
73
|
+
const eo = 12 + i * 16;
|
|
74
|
+
const tag = input.toString("ascii", eo, eo + 4);
|
|
75
|
+
const checksum = input.readUInt32BE(eo + 4);
|
|
76
|
+
const offset = input.readUInt32BE(eo + 8);
|
|
77
|
+
const length = input.readUInt32BE(eo + 12);
|
|
78
|
+
if (offset + length > input.length) return input;
|
|
79
|
+
tables.push({
|
|
80
|
+
tag,
|
|
81
|
+
checksum,
|
|
82
|
+
offset: 0,
|
|
83
|
+
originalOffset: offset,
|
|
84
|
+
data: input.slice(offset, offset + length)
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
const nameIdx = tables.findIndex((t) => t.tag === "name");
|
|
88
|
+
if (nameIdx === -1) return input;
|
|
89
|
+
const nameBuf = tables[nameIdx].data;
|
|
90
|
+
if (nameBuf.length < 6) return input;
|
|
91
|
+
if (nameBuf.readUInt16BE(0) !== 0) return input;
|
|
92
|
+
const recordCount = nameBuf.readUInt16BE(2);
|
|
93
|
+
const stringOffset = nameBuf.readUInt16BE(4);
|
|
94
|
+
if (nameBuf.length < 6 + recordCount * 12) return input;
|
|
95
|
+
const records = [];
|
|
96
|
+
for (let i = 0; i < recordCount; i += 1) {
|
|
97
|
+
const ro = 6 + i * 12;
|
|
98
|
+
const platformID = nameBuf.readUInt16BE(ro);
|
|
99
|
+
const encodingID = nameBuf.readUInt16BE(ro + 2);
|
|
100
|
+
const languageID = nameBuf.readUInt16BE(ro + 4);
|
|
101
|
+
const nameID = nameBuf.readUInt16BE(ro + 6);
|
|
102
|
+
const length = nameBuf.readUInt16BE(ro + 8);
|
|
103
|
+
const offset = nameBuf.readUInt16BE(ro + 10);
|
|
104
|
+
const bytes = nameBuf.slice(
|
|
105
|
+
stringOffset + offset,
|
|
106
|
+
stringOffset + offset + length
|
|
107
|
+
);
|
|
108
|
+
records.push({ platformID, encodingID, languageID, nameID, bytes });
|
|
109
|
+
}
|
|
110
|
+
const survivors = [];
|
|
111
|
+
for (const r of records) {
|
|
112
|
+
const verdict = decide(r);
|
|
113
|
+
if (verdict.action === "drop") continue;
|
|
114
|
+
if (verdict.action === "set") {
|
|
115
|
+
r.bytes = encodeString(r, verdict.value);
|
|
116
|
+
}
|
|
117
|
+
survivors.push(r);
|
|
118
|
+
}
|
|
119
|
+
records.length = 0;
|
|
120
|
+
records.push(...survivors);
|
|
121
|
+
const rebuiltName = buildNameTable(records);
|
|
122
|
+
tables[nameIdx] = {
|
|
123
|
+
...tables[nameIdx],
|
|
124
|
+
data: rebuiltName,
|
|
125
|
+
checksum: sfntChecksum(rebuiltName)
|
|
126
|
+
};
|
|
127
|
+
tables.sort((a, b) => a.originalOffset - b.originalOffset);
|
|
128
|
+
let cursor = 12 + tables.length * 16;
|
|
129
|
+
for (const t of tables) {
|
|
130
|
+
cursor = cursor + 3 & ~3;
|
|
131
|
+
t.offset = cursor;
|
|
132
|
+
cursor += t.data.length;
|
|
133
|
+
}
|
|
134
|
+
const totalSize = cursor + 3 & ~3;
|
|
135
|
+
const out = Buffer.alloc(totalSize);
|
|
136
|
+
input.copy(out, 0, 0, 12);
|
|
137
|
+
out.writeUInt16BE(tables.length, 4);
|
|
138
|
+
const dirTables = [...tables].sort(
|
|
139
|
+
(a, b) => a.tag < b.tag ? -1 : a.tag > b.tag ? 1 : 0
|
|
140
|
+
);
|
|
141
|
+
for (let i = 0; i < dirTables.length; i += 1) {
|
|
142
|
+
const t = dirTables[i];
|
|
143
|
+
const eo = 12 + i * 16;
|
|
144
|
+
out.write(t.tag, eo, 4, "ascii");
|
|
145
|
+
out.writeUInt32BE(t.checksum, eo + 4);
|
|
146
|
+
out.writeUInt32BE(t.offset, eo + 8);
|
|
147
|
+
out.writeUInt32BE(t.data.length, eo + 12);
|
|
148
|
+
}
|
|
149
|
+
for (const t of tables) {
|
|
150
|
+
t.data.copy(out, t.offset);
|
|
151
|
+
}
|
|
152
|
+
const headTable = tables.find((t) => t.tag === "head");
|
|
153
|
+
if (headTable && headTable.data.length >= 12) {
|
|
154
|
+
out.writeUInt32BE(0, headTable.offset + 8);
|
|
155
|
+
const fontSum = sfntChecksum(out);
|
|
156
|
+
const adjustment = MAGIC_HEAD_CHECKSUM - fontSum >>> 0;
|
|
157
|
+
out.writeUInt32BE(adjustment, headTable.offset + 8);
|
|
158
|
+
}
|
|
159
|
+
return out;
|
|
160
|
+
}
|
|
161
|
+
function rewriteFontFamilyName(input, newFamily) {
|
|
162
|
+
const psForbidden = /[[\](){}<>/%]/g;
|
|
163
|
+
const isAscii = /^[\x00-\x7f]*$/.test(newFamily);
|
|
164
|
+
const psName = newFamily.replace(/\s+/g, "").replace(psForbidden, "").replace(/[^\x21-\x7e]/g, "");
|
|
165
|
+
return rewriteNameTable(input, (r) => {
|
|
166
|
+
if (!FAMILY_NAME_IDS.has(r.nameID)) return { action: "keep" };
|
|
167
|
+
if (r.platformID === 1 && !isAscii) return { action: "drop" };
|
|
168
|
+
return { action: "set", value: r.nameID === 6 ? psName : newFamily };
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
var STANDARD_SUBFAMILY = {
|
|
172
|
+
100: "Thin",
|
|
173
|
+
200: "ExtraLight",
|
|
174
|
+
300: "Light",
|
|
175
|
+
400: "Regular",
|
|
176
|
+
500: "Medium",
|
|
177
|
+
600: "SemiBold",
|
|
178
|
+
700: "Bold",
|
|
179
|
+
800: "ExtraBold",
|
|
180
|
+
900: "Black"
|
|
181
|
+
};
|
|
182
|
+
function standardSubfamilyNames(weight, italic) {
|
|
183
|
+
const base = STANDARD_SUBFAMILY[weight];
|
|
184
|
+
if (!base) return null;
|
|
185
|
+
return {
|
|
186
|
+
typographic: italic ? `${base} Italic` : base,
|
|
187
|
+
legacy: weight >= 600 ? italic ? "Bold Italic" : "Bold" : italic ? "Italic" : "Regular"
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
function rewriteFontSubfamilyNames(input, weight, italic) {
|
|
191
|
+
const std = standardSubfamilyNames(weight, italic);
|
|
192
|
+
if (!std) return input;
|
|
193
|
+
return rewriteNameTable(input, (r) => {
|
|
194
|
+
if (r.nameID === 2) return { action: "set", value: std.legacy };
|
|
195
|
+
if (r.nameID === 17) return { action: "set", value: std.typographic };
|
|
196
|
+
return { action: "keep" };
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// src/fonts/sources/url-allowlist.ts
|
|
201
|
+
var FONT_URL_ALLOWLIST = [
|
|
202
|
+
"fonts.gstatic.com",
|
|
203
|
+
"fonts.googleapis.com",
|
|
204
|
+
"cdn.jsdelivr.net"
|
|
205
|
+
];
|
|
206
|
+
function isAllowedFontUrl(url) {
|
|
207
|
+
let parsed;
|
|
208
|
+
try {
|
|
209
|
+
parsed = new URL(url);
|
|
210
|
+
} catch {
|
|
211
|
+
return false;
|
|
212
|
+
}
|
|
213
|
+
if (parsed.protocol !== "https:") return false;
|
|
214
|
+
return FONT_URL_ALLOWLIST.includes(parsed.hostname.toLowerCase());
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// src/fonts/sources/format.ts
|
|
218
|
+
function detectFontFormat(buf) {
|
|
219
|
+
if (buf.length < 4) return "unknown";
|
|
220
|
+
const b0 = buf[0], b1 = buf[1], b2 = buf[2], b3 = buf[3];
|
|
221
|
+
if (b0 === 0 && b1 === 1 && b2 === 0 && b3 === 0 || b0 === 116 && b1 === 114 && b2 === 117 && b3 === 101 || b0 === 116 && b1 === 121 && b2 === 112 && b3 === 49) {
|
|
222
|
+
return "ttf";
|
|
223
|
+
}
|
|
224
|
+
if (b0 === 79 && b1 === 84 && b2 === 84 && b3 === 79) return "otf";
|
|
225
|
+
if (b0 === 119 && b1 === 79 && b2 === 70 && b3 === 70) return "woff";
|
|
226
|
+
if (b0 === 119 && b1 === 79 && b2 === 70 && b3 === 50) return "woff2";
|
|
227
|
+
if (buf.length >= 36 && buf[34] === 76 && buf[35] === 80) return "eot";
|
|
228
|
+
if (b0 === 128 && b1 === 1) return "pfb";
|
|
229
|
+
if (buf.length >= 14 && buf.slice(0, 14).toString("ascii") === "%!PS-AdobeFont") {
|
|
230
|
+
return "pfb";
|
|
231
|
+
}
|
|
232
|
+
return "unknown";
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
export {
|
|
236
|
+
rewriteFontFamilyName,
|
|
237
|
+
standardSubfamilyNames,
|
|
238
|
+
rewriteFontSubfamilyNames,
|
|
239
|
+
FONT_URL_ALLOWLIST,
|
|
240
|
+
isAllowedFontUrl,
|
|
241
|
+
detectFontFormat
|
|
242
|
+
};
|
|
243
|
+
//# sourceMappingURL=chunk-MCUIFSUN.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/fonts/sources/ttf-name.ts","../src/fonts/sources/url-allowlist.ts","../src/fonts/sources/format.ts"],"sourcesContent":["/**\n * Name-table rewriting for TTF/OTF sfnt fonts. Two public transforms share\n * the rebuild machinery:\n *\n * - `rewriteFontFamilyName` — rewrite `nameID` 1 / 4 / 6 / 16 to a\n * synthetic family name. Used by the preview-side font stagers so that\n * running-text references like `\"Inter Light\"` resolve to the correct\n * face when the stager registers it with Core Text / fontconfig / GDI\n * (all of which index by the font's internal `name` table rather than\n * the filename).\n *\n * - `rewriteFontSubfamilyNames` — rewrite `nameID` 2 / 17 to the standard\n * subfamily strings for a (weight, italic) pair. Used by the variable-\n * font instancer: harfbuzz preserves the source font's name records\n * verbatim, so an instanced Bold would otherwise keep the variable\n * font's default-instance \"Regular\" subfamily and trip\n * `validateFontMetadata`.\n *\n * Each transform rebuilds the whole font: new `name` table bytes, new\n * table directory with shifted offsets, recomputed per-table checksums,\n * and the magic `head.checkSumAdjustment` recomputed against the whole\n * output buffer. Nothing else is touched.\n *\n * OTF (CFF-flavoured) and TTF (glyf-flavoured) share the sfnt outer\n * structure, so the same code handles both.\n */\n\nconst FAMILY_NAME_IDS = new Set<number>([\n 1, // Font Family\n 4, // Full Font Name\n 6, // PostScript Name\n 16, // Typographic/Preferred Family\n]);\n\nconst MAGIC_HEAD_CHECKSUM = 0xb1b0afba;\n\ninterface NameRecord {\n platformID: number;\n encodingID: number;\n languageID: number;\n nameID: number;\n bytes: Buffer;\n}\n\ninterface TableEntry {\n tag: string;\n checksum: number;\n /** Assigned when rebuilding. */\n offset: number;\n data: Buffer;\n originalOffset: number;\n}\n\n/**\n * Compute the 32-bit big-endian uint sum of `buf`, treating it as a\n * stream of uint32s zero-padded to a 4-byte boundary. Used for per-table\n * checksums and the whole-font `head.checkSumAdjustment`.\n */\nfunction sfntChecksum(buf: Buffer): number {\n let sum = 0;\n const end = buf.length;\n const aligned = end - (end % 4);\n for (let i = 0; i < aligned; i += 4) {\n sum = (sum + buf.readUInt32BE(i)) >>> 0;\n }\n if (aligned < end) {\n let chunk = 0;\n const remaining = end - aligned;\n if (remaining >= 1) chunk |= buf[aligned] << 24;\n if (remaining >= 2) chunk |= buf[aligned + 1] << 16;\n if (remaining >= 3) chunk |= buf[aligned + 2] << 8;\n sum = (sum + chunk) >>> 0;\n }\n return sum >>> 0;\n}\n\nfunction encodeString(record: NameRecord, value: string): Buffer {\n // Platform 3 (Microsoft) and 0 (Unicode) use UTF-16 BE. Platform 1\n // (Macintosh) uses a legacy Roman encoding we approximate with ASCII —\n // non-ASCII family names are rare in this code path.\n if (record.platformID === 1) {\n return Buffer.from(value, 'ascii');\n }\n const out = Buffer.alloc(value.length * 2);\n for (let i = 0; i < value.length; i += 1) {\n out.writeUInt16BE(value.charCodeAt(i), i * 2);\n }\n return out;\n}\n\nfunction buildNameTable(records: NameRecord[]): Buffer {\n const count = records.length;\n const headerSize = 6 + count * 12;\n let heapSize = 0;\n for (const r of records) heapSize += r.bytes.length;\n const raw = Buffer.alloc(headerSize + heapSize);\n raw.writeUInt16BE(0, 0); // format 0\n raw.writeUInt16BE(count, 2);\n raw.writeUInt16BE(headerSize, 4); // stringOffset\n let heapCursor = 0;\n for (let i = 0; i < count; i += 1) {\n const r = records[i];\n const ro = 6 + i * 12;\n raw.writeUInt16BE(r.platformID, ro);\n raw.writeUInt16BE(r.encodingID, ro + 2);\n raw.writeUInt16BE(r.languageID, ro + 4);\n raw.writeUInt16BE(r.nameID, ro + 6);\n raw.writeUInt16BE(r.bytes.length, ro + 8);\n raw.writeUInt16BE(heapCursor, ro + 10);\n r.bytes.copy(raw, headerSize + heapCursor);\n heapCursor += r.bytes.length;\n }\n return raw;\n}\n\n/** Per-record decision for `rewriteNameTable`. */\ntype NameRewrite =\n | { action: 'keep' }\n | { action: 'drop' }\n | { action: 'set'; value: string };\n\n/**\n * Return a copy of `input` whose name records have been mapped through\n * `decide`. Returns the original buffer unchanged if the font has no\n * `name` table or the sfnt header is invalid.\n */\nfunction rewriteNameTable(\n input: Buffer,\n decide: (record: NameRecord) => NameRewrite\n): Buffer {\n if (input.length < 12) return input;\n const version = input.readUInt32BE(0);\n // Accept sfnt (0x00010000), OTTO (OpenType CFF), true, typ1.\n const isSfnt =\n version === 0x00010000 ||\n version === 0x4f54544f /* OTTO */ ||\n version === 0x74727565 /* true */ ||\n version === 0x74797031; /* typ1 */\n if (!isSfnt) return input;\n\n const numTables = input.readUInt16BE(4);\n if (numTables === 0 || input.length < 12 + numTables * 16) return input;\n\n // Read every table's directory entry, slurp its data.\n const tables: TableEntry[] = [];\n for (let i = 0; i < numTables; i += 1) {\n const eo = 12 + i * 16;\n const tag = input.toString('ascii', eo, eo + 4);\n const checksum = input.readUInt32BE(eo + 4);\n const offset = input.readUInt32BE(eo + 8);\n const length = input.readUInt32BE(eo + 12);\n if (offset + length > input.length) return input;\n tables.push({\n tag,\n checksum,\n offset: 0,\n originalOffset: offset,\n data: input.slice(offset, offset + length),\n });\n }\n\n const nameIdx = tables.findIndex((t) => t.tag === 'name');\n if (nameIdx === -1) return input;\n\n // Parse existing name records so we preserve all non-family entries.\n const nameBuf = tables[nameIdx].data;\n if (nameBuf.length < 6) return input;\n // `buildNameTable` emits format 0, which has no language-tag section. A\n // format-1 table keeps its tags after the name records, and any record with\n // languageID >= 0x8000 is an index into them — rewriting it as format 0\n // would strip the tags and leave those records pointing at nothing. Leaving\n // the font unstamped is the lesser loss, so hand it back untouched.\n // Preserving the tag section (and re-homing its string offsets) is the\n // follow-up if a real font ever needs the rewrite.\n if (nameBuf.readUInt16BE(0) !== 0) return input;\n const recordCount = nameBuf.readUInt16BE(2);\n const stringOffset = nameBuf.readUInt16BE(4);\n if (nameBuf.length < 6 + recordCount * 12) return input;\n\n const records: NameRecord[] = [];\n for (let i = 0; i < recordCount; i += 1) {\n const ro = 6 + i * 12;\n const platformID = nameBuf.readUInt16BE(ro);\n const encodingID = nameBuf.readUInt16BE(ro + 2);\n const languageID = nameBuf.readUInt16BE(ro + 4);\n const nameID = nameBuf.readUInt16BE(ro + 6);\n const length = nameBuf.readUInt16BE(ro + 8);\n const offset = nameBuf.readUInt16BE(ro + 10);\n const bytes = nameBuf.slice(\n stringOffset + offset,\n stringOffset + offset + length\n );\n records.push({ platformID, encodingID, languageID, nameID, bytes });\n }\n\n const survivors: NameRecord[] = [];\n for (const r of records) {\n const verdict = decide(r);\n if (verdict.action === 'drop') continue;\n if (verdict.action === 'set') {\n r.bytes = encodeString(r, verdict.value);\n }\n survivors.push(r);\n }\n records.length = 0;\n records.push(...survivors);\n\n const rebuiltName = buildNameTable(records);\n tables[nameIdx] = {\n ...tables[nameIdx],\n data: rebuiltName,\n checksum: sfntChecksum(rebuiltName),\n };\n\n // Preserve original physical order so tables whose offsets follow each\n // other stay contiguous (some consumers skim by offset rather than\n // directory). Offsets get reassigned either way — this is purely\n // aesthetic. Sort is stable.\n tables.sort((a, b) => a.originalOffset - b.originalOffset);\n\n // Assign new offsets, 4-byte aligned.\n let cursor = 12 + tables.length * 16;\n for (const t of tables) {\n cursor = (cursor + 3) & ~3;\n t.offset = cursor;\n cursor += t.data.length;\n }\n const totalSize = (cursor + 3) & ~3;\n\n const out = Buffer.alloc(totalSize);\n // Header — copy sfnt version, entrySelector, etc. verbatim; we preserve\n // numTables since we're not adding/removing entries.\n input.copy(out, 0, 0, 12);\n out.writeUInt16BE(tables.length, 4);\n\n // Directory entries go in alphabetical tag order per the sfnt spec.\n const dirTables = [...tables].sort((a, b) =>\n a.tag < b.tag ? -1 : a.tag > b.tag ? 1 : 0\n );\n for (let i = 0; i < dirTables.length; i += 1) {\n const t = dirTables[i];\n const eo = 12 + i * 16;\n out.write(t.tag, eo, 4, 'ascii');\n out.writeUInt32BE(t.checksum, eo + 4);\n out.writeUInt32BE(t.offset, eo + 8);\n out.writeUInt32BE(t.data.length, eo + 12);\n }\n\n // Write each table's bytes at its new offset. `out` is zero-filled, so\n // the 0–3 byte alignment padding after each table is already correct.\n for (const t of tables) {\n t.data.copy(out, t.offset);\n }\n\n // Recompute head.checkSumAdjustment. The algorithm: zero the field,\n // sum the whole font, then set the field to MAGIC - sum.\n const headTable = tables.find((t) => t.tag === 'head');\n if (headTable && headTable.data.length >= 12) {\n out.writeUInt32BE(0, headTable.offset + 8);\n const fontSum = sfntChecksum(out);\n const adjustment = (MAGIC_HEAD_CHECKSUM - fontSum) >>> 0;\n out.writeUInt32BE(adjustment, headTable.offset + 8);\n }\n\n return out;\n}\n\n/**\n * Return a copy of `input` whose name table has `nameID` 1/4/6/16 rewritten\n * to `newFamily`. Returns the original buffer unchanged if the font has no\n * `name` table or the sfnt header is invalid.\n */\nexport function rewriteFontFamilyName(\n input: Buffer,\n newFamily: string\n): Buffer {\n // PostScript names (nameID 6) are restricted to printable ASCII 33-126\n // minus `[](){}<>/%` per the OpenType spec — fold spaces out and strip\n // any forbidden chars so Word doesn't silently reject the font.\n const psForbidden = /[[\\](){}<>/%]/g;\n // eslint-disable-next-line no-control-regex\n const isAscii = /^[\\x00-\\x7f]*$/.test(newFamily);\n const psName = newFamily\n .replace(/\\s+/g, '')\n .replace(psForbidden, '')\n // eslint-disable-next-line no-control-regex\n .replace(/[^\\x21-\\x7e]/g, '');\n return rewriteNameTable(input, (r) => {\n if (!FAMILY_NAME_IDS.has(r.nameID)) return { action: 'keep' };\n // Platform 1 (Macintosh Roman) only round-trips ASCII. For non-ASCII\n // family names (e.g. CJK), `Buffer.from(value, 'ascii')` silently\n // drops the high bytes and produces garbled Roman-encoded strings\n // that Core Text may still index. Drop those records instead —\n // platforms 0 (Unicode) and 3 (Microsoft) carry the UTF-16 form and\n // are what modern consumers prefer anyway.\n if (r.platformID === 1 && !isAscii) return { action: 'drop' };\n return { action: 'set', value: r.nameID === 6 ? psName : newFamily };\n });\n}\n\nconst STANDARD_SUBFAMILY: Record<number, string> = {\n 100: 'Thin',\n 200: 'ExtraLight',\n 300: 'Light',\n 400: 'Regular',\n 500: 'Medium',\n 600: 'SemiBold',\n 700: 'Bold',\n 800: 'ExtraBold',\n 900: 'Black',\n};\n\n/**\n * Standard OpenType subfamily strings for a (weight, italic) pair, or null\n * for a non-standard weight. `typographic` is the nameID 17 form (full\n * weight vocabulary); `legacy` is the nameID 2 form, restricted to the\n * four-style RIBBI model (Regular/Italic/Bold/Bold Italic) that GDI-era\n * consumers expect. Single source of truth shared with\n * `validateFontMetadata` so writer and checker cannot diverge.\n */\nexport function standardSubfamilyNames(\n weight: number,\n italic: boolean\n): { typographic: string; legacy: string } | null {\n const base = STANDARD_SUBFAMILY[weight];\n if (!base) return null;\n return {\n typographic: italic ? `${base} Italic` : base,\n legacy:\n weight >= 600\n ? italic\n ? 'Bold Italic'\n : 'Bold'\n : italic\n ? 'Italic'\n : 'Regular',\n };\n}\n\n/**\n * Return a copy of `input` whose existing `nameID` 2/17 records carry the\n * standard subfamily strings for (weight, italic). Missing records are not\n * added. Returns the original buffer unchanged for non-standard weights,\n * or if the font has no `name` table or the sfnt header is invalid.\n */\nexport function rewriteFontSubfamilyNames(\n input: Buffer,\n weight: number,\n italic: boolean\n): Buffer {\n const std = standardSubfamilyNames(weight, italic);\n if (!std) return input;\n return rewriteNameTable(input, (r) => {\n if (r.nameID === 2) return { action: 'set', value: std.legacy };\n if (r.nameID === 17) return { action: 'set', value: std.typographic };\n return { action: 'keep' };\n });\n}\n","/**\n * Hostname allowlist for font fetchers.\n *\n * `url-fetcher` and `variable-fetcher` can be handed arbitrary URLs via\n * `FontRegistryEntry.sources`, which may originate from document JSON. Without\n * a guard, a malicious doc could point fetchers at internal hosts (SSRF), the\n * filesystem (`file://`), or the IMDS endpoint. Limit downloads to the hosts\n * our catalog + UPSTREAM_OVERRIDES actually target.\n *\n * Keep the list small and HTTPS-only. Expansions should be deliberate code\n * reviews, not config-driven — the cost of a new domain is the code change.\n */\n\nexport const FONT_URL_ALLOWLIST: readonly string[] = [\n 'fonts.gstatic.com',\n 'fonts.googleapis.com',\n 'cdn.jsdelivr.net',\n];\n\nexport function isAllowedFontUrl(url: string): boolean {\n let parsed: URL;\n try {\n parsed = new URL(url);\n } catch {\n return false;\n }\n if (parsed.protocol !== 'https:') return false;\n return FONT_URL_ALLOWLIST.includes(parsed.hostname.toLowerCase());\n}\n","/**\n * Font format detection from magic bytes.\n * Source: OpenType spec + WOFF1/WOFF2 W3C specs.\n */\n\nimport type { ResolvedFontSource } from '../types';\n\nexport function detectFontFormat(buf: Buffer): ResolvedFontSource['format'] {\n if (buf.length < 4) return 'unknown';\n\n const b0 = buf[0],\n b1 = buf[1],\n b2 = buf[2],\n b3 = buf[3];\n\n // TTF: 0x00010000 (SFNT) or 'true' (0x74727565) or 'typ1' (0x74797031)\n if (\n (b0 === 0x00 && b1 === 0x01 && b2 === 0x00 && b3 === 0x00) ||\n (b0 === 0x74 && b1 === 0x72 && b2 === 0x75 && b3 === 0x65) ||\n (b0 === 0x74 && b1 === 0x79 && b2 === 0x70 && b3 === 0x31)\n ) {\n return 'ttf';\n }\n // OTF: 'OTTO'\n if (b0 === 0x4f && b1 === 0x54 && b2 === 0x54 && b3 === 0x4f) return 'otf';\n // WOFF: 'wOFF'\n if (b0 === 0x77 && b1 === 0x4f && b2 === 0x46 && b3 === 0x46) return 'woff';\n // WOFF2: 'wOF2'\n if (b0 === 0x77 && b1 === 0x4f && b2 === 0x46 && b3 === 0x32) return 'woff2';\n // EOT: version bytes at offset 8-11 — rougher signature\n if (buf.length >= 36 && buf[34] === 0x4c && buf[35] === 0x50) return 'eot';\n // PostScript Type 1 (.pfb) — binary container marker byte 0x80 followed by\n // segment type 0x01 (ASCII). Also match the text-form ASCII header\n // \"%!PS-AdobeFont\". Note: .pfm (metric files) have no reliable magic and\n // stay in 'unknown' — same treatment (rejection at the loader).\n if (b0 === 0x80 && b1 === 0x01) return 'pfb';\n if (\n buf.length >= 14 &&\n buf.slice(0, 14).toString('ascii') === '%!PS-AdobeFont'\n ) {\n return 'pfb';\n }\n\n return 'unknown';\n}\n\n/**\n * Formats we detect but cannot legally embed in an OOXML document:\n * WOFF/WOFF2 are web-only containers; PostScript (.pfb) is explicitly\n * disallowed by Microsoft's embedding guidance.\n */\nexport const UNEMBEDDABLE_FORMATS = new Set<ResolvedFontSource['format']>([\n 'woff',\n 'woff2',\n 'pfb',\n]);\n"],"mappings":"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","names":[]}
|
|
@@ -166,11 +166,12 @@ var RendererRegistry = class {
|
|
|
166
166
|
/**
|
|
167
167
|
* Register a lazily-constructed renderer.
|
|
168
168
|
*
|
|
169
|
-
* The factory is async and only invoked on selection, so
|
|
170
|
-
*
|
|
169
|
+
* The factory is async and only invoked on selection, so choosing one
|
|
170
|
+
* renderer never imports another one's backend.
|
|
171
171
|
*/
|
|
172
172
|
register(id, factory) {
|
|
173
173
|
this.renderers.set(id, factory);
|
|
174
|
+
this.statusCache = void 0;
|
|
174
175
|
}
|
|
175
176
|
/** Renderer ids registered for this format, in registration order. */
|
|
176
177
|
ids() {
|
|
@@ -188,7 +189,7 @@ var RendererRegistry = class {
|
|
|
188
189
|
*
|
|
189
190
|
* An unknown id is `UnknownRendererError`, which carries the id asked for and
|
|
190
191
|
* the ones that exist, so a caller boundary can answer "bad request" rather
|
|
191
|
-
* than "the server broke". A
|
|
192
|
+
* than "the server broke". A backend that will not load is re-thrown with an
|
|
192
193
|
* actionable install hint.
|
|
193
194
|
*/
|
|
194
195
|
async resolve(id) {
|
|
@@ -203,6 +204,44 @@ var RendererRegistry = class {
|
|
|
203
204
|
throw enrichLoadFailure(error, this.format, selected);
|
|
204
205
|
}
|
|
205
206
|
}
|
|
207
|
+
/**
|
|
208
|
+
* Every registered renderer, with whether it can actually be loaded here.
|
|
209
|
+
*
|
|
210
|
+
* Answers the question by loading each one, which is the only answer that
|
|
211
|
+
* cannot be wrong — a resolver check would still miss a backend that
|
|
212
|
+
* resolves and then throws on import.
|
|
213
|
+
*
|
|
214
|
+
* Memoized, and the promise rather than its value, so concurrent callers
|
|
215
|
+
* share one probe instead of racing several. Nothing installs a package into
|
|
216
|
+
* a running process, so the answer cannot go stale within one; without this
|
|
217
|
+
* `jto_validate` would pay a package import on every call, which is the tool
|
|
218
|
+
* an agent uses after every edit.
|
|
219
|
+
*/
|
|
220
|
+
statuses() {
|
|
221
|
+
this.statusCache ??= this.probeStatuses();
|
|
222
|
+
return this.statusCache;
|
|
223
|
+
}
|
|
224
|
+
statusCache;
|
|
225
|
+
async probeStatuses() {
|
|
226
|
+
return Promise.all(
|
|
227
|
+
this.ids().map(async (id) => {
|
|
228
|
+
const base = { id, default: id === this.defaultId };
|
|
229
|
+
try {
|
|
230
|
+
await this.resolve(id);
|
|
231
|
+
return { ...base, available: true };
|
|
232
|
+
} catch (error) {
|
|
233
|
+
const missing = error instanceof Error && error.name === RENDERER_DEPENDENCY_MISSING;
|
|
234
|
+
const pkg = missing && error instanceof Error ? error.packageName : void 0;
|
|
235
|
+
return {
|
|
236
|
+
...base,
|
|
237
|
+
available: false,
|
|
238
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
239
|
+
...pkg ? { installHint: `pnpm add ${pkg}` } : {}
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
})
|
|
243
|
+
);
|
|
244
|
+
}
|
|
206
245
|
};
|
|
207
246
|
function enrichLoadFailure(error, format, rendererId) {
|
|
208
247
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -212,14 +251,17 @@ function enrichLoadFailure(error, format, rendererId) {
|
|
|
212
251
|
if (!isMissingModule) {
|
|
213
252
|
return error instanceof Error ? error : new Error(message);
|
|
214
253
|
}
|
|
215
|
-
const pkg = missingPackageName(message)
|
|
254
|
+
const pkg = missingPackageName(message);
|
|
255
|
+
const named = pkg ?? `the "${rendererId}" backend`;
|
|
216
256
|
const enriched = new Error(
|
|
217
|
-
`The "${rendererId}" ${format} renderer requires ${
|
|
257
|
+
`The "${rendererId}" ${format} renderer requires ${named}, which is not installed. Install it with: pnpm add ${named}
|
|
218
258
|
Original error: ${message}`
|
|
219
259
|
);
|
|
220
|
-
enriched.name =
|
|
260
|
+
enriched.name = RENDERER_DEPENDENCY_MISSING;
|
|
261
|
+
if (pkg) enriched.packageName = pkg;
|
|
221
262
|
return enriched;
|
|
222
263
|
}
|
|
264
|
+
var RENDERER_DEPENDENCY_MISSING = "RendererDependencyMissingError";
|
|
223
265
|
function missingPackageName(message) {
|
|
224
266
|
const match = /Cannot find (?:module|package) ['"]([^'"]+)['"]/.exec(message) ?? /Failed to resolve (?:module|import)[: ]+['"]?([^'"\s]+)/.exec(message);
|
|
225
267
|
return match?.[1];
|
|
@@ -674,6 +716,7 @@ export {
|
|
|
674
716
|
diagnoseUnsupportedFeatures,
|
|
675
717
|
assertRendererSupports,
|
|
676
718
|
RendererRegistry,
|
|
719
|
+
RENDERER_DEPENDENCY_MISSING,
|
|
677
720
|
CHART_WORKBOOK_SHEET_NAME,
|
|
678
721
|
CHART_PACKAGE_RELATIONSHIP,
|
|
679
722
|
CHART_WORKBOOK_CONTENT_TYPE,
|
|
@@ -688,4 +731,4 @@ export {
|
|
|
688
731
|
chartInputSignature,
|
|
689
732
|
matchChartParts
|
|
690
733
|
};
|
|
691
|
-
//# sourceMappingURL=chunk-
|
|
734
|
+
//# sourceMappingURL=chunk-QLZNOXT5.js.map
|