diff --git a/.changeset/sweet-months-speak.md b/.changeset/sweet-months-speak.md new file mode 100644 index 0000000..b18f049 --- /dev/null +++ b/.changeset/sweet-months-speak.md @@ -0,0 +1,5 @@ +--- +"penpot-exporter": minor +--- + +Added support for Source Sans Pro font (penpot local font) diff --git a/plugin-src/transformers/partials/transformText.ts b/plugin-src/transformers/partials/transformText.ts index d5421b2..57db0f3 100644 --- a/plugin-src/transformers/partials/transformText.ts +++ b/plugin-src/transformers/partials/transformText.ts @@ -1,18 +1,15 @@ import { transformFills } from '@plugin/transformers/partials'; -import { - translateFills, - translateHorizontalAlign, - translateVerticalAlign -} from '@plugin/translators'; +import { translateFills } from '@plugin/translators'; import { translateFontId, translateFontStyle, - translateFontVariantId, translateGrowType, + translateHorizontalAlign, translateLetterSpacing, translateLineHeight, translateTextDecoration, - translateTextTransform + translateTextTransform, + translateVerticalAlign } from '@plugin/translators/text'; import { TextStyle } from '@ui/lib/types/text/textContent'; @@ -74,9 +71,8 @@ const transformTextStyle = ( > ): Partial => { return { + ...translateFontId(segment.fontName, segment.fontWeight), fontFamily: segment.fontName.family, - fontId: translateFontId(segment.fontName), - fontVariantId: translateFontVariantId(segment.fontName, segment.fontWeight), fontSize: segment.fontSize.toString(), fontStyle: translateFontStyle(segment.fontName.style), fontWeight: segment.fontWeight.toString(), diff --git a/plugin-src/translators/index.ts b/plugin-src/translators/index.ts index 1110239..c359ca9 100644 --- a/plugin-src/translators/index.ts +++ b/plugin-src/translators/index.ts @@ -1,7 +1,5 @@ export * from './translateBlendMode'; export * from './translateShadowEffects'; export * from './translateFills'; -export * from './translateHorizontalAlign'; export * from './translateStrokes'; export * from './translateVectorPaths'; -export * from './translateVerticalAlign'; diff --git a/plugin-src/translators/text/custom/index.ts b/plugin-src/translators/text/custom/index.ts new file mode 100644 index 0000000..5e609ae --- /dev/null +++ b/plugin-src/translators/text/custom/index.ts @@ -0,0 +1 @@ +export * from './translateCustomFont'; diff --git a/plugin-src/translators/text/custom/translateCustomFont.ts b/plugin-src/translators/text/custom/translateCustomFont.ts new file mode 100644 index 0000000..eda5915 --- /dev/null +++ b/plugin-src/translators/text/custom/translateCustomFont.ts @@ -0,0 +1,17 @@ +import slugify from 'slugify'; + +import { FontId } from '@ui/lib/types/text/textContent'; + +/** + * @TODO: implement custom font loading for Penpot + */ +export const translateCustomFont = (fontName: FontName): FontId | undefined => { + // For now display a message in the UI, so the user knows + // that the file is using a custom font not present in Penpot + figma.ui.postMessage({ type: 'FONT_NAME', data: fontName.family }); + + return { + fontId: slugify(fontName.family.toLowerCase()), + fontVariantId: fontName.style.toLowerCase().replace(/\s/g, '') + }; +}; diff --git a/plugin-src/gfonts.json b/plugin-src/translators/text/gfonts/gfonts.json similarity index 100% rename from plugin-src/gfonts.json rename to plugin-src/translators/text/gfonts/gfonts.json diff --git a/plugin-src/translators/text/gfonts/googleFont.ts b/plugin-src/translators/text/gfonts/googleFont.ts new file mode 100644 index 0000000..e5b4bf5 --- /dev/null +++ b/plugin-src/translators/text/gfonts/googleFont.ts @@ -0,0 +1,11 @@ +export type GoogleFont = { + family: string; + variants?: string[]; + subsets?: string[]; + version: string; + lastModified: string; + files?: { [key: string]: string | undefined }; + category: string; + kind: string; + menu: string; +}; diff --git a/plugin-src/translators/text/gfonts/index.ts b/plugin-src/translators/text/gfonts/index.ts new file mode 100644 index 0000000..342dc47 --- /dev/null +++ b/plugin-src/translators/text/gfonts/index.ts @@ -0,0 +1,3 @@ +export * from './googleFont'; +export * from './translateGoogleFont'; +export * from './translateFontVariantId'; diff --git a/plugin-src/translators/text/gfonts/translateFontVariantId.ts b/plugin-src/translators/text/gfonts/translateFontVariantId.ts new file mode 100644 index 0000000..fe1fd3e --- /dev/null +++ b/plugin-src/translators/text/gfonts/translateFontVariantId.ts @@ -0,0 +1,20 @@ +import { GoogleFont } from './googleFont'; + +export const translateFontVariantId = ( + googleFont: GoogleFont, + fontName: FontName, + fontWeight: number +) => { + // check match directly by style + const variant = googleFont.variants?.find(variant => variant === fontName.style.toLowerCase()); + + if (variant !== undefined) return variant; + + // check match by style and weight + const italic = fontName.style.toLowerCase().includes('italic') ? 'italic' : ''; + const variantWithWeight = googleFont.variants?.find( + variant => variant === `${fontWeight.toString()}${italic}` + ); + + if (variantWithWeight !== undefined) return variantWithWeight; +}; diff --git a/plugin-src/translators/text/gfonts/translateGoogleFont.ts b/plugin-src/translators/text/gfonts/translateGoogleFont.ts new file mode 100644 index 0000000..d57a295 --- /dev/null +++ b/plugin-src/translators/text/gfonts/translateGoogleFont.ts @@ -0,0 +1,23 @@ +import slugify from 'slugify'; + +import { translateFontVariantId } from '@plugin/translators/text/gfonts'; + +import { FontId } from '@ui/lib/types/text/textContent'; + +import { items as gfonts } from './gfonts.json'; +import { GoogleFont } from './googleFont'; + +export const translateGoogleFont = (fontName: FontName, fontWeight: number): FontId | undefined => { + const googleFont = getGoogleFont(fontName); + + if (googleFont === undefined) return; + + return { + fontId: `gfont-${slugify(fontName.family.toLowerCase())}`, + fontVariantId: translateFontVariantId(googleFont, fontName, fontWeight) + }; +}; + +const getGoogleFont = (fontName: FontName): GoogleFont | undefined => { + return gfonts.find(font => font.family === fontName.family); +}; diff --git a/plugin-src/translators/text/index.ts b/plugin-src/translators/text/index.ts index e68f3ec..9cdbedc 100644 --- a/plugin-src/translators/text/index.ts +++ b/plugin-src/translators/text/index.ts @@ -1,7 +1,9 @@ -export * from './translateFontIds'; +export * from './translateFontId'; export * from './translateFontStyle'; export * from './translateGrowType'; +export * from './translateHorizontalAlign'; export * from './translateLetterSpacing'; export * from './translateLineHeight'; export * from './translateTextDecoration'; export * from './translateTextTransform'; +export * from './translateVerticalAlign'; diff --git a/plugin-src/translators/text/local/index.ts b/plugin-src/translators/text/local/index.ts new file mode 100644 index 0000000..e0b4a28 --- /dev/null +++ b/plugin-src/translators/text/local/index.ts @@ -0,0 +1,3 @@ +export * from './localFont'; +export * from './translateLocalFont'; +export * from './translateFontVariantId'; diff --git a/plugin-src/translators/text/local/localFont.ts b/plugin-src/translators/text/local/localFont.ts new file mode 100644 index 0000000..c3a9a5d --- /dev/null +++ b/plugin-src/translators/text/local/localFont.ts @@ -0,0 +1,14 @@ +export type LocalFont = { + id: string; + name: string; + family: string; + variants?: Variant[]; +}; + +type Variant = { + id: string; + name: string; + weight: string; + style: string; + suffix?: string; +}; diff --git a/plugin-src/translators/text/local/localFonts.json b/plugin-src/translators/text/local/localFonts.json new file mode 100644 index 0000000..fa76585 --- /dev/null +++ b/plugin-src/translators/text/local/localFonts.json @@ -0,0 +1,75 @@ +{ + "items": [ + { + "id": "sourcesanspro", + "name": "Source Sans Pro", + "family": "sourcesanspro", + "variants": [ + { + "id": "200", + "name": "200", + "weight": "200", + "style": "normal", + "suffix": "extralight" + }, + { + "id": "200italic", + "name": "200 (italic)", + "weight": "200", + "style": "italic", + "suffix": "extralightitalic" + }, + { + "id": "300", + "name": "300", + "weight": "300", + "style": "normal", + "suffix": "light" + }, + { + "id": "300italic", + "name": "300 (italic)", + "weight": "300", + "style": "italic", + "suffix": "lightitalic" + }, + { + "id": "regular", + "name": "regular", + "weight": "400", + "style": "normal" + }, + { + "id": "italic", + "name": "italic", + "weight": "400", + "style": "italic" + }, + { + "id": "bold", + "name": "bold", + "weight": "bold", + "style": "normal" + }, + { + "id": "bolditalic", + "name": "bold (italic)", + "weight": "bold", + "style": "italic" + }, + { + "id": "black", + "name": "black", + "weight": "900", + "style": "normal" + }, + { + "id": "blackitalic", + "name": "black (italic)", + "weight": "900", + "style": "italic" + } + ] + } + ] +} diff --git a/plugin-src/translators/text/local/translateFontVariantId.ts b/plugin-src/translators/text/local/translateFontVariantId.ts new file mode 100644 index 0000000..b8c220b --- /dev/null +++ b/plugin-src/translators/text/local/translateFontVariantId.ts @@ -0,0 +1,30 @@ +import { LocalFont } from './localFont'; + +export const translateFontVariantId = ( + localFont: LocalFont, + fontName: FontName, + fontWeight: number +): string | undefined => { + // check match by style and weight + const italic = fontName.style.toLowerCase().includes('italic'); + const variantWithStyleWeight = localFont.variants?.find( + variant => + variant.weight === fontWeight.toString() && variant.style === (italic ? 'italic' : 'normal') + ); + + if (variantWithStyleWeight !== undefined) return variantWithStyleWeight.id; + + // check match directly by suffix if exists + const variant = localFont.variants?.find( + variant => variant.suffix === fontName.style.toLowerCase().replace(/\s/g, '') + ); + + if (variant !== undefined) return variant.id; + + // check match directly by id + const variantById = localFont.variants?.find( + variant => variant.id === fontName.style.toLowerCase().replace(/\s/g, '') + ); + + if (variantById !== undefined) return variantById.id; +}; diff --git a/plugin-src/translators/text/local/translateLocalFont.ts b/plugin-src/translators/text/local/translateLocalFont.ts new file mode 100644 index 0000000..959ee8a --- /dev/null +++ b/plugin-src/translators/text/local/translateLocalFont.ts @@ -0,0 +1,20 @@ +import { LocalFont, translateFontVariantId } from '@plugin/translators/text/local'; + +import { FontId } from '@ui/lib/types/text/textContent'; + +import { items as localFonts } from './localFonts.json'; + +export const translateLocalFont = (fontName: FontName, fontWeight: number): FontId | undefined => { + const localFont = getLocalFont(fontName); + + if (localFont === undefined) return; + + return { + fontId: localFont.id, + fontVariantId: translateFontVariantId(localFont, fontName, fontWeight) + }; +}; + +const getLocalFont = (fontName: FontName): LocalFont | undefined => { + return localFonts.find(localFont => localFont.name === fontName.family); +}; diff --git a/plugin-src/translators/text/translateFontId.ts b/plugin-src/translators/text/translateFontId.ts new file mode 100644 index 0000000..6eeda67 --- /dev/null +++ b/plugin-src/translators/text/translateFontId.ts @@ -0,0 +1,13 @@ +import { FontId } from '@ui/lib/types/text/textContent'; + +import { translateCustomFont } from './custom'; +import { translateGoogleFont } from './gfonts'; +import { translateLocalFont } from './local'; + +export const translateFontId = (fontName: FontName, fontWeight: number): FontId | undefined => { + return ( + translateGoogleFont(fontName, fontWeight) ?? + translateLocalFont(fontName, fontWeight) ?? + translateCustomFont(fontName) + ); +}; diff --git a/plugin-src/translators/text/translateFontIds.ts b/plugin-src/translators/text/translateFontIds.ts deleted file mode 100644 index 4c91383..0000000 --- a/plugin-src/translators/text/translateFontIds.ts +++ /dev/null @@ -1,60 +0,0 @@ -import slugify from 'slugify'; - -import { items as gfonts } from '@plugin/gfonts.json'; - -export const translateFontId = (fontName: FontName): string => { - if (isGfont(fontName)) { - return `gfont-${slugify(fontName.family.toLowerCase())}`; - } - - // @TODO: check if source sans pro - - // always send font name if not gfont or source sans pro - figma.ui.postMessage({ type: 'FONT_NAME', data: fontName.family }); - - // @TODO: custom font - return slugify(fontName.family.toLowerCase()); -}; - -export const translateFontVariantId = (fontName: FontName, fontWeight: number) => { - const variantId = translateGfontVariantId(fontName, fontWeight); - if (variantId !== undefined) { - return variantId; - } - - // @TODO: Custom font - // @TODO: Source Sans pro - return fontName.style.toLowerCase().replace(/\s/g, ''); -}; - -const findGoogleFont = (fontName: FontName) => { - return gfonts.find(font => font.family === fontName.family); -}; - -const isGfont = (fontName: FontName): boolean => { - return findGoogleFont(fontName) !== undefined; -}; - -const translateGfontVariantId = (fontName: FontName, fontWeight: number): string | undefined => { - const gfont = findGoogleFont(fontName); - - if (gfont === undefined) { - return; - } - - // check match directly by style - const variant = gfont.variants.find(variant => variant === fontName.style.toLowerCase()); - if (variant !== undefined) { - return variant; - } - - // check match by style and weight - const italic = fontName.style.toLowerCase().includes('italic') ? 'italic' : ''; - const variantWithWeight = gfont.variants.find( - variant => variant === `${fontWeight.toString()}${italic}` - ); - - if (variantWithWeight !== undefined) { - return variantWithWeight; - } -}; diff --git a/plugin-src/translators/translateHorizontalAlign.ts b/plugin-src/translators/text/translateHorizontalAlign.ts similarity index 100% rename from plugin-src/translators/translateHorizontalAlign.ts rename to plugin-src/translators/text/translateHorizontalAlign.ts diff --git a/plugin-src/translators/translateVerticalAlign.ts b/plugin-src/translators/text/translateVerticalAlign.ts similarity index 100% rename from plugin-src/translators/translateVerticalAlign.ts rename to plugin-src/translators/text/translateVerticalAlign.ts diff --git a/ui-src/lib/types/text/textContent.d.ts b/ui-src/lib/types/text/textContent.d.ts index 02815af..79cd25d 100644 --- a/ui-src/lib/types/text/textContent.d.ts +++ b/ui-src/lib/types/text/textContent.d.ts @@ -28,10 +28,8 @@ type TextNode = { key?: string; } & TextStyle; -type TextStyle = { - fontId?: string; +type TextStyle = FontId & { fontFamily?: string; - fontVariantId?: string; fontSize?: string; fontStyle?: TextFontStyle; fontWeight?: string; @@ -46,3 +44,8 @@ type TextStyle = { textDirection?: 'ltr' | 'rtl' | 'auto'; fills?: Fill[]; }; + +export type FontId = { + fontId?: string; + fontVariantId?: string; +};