Skip to content

Commit

Permalink
Optimize images before generating zip file (#141)
Browse files Browse the repository at this point in the history
* Optimize images before generating zip file

* add changelog

* refactor

* fix lint
  • Loading branch information
jordisala1991 authored Jun 5, 2024
1 parent 4b711b3 commit 3094f05
Show file tree
Hide file tree
Showing 45 changed files with 323 additions and 222 deletions.
5 changes: 5 additions & 0 deletions .changeset/pretty-plants-kick.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"penpot-exporter": minor
---

Optimize images before generating zip file
12 changes: 5 additions & 7 deletions plugin-src/ImageLibrary.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
import { ImageColor } from '@ui/lib/types/utils/imageColor';

class ImageLibrary {
private images: Record<string, ImageColor> = {};
private images: Record<string, Image | null> = {};

public register(hash: string, image: ImageColor) {
public register(hash: string, image: Image | null) {
this.images[hash] = image;
}

public get(hash: string): ImageColor | undefined {
public get(hash: string): Image | null | undefined {
return this.images[hash];
}

public all(): Record<string, ImageColor> {
public all(): Record<string, Image | null> {
return this.images;
}

public init(images: Record<string, ImageColor>): void {
public init(images: Record<string, Image | null>): void {
this.images = images;
}
}
Expand Down
6 changes: 3 additions & 3 deletions plugin-src/transformers/partials/transformFills.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import { translateFills } from '@plugin/translators/fills';

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

export const transformFills = async (
export const transformFills = (
node: MinimalFillsMixin & DimensionAndPositionMixin
): Promise<Pick<ShapeAttributes, 'fills'>> => {
): Pick<ShapeAttributes, 'fills'> => {
return {
fills: await translateFills(node.fills)
fills: translateFills(node.fills)
};
};
12 changes: 6 additions & 6 deletions plugin-src/transformers/partials/transformStrokes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ const hasFillGeometry = (node: GeometryMixin): boolean => {
return node.fillGeometry.length > 0;
};

export const transformStrokes = async (
export const transformStrokes = (
node: GeometryMixin | (GeometryMixin & IndividualStrokesMixin)
): Promise<Pick<ShapeAttributes, 'strokes'>> => {
): Pick<ShapeAttributes, 'strokes'> => {
const vectorNetwork = isVectorLike(node) ? node.vectorNetwork : undefined;

const strokeCaps = (stroke: Stroke) => {
Expand All @@ -30,15 +30,15 @@ export const transformStrokes = async (
};

return {
strokes: await translateStrokes(node, strokeCaps)
strokes: translateStrokes(node, strokeCaps)
};
};

export const transformStrokesFromVector = async (
export const transformStrokesFromVector = (
node: VectorNode,
vector: Command[],
vectorRegion: VectorRegion | undefined
): Promise<Pick<ShapeAttributes, 'strokes'>> => {
): Pick<ShapeAttributes, 'strokes'> => {
const strokeCaps = (stroke: Stroke) => {
if (vectorRegion !== undefined) return stroke;

Expand All @@ -54,7 +54,7 @@ export const transformStrokesFromVector = async (
};

return {
strokes: await translateStrokes(node, strokeCaps)
strokes: translateStrokes(node, strokeCaps)
};
};

Expand Down
8 changes: 3 additions & 5 deletions plugin-src/transformers/partials/transformText.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@ import { translateGrowType, translateVerticalAlign } from '@plugin/translators/t

import { TextAttributes, TextShape } from '@ui/lib/types/shapes/textShape';

export const transformText = async (
node: TextNode
): Promise<TextAttributes & Pick<TextShape, 'growType'>> => {
export const transformText = (node: TextNode): TextAttributes & Pick<TextShape, 'growType'> => {
const styledTextSegments = node.getStyledTextSegments([
'fontName',
'fontSize',
Expand All @@ -31,9 +29,9 @@ export const transformText = async (
children: [
{
type: 'paragraph',
children: await translateStyleTextSegments(node, styledTextSegments),
children: translateStyleTextSegments(node, styledTextSegments),
...transformTextStyle(node, styledTextSegments[0]),
...(await transformFills(node))
...transformFills(node)
}
]
}
Expand Down
60 changes: 24 additions & 36 deletions plugin-src/transformers/partials/transformVectorPaths.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,40 +31,30 @@ export const transformVectorPathsAsContent = (
};
};

export const transformVectorPaths = async (
export const transformVectorPaths = (
node: VectorNode,
baseX: number,
baseY: number
): Promise<PathShape[]> => {
const pathShapes = await Promise.all(
node.vectorPaths
.filter((vectorPath, index) => {
return (
nodeHasFills(node, vectorPath, (node.vectorNetwork.regions ?? [])[index]) ||
node.strokes.length > 0
);
})
.map((vectorPath, index) =>
transformVectorPath(
node,
vectorPath,
(node.vectorNetwork.regions ?? [])[index],
baseX,
baseY
)
)
);
): PathShape[] => {
const pathShapes = node.vectorPaths
.filter((vectorPath, index) => {
return (
nodeHasFills(node, vectorPath, (node.vectorNetwork.regions ?? [])[index]) ||
node.strokes.length > 0
);
})
.map((vectorPath, index) =>
transformVectorPath(node, vectorPath, (node.vectorNetwork.regions ?? [])[index], baseX, baseY)
);

const geometryShapes = await Promise.all(
node.fillGeometry
.filter(
geometry =>
!node.vectorPaths.find(
vectorPath => normalizePath(vectorPath.data) === normalizePath(geometry.data)
)
)
.map(geometry => transformVectorPath(node, geometry, undefined, baseX, baseY))
);
const geometryShapes = node.fillGeometry
.filter(
geometry =>
!node.vectorPaths.find(
vectorPath => normalizePath(vectorPath.data) === normalizePath(geometry.data)
)
)
.map(geometry => transformVectorPath(node, geometry, undefined, baseX, baseY));

return [...geometryShapes, ...pathShapes];
};
Expand Down Expand Up @@ -96,27 +86,25 @@ const nodeHasFills = (
return !!(vectorPath.windingRule !== 'NONE' && (vectorRegion?.fills || node.fills));
};

const transformVectorPath = async (
const transformVectorPath = (
node: VectorNode,
vectorPath: VectorPath,
vectorRegion: VectorRegion | undefined,
baseX: number,
baseY: number
): Promise<PathShape> => {
): PathShape => {
const normalizedPaths = parseSVG(vectorPath.data);

return {
type: 'path',
name: 'svg-path',
content: translateCommandsToSegments(normalizedPaths, baseX + node.x, baseY + node.y),
fills:
vectorPath.windingRule === 'NONE'
? []
: await translateFills(vectorRegion?.fills ?? node.fills),
vectorPath.windingRule === 'NONE' ? [] : translateFills(vectorRegion?.fills ?? node.fills),
svgAttrs: {
fillRule: translateWindingRule(vectorPath.windingRule)
},
...(await transformStrokesFromVector(node, normalizedPaths, vectorRegion)),
...transformStrokesFromVector(node, normalizedPaths, vectorRegion),
...transformEffects(node),
...transformDimensionAndPositionFromVectorPath(vectorPath, baseX, baseY),
...transformSceneNode(node),
Expand Down
4 changes: 2 additions & 2 deletions plugin-src/transformers/transformBooleanNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ export const transformBooleanNode = async (
boolType: translateBoolType(node.booleanOperation),
...transformFigmaIds(node),
...(await transformChildren(node, baseX, baseY)),
...(await transformFills(node)),
...transformFills(node),
...transformEffects(node),
...(await transformStrokes(node)),
...transformStrokes(node),
...transformDimensionAndPosition(node, baseX, baseY),
...transformSceneNode(node),
...transformBlend(node),
Expand Down
4 changes: 2 additions & 2 deletions plugin-src/transformers/transformComponentNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ export const transformComponentNode = async (
name: node.name,
path: node.parent?.type === 'COMPONENT_SET' ? node.parent.name : '',
...transformFigmaIds(node),
...(await transformFills(node)),
...transformFills(node),
...transformEffects(node),
...(await transformStrokes(node)),
...transformStrokes(node),
...transformSceneNode(node),
...transformBlend(node),
...transformProportion(node),
Expand Down
12 changes: 11 additions & 1 deletion plugin-src/transformers/transformDocumentNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,20 @@ export const transformDocumentNode = async (node: DocumentNode): Promise<PenpotD
await sleep(0);
}

const images: Record<string, Uint8Array> = {};

for (const [key, image] of Object.entries(imagesLibrary.all())) {
const bytes = await image?.getBytesAsync();

if (!bytes) continue;

images[key] = bytes;
}

return {
name: node.name,
children,
components: componentsLibrary.all(),
images: imagesLibrary.all()
images
};
};
8 changes: 4 additions & 4 deletions plugin-src/transformers/transformEllipseNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,18 @@ import {

import { CircleShape } from '@ui/lib/types/shapes/circleShape';

export const transformEllipseNode = async (
export const transformEllipseNode = (
node: EllipseNode,
baseX: number,
baseY: number
): Promise<CircleShape> => {
): CircleShape => {
return {
type: 'circle',
name: node.name,
...transformFigmaIds(node),
...(await transformFills(node)),
...transformFills(node),
...transformEffects(node),
...(await transformStrokes(node)),
...transformStrokes(node),
...transformDimension(node),
...transformRotationAndPosition(node, baseX, baseY),
...transformSceneNode(node),
Expand Down
4 changes: 2 additions & 2 deletions plugin-src/transformers/transformFrameNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export const transformFrameNode = async (
// they plan to add it in the future. Refactor this when available.
frameSpecificAttributes = {
// @see: https://forum.figma.com/t/why-are-strokes-not-available-on-section-nodes/41658
...(await transformStrokes(node)),
...transformStrokes(node),
// @see: https://forum.figma.com/t/add-a-blendmode-property-for-sectionnode/58560
...transformBlend(node),
...transformProportion(node),
Expand All @@ -43,7 +43,7 @@ export const transformFrameNode = async (
name: node.name,
showContent: isSectionNode(node) ? true : !node.clipsContent,
...transformFigmaIds(node),
...(await transformFills(node)),
...transformFills(node),
...frameSpecificAttributes,
...(await transformChildren(node, baseX + node.x, baseY + node.y)),
...transformDimensionAndPosition(node, baseX, baseY),
Expand Down
7 changes: 5 additions & 2 deletions plugin-src/transformers/transformInstanceNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ export const transformInstanceNode = async (
mainComponentFigmaId: mainComponent.id,
isComponentRoot: isComponentRoot(node),
...transformFigmaIds(node),
...(await transformFills(node)),
...transformFills(node),
...transformEffects(node),
...(await transformStrokes(node)),
...transformStrokes(node),
...transformSceneNode(node),
...transformBlend(node),
...transformProportion(node),
Expand Down Expand Up @@ -60,11 +60,14 @@ const isUnprocessableComponent = (mainComponent: ComponentNode): boolean => {

const isComponentRoot = (node: InstanceNode): boolean => {
let parent = node.parent;

while (parent !== null) {
if (parent.type === 'COMPONENT' || parent.type === 'INSTANCE') {
return false;
}

parent = parent.parent;
}

return true;
};
8 changes: 4 additions & 4 deletions plugin-src/transformers/transformPathNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,17 @@ const hasFillGeometry = (node: StarNode | LineNode | PolygonNode): boolean => {
return 'fillGeometry' in node && node.fillGeometry.length > 0;
};

export const transformPathNode = async (
export const transformPathNode = (
node: StarNode | LineNode | PolygonNode,
baseX: number,
baseY: number
): Promise<PathShape> => {
): PathShape => {
return {
type: 'path',
name: node.name,
...transformFigmaIds(node),
...(hasFillGeometry(node) ? await transformFills(node) : []),
...(await transformStrokes(node)),
...(hasFillGeometry(node) ? transformFills(node) : []),
...transformStrokes(node),
...transformEffects(node),
...transformVectorPathsAsContent(node, baseX, baseY),
...transformDimensionAndPosition(node, baseX, baseY),
Expand Down
8 changes: 4 additions & 4 deletions plugin-src/transformers/transformRectangleNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,18 @@ import {

import { RectShape } from '@ui/lib/types/shapes/rectShape';

export const transformRectangleNode = async (
export const transformRectangleNode = (
node: RectangleNode,
baseX: number,
baseY: number
): Promise<RectShape> => {
): RectShape => {
return {
type: 'rect',
name: node.name,
...transformFigmaIds(node),
...(await transformFills(node)),
...transformFills(node),
...transformEffects(node),
...(await transformStrokes(node)),
...transformStrokes(node),
...transformDimension(node),
...transformRotationAndPosition(node, baseX, baseY),
...transformSceneNode(node),
Expand Down
10 changes: 5 additions & 5 deletions plugin-src/transformers/transformSceneNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@ export const transformSceneNode = async (

switch (node.type) {
case 'RECTANGLE':
penpotNode = await transformRectangleNode(node, baseX, baseY);
penpotNode = transformRectangleNode(node, baseX, baseY);
break;
case 'ELLIPSE':
penpotNode = await transformEllipseNode(node, baseX, baseY);
penpotNode = transformEllipseNode(node, baseX, baseY);
break;
case 'SECTION':
case 'FRAME':
Expand All @@ -41,15 +41,15 @@ export const transformSceneNode = async (
penpotNode = await transformGroupNode(node, baseX, baseY);
break;
case 'TEXT':
penpotNode = await transformTextNode(node, baseX, baseY);
penpotNode = transformTextNode(node, baseX, baseY);
break;
case 'VECTOR':
penpotNode = await transformVectorNode(node, baseX, baseY);
penpotNode = transformVectorNode(node, baseX, baseY);
break;
case 'STAR':
case 'POLYGON':
case 'LINE':
penpotNode = await transformPathNode(node, baseX, baseY);
penpotNode = transformPathNode(node, baseX, baseY);
break;
case 'BOOLEAN_OPERATION':
penpotNode = await transformBooleanNode(node, baseX, baseY);
Expand Down
Loading

0 comments on commit 3094f05

Please sign in to comment.