Skip to content

Commit

Permalink
Text libraries (#185)
Browse files Browse the repository at this point in the history
* color library

* fixes

* wip

* wip

* wip

* wip

* working

* improvements

* changeset

* changeset

* changeset & cleaning

* changeset & cleaning

* improvements

* fixes

* rebase
  • Loading branch information
Cenadros authored Jun 25, 2024
1 parent a58f9e9 commit d3c144e
Show file tree
Hide file tree
Showing 42 changed files with 506 additions and 155 deletions.
5 changes: 5 additions & 0 deletions .changeset/heavy-timers-sit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"penpot-exporter": minor
---

Add support for typographies
5 changes: 5 additions & 0 deletions .changeset/mean-clouds-jog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"penpot-exporter": patch
---

Improve font weight translation
5 changes: 5 additions & 0 deletions .changeset/selfish-spies-cover.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"penpot-exporter": patch
---

Fix letter spacing
21 changes: 21 additions & 0 deletions plugin-src/TextLibrary.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
class TextLibrary {
private styles: Map<string, TextStyle | undefined> = new Map();

public register(id: string, styles?: TextStyle | undefined) {
this.styles.set(id, styles);
}

public get(id: string): TextStyle | undefined {
return this.styles.get(id);
}

public has(id: string): boolean {
return this.styles.has(id);
}

public all(): Record<string, TextStyle | undefined> {
return Object.fromEntries(this.styles.entries());
}
}

export const textLibrary = new TextLibrary();
14 changes: 3 additions & 11 deletions plugin-src/transformers/partials/transformFills.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
import { translateFillStyleId, translateFills } from '@plugin/translators/fills';
import { StyleTextSegment } from '@plugin/translators/text/paragraph';
import { TextSegment } from '@plugin/translators/text/paragraph';

import { ShapeAttributes } from '@ui/lib/types/shapes/shape';
import { TextStyle } from '@ui/lib/types/shapes/textShape';

export const transformFills = (
node:
| (MinimalFillsMixin & DimensionAndPositionMixin)
| VectorRegion
| VectorNode
| StyleTextSegment
node: (MinimalFillsMixin & DimensionAndPositionMixin) | VectorRegion | VectorNode | TextSegment
): Pick<ShapeAttributes, 'fills' | 'fillStyleId'> | Pick<TextStyle, 'fills' | 'fillStyleId'> => {
if (hasFillStyle(node)) {
return {
Expand Down Expand Up @@ -39,11 +35,7 @@ export const transformVectorFills = (
};

const hasFillStyle = (
node:
| (MinimalFillsMixin & DimensionAndPositionMixin)
| VectorRegion
| VectorNode
| StyleTextSegment
node: (MinimalFillsMixin & DimensionAndPositionMixin) | VectorRegion | VectorNode | TextSegment
): boolean => {
return (
node.fillStyleId !== figma.mixed &&
Expand Down
7 changes: 4 additions & 3 deletions plugin-src/transformers/partials/transformText.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { transformFills } from '@plugin/transformers/partials';
import { transformTextStyle, translateStyleTextSegments } from '@plugin/translators/text';
import { transformTextStyle, translateTextSegments } from '@plugin/translators/text';
import { translateGrowType, translateVerticalAlign } from '@plugin/translators/text/properties';

import { TextAttributes, TextShape } from '@ui/lib/types/shapes/textShape';
Expand All @@ -16,7 +16,8 @@ export const transformText = (node: TextNode): TextAttributes & Pick<TextShape,
'indentation',
'listOptions',
'fills',
'fillStyleId'
'fillStyleId',
'textStyleId'
]);

return {
Expand All @@ -30,7 +31,7 @@ export const transformText = (node: TextNode): TextAttributes & Pick<TextShape,
children: [
{
type: 'paragraph',
children: translateStyleTextSegments(node, styledTextSegments),
children: translateTextSegments(node, styledTextSegments),
...transformTextStyle(node, styledTextSegments[0]),
...transformFills(node)
}
Expand Down
61 changes: 56 additions & 5 deletions plugin-src/transformers/transformDocumentNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,26 @@ import { componentsLibrary } from '@plugin/ComponentLibrary';
import { imagesLibrary } from '@plugin/ImageLibrary';
import { remoteComponentLibrary } from '@plugin/RemoteComponentLibrary';
import { styleLibrary } from '@plugin/StyleLibrary';
import { textLibrary } from '@plugin/TextLibrary';
import { translateRemoteChildren } from '@plugin/translators';
import { translatePaintStyles } from '@plugin/translators/fills';
import { translatePaintStyle, translateTextStyle } from '@plugin/translators/styles';
import { sleep } from '@plugin/utils';

import { PenpotPage } from '@ui/lib/types/penpotPage';
import { TypographyStyle } from '@ui/lib/types/shapes/textShape';
import { FillStyle } from '@ui/lib/types/utils/fill';
import { PenpotDocument } from '@ui/types';

import { transformPageNode } from '.';

const isPaintStyle = (style: BaseStyle): style is PaintStyle => {
return style.type === 'PAINT';
};

const isTextStyle = (style: BaseStyle): style is TextStyle => {
return style.type === 'TEXT';
};

const downloadImages = async (): Promise<Record<string, Uint8Array>> => {
const imageToDownload = Object.entries(imagesLibrary.all());
const images: Record<string, Uint8Array> = {};
Expand Down Expand Up @@ -71,7 +81,7 @@ const getFillStyles = async (): Promise<Record<string, FillStyle>> => {
for (const [styleId, paintStyle] of stylesToFetch) {
const figmaStyle = paintStyle ?? (await figma.getStyleByIdAsync(styleId));
if (figmaStyle && isPaintStyle(figmaStyle)) {
styles[styleId] = translatePaintStyles(figmaStyle);
styles[styleId] = translatePaintStyle(figmaStyle);
}

figma.ui.postMessage({
Expand All @@ -87,8 +97,41 @@ const getFillStyles = async (): Promise<Record<string, FillStyle>> => {
return styles;
};

const isPaintStyle = (style: BaseStyle): style is PaintStyle => {
return style.type === 'PAINT';
const getTextStyles = async (): Promise<Record<string, TypographyStyle>> => {
const stylesToFetch = Object.entries(textLibrary.all());
const styles: Record<string, TypographyStyle> = {};

if (stylesToFetch.length === 0) return styles;

let currentStyle = 1;

figma.ui.postMessage({
type: 'PROGRESS_TOTAL_ITEMS',
data: stylesToFetch.length
});

figma.ui.postMessage({
type: 'PROGRESS_STEP',
data: 'typographies'
});

for (const [styleId, style] of stylesToFetch) {
const figmaStyle = style ?? (await figma.getStyleByIdAsync(styleId));
if (figmaStyle && isTextStyle(figmaStyle)) {
styles[styleId] = translateTextStyle(figmaStyle);
}

figma.ui.postMessage({
type: 'PROGRESS_PROCESSED_ITEMS',
data: currentStyle++
});

await sleep(0);
}

await sleep(20);

return styles;
};

const processPages = async (node: DocumentNode): Promise<PenpotPage[]> => {
Expand Down Expand Up @@ -122,6 +165,11 @@ export const transformDocumentNode = async (node: DocumentNode): Promise<PenpotD
styleLibrary.register(style.id, style);
});

const localTextStyles = await figma.getLocalTextStylesAsync();
localTextStyles.forEach(style => {
textLibrary.register(style.id, style);
});

const children = await processPages(node);

if (remoteComponentLibrary.remaining() > 0) {
Expand All @@ -135,11 +183,14 @@ export const transformDocumentNode = async (node: DocumentNode): Promise<PenpotD

const images = await downloadImages();

const typographies = await getTextStyles();

return {
name: node.name,
children,
components: componentsLibrary.all(),
images,
styles
styles,
typographies
};
};
1 change: 0 additions & 1 deletion plugin-src/translators/fills/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
export * from './translateFills';
export * from './translateImageFill';
export * from './translatePaintStyles';
export * from './translateSolidFill';
2 changes: 2 additions & 0 deletions plugin-src/translators/styles/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './translatePaintStyle';
export * from './translateTextStyle';
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,14 @@ import { translateFill } from '@plugin/translators/fills/translateFills';

import { FillStyle } from '@ui/lib/types/utils/fill';

export const translatePaintStyles = (figmaStyle: PaintStyle): FillStyle => {
export const translatePaintStyle = (figmaStyle: PaintStyle): FillStyle => {
const fillStyle: FillStyle = {
name: figmaStyle.name,
fills: [],
colors: []
};

const colorName = (figmaStyle: PaintStyle, index: number): string => {
// @TODO: Think something better
return figmaStyle.paints.length > 1 ? `Color ${index + 1}` : figmaStyle.name;
};

Expand Down
32 changes: 32 additions & 0 deletions plugin-src/translators/styles/translateTextStyle.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { translateFontName } from '@plugin/translators/text/font';
import {
translateFontStyle,
translateLetterSpacing,
translateLineHeight,
translateTextDecoration,
translateTextTransform
} from '@plugin/translators/text/properties';

import { TypographyStyle } from '@ui/lib/types/shapes/textShape';

export const translateTextStyle = (figmaStyle: TextStyle): TypographyStyle => {
const path = figmaStyle.remote ? 'Remote / ' : '';

return {
name: figmaStyle.name,
textStyle: {
...translateFontName(figmaStyle.fontName),
fontFamily: figmaStyle.fontName.family,
fontSize: figmaStyle.fontSize.toString(),
fontStyle: translateFontStyle(figmaStyle.fontName.style),
textDecoration: translateTextDecoration(figmaStyle),
letterSpacing: translateLetterSpacing(figmaStyle),
textTransform: translateTextTransform(figmaStyle),
lineHeight: translateLineHeight(figmaStyle)
},
typography: {
path,
name: figmaStyle.name
}
};
};
13 changes: 9 additions & 4 deletions plugin-src/translators/text/font/custom/translateCustomFont.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import { getCustomFontId, translateFontVariantId } from '@plugin/translators/text/font/custom';

import { FontId } from '@ui/lib/types/shapes/textShape';
import { TextTypography } from '@ui/lib/types/shapes/textShape';

export const translateCustomFont = (fontName: FontName, fontWeight: number): FontId | undefined => {
export const translateCustomFont = (
fontName: FontName,
fontWeight: string
): Pick<TextTypography, 'fontId' | 'fontVariantId' | 'fontWeight'> | undefined => {
const customFontId = getCustomFontId(fontName);
return {
fontId: `custom-${getCustomFontId(fontName)}`,
fontVariantId: translateFontVariantId(fontName, fontWeight)
fontId: customFontId ? `custom-${customFontId}` : '',
fontVariantId: translateFontVariantId(fontName, fontWeight),
fontWeight
};
};
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export const translateFontVariantId = (fontName: FontName, fontWeight: number) => {
export const translateFontVariantId = (fontName: FontName, fontWeight: string) => {
const style = fontName.style.toLowerCase().includes('italic') ? 'italic' : 'normal';

return `${style}-${fontWeight.toString()}`;
return `${style}-${fontWeight}`;
};
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { GoogleFont } from './googleFont';
export const translateFontVariantId = (
googleFont: GoogleFont,
fontName: FontName,
fontWeight: number
fontWeight: string
) => {
// check match directly by style
const variant = googleFont.variants?.find(variant => variant === fontName.style.toLowerCase());
Expand All @@ -13,7 +13,7 @@ export const translateFontVariantId = (
// check match by style and weight
const italic = fontName.style.toLowerCase().includes('italic') ? 'italic' : '';
const variantWithWeight = googleFont.variants?.find(
variant => variant === `${fontWeight.toString()}${italic}`
variant => variant === `${fontWeight}${italic}`
);

if (variantWithWeight !== undefined) return variantWithWeight;
Expand Down
10 changes: 7 additions & 3 deletions plugin-src/translators/text/font/gfonts/translateGoogleFont.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,25 @@ import slugify from 'slugify';
import { Cache } from '@plugin/Cache';
import { translateFontVariantId } from '@plugin/translators/text/font/gfonts';

import { FontId } from '@ui/lib/types/shapes/textShape';
import { TextTypography } from '@ui/lib/types/shapes/textShape';

import { items as gfonts } from './gfonts.json';
import { GoogleFont } from './googleFont';

const fontsCache = new Cache<string, GoogleFont>({ max: 30 });

export const translateGoogleFont = (fontName: FontName, fontWeight: number): FontId | undefined => {
export const translateGoogleFont = (
fontName: FontName,
fontWeight: string
): Pick<TextTypography, 'fontId' | 'fontVariantId' | 'fontWeight'> | undefined => {
const googleFont = getGoogleFont(fontName);

if (googleFont === undefined) return;

return {
fontId: `gfont-${slugify(fontName.family.toLowerCase())}`,
fontVariantId: translateFontVariantId(googleFont, fontName, fontWeight)
fontVariantId: translateFontVariantId(googleFont, fontName, fontWeight),
fontWeight
};
};

Expand Down
2 changes: 1 addition & 1 deletion plugin-src/translators/text/font/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export * from './translateFontId';
export * from './translateFontName';
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,12 @@ import { LocalFont } from './localFont';
export const translateFontVariantId = (
localFont: LocalFont,
fontName: FontName,
fontWeight: number
fontWeight: string
): 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')
variant => variant.weight === fontWeight && variant.style === (italic ? 'italic' : 'normal')
);

if (variantWithStyleWeight !== undefined) return variantWithStyleWeight.id;
Expand Down
10 changes: 7 additions & 3 deletions plugin-src/translators/text/font/local/translateLocalFont.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
import { LocalFont, translateFontVariantId } from '@plugin/translators/text/font/local';

import { FontId } from '@ui/lib/types/shapes/textShape';
import { TextTypography } from '@ui/lib/types/shapes/textShape';

import { items as localFonts } from './localFonts.json';

export const translateLocalFont = (fontName: FontName, fontWeight: number): FontId | undefined => {
export const translateLocalFont = (
fontName: FontName,
fontWeight: string
): Pick<TextTypography, 'fontId' | 'fontVariantId' | 'fontWeight'> | undefined => {
const localFont = getLocalFont(fontName);

if (localFont === undefined) return;

return {
fontId: localFont.id,
fontVariantId: translateFontVariantId(localFont, fontName, fontWeight)
fontVariantId: translateFontVariantId(localFont, fontName, fontWeight),
fontWeight
};
};

Expand Down
13 changes: 0 additions & 13 deletions plugin-src/translators/text/font/translateFontId.ts

This file was deleted.

Loading

0 comments on commit d3c144e

Please sign in to comment.