Skip to content

Commit

Permalink
Initial commit.
Browse files Browse the repository at this point in the history
  • Loading branch information
charliecm committed Aug 10, 2019
0 parents commit f1c8d30
Show file tree
Hide file tree
Showing 6 changed files with 945 additions and 0 deletions.
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Fix San Francisco

**Warning:** This plugin won't fix the city's problems, but it will automatically apply the [correct font variant and tracking](https://developer.apple.com/design/human-interface-guidelines/ios/visual-design/typography/#font-usage-and-tracking) for selected texts using the San Francisco typeface.

Usually, you'd use the built-in text styles when designing for iOS. However, if your project needs to use custom font sizes, this plugin's got your back for a more accurate representation of how they're rendered in iOS.

Inspired by [Sketch-SF-UI-Font-Fixer](https://github.com/kylehickinson/Sketch-SF-UI-Font-Fixer).
129 changes: 129 additions & 0 deletions code.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
/**
* Fix San Francisco
* Fixes texts with SF typeface according to Apple's official tracking table:
* https://developer.apple.com/design/human-interface-guidelines/ios/visual-design/typography/#font-usage-and-tracking
*/
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
const FONT_DISPLAY = "SF Pro Display";
const FONT_TEXT = "SF Pro Text";
const SIZE_MIN = 6;
const SIZE_MAX = 79;
const SIZE_SWAP = 20;
const TRACKING_UNIT = 1000;
// Fills missing font size values inbetween defined values
function fillTracking(arr, min, max) {
let value = 0;
for (let i = min; i < max; i++) {
if (i in arr) {
value = arr[i];
continue;
}
arr[i] = value;
}
return arr;
}
const TRACKING_DISPLAY = fillTracking({
20: 19,
21: 17,
22: 16,
24: 15,
25: 14,
27: 13,
30: 12,
33: 11,
40: 10,
44: 9,
48: 8,
50: 7,
53: 6,
56: 5,
60: 4,
65: 3,
69: 2
}, SIZE_SWAP, SIZE_MAX);
const TRACKING_TEXT = fillTracking({
6: 41,
8: 26,
9: 19,
10: 12,
11: 6,
12: 0,
13: -6,
14: -11,
15: -16,
16: -20,
17: -24,
18: -25,
19: -26
}, SIZE_MIN, SIZE_SWAP);
function traverse() {
return __awaiter(this, void 0, void 0, function* () {
const nodes = figma.currentPage.selection;
let modifiedCount = 0;
for (let i = 0; i < nodes.length; i++) {
const node = nodes[i];
if (node.type !== "TEXT")
continue;
let isModified = false;
let fontFamily = node.fontName.family;
const fontSize = node.fontSize;
const letterSpacing = node.letterSpacing;
if (fontFamily !== FONT_DISPLAY && fontFamily !== FONT_TEXT)
continue;
if (fontFamily === FONT_DISPLAY && fontSize < SIZE_SWAP) {
fontFamily = FONT_TEXT;
isModified = true;
}
if (fontFamily === FONT_TEXT && fontSize >= SIZE_SWAP) {
fontFamily = FONT_DISPLAY;
isModified = true;
}
// Load and assign font family
const fontName = {
family: fontFamily,
style: node.fontName.style
};
yield figma.loadFontAsync(fontName);
node.fontName = fontName;
// Set tracking
switch (fontFamily) {
case FONT_DISPLAY:
if (fontSize >= SIZE_MAX) {
node.letterSpacing = {
value: 0,
unit: "PIXELS"
};
break;
}
node.letterSpacing = {
value: (fontSize * TRACKING_DISPLAY[fontSize]) / TRACKING_UNIT,
unit: "PIXELS"
};
break;
case FONT_TEXT:
node.letterSpacing = {
value: (fontSize * TRACKING_TEXT[Math.max(SIZE_MIN, fontSize)]) /
TRACKING_UNIT,
unit: "PIXELS"
};
break;
}
// Check if text node is modified
if (JSON.stringify(node.letterSpacing) !== JSON.stringify(letterSpacing))
isModified = true;
if (isModified)
modifiedCount++;
}
figma.closePlugin(modifiedCount
? `Updated ${modifiedCount} texts with SF typeface.`
: "No texts were updated.");
});
}
traverse();
143 changes: 143 additions & 0 deletions code.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
/**
* Fix San Francisco
* Fixes texts with SF typeface according to Apple's official tracking table:
* https://developer.apple.com/design/human-interface-guidelines/ios/visual-design/typography/#font-usage-and-tracking
*/

const FONT_DISPLAY = "SF Pro Display"
const FONT_TEXT = "SF Pro Text"

const SIZE_MIN = 6
const SIZE_MAX = 79
const SIZE_SWAP = 20
const TRACKING_UNIT = 1000

// Fills missing font size values inbetween defined values
function fillTracking(arr, min, max) {
let value = 0
for (let i = min; i < max; i++) {
if (i in arr) {
value = arr[i]
continue
}
arr[i] = value
}
return arr
}

const TRACKING_DISPLAY = fillTracking(
{
20: 19,
21: 17,
22: 16,
24: 15,
25: 14,
27: 13,
30: 12,
33: 11,
40: 10,
44: 9,
48: 8,
50: 7,
53: 6,
56: 5,
60: 4,
65: 3,
69: 2
},
SIZE_SWAP,
SIZE_MAX
)

const TRACKING_TEXT = fillTracking(
{
6: 41,
8: 26,
9: 19,
10: 12,
11: 6,
12: 0,
13: -6,
14: -11,
15: -16,
16: -20,
17: -24,
18: -25,
19: -26
},
SIZE_MIN,
SIZE_SWAP
)

async function traverse() {
const nodes = figma.currentPage.selection
let modifiedCount = 0

for (let i = 0; i < nodes.length; i++) {
const node = nodes[i]
if (node.type !== "TEXT") continue

let isModified = false
let fontFamily = (node.fontName as FontName).family
const fontSize = node.fontSize as number
const letterSpacing = node.letterSpacing

if (fontFamily !== FONT_DISPLAY && fontFamily !== FONT_TEXT) continue

if (fontFamily === FONT_DISPLAY && fontSize < SIZE_SWAP) {
fontFamily = FONT_TEXT
isModified = true
}

if (fontFamily === FONT_TEXT && fontSize >= SIZE_SWAP) {
fontFamily = FONT_DISPLAY
isModified = true
}

// Load and assign font family
const fontName = {
family: fontFamily,
style: (node.fontName as FontName).style
}
await figma.loadFontAsync(fontName)
node.fontName = fontName

// Set tracking
switch (fontFamily) {
case FONT_DISPLAY:
if (fontSize >= SIZE_MAX) {
node.letterSpacing = {
value: 0,
unit: "PIXELS"
}
break
}
node.letterSpacing = {
value: (fontSize * TRACKING_DISPLAY[fontSize]) / TRACKING_UNIT,
unit: "PIXELS"
}
break
case FONT_TEXT:
node.letterSpacing = {
value:
(fontSize * TRACKING_TEXT[Math.max(SIZE_MIN, fontSize)]) /
TRACKING_UNIT,
unit: "PIXELS"
}
break
}

// Check if text node is modified
if (JSON.stringify(node.letterSpacing) !== JSON.stringify(letterSpacing))
isModified = true
if (isModified) modifiedCount++
}

figma.closePlugin(
modifiedCount
? `Updated ${modifiedCount} texts with SF typeface.`
: "No texts were updated."
)
}

traverse()
Loading

0 comments on commit f1c8d30

Please sign in to comment.