diff --git a/packages/core/src/kiwi/binary/codec.ts b/packages/core/src/kiwi/binary/codec.ts index e012b4f9a..85317cab4 100644 --- a/packages/core/src/kiwi/binary/codec.ts +++ b/packages/core/src/kiwi/binary/codec.ts @@ -351,6 +351,8 @@ export interface NodeChange { truncationStartIndex?: number truncatedHeight?: number } + styleType?: string + styleIdForText?: { guid?: GUID } textUserLayoutVersion?: number textExplicitLayoutVersion?: number textBidiVersion?: number diff --git a/packages/core/src/kiwi/fig/import.ts b/packages/core/src/kiwi/fig/import.ts index 36c9b94aa..e1da9fe74 100644 --- a/packages/core/src/kiwi/fig/import.ts +++ b/packages/core/src/kiwi/fig/import.ts @@ -349,6 +349,32 @@ function parseDocumentColorSpace(nodeChanges: NodeChange[]): 'srgb' | 'display-p return documentNode?.documentColorProfile === 'DISPLAY_P3' ? 'display-p3' : 'srgb' } +function applyTextStyleRefs(changeMap: Map): void { + const textStyleFields = [ + 'fontSize', + 'fontName', + 'lineHeight', + 'letterSpacing', + 'textDecoration', + 'textCase' + ] as const + + for (const nc of changeMap.values()) { + if (nc.type !== 'TEXT') continue + const styleGuid = nc.styleIdForText?.guid + if (!styleGuid) continue + const style = changeMap.get(guidToString(styleGuid)) + if (style?.type !== 'TEXT' || style.styleType !== 'TEXT') continue + for (const field of textStyleFields) { + if (field === 'textDecoration') { + nc.textDecoration = style.textDecoration + } else if (style[field] !== undefined) { + nc[field] = style[field] as never + } + } + } +} + export interface FigImportOptions { populate?: 'all' | 'first-page' } @@ -373,6 +399,7 @@ export function importNodeChanges( } const { changeMap, parentMap, childrenMap } = buildChangeMaps(nodeChanges) + applyTextStyleRefs(changeMap) const assetRefs = buildAssetRefMap(changeMap) setVariableColorResolver(buildVariableColorResolver(changeMap, assetRefs)) diff --git a/tests/engine/io/fig/import/legacy/text.test.ts b/tests/engine/io/fig/import/legacy/text.test.ts index 8f8352715..052a08df2 100644 --- a/tests/engine/io/fig/import/legacy/text.test.ts +++ b/tests/engine/io/fig/import/legacy/text.test.ts @@ -49,6 +49,32 @@ describe('fig-import: text properties', () => { expect(n.lineHeight).toBe(16.94) }) + test('applies shared text style refs', () => { + const graph = importNodeChanges([ + doc(), + canvas(), + node('TEXT', 20, 1, { + name: 'Body style', + styleType: 'TEXT', + fontSize: 16, + fontName: { family: 'Inter', style: 'Regular' }, + lineHeight: { value: 24, units: 'PIXELS' } + } as Partial), + node('TEXT', 10, 1, { + textData: { characters: 'Styled text' }, + fontSize: 48, + fontName: { family: 'Inter', style: 'Extra Bold' }, + textDecoration: 'UNDERLINE', + styleIdForText: { guid: { sessionID: 1, localID: 20 } } + } as Partial) + ]) + const styled = graph.getChildren(graph.getPages()[0].id).find((node) => node.text === 'Styled text') + expect(styled?.fontSize).toBe(16) + expect(styled?.fontWeight).toBe(400) + expect(styled?.lineHeight).toBe(24) + expect(styled?.textDecoration).toBe('NONE') + }) + test('font weight mapping', () => { const cases = [ ['Bold', 700],