Skip to content
This repository was archived by the owner on Jul 13, 2024. It is now read-only.

Commit c3adf2d

Browse files
committed
#30 wip base svg-to-dtif structure
1 parent 5fccc9c commit c3adf2d

26 files changed

+468
-41
lines changed

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
"chalk": "^5.2.0",
2929
"concurrently": "^8.1.0",
3030
"esbuild": "^0.17.19",
31+
"esno": "^0.17.0",
3132
"execa": "^7.1.1",
3233
"prettier": "^2.8.7",
3334
"rollup": "^3.21.7",
@@ -37,6 +38,7 @@
3738
"rollup-plugin-import-css": "^3.2.1",
3839
"rollup-plugin-node-externals": "^6.0.1",
3940
"rollup-plugin-postcss": "^4.0.2",
41+
"rollup-plugin-typescript-paths": "^1.4.0",
4042
"rollup-plugin-visualizer": "^5.9.0",
4143
"shx": "^0.3.4",
4244
"turbo": "^1.9.3",

packages/dtif-to-svg/README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,12 @@
11
# `dtif-to-svg`
22
Convert DTIF to SVG using D3.js.
3+
4+
## Why `opentype.js`
5+
6+
TypeScript Support: opentype.js comes with built-in TypeScript definitions. This makes it a more suitable choice for projects written in TypeScript, as it allows for type checking and autocompletion in compatible editors.
7+
8+
NPM Package: opentype.js is available as an NPM package, which simplifies the installation process and makes version management easier.
9+
10+
No Dependency on window: Unlike Typr.js, opentype.js doesn't rely on the global window object for its functionality. This makes it more portable and suitable for environments where window may not be available, such as server-side Node.js or worker threads.
11+
12+
It's worth noting, however, that there are trade-offs associated with choosing opentype.js over Typr.js. Some users have reported that opentype.js might be slower and less precise than Typr.js in some cases. Nonetheless, we believe that the advantages of opentype.js, especially its support for TypeScript and its availability as an NPM package, outweigh these potential downsides for the purposes of dtif-to-svg.

