@zseven-w/pen-figma
v0.7.6
Published
Figma .fig file parser and converter for OpenPencil
Downloads
2,061
Readme
@zseven-w/pen-figma
Figma .fig file parser and converter for OpenPencil. Import Figma designs directly into the OpenPencil document model — binary file parsing, multi-page support, clipboard paste, and full style conversion.
Install
npm install @zseven-w/pen-figma
# or
bun add @zseven-w/pen-figmaOverview
This package handles the complete pipeline from Figma's proprietary binary format to OpenPencil's PenDocument:
.fig binary → Kiwi schema decode → FigmaNodeChange[] → tree building → PenNode[] → PenDocumentIt supports:
- Binary
.figfiles (Kiwi schema + zstd/zip compression) - Figma clipboard HTML (copy from Figma → paste in OpenPencil)
- All node types: frames, groups, components, instances, shapes, text, vectors, images
- Full style conversion: fills, strokes, effects, gradients, auto-layout, typography
Usage
Parse a .fig file
import { parseFigFile, figmaAllPagesToPenDocument } from '@zseven-w/pen-figma';
const buffer = await fs.readFile('design.fig');
const figFile = parseFigFile(buffer);
const document = figmaAllPagesToPenDocument(figFile);
console.log(`Imported ${document.pages?.length} pages`);Single page import
import { parseFigFile, getFigmaPages, figmaToPenDocument } from '@zseven-w/pen-figma';
const figFile = parseFigFile(buffer);
const pages = getFigmaPages(figFile);
// Import just the first page
const document = figmaToPenDocument(figFile, pages[0]);Clipboard paste
Detect and convert Figma clipboard data (when users copy from Figma and paste into OpenPencil):
import {
isFigmaClipboardHtml,
extractFigmaClipboardData,
figmaClipboardToNodes,
} from '@zseven-w/pen-figma';
document.addEventListener('paste', (e) => {
const html = e.clipboardData?.getData('text/html');
if (html && isFigmaClipboardHtml(html)) {
const data = extractFigmaClipboardData(html);
const nodes = figmaClipboardToNodes(data);
// Insert nodes into document...
}
});Convert individual nodes
For lower-level access (e.g., incremental sync):
import { figmaNodeChangesToPenNodes } from '@zseven-w/pen-figma';
const penNodes = figmaNodeChangesToPenNodes(figmaNodeChanges, options);Icon resolution
Register an icon lookup function for converting Figma component instances to icon nodes:
import { setIconLookup } from '@zseven-w/pen-figma';
setIconLookup((name) => {
// Return SVG path data for the icon name, or null
return iconRegistry[name]?.path ?? null;
});Image resolution
Resolve embedded image blob references to data URLs:
import { resolveImageBlobs } from '@zseven-w/pen-figma';
const images = resolveImageBlobs(figFile);
// Map<blobHash, dataURL>Conversion Coverage
Node Types
| Figma Type | PenNode Type | Notes |
| ---------- | ------------ | ------------------------------------------- |
| FRAME | frame | With auto-layout, constraints, clip content |
| GROUP | group | Preserves child transforms |
| COMPONENT | frame | Marked as reusable |
| INSTANCE | frame | Resolved with overrides |
| RECTANGLE | rectangle | Corner radius (uniform + per-corner) |
| ELLIPSE | ellipse | Arc support |
| LINE | line | Stroke properties |
| TEXT | text | Rich text with styled segments |
| VECTOR | path | Complex paths, boolean ops, stars, polygons |
| IMAGE | image | Embedded or referenced |
Styles
| Figma Style | Conversion |
| ---------------- | --------------------------------------------------------------------- |
| Solid fills | PenFill with solid type |
| Linear gradients | PenFill with gradient stops + angle |
| Radial gradients | PenFill with center + radius |
| Image fills | PenFill with image source |
| Strokes | PenStroke with cap, join, dash |
| Drop shadows | PenEffect shadow |
| Inner shadows | PenEffect inner shadow |
| Blur | PenEffect blur |
| Auto-layout | layout, gap, padding, justifyContent, alignItems |
| Text styles | fontSize, fontWeight, fontFamily, lineHeight, letterSpacing |
| Rich text | StyledTextSegment[] with per-run styles |
API Reference
| Function | Description |
| ------------------------------------- | --------------------------------------------- |
| parseFigFile(buffer) | Parse binary .fig file → FigmaDecodedFile |
| figmaAllPagesToPenDocument(file) | Convert all pages → PenDocument |
| figmaToPenDocument(file, page) | Convert single page → PenDocument |
| getFigmaPages(file) | List available pages |
| figmaNodeChangesToPenNodes(changes) | Convert raw node changes → PenNode[] |
| isFigmaClipboardHtml(html) | Detect Figma clipboard data |
| extractFigmaClipboardData(html) | Extract base64 .fig from clipboard HTML |
| figmaClipboardToNodes(data) | Convert clipboard data → PenNode[] |
| resolveImageBlobs(file) | Resolve image references → data URLs |
| setIconLookup(fn) | Register icon name → SVG path resolver |
