mirror of
https://github.com/penpot/penpot-exporter-figma-plugin.git
synced 2025-01-03 05:10:13 -05:00
Local Fonts (#81)
* moved validate font logic * google fonts working * fixes * minor improvements * fix linter * local fonts * fixes * Changeset * changeset * refactor * refactor * update branch * minor fix * move files around * try to refactor * refactor * add todo * refactor * refactor * refactor * refactor * refactor * refactor * refactor move files to their concrete location --------- Co-authored-by: Jordi Sala Morales <jordism91@gmail.com>
This commit is contained in:
parent
8021da2623
commit
58f7b0ab2c
21 changed files with 249 additions and 75 deletions
5
.changeset/sweet-months-speak.md
Normal file
5
.changeset/sweet-months-speak.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
"penpot-exporter": minor
|
||||||
|
---
|
||||||
|
|
||||||
|
Added support for Source Sans Pro font (penpot local font)
|
|
@ -1,18 +1,15 @@
|
||||||
import { transformFills } from '@plugin/transformers/partials';
|
import { transformFills } from '@plugin/transformers/partials';
|
||||||
import {
|
import { translateFills } from '@plugin/translators';
|
||||||
translateFills,
|
|
||||||
translateHorizontalAlign,
|
|
||||||
translateVerticalAlign
|
|
||||||
} from '@plugin/translators';
|
|
||||||
import {
|
import {
|
||||||
translateFontId,
|
translateFontId,
|
||||||
translateFontStyle,
|
translateFontStyle,
|
||||||
translateFontVariantId,
|
|
||||||
translateGrowType,
|
translateGrowType,
|
||||||
|
translateHorizontalAlign,
|
||||||
translateLetterSpacing,
|
translateLetterSpacing,
|
||||||
translateLineHeight,
|
translateLineHeight,
|
||||||
translateTextDecoration,
|
translateTextDecoration,
|
||||||
translateTextTransform
|
translateTextTransform,
|
||||||
|
translateVerticalAlign
|
||||||
} from '@plugin/translators/text';
|
} from '@plugin/translators/text';
|
||||||
|
|
||||||
import { TextStyle } from '@ui/lib/types/text/textContent';
|
import { TextStyle } from '@ui/lib/types/text/textContent';
|
||||||
|
@ -74,9 +71,8 @@ const transformTextStyle = (
|
||||||
>
|
>
|
||||||
): Partial<TextStyle> => {
|
): Partial<TextStyle> => {
|
||||||
return {
|
return {
|
||||||
|
...translateFontId(segment.fontName, segment.fontWeight),
|
||||||
fontFamily: segment.fontName.family,
|
fontFamily: segment.fontName.family,
|
||||||
fontId: translateFontId(segment.fontName),
|
|
||||||
fontVariantId: translateFontVariantId(segment.fontName, segment.fontWeight),
|
|
||||||
fontSize: segment.fontSize.toString(),
|
fontSize: segment.fontSize.toString(),
|
||||||
fontStyle: translateFontStyle(segment.fontName.style),
|
fontStyle: translateFontStyle(segment.fontName.style),
|
||||||
fontWeight: segment.fontWeight.toString(),
|
fontWeight: segment.fontWeight.toString(),
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
export * from './translateBlendMode';
|
export * from './translateBlendMode';
|
||||||
export * from './translateShadowEffects';
|
export * from './translateShadowEffects';
|
||||||
export * from './translateFills';
|
export * from './translateFills';
|
||||||
export * from './translateHorizontalAlign';
|
|
||||||
export * from './translateStrokes';
|
export * from './translateStrokes';
|
||||||
export * from './translateVectorPaths';
|
export * from './translateVectorPaths';
|
||||||
export * from './translateVerticalAlign';
|
|
||||||
|
|
1
plugin-src/translators/text/custom/index.ts
Normal file
1
plugin-src/translators/text/custom/index.ts
Normal file
|
@ -0,0 +1 @@
|
||||||
|
export * from './translateCustomFont';
|
17
plugin-src/translators/text/custom/translateCustomFont.ts
Normal file
17
plugin-src/translators/text/custom/translateCustomFont.ts
Normal file
|
@ -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, '')
|
||||||
|
};
|
||||||
|
};
|
11
plugin-src/translators/text/gfonts/googleFont.ts
Normal file
11
plugin-src/translators/text/gfonts/googleFont.ts
Normal file
|
@ -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;
|
||||||
|
};
|
3
plugin-src/translators/text/gfonts/index.ts
Normal file
3
plugin-src/translators/text/gfonts/index.ts
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
export * from './googleFont';
|
||||||
|
export * from './translateGoogleFont';
|
||||||
|
export * from './translateFontVariantId';
|
20
plugin-src/translators/text/gfonts/translateFontVariantId.ts
Normal file
20
plugin-src/translators/text/gfonts/translateFontVariantId.ts
Normal file
|
@ -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;
|
||||||
|
};
|
23
plugin-src/translators/text/gfonts/translateGoogleFont.ts
Normal file
23
plugin-src/translators/text/gfonts/translateGoogleFont.ts
Normal file
|
@ -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);
|
||||||
|
};
|
|
@ -1,7 +1,9 @@
|
||||||
export * from './translateFontIds';
|
export * from './translateFontId';
|
||||||
export * from './translateFontStyle';
|
export * from './translateFontStyle';
|
||||||
export * from './translateGrowType';
|
export * from './translateGrowType';
|
||||||
|
export * from './translateHorizontalAlign';
|
||||||
export * from './translateLetterSpacing';
|
export * from './translateLetterSpacing';
|
||||||
export * from './translateLineHeight';
|
export * from './translateLineHeight';
|
||||||
export * from './translateTextDecoration';
|
export * from './translateTextDecoration';
|
||||||
export * from './translateTextTransform';
|
export * from './translateTextTransform';
|
||||||
|
export * from './translateVerticalAlign';
|
||||||
|
|
3
plugin-src/translators/text/local/index.ts
Normal file
3
plugin-src/translators/text/local/index.ts
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
export * from './localFont';
|
||||||
|
export * from './translateLocalFont';
|
||||||
|
export * from './translateFontVariantId';
|
14
plugin-src/translators/text/local/localFont.ts
Normal file
14
plugin-src/translators/text/local/localFont.ts
Normal file
|
@ -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;
|
||||||
|
};
|
75
plugin-src/translators/text/local/localFonts.json
Normal file
75
plugin-src/translators/text/local/localFonts.json
Normal file
|
@ -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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
30
plugin-src/translators/text/local/translateFontVariantId.ts
Normal file
30
plugin-src/translators/text/local/translateFontVariantId.ts
Normal file
|
@ -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;
|
||||||
|
};
|
20
plugin-src/translators/text/local/translateLocalFont.ts
Normal file
20
plugin-src/translators/text/local/translateLocalFont.ts
Normal file
|
@ -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);
|
||||||
|
};
|
13
plugin-src/translators/text/translateFontId.ts
Normal file
13
plugin-src/translators/text/translateFontId.ts
Normal file
|
@ -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)
|
||||||
|
);
|
||||||
|
};
|
|
@ -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;
|
|
||||||
}
|
|
||||||
};
|
|
9
ui-src/lib/types/text/textContent.d.ts
vendored
9
ui-src/lib/types/text/textContent.d.ts
vendored
|
@ -28,10 +28,8 @@ type TextNode = {
|
||||||
key?: string;
|
key?: string;
|
||||||
} & TextStyle;
|
} & TextStyle;
|
||||||
|
|
||||||
type TextStyle = {
|
type TextStyle = FontId & {
|
||||||
fontId?: string;
|
|
||||||
fontFamily?: string;
|
fontFamily?: string;
|
||||||
fontVariantId?: string;
|
|
||||||
fontSize?: string;
|
fontSize?: string;
|
||||||
fontStyle?: TextFontStyle;
|
fontStyle?: TextFontStyle;
|
||||||
fontWeight?: string;
|
fontWeight?: string;
|
||||||
|
@ -46,3 +44,8 @@ type TextStyle = {
|
||||||
textDirection?: 'ltr' | 'rtl' | 'auto';
|
textDirection?: 'ltr' | 'rtl' | 'auto';
|
||||||
fills?: Fill[];
|
fills?: Fill[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type FontId = {
|
||||||
|
fontId?: string;
|
||||||
|
fontVariantId?: string;
|
||||||
|
};
|
||||||
|
|
Loading…
Reference in a new issue