Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 15 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ jobs:
name: Lint
runs-on: macos-15
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
lfs: true

- name: Setup mise environment
run: source Scripts/environment.sh
Expand All @@ -34,7 +36,9 @@ jobs:
DEVELOPER_DIR: "/Applications/Xcode_16.3.app/Contents/Developer"

steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
lfs: true

- name: Cache SPM dependencies
uses: actions/cache@v4
Expand Down Expand Up @@ -69,7 +73,15 @@ jobs:
container:
image: swift:6.0
steps:
- uses: actions/checkout@v5
- name: Install Git LFS
run: |
apt-get update
apt-get install -y git-lfs
git lfs install

- uses: actions/checkout@v6
with:
lfs: true

- name: Show Swift version
run: swift --version
Expand Down
21 changes: 15 additions & 6 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,16 @@ jobs:
container: ${{ matrix.container }}

steps:
- uses: actions/checkout@v5
- name: Install Git LFS (Linux)
if: matrix.platform == 'linux-x64'
run: |
apt-get update
apt-get install -y git-lfs
git lfs install

- uses: actions/checkout@v6
with:
lfs: true

- name: Select Xcode 16.3 (macOS)
if: matrix.platform == 'macos'
Expand All @@ -44,7 +53,6 @@ jobs:
- name: Install system dependencies (Linux)
if: matrix.platform == 'linux-x64'
run: |
apt-get update
apt-get install -y libcurl4-openssl-dev libxml2-dev libssl-dev

- name: Set version from tag
Expand All @@ -63,12 +71,13 @@ jobs:
- name: Create release archive (macOS)
if: matrix.platform == 'macos'
run: |
mkdir -p dist
mkdir -p dist/Libraries
cp .build/${{ matrix.build-path }}/exfig dist/ExFig
cp -r .build/${{ matrix.build-path }}/exfig_AndroidExport.bundle dist/
cp -r .build/${{ matrix.build-path }}/exfig_XcodeExport.bundle dist/
cp -r .build/${{ matrix.build-path }}/exfig_FlutterExport.bundle dist/
cp -r .build/${{ matrix.build-path }}/exfig_WebExport.bundle dist/
cp Libraries/macos/libresvg.dylib dist/Libraries/
cp LICENSE dist/
cd dist && zip -r ../${{ matrix.archive-name }}.zip .

Expand Down Expand Up @@ -98,7 +107,7 @@ jobs:
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
ref: main
fetch-depth: 0
Expand Down Expand Up @@ -131,7 +140,7 @@ jobs:
needs: [build, update-version]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
fetch-depth: 0

Expand Down Expand Up @@ -180,7 +189,7 @@ jobs:
echo "linux=${LINUX_SHA}" >> $GITHUB_OUTPUT

- name: Checkout Homebrew tap
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
repository: alexey1312/homebrew-exfig
token: ${{ secrets.HOMEBREW_TAP_TOKEN }}
Expand Down
45 changes: 45 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,51 @@ let loader = ImagesLoader(client: client, params: params, platform: .ios, logger

**Frame name resolution:** `entry.figmaFrameName` → `params.common?.images?.figmaFrameName` → `"Illustrations"`

### SVG Source Format

Images can use SVG as the source format from Figma API, with local rasterization using resvg for higher quality results:

```yaml
# iOS example - SVG source with PNG output
ios:
images:
- figmaFrameName: "Illustrations"
assetsFolder: "Illustrations"
sourceFormat: svg # Fetch SVG, rasterize locally to PNG
scales: [1, 2, 3]

# Android example - SVG source with WebP output
android:
images:
- figmaFrameName: "Illustrations"
output: "src/main/res/"
format: webp
sourceFormat: svg # Fetch SVG, rasterize locally to WebP
webpOptions:
encoding: lossless
```

**How it works:**

1. Figma API returns SVG instead of PNG
2. ExFig uses resvg (Rust library) to rasterize SVG locally
3. Output is encoded to PNG (iOS) or WebP (Android) at configured scales

**Benefits:**

- Higher quality than Figma's server-side PNG rendering
- Consistent results across all scales
- Smaller file sizes with lossless WebP

**Key files:**

| File | Purpose |
| ----------------------------------------------- | -------------------------------- |
| `Sources/Resvg/SvgRasterizer.swift` | Swift wrapper for resvg C API |
| `Sources/ExFig/Output/SvgToWebpConverter.swift` | SVG → WebP conversion |
| `Sources/ExFig/Output/SvgToPngConverter.swift` | SVG → PNG conversion |
| `Libraries/macos/libresvg.dylib` | Pre-built resvg universal binary |

### TerminalUI Usage

```swift
Expand Down
3 changes: 3 additions & 0 deletions Libraries/linux/libresvg.a
Git LFS file not shown
3 changes: 3 additions & 0 deletions Libraries/macos/libresvg.dylib
Git LFS file not shown
46 changes: 46 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ let package = Package(
"FlutterExport",
"WebExport",
"SVGKit",
"Resvg",
.product(name: "XcodeProj", package: "XcodeProj"),
.product(name: "ArgumentParser", package: "swift-argument-parser"),
.product(name: "Yams", package: "Yams"),
Expand All @@ -49,6 +50,51 @@ let package = Package(
]
),

// resvg C API bindings
.target(
name: "CResvg",
path: "Sources/CResvg",
publicHeadersPath: "include",
linkerSettings: [
// macOS: dynamic library with rpath
.unsafeFlags(
["-L", "Libraries/macos", "-lresvg"],
.when(platforms: [.macOS])
),
// rpath for debug build: .build/debug/exfig -> ../../Libraries/macos
.unsafeFlags(
["-Xlinker", "-rpath", "-Xlinker", "@executable_path/../../Libraries/macos"],
.when(platforms: [.macOS])
),
// rpath for release build: .build/apple/Products/Release/exfig -> ../../../../Libraries/macos
.unsafeFlags(
["-Xlinker", "-rpath", "-Xlinker", "@executable_path/../../../../Libraries/macos"],
.when(platforms: [.macOS])
),
// rpath for release distribution: ExFig + Libraries/libresvg.dylib
.unsafeFlags(
["-Xlinker", "-rpath", "-Xlinker", "@executable_path/Libraries"],
.when(platforms: [.macOS])
),
// rpath for test bundle: .build/*/debug/*.xctest/Contents/MacOS/*
.unsafeFlags(
["-Xlinker", "-rpath", "-Xlinker", "@executable_path/../../../../../../Libraries/macos"],
.when(platforms: [.macOS])
),
// Linux: static library (no rpath needed)
.unsafeFlags(
["-L", "Libraries/linux", "-lresvg"],
.when(platforms: [.linux])
),
]
),

// Swift wrapper for resvg
.target(
name: "Resvg",
dependencies: ["CResvg"]
),

// Shared target
.target(
name: "ExFigCore"
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
[![CI](https://github.com/alexey1312/ExFig/actions/workflows/ci.yml/badge.svg)](https://github.com/alexey1312/ExFig/actions/workflows/ci.yml)
[![Release](https://github.com/alexey1312/ExFig/actions/workflows/release.yml/badge.svg)](https://github.com/alexey1312/ExFig/actions/workflows/release.yml)
[![Docs](https://github.com/alexey1312/ExFig/actions/workflows/deploy-docc.yml/badge.svg)](https://alexey1312.github.io/ExFig/documentation/exfig)
![Coverage](https://img.shields.io/badge/coverage-49.43%25-yellow)
![Coverage](https://img.shields.io/badge/coverage-47.72%25-yellow)
[![License](https://img.shields.io/github/license/alexey1312/ExFig.svg)](LICENSE)

Command-line utility to export colors, typography, icons, and images from Figma to Xcode, Android Studio, Flutter, and
Expand Down
Loading
Loading