0
Fork 0
mirror of https://github.com/penpot/penpot-exporter-figma-plugin.git synced 2024-12-22 05:33:02 -05:00

Solid Fills (#39)

* structure for fills

* structure for fills in text and improvements

* fixes

* improvements
This commit is contained in:
Alex Sánchez 2024-04-17 15:27:53 +02:00 committed by GitHub
parent ebdf3ad9c3
commit e2b5b4a7ea
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 145 additions and 111 deletions

View file

@ -1,6 +1,8 @@
export * from './transformBlend';
export * from './transformChildren';
export * from './transformDimensionAndPosition';
export * from './transformFills';
export * from './transformSceneNode';
export * from './transformStrokes';
export * from './transformTextStyle';
export * from './transformVectorPaths';

View file

@ -0,0 +1,11 @@
import { translateFills } from '@plugin/translators';
import { ShapeAttributes } from '@ui/lib/types/shape/shapeAttributes';
export const transformFills = (
node: MinimalFillsMixin & DimensionAndPositionMixin
): Partial<ShapeAttributes> => {
return {
fills: translateFills(node.fills, node.width, node.height)
};
};

View file

@ -0,0 +1,29 @@
import { translateTextDecoration, translateTextTransform } from '@plugin/translators';
import { TextStyle } from '@ui/lib/types/text/textContent';
export const transformTextStyle = (
node: Pick<
StyledTextSegment,
| 'characters'
| 'start'
| 'end'
| 'fontName'
| 'fontSize'
| 'fontWeight'
| 'lineHeight'
| 'letterSpacing'
| 'textCase'
| 'textDecoration'
| 'fills'
>
): Partial<TextStyle> => {
return {
fontFamily: node.fontName.family,
fontSize: node.fontSize.toString(),
fontStyle: node.fontName.style,
fontWeight: node.fontWeight.toString(),
textDecoration: translateTextDecoration(node),
textTransform: translateTextTransform(node)
};
};

View file

@ -1,10 +1,10 @@
import {
transformBlend,
transformDimensionAndPosition,
transformFills,
transformSceneNode,
transformStrokes
} from '@plugin/transformers/partials';
import { translateFills } from '@plugin/translators';
import { CircleShape } from '@ui/lib/types/circle/circleShape';
@ -16,7 +16,7 @@ export const transformEllipseNode = (
return {
type: 'circle',
name: node.name,
fills: translateFills(node.fills, node.width, node.height),
...transformFills(node),
...transformStrokes(node),
...transformDimensionAndPosition(node, baseX, baseY),
...transformSceneNode(node),

View file

@ -1,11 +1,11 @@
import {
transformBlend,
transformChildren,
transformDimensionAndPosition,
transformFills,
transformSceneNode,
transformStrokes
} from '@plugin/transformers/partials';
import { transformChildren } from '@plugin/transformers/partials';
import { translateFills } from '@plugin/translators';
import { FrameShape } from '@ui/lib/types/frame/frameShape';
@ -21,7 +21,7 @@ export const transformFrameNode = async (
return {
type: 'frame',
name: node.name,
fills: translateFills(node.fills, node.width, node.height),
...transformFills(node),
// Figma API does not expose strokes for sections,
// they plan to add it in the future. Refactor this when available.
// @see: https://forum.figma.com/t/why-are-strokes-not-available-on-section-nodes/41658

View file

@ -1,14 +1,18 @@
import {
transformBlend,
transformDimensionAndPosition,
transformFills,
transformSceneNode,
transformStrokes,
transformVectorPaths
} from '@plugin/transformers/partials';
import { translateFills } from '@plugin/translators';
import { PathShape } from '@ui/lib/types/path/pathShape';
const hasFillGeometry = (node: VectorNode | StarNode | LineNode | PolygonNode): boolean => {
return 'fillGeometry' in node && node.fillGeometry.length > 0;
};
export const transformPathNode = (
node: VectorNode | StarNode | LineNode | PolygonNode,
baseX: number,
@ -16,7 +20,7 @@ export const transformPathNode = (
): PathShape => {
return {
name: node.name,
fills: node.fillGeometry.length ? translateFills(node.fills, node.width, node.height) : [],
...(hasFillGeometry(node) ? transformFills(node) : []),
...transformStrokes(node),
...transformVectorPaths(node, baseX, baseY),
...transformDimensionAndPosition(node, baseX, baseY),

View file

@ -1,10 +1,10 @@
import {
transformBlend,
transformDimensionAndPosition,
transformFills,
transformSceneNode,
transformStrokes
} from '@plugin/transformers/partials';
import { translateFills } from '@plugin/translators';
import { RectShape } from '@ui/lib/types/rect/rectShape';
@ -16,7 +16,7 @@ export const transformRectangleNode = (
return {
type: 'rect',
name: node.name,
fills: translateFills(node.fills, node.width, node.height),
...transformFills(node),
...transformStrokes(node),
...transformDimensionAndPosition(node, baseX, baseY),
...transformSceneNode(node),

View file

@ -1,15 +1,12 @@
import {
transformBlend,
transformDimensionAndPosition,
transformSceneNode
transformFills,
transformSceneNode,
transformTextStyle
} from '@plugin/transformers/partials';
import {
translateFills,
translateTextDecoration,
translateTextTransform
} from '@plugin/translators';
import { translateStyledTextSegments } from '@plugin/translators';
import { TextNode as PenpotTextNode } from '@ui/lib/types/text/textContent';
import { TextShape } from '@ui/lib/types/text/textShape';
export const transformTextNode = (node: TextNode, baseX: number, baseY: number): TextShape => {
@ -24,21 +21,6 @@ export const transformTextNode = (node: TextNode, baseX: number, baseY: number):
'fills'
]);
const children: PenpotTextNode[] = styledTextSegments.map(segment => {
figma.ui.postMessage({ type: 'FONT_NAME', data: segment.fontName.family });
return {
text: segment.characters,
fills: translateFills(segment.fills, node.width, node.height),
fontFamily: segment.fontName.family,
fontSize: segment.fontSize.toString(),
fontStyle: segment.fontName.style,
fontWeight: segment.fontWeight.toString(),
textDecoration: translateTextDecoration(segment),
textTransform: translateTextTransform(segment)
};
});
return {
type: 'text',
name: node.name,
@ -50,14 +32,9 @@ export const transformTextNode = (node: TextNode, baseX: number, baseY: number):
children: [
{
type: 'paragraph',
fills: translateFills(node.fills, node.width, node.height),
fontFamily: children[0].fontFamily,
fontSize: children[0].fontSize,
fontStyle: children[0].fontStyle,
fontWeight: children[0].fontWeight,
textDecoration: children[0].textDecoration,
textTransform: children[0].textTransform,
children: children
children: translateStyledTextSegments(styledTextSegments, node.width, node.height),
...(styledTextSegments.length ? transformTextStyle(styledTextSegments[0]) : {}),
...transformFills(node)
}
]
}

View file

@ -1,8 +1,7 @@
export * from './translateBlendMode';
export * from './translateFills';
export * from './translateGradientLinearFill';
export * from './translateSolidFill';
export * from './translateStrokes';
export * from './translateStyledTextSegments';
export * from './translateTextDecoration';
export * from './translateTextTransform';
export * from './translateVectorPaths';

View file

@ -1,7 +1,7 @@
import { Fill } from '@ui/lib/types/utils/fill';
import { rgbToHex } from '@plugin/utils';
import { calculateLinearGradient } from '@plugin/utils/calculateLinearGradient';
import { translateGradientLinearFill } from './translateGradientLinearFill';
import { translateSolidFill } from './translateSolidFill';
import { Fill } from '@ui/lib/types/utils/fill';
export const translateFill = (fill: Paint, width: number, height: number): Fill | undefined => {
switch (fill.type) {
@ -19,19 +19,51 @@ export const translateFills = (
width: number,
height: number
): Fill[] => {
// @TODO: think better variable name
// @TODO: make it work with figma.mixed
const fills2 = fills === figma.mixed ? [] : fills;
const figmaFills = fills === figma.mixed ? [] : fills;
const penpotFills: Fill[] = [];
const penpotFills = [];
for (const fill of fills2) {
for (const fill of figmaFills) {
const penpotFill = translateFill(fill, width, height);
if (penpotFill) {
// colors are applied in reverse order in Figma, that's why we unshift
penpotFills.unshift(penpotFill);
}
}
return penpotFills;
};
const translateSolidFill = (fill: SolidPaint): Fill => {
return {
fillColor: rgbToHex(fill.color),
fillOpacity: !fill.visible ? 0 : fill.opacity
};
};
const translateGradientLinearFill = (fill: GradientPaint, width: number, height: number): Fill => {
const points = calculateLinearGradient(width, height, fill.gradientTransform);
return {
fillColorGradient: {
type: 'linear',
startX: points.start[0] / width,
startY: points.start[1] / height,
endX: points.end[0] / width,
endY: points.end[1] / height,
width: 1,
stops: [
{
color: rgbToHex(fill.gradientStops[0].color),
offset: fill.gradientStops[0].position,
opacity: fill.gradientStops[0].color.a * (fill.opacity ?? 1)
},
{
color: rgbToHex(fill.gradientStops[1].color),
offset: fill.gradientStops[1].position,
opacity: fill.gradientStops[1].color.a * (fill.opacity ?? 1)
}
]
},
fillOpacity: fill.visible === false ? 0 : undefined
};
};

View file

@ -1,36 +0,0 @@
import { rgbToHex } from '@plugin/utils';
import { calculateLinearGradient } from '@plugin/utils/calculateLinearGradient';
import { Fill } from '@ui/lib/types/utils/fill';
export const translateGradientLinearFill = (
fill: GradientPaint,
width: number,
height: number
): Fill => {
const points = calculateLinearGradient(width, height, fill.gradientTransform);
return {
fillColorGradient: {
type: 'linear',
startX: points.start[0] / width,
startY: points.start[1] / height,
endX: points.end[0] / width,
endY: points.end[1] / height,
width: 1,
stops: [
{
color: rgbToHex(fill.gradientStops[0].color),
offset: fill.gradientStops[0].position,
opacity: fill.gradientStops[0].color.a * (fill.opacity ?? 1)
},
{
color: rgbToHex(fill.gradientStops[1].color),
offset: fill.gradientStops[1].position,
opacity: fill.gradientStops[1].color.a * (fill.opacity ?? 1)
}
]
},
fillOpacity: fill.visible === false ? 0 : undefined
};
};

View file

@ -1,10 +0,0 @@
import { rgbToHex } from '@plugin/utils';
import { Fill } from '@ui/lib/types/utils/fill';
export const translateSolidFill = (fill: SolidPaint): Fill => {
return {
fillColor: rgbToHex(fill.color),
fillOpacity: fill.visible === false ? 0 : fill.opacity
};
};

View file

@ -0,0 +1,33 @@
import { transformTextStyle } from '@plugin/transformers/partials';
import { translateFills } from '@plugin/translators/translateFills';
import { TextNode } from '@ui/lib/types/text/textContent';
export const translateStyledTextSegments = (
segments: Pick<
StyledTextSegment,
| 'characters'
| 'start'
| 'end'
| 'fontName'
| 'fontSize'
| 'fontWeight'
| 'lineHeight'
| 'letterSpacing'
| 'textCase'
| 'textDecoration'
| 'fills'
>[],
width: number,
height: number
): TextNode[] => {
return segments.map(segment => {
figma.ui.postMessage({ type: 'FONT_NAME', data: segment.fontName.family });
return {
fills: translateFills(segment.fills, width, height),
text: segment.characters,
...transformTextStyle(segment)
};
});
};

View file

@ -11,5 +11,5 @@ export type FrameAttributes = {
hideFillOnExport?: boolean;
showContent?: boolean;
hideInViewer?: boolean;
fills: Fill[];
fills?: Fill[];
};

View file

@ -15,30 +15,23 @@ type ParagraphSet = {
type Paragraph = {
type: 'paragraph';
key?: string;
fills?: Fill[];
fontFamily?: string;
fontSize?: string;
fontStyle?: string;
fontWeight?: string;
direction?: string;
textDecoration?: string;
textTransform?: string;
typographyRefId?: string;
typographyRefFile?: string;
children: TextNode[];
};
} & TextStyle;
type TextNode = {
text: string;
key?: string;
fills?: Fill[];
} & TextStyle;
type TextStyle = {
fontFamily?: string;
fontSize?: string;
fontStyle?: string;
fontWeight?: string;
direction?: string;
textDecoration?: string;
textTransform?: string;
direction?: string;
typographyRefId?: string;
typographyRefFile?: string;
fills?: Fill[];
};