packages/dtif-to-svg/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
"license": "MIT",
2121
"dependencies": {
2222
"@pda/openapi-fetch": "workspace:^",
23-
"@remotion/paths": "^4.0.6",
23+
"@pda/utils": "workspace:^",
2424
"d3-selection": "^3.0.0",
2525
"opentype.js": "^1.3.4",
2626
"webfontloader": "^1.6.28"
@@ -31,11 +31,13 @@
3131
"devDependencies": {
3232
"@pda/jest-presets": "workspace:*",
3333
"@pda/tsconfig": "workspace:*",
34+
"@pda/types": "workspace:^",
3435
"@types/d3": "^7.4.0",
3536
"@types/d3-selection": "^3.0.5",
3637
"@types/jest": "^29.5.1",
3738
"@types/node": "^18.15.13",
3839
"@types/opentype.js": "^1.3.4",
40+
"@types/react": "^18.2.14",
3941
"eslint": "^8.38.0",
4042
"eslint-config-pda-base": "workspace:*",
4143
"jest": "^29.5.0",

packages/dtif-to-svg/src/d3.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,4 @@ const d3 = (() => {
2323
return getD3;
2424
})();
2525

26-
export { d3 };
26+
export default d3;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './transform-to-css';
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { TTransform } from '@pda/types/dtif';
2+
import type { CSSProperties } from 'react';
3+
import { T2DMatrixData, extractMatrixData } from '../math/extract-matrix-data';
4+
5+
export function transformToCSS(
6+
transform: TTransform | T2DMatrixData
7+
): CSSProperties {
8+
const {
9+
rotation,
10+
scaleX,
11+
scaleY,
12+
tx: x,
13+
ty: y,
14+
} = Array.isArray(transform) ? extractMatrixData(transform) : transform;
15+
16+
return {
17+
transform: `translate(${x}px, ${y}px) rotate(${
18+
// We negate the rotation to correct for Figma's clockwise rotation
19+
-rotation
20+
}deg) scale(${scaleX}, ${scaleY})`,
21+
transformOrigin: '0 0', // top left
22+
};
23+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import { TD3SVGElementSelection, TD3Selection } from '@/types';
2+
import {
3+
TEllipseNode,
4+
TPolygonNode,
5+
TRectangleNode,
6+
TStarNode,
7+
} from '@pda/types/dtif';
8+
import { getElementId } from '../other';
9+
import { appendNodeWrapperGElement } from './append-node-wrapper-g-element';
10+
11+
export async function appendBasicShapeNodeElement(
12+
parent: TD3SVGElementSelection,
13+
props: {
14+
node: TRectangleNode | TEllipseNode | TStarNode | TPolygonNode;
15+
shapeAppend: (
16+
parent: TD3SVGElementSelection
17+
) => Promise<TD3SVGElementSelection>;
18+
index?: number;
19+
}
20+
): Promise<TD3Selection<SVGGElement>> {
21+
const { node, shapeAppend, index = 0 } = props;
22+
const fillClipPathId = getElementId({
23+
id: node.id,
24+
index,
25+
type: node.type.toLocaleLowerCase(),
26+
category: 'fill-clip',
27+
isDefinition: true,
28+
});
29+
30+
// Create element
31+
const gElement = appendNodeWrapperGElement(parent, { node, index });
32+
33+
// Append children
34+
await appendDefsElement(gElement, { fillClipPathId, shapeAppend });
35+
// TODO: fill
36+
37+
return gElement;
38+
}
39+
40+
async function appendDefsElement(
41+
parent: TD3SVGElementSelection,
42+
props: {
43+
fillClipPathId: string;
44+
shapeAppend: (
45+
parent: TD3SVGElementSelection
46+
) => Promise<TD3SVGElementSelection>;
47+
}
48+
) {
49+
const defsElement = parent.append('defs');
50+
await appendClipPathElement(defsElement, props);
51+
return defsElement;
52+
}
53+
54+
async function appendClipPathElement(
55+
parent: TD3SVGElementSelection,
56+
props: {
57+
fillClipPathId: string;
58+
shapeAppend: (
59+
parent: TD3SVGElementSelection
60+
) => Promise<TD3SVGElementSelection>;
61+
}
62+
) {
63+
const { fillClipPathId, shapeAppend } = props;
64+
const clipPathElement = parent.append('clipPath').attr('id', fillClipPathId);
65+
66+
// Append children
67+
await shapeAppend(clipPathElement);
68+
69+
return clipPathElement;
70+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { TD3SVGElementSelection } from '@/types';
2+
import { TNode } from '@pda/types/dtif';
3+
import { transformToCSS } from '../css/transform-to-css';
4+
import { getElementId } from '../other/get-element-id';
5+
import { applyCSSToD3 } from './apply-css-to-d3';
6+
7+
export function appendNodeWrapperGElement(
8+
parent: TD3SVGElementSelection,
9+
props: { node: TNode; index?: number }
10+
) {
11+
const { node, index = 0 } = props;
12+
13+
// Create element
14+
const gElement = parent
15+
.append('g')
16+
.attr(
17+
'id',
18+
getElementId({ id: node.id, index, type: node.type.toLocaleLowerCase() })
19+
);
20+
21+
// Apply styles
22+
applyCSSToD3(gElement, {
23+
display: node.isVisible ? 'block' : 'none',
24+
opacity: node.opacity,
25+
...transformToCSS(node.relativeTransform),
26+
});
27+
28+
return gElement;
29+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import type { CSSProperties } from 'react';
2+
import { TD3Selection } from '../../types';
3+
4+
export function applyCSSToD3(
5+
selection: TD3Selection<any>,
6+
styles: CSSProperties
7+
): TD3Selection<any> {
8+
for (const key in styles) {
9+
selection.style(key, camelToKebabCase(styles[key]));
10+
}
11+
return selection;
12+
}
13+
14+
function camelToKebabCase(str: string): string {
15+
return str.replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g, '$1-$2').toLowerCase();
16+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export * from './append-basic-shape-node-element';
2+
export * from './append-node-wrapper-g-element';
3+
export * from './apply-css-to-d3';

0 commit comments

Comments
 (0)