0
Fork 0
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:
Alex Sánchez 2024-04-30 08:00:11 +02:00 committed by GitHub
parent 8021da2623
commit 58f7b0ab2c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 249 additions and 75 deletions

View file

@ -0,0 +1,5 @@
---
"penpot-exporter": minor
---
Added support for Source Sans Pro font (penpot local font)

View file

@ -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(),

View file

@ -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';

View file

@ -0,0 +1 @@
export * from './translateCustomFont';

View 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, '')
};
};

View 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;
};

View file

@ -0,0 +1,3 @@
export * from './googleFont';
export * from './translateGoogleFont';
export * from './translateFontVariantId';

View 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;
};

View 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);
};

View file

@ -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';

View file

@ -0,0 +1,3 @@
export * from './localFont';
export * from './translateLocalFont';
export * from './translateFontVariantId';

View 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;
};

View 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"
}
]
}
]
}

View 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;
};

View 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);
};

View 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)
);
};

View file

@ -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;
}
};

View file

@ -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;
};