abstract-image 3.2.5 → 3.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. package/CHANGELOG.md +38 -27
  2. package/LICENSE +21 -21
  3. package/README.md +73 -73
  4. package/lib/exporters/__tests__/dxf2d-export-image/test-defs/dxf2d-ellipse.js +379 -379
  5. package/lib/exporters/__tests__/dxf2d-export-image/test-defs/dxf2d-group.js +123 -123
  6. package/lib/exporters/__tests__/dxf2d-export-image/test-defs/dxf2d-line.js +55 -55
  7. package/lib/exporters/__tests__/dxf2d-export-image/test-defs/dxf2d-polygon.js +89 -89
  8. package/lib/exporters/__tests__/dxf2d-export-image/test-defs/dxf2d-polyline.js +79 -79
  9. package/lib/exporters/__tests__/dxf2d-export-image/test-defs/dxf2d-rectangle.js +99 -99
  10. package/lib/exporters/__tests__/dxf2d-export-image/test-defs/dxf2d-text-growth-directions.js +135 -135
  11. package/lib/exporters/__tests__/dxf2d-export-image/test-defs/dxf2d-text.js +63 -63
  12. package/lib/exporters/__tests__/eps-export-image/test-defs/eps-ellipse.js +24 -24
  13. package/lib/exporters/__tests__/eps-export-image/test-defs/eps-empty-text.js +26 -26
  14. package/lib/exporters/__tests__/eps-export-image/test-defs/eps-group.js +31 -31
  15. package/lib/exporters/__tests__/eps-export-image/test-defs/eps-line.js +20 -20
  16. package/lib/exporters/__tests__/eps-export-image/test-defs/eps-polygon.js +34 -34
  17. package/lib/exporters/__tests__/eps-export-image/test-defs/eps-polyline.js +26 -26
  18. package/lib/exporters/__tests__/eps-export-image/test-defs/eps-rectangle.js +20 -20
  19. package/lib/exporters/__tests__/eps-export-image/test-defs/eps-text-growth-directions.js +59 -59
  20. package/lib/exporters/__tests__/eps-export-image/test-defs/eps-text.js +26 -26
  21. package/lib/exporters/__tests__/exception/png-unsupported.test.js +1 -1
  22. package/lib/exporters/__tests__/exception/png-unsupported.test.js.map +1 -1
  23. package/lib/exporters/__tests__/png-export-image/test-defs/png-createPNG.js +1 -1
  24. package/lib/exporters/__tests__/png-export-image/test-defs/png-createPNG.js.map +1 -1
  25. package/lib/exporters/__tests__/react-svg-export-image/test-defs/react-svg-binary-png.d.ts.map +1 -1
  26. package/lib/exporters/__tests__/react-svg-export-image/test-defs/react-svg-binary-png.js +2 -2
  27. package/lib/exporters/__tests__/react-svg-export-image/test-defs/react-svg-binary-png.js.map +1 -1
  28. package/lib/exporters/__tests__/react-svg-export-image/test-defs/react-svg-binary-url.d.ts +3 -0
  29. package/lib/exporters/__tests__/react-svg-export-image/test-defs/react-svg-binary-url.d.ts.map +1 -0
  30. package/lib/exporters/__tests__/react-svg-export-image/test-defs/react-svg-binary-url.js +33 -0
  31. package/lib/exporters/__tests__/react-svg-export-image/test-defs/react-svg-binary-url.js.map +1 -0
  32. package/lib/exporters/__tests__/react-svg-export-image/test-defs/react-svg-binary.js +1 -1
  33. package/lib/exporters/__tests__/react-svg-export-image/test-defs/react-svg-binary.js.map +1 -1
  34. package/lib/exporters/__tests__/svg-export-image/test-defs/svg-binary.js +1 -1
  35. package/lib/exporters/__tests__/svg-export-image/test-defs/svg-binary.js.map +1 -1
  36. package/lib/exporters/__tests__/svg-export-image.test.d.ts +2 -0
  37. package/lib/exporters/__tests__/svg-export-image.test.d.ts.map +1 -0
  38. package/lib/exporters/__tests__/svg-export-image.test.js +35 -0
  39. package/lib/exporters/__tests__/svg-export-image.test.js.map +1 -0
  40. package/lib/exporters/png-export-image.js +2 -2
  41. package/lib/exporters/png-export-image.js.map +1 -1
  42. package/lib/exporters/react-svg-export-image.d.ts.map +1 -1
  43. package/lib/exporters/react-svg-export-image.js +23 -11
  44. package/lib/exporters/react-svg-export-image.js.map +1 -1
  45. package/lib/exporters/svg-export-image.d.ts.map +1 -1
  46. package/lib/exporters/svg-export-image.js +46 -15
  47. package/lib/exporters/svg-export-image.js.map +1 -1
  48. package/lib/model/component.d.ts +11 -2
  49. package/lib/model/component.d.ts.map +1 -1
  50. package/lib/model/component.js.map +1 -1
  51. package/package.json +4 -2
  52. package/src/__stories__/react-svg-export/example-1.stories.tsx +54 -54
  53. package/src/__stories__/svg-export/example-1.stories.tsx +42 -42
  54. package/src/exporters/__tests__/dxf2d-export-image/export-test-def.ts +11 -11
  55. package/src/exporters/__tests__/dxf2d-export-image/export.test.tsx +13 -13
  56. package/src/exporters/__tests__/dxf2d-export-image/test-defs/dxf2d-ellipse.ts +405 -405
  57. package/src/exporters/__tests__/dxf2d-export-image/test-defs/dxf2d-group.ts +166 -166
  58. package/src/exporters/__tests__/dxf2d-export-image/test-defs/dxf2d-line.ts +80 -80
  59. package/src/exporters/__tests__/dxf2d-export-image/test-defs/dxf2d-polygon.ts +114 -114
  60. package/src/exporters/__tests__/dxf2d-export-image/test-defs/dxf2d-polyline.ts +103 -103
  61. package/src/exporters/__tests__/dxf2d-export-image/test-defs/dxf2d-rectangle.ts +125 -125
  62. package/src/exporters/__tests__/dxf2d-export-image/test-defs/dxf2d-text-growth-directions.ts +214 -214
  63. package/src/exporters/__tests__/dxf2d-export-image/test-defs/dxf2d-text.ts +97 -97
  64. package/src/exporters/__tests__/eps-export-image/export-test-def.ts +11 -11
  65. package/src/exporters/__tests__/eps-export-image/export.test.tsx +13 -13
  66. package/src/exporters/__tests__/eps-export-image/test-defs/eps-ellipse.ts +50 -50
  67. package/src/exporters/__tests__/eps-export-image/test-defs/eps-empty-text.ts +60 -60
  68. package/src/exporters/__tests__/eps-export-image/test-defs/eps-group.ts +74 -74
  69. package/src/exporters/__tests__/eps-export-image/test-defs/eps-line.ts +45 -45
  70. package/src/exporters/__tests__/eps-export-image/test-defs/eps-polygon.ts +65 -65
  71. package/src/exporters/__tests__/eps-export-image/test-defs/eps-polyline.ts +58 -58
  72. package/src/exporters/__tests__/eps-export-image/test-defs/eps-rectangle.ts +46 -46
  73. package/src/exporters/__tests__/eps-export-image/test-defs/eps-text-growth-directions.ts +138 -138
  74. package/src/exporters/__tests__/eps-export-image/test-defs/eps-text.ts +60 -60
  75. package/src/exporters/__tests__/exception/png-unsupported.test.tsx +25 -25
  76. package/src/exporters/__tests__/exception/react-svg-direction-exception.test.tsx +65 -65
  77. package/src/exporters/__tests__/exception/svg-direction-exception.test.tsx +65 -65
  78. package/src/exporters/__tests__/png-export-image/export-test-def.ts +11 -11
  79. package/src/exporters/__tests__/png-export-image/export.test.tsx +13 -13
  80. package/src/exporters/__tests__/png-export-image/test-defs/png-createPNG.tsx +26 -26
  81. package/src/exporters/__tests__/react-svg-export-image/export-test-def.tsx +13 -13
  82. package/src/exporters/__tests__/react-svg-export-image/export.test.tsx +13 -13
  83. package/src/exporters/__tests__/react-svg-export-image/test-defs/react-svg-binary-png.tsx +26 -27
  84. package/src/exporters/__tests__/react-svg-export-image/test-defs/react-svg-binary-url.tsx +26 -0
  85. package/src/exporters/__tests__/react-svg-export-image/test-defs/react-svg-binary.tsx +25 -25
  86. package/src/exporters/__tests__/react-svg-export-image/test-defs/react-svg-callback.tsx +60 -60
  87. package/src/exporters/__tests__/react-svg-export-image/test-defs/react-svg-ellipse.tsx +28 -28
  88. package/src/exporters/__tests__/react-svg-export-image/test-defs/react-svg-empty-text.tsx +35 -35
  89. package/src/exporters/__tests__/react-svg-export-image/test-defs/react-svg-group.tsx +44 -44
  90. package/src/exporters/__tests__/react-svg-export-image/test-defs/react-svg-line.tsx +26 -26
  91. package/src/exporters/__tests__/react-svg-export-image/test-defs/react-svg-polygon.tsx +32 -32
  92. package/src/exporters/__tests__/react-svg-export-image/test-defs/react-svg-polyline.tsx +33 -33
  93. package/src/exporters/__tests__/react-svg-export-image/test-defs/react-svg-rectangle.tsx +27 -27
  94. package/src/exporters/__tests__/react-svg-export-image/test-defs/react-svg-subimage.tsx +36 -36
  95. package/src/exporters/__tests__/react-svg-export-image/test-defs/react-svg-text-bold.tsx +50 -50
  96. package/src/exporters/__tests__/react-svg-export-image/test-defs/react-svg-text-growth-directions.tsx +80 -80
  97. package/src/exporters/__tests__/react-svg-export-image/test-defs/react-svg-text-italic.tsx +65 -65
  98. package/src/exporters/__tests__/react-svg-export-image/test-defs/react-svg-text-sub.tsx +35 -35
  99. package/src/exporters/__tests__/react-svg-export-image/test-defs/react-svg-text.tsx +35 -35
  100. package/src/exporters/__tests__/svg-export-image/export-test-def.ts +11 -11
  101. package/src/exporters/__tests__/svg-export-image/export.test.tsx +13 -13
  102. package/src/exporters/__tests__/svg-export-image/test-defs/svg-binary.tsx +25 -25
  103. package/src/exporters/__tests__/svg-export-image/test-defs/svg-ellipse.ts +27 -27
  104. package/src/exporters/__tests__/svg-export-image/test-defs/svg-empty-text.ts +34 -34
  105. package/src/exporters/__tests__/svg-export-image/test-defs/svg-group.ts +44 -44
  106. package/src/exporters/__tests__/svg-export-image/test-defs/svg-line.ts +26 -26
  107. package/src/exporters/__tests__/svg-export-image/test-defs/svg-polygon.ts +32 -32
  108. package/src/exporters/__tests__/svg-export-image/test-defs/svg-polyline.ts +33 -33
  109. package/src/exporters/__tests__/svg-export-image/test-defs/svg-rectangle.ts +27 -27
  110. package/src/exporters/__tests__/svg-export-image/test-defs/svg-text-bold.ts +50 -50
  111. package/src/exporters/__tests__/svg-export-image/test-defs/svg-text-growth-directions.ts +80 -80
  112. package/src/exporters/__tests__/svg-export-image/test-defs/svg-text-italic.ts +65 -65
  113. package/src/exporters/__tests__/svg-export-image/test-defs/svg-text.ts +35 -35
  114. package/src/exporters/dxf2d-export-image.ts +218 -218
  115. package/src/exporters/eps-export-image.ts +154 -154
  116. package/src/exporters/index.ts +3 -3
  117. package/src/exporters/png-export-image.ts +12 -12
  118. package/src/exporters/react-svg-export-image.tsx +315 -305
  119. package/src/exporters/svg-export-image.ts +309 -300
  120. package/src/index.ts +11 -11
  121. package/src/model/__tests__/color/export-test-def.ts +13 -13
  122. package/src/model/__tests__/color/export.test.tsx +14 -14
  123. package/src/model/__tests__/color/test-defs/color-from-string.ts +46 -46
  124. package/src/model/__tests__/color/test-defs/color-to-string.ts +35 -35
  125. package/src/model/__tests__/color/test-defs/color-undefined-2.ts +8 -8
  126. package/src/model/__tests__/color/test-defs/color-undefined.ts +8 -8
  127. package/src/model/abstract-image.ts +25 -25
  128. package/src/model/color.ts +52 -52
  129. package/src/model/component.ts +279 -266
  130. package/src/model/index.ts +5 -5
  131. package/src/model/point.ts +11 -11
  132. package/src/model/size.ts +11 -11
@@ -1,300 +1,309 @@
1
- import * as AbstractImage from "../model/index";
2
-
3
- export function createSVG(image: AbstractImage.AbstractImage, pixelWidth?: number, pixelHeight?: number): string {
4
- const imageElements = image.components.map((c: AbstractImage.Component) => abstractComponentToSVG(c));
5
-
6
- return createElement(
7
- "svg",
8
- {
9
- xmlns: "http://www.w3.org/2000/svg",
10
- width: `${pixelWidth || image.size.width}px`,
11
- height: `${pixelHeight || image.size.height}px`,
12
- viewBox: [0, 0, image.size.width, image.size.height].join(" "),
13
- },
14
- imageElements
15
- );
16
- }
17
-
18
- function abstractComponentToSVG(component: AbstractImage.Component): string {
19
- switch (component.type) {
20
- case "group":
21
- return createElement(
22
- "g",
23
- {
24
- name: component.name,
25
- },
26
- component.children.map((c) => abstractComponentToSVG(c))
27
- );
28
- case "binaryimage":
29
- switch (component.format) {
30
- case "svg":
31
- const svg = Buffer.from(
32
- component.data
33
- .reduce((a, b) => a + String.fromCharCode(b), "")
34
- .replace('<?xml version="1.0" encoding="utf-8"?>', "")
35
- ).toString("base64");
36
- return createElement(
37
- "image",
38
- {
39
- x: component.topLeft.x.toString(),
40
- y: component.topLeft.y.toString(),
41
- width: (component.bottomRight.x - component.topLeft.x).toString(),
42
- height: (component.bottomRight.y - component.topLeft.y).toString(),
43
- href: `data:image/svg+xml;base64,${svg}`,
44
- },
45
- []
46
- );
47
- default:
48
- return "";
49
- }
50
- case "subimage":
51
- return "";
52
- case "line":
53
- return createElement(
54
- "line",
55
- {
56
- x1: component.start.x.toString(),
57
- y1: component.start.y.toString(),
58
- x2: component.end.x.toString(),
59
- y2: component.end.y.toString(),
60
- stroke: colorToRgb(component.strokeColor),
61
- strokeOpacity: colorToOpacity(component.strokeColor),
62
- strokeWidth: component.strokeThickness.toString(),
63
- },
64
- []
65
- );
66
- case "polyline":
67
- return createElement(
68
- "polyline",
69
- {
70
- fill: "none",
71
- points: component.points.map((p) => p.x.toString() + "," + p.y.toString()).join(" "),
72
- stroke: colorToRgb(component.strokeColor),
73
- strokeOpacity: colorToOpacity(component.strokeColor),
74
- strokeWidth: component.strokeThickness.toString(),
75
- },
76
- []
77
- );
78
- case "text":
79
- if (!component.text) {
80
- return "";
81
- }
82
- const lineHeight = component.fontSize;
83
-
84
- const shadowStyle = {
85
- textAnchor: getTextAnchor(component.horizontalGrowthDirection),
86
- fontSize: component.fontSize.toString() + "px",
87
- fontWeight: component.fontWeight,
88
- fontFamily: component.fontFamily,
89
- stroke: colorToRgb(component.strokeColor),
90
- strokeOpacity: colorToOpacity(component.strokeColor),
91
- strokeWidth: component.strokeThickness.toString() + "px",
92
- };
93
-
94
- const style = {
95
- textAnchor: getTextAnchor(component.horizontalGrowthDirection),
96
- fontSize: component.fontSize.toString() + "px",
97
- fontWeight: component.fontWeight,
98
- fontFamily: component.fontFamily,
99
- fill: colorToRgb(component.textColor),
100
- fillOpacity: colorToOpacity(component.textColor),
101
- };
102
-
103
- const dy = getBaselineAdjustment(component.verticalGrowthDirection);
104
-
105
- const transform =
106
- "rotate(" +
107
- component.clockwiseRotationDegrees.toString() +
108
- " " +
109
- component.position.x.toString() +
110
- " " +
111
- component.position.y.toString() +
112
- ")";
113
-
114
- const lines: Array<string> = component.text !== null ? component.text.split("\n") : [];
115
-
116
- const tSpans = lines.map((t) =>
117
- createElement(
118
- "tspan",
119
- {
120
- x: component.position.x.toString(),
121
- y: (component.position.y + (lines.indexOf(t) + dy) * lineHeight).toString(),
122
- height: lineHeight.toString() + "px",
123
- },
124
- [
125
- t
126
- .replace("<sub>", `<tspan style="font-size: ${component.fontSize * 0.8}px" baseline-shift="sub">`)
127
- .replace("</sub>", "</tspan>"),
128
- ]
129
- )
130
- );
131
-
132
- const cs: Array<string> = [];
133
-
134
- if (component.strokeThickness > 0 && component.strokeColor !== null) {
135
- cs.push(
136
- createElement(
137
- "text",
138
- {
139
- style: objectToAttributeValue(shadowStyle),
140
- transform: transform,
141
- },
142
- tSpans
143
- )
144
- );
145
- }
146
- cs.push(
147
- createElement(
148
- "text",
149
- {
150
- style: objectToAttributeValue(style),
151
- transform: transform,
152
- },
153
- tSpans
154
- )
155
- );
156
- return cs.join();
157
- case "ellipse":
158
- const rx = Math.abs(component.bottomRight.x - component.topLeft.x) * 0.5;
159
- const ry = Math.abs(component.bottomRight.y - component.topLeft.y) * 0.5;
160
- const cx = (component.bottomRight.x + component.topLeft.x) * 0.5;
161
- const cy = (component.bottomRight.y + component.topLeft.y) * 0.5;
162
- return createElement(
163
- "ellipse",
164
- {
165
- cx: cx.toString(),
166
- cy: cy.toString(),
167
- rx: rx.toString(),
168
- ry: ry.toString(),
169
- stroke: colorToRgb(component.strokeColor),
170
- strokeOpacity: colorToOpacity(component.strokeColor),
171
- strokeWidth: component.strokeThickness.toString(),
172
- fill: colorToRgb(component.fillColor),
173
- fillOpacity: colorToOpacity(component.fillColor),
174
- },
175
- []
176
- );
177
- case "polygon":
178
- return createElement(
179
- "polygon",
180
- {
181
- points: component.points.map((p) => p.x.toString() + "," + p.y.toString()).join(" "),
182
- stroke: colorToRgb(component.strokeColor),
183
- strokeOpacity: colorToOpacity(component.strokeColor),
184
- strokeWidth: component.strokeThickness.toString(),
185
- fill: colorToRgb(component.fillColor),
186
- fillOpacity: colorToOpacity(component.fillColor),
187
- },
188
- []
189
- );
190
- case "rectangle":
191
- return createElement(
192
- "rect",
193
- {
194
- x: component.topLeft.x.toString(),
195
- y: component.topLeft.y.toString(),
196
- width: Math.abs(component.bottomRight.x - component.topLeft.x).toString(),
197
- height: Math.abs(component.bottomRight.y - component.topLeft.y).toString(),
198
- stroke: colorToRgb(component.strokeColor),
199
- strokeOpacity: colorToOpacity(component.strokeColor),
200
- strokeWidth: component.strokeThickness.toString(),
201
- fill: colorToRgb(component.fillColor),
202
- fillOpacity: colorToOpacity(component.fillColor),
203
- },
204
- []
205
- );
206
- default:
207
- return "";
208
- }
209
- }
210
-
211
- interface Attributes {
212
- readonly [key: string]: string;
213
- }
214
-
215
- function createElement(elementName: string, attributes: Attributes, innerElements: string[]): string {
216
- const formattedName = convertUpperToHyphenLower(elementName);
217
- let element = `<${formattedName}`;
218
-
219
- if (Object.keys(attributes).length > 0) {
220
- element = Object.keys(attributes).reduce((previousValue: string, currentValue: string) => {
221
- if (attributes[currentValue]) {
222
- return previousValue + ` ${convertUpperToHyphenLower(currentValue)}="${attributes[currentValue]}"`;
223
- } else {
224
- return previousValue;
225
- }
226
- }, element);
227
- }
228
-
229
- element += ">";
230
-
231
- if (innerElements.length > 0) {
232
- element = innerElements.reduce((previousValue: string, currentValue: string) => {
233
- if (!currentValue || currentValue.length < 1) {
234
- return previousValue;
235
- } else {
236
- return previousValue + `${currentValue}`;
237
- }
238
- }, element);
239
- }
240
-
241
- element += `</${formattedName}>`;
242
-
243
- return element;
244
- }
245
-
246
- function objectToAttributeValue(attributes: Attributes): string {
247
- if (attributes && Object.keys(attributes).length > 0) {
248
- return Object.keys(attributes).reduce((previousValue: string, currentValue: string) => {
249
- if (attributes[currentValue]) {
250
- return previousValue + `${convertUpperToHyphenLower(currentValue)}:${attributes[currentValue]};`;
251
- } else {
252
- return previousValue;
253
- }
254
- }, "");
255
- }
256
-
257
- return "";
258
- }
259
-
260
- function convertUpperToHyphenLower(elementName: string): string {
261
- function upperToHyphenLower(match: string): string {
262
- return "-" + match.toLowerCase();
263
- }
264
-
265
- return elementName !== "viewBox" ? elementName.replace(/[A-Z]/g, upperToHyphenLower) : elementName;
266
- }
267
-
268
- function getBaselineAdjustment(d: AbstractImage.GrowthDirection): number {
269
- if (d === "up") {
270
- return 0.0;
271
- }
272
- if (d === "uniform") {
273
- return 0.5;
274
- }
275
- if (d === "down") {
276
- return 1.0;
277
- }
278
- throw "Unknown text alignment " + d;
279
- }
280
-
281
- function getTextAnchor(d: AbstractImage.GrowthDirection): string {
282
- if (d === "left") {
283
- return "end";
284
- }
285
- if (d === "uniform") {
286
- return "middle";
287
- }
288
- if (d === "right") {
289
- return "start";
290
- }
291
- throw "Unknown text alignment " + d;
292
- }
293
-
294
- function colorToRgb(color: AbstractImage.Color): string {
295
- return `rgb(${color.r.toString()}, ${color.g.toString()}, ${color.b.toString()})`;
296
- }
297
-
298
- function colorToOpacity(color: AbstractImage.Color): string {
299
- return (color.a / 255).toString();
300
- }
1
+ import * as B64 from "base64-js";
2
+ import * as AbstractImage from "../model/index";
3
+
4
+ export function createSVG(image: AbstractImage.AbstractImage, pixelWidth?: number, pixelHeight?: number): string {
5
+ const imageElements = image.components.map((c: AbstractImage.Component) => abstractComponentToSVG(c));
6
+
7
+ return createElement(
8
+ "svg",
9
+ {
10
+ xmlns: "http://www.w3.org/2000/svg",
11
+ width: `${pixelWidth || image.size.width}px`,
12
+ height: `${pixelHeight || image.size.height}px`,
13
+ viewBox: [0, 0, image.size.width, image.size.height].join(" "),
14
+ },
15
+ imageElements
16
+ );
17
+ }
18
+
19
+ function abstractComponentToSVG(component: AbstractImage.Component): string {
20
+ switch (component.type) {
21
+ case "group":
22
+ return createElement(
23
+ "g",
24
+ {
25
+ name: component.name,
26
+ },
27
+ component.children.map((c) => abstractComponentToSVG(c))
28
+ );
29
+ case "binaryimage":
30
+ const url = getImageUrl(component.format, component.data);
31
+ return createElement(
32
+ "image",
33
+ {
34
+ x: component.topLeft.x.toString(),
35
+ y: component.topLeft.y.toString(),
36
+ width: (component.bottomRight.x - component.topLeft.x).toString(),
37
+ height: (component.bottomRight.y - component.topLeft.y).toString(),
38
+ href: url,
39
+ },
40
+ []
41
+ );
42
+ case "subimage":
43
+ return "";
44
+ case "line":
45
+ return createElement(
46
+ "line",
47
+ {
48
+ x1: component.start.x.toString(),
49
+ y1: component.start.y.toString(),
50
+ x2: component.end.x.toString(),
51
+ y2: component.end.y.toString(),
52
+ stroke: colorToRgb(component.strokeColor),
53
+ strokeOpacity: colorToOpacity(component.strokeColor),
54
+ strokeWidth: component.strokeThickness.toString(),
55
+ },
56
+ []
57
+ );
58
+ case "polyline":
59
+ return createElement(
60
+ "polyline",
61
+ {
62
+ fill: "none",
63
+ points: component.points.map((p) => p.x.toString() + "," + p.y.toString()).join(" "),
64
+ stroke: colorToRgb(component.strokeColor),
65
+ strokeOpacity: colorToOpacity(component.strokeColor),
66
+ strokeWidth: component.strokeThickness.toString(),
67
+ },
68
+ []
69
+ );
70
+ case "text":
71
+ if (!component.text) {
72
+ return "";
73
+ }
74
+ const lineHeight = component.fontSize;
75
+
76
+ const shadowStyle = {
77
+ textAnchor: getTextAnchor(component.horizontalGrowthDirection),
78
+ fontSize: component.fontSize.toString() + "px",
79
+ fontWeight: component.fontWeight,
80
+ fontFamily: component.fontFamily,
81
+ stroke: colorToRgb(component.strokeColor),
82
+ strokeOpacity: colorToOpacity(component.strokeColor),
83
+ strokeWidth: component.strokeThickness.toString() + "px",
84
+ };
85
+
86
+ const style = {
87
+ textAnchor: getTextAnchor(component.horizontalGrowthDirection),
88
+ fontSize: component.fontSize.toString() + "px",
89
+ fontWeight: component.fontWeight,
90
+ fontFamily: component.fontFamily,
91
+ fill: colorToRgb(component.textColor),
92
+ fillOpacity: colorToOpacity(component.textColor),
93
+ };
94
+
95
+ const dy = getBaselineAdjustment(component.verticalGrowthDirection);
96
+
97
+ const transform =
98
+ "rotate(" +
99
+ component.clockwiseRotationDegrees.toString() +
100
+ " " +
101
+ component.position.x.toString() +
102
+ " " +
103
+ component.position.y.toString() +
104
+ ")";
105
+
106
+ const lines: Array<string> = component.text !== null ? component.text.split("\n") : [];
107
+
108
+ const tSpans = lines.map((t) =>
109
+ createElement(
110
+ "tspan",
111
+ {
112
+ x: component.position.x.toString(),
113
+ y: (component.position.y + (lines.indexOf(t) + dy) * lineHeight).toString(),
114
+ height: lineHeight.toString() + "px",
115
+ },
116
+ [
117
+ t
118
+ .replace("<sub>", `<tspan style="font-size: ${component.fontSize * 0.8}px" baseline-shift="sub">`)
119
+ .replace("</sub>", "</tspan>"),
120
+ ]
121
+ )
122
+ );
123
+
124
+ const cs: Array<string> = [];
125
+
126
+ if (component.strokeThickness > 0 && component.strokeColor !== null) {
127
+ cs.push(
128
+ createElement(
129
+ "text",
130
+ {
131
+ style: objectToAttributeValue(shadowStyle),
132
+ transform: transform,
133
+ },
134
+ tSpans
135
+ )
136
+ );
137
+ }
138
+ cs.push(
139
+ createElement(
140
+ "text",
141
+ {
142
+ style: objectToAttributeValue(style),
143
+ transform: transform,
144
+ },
145
+ tSpans
146
+ )
147
+ );
148
+ return cs.join();
149
+ case "ellipse":
150
+ const rx = Math.abs(component.bottomRight.x - component.topLeft.x) * 0.5;
151
+ const ry = Math.abs(component.bottomRight.y - component.topLeft.y) * 0.5;
152
+ const cx = (component.bottomRight.x + component.topLeft.x) * 0.5;
153
+ const cy = (component.bottomRight.y + component.topLeft.y) * 0.5;
154
+ return createElement(
155
+ "ellipse",
156
+ {
157
+ cx: cx.toString(),
158
+ cy: cy.toString(),
159
+ rx: rx.toString(),
160
+ ry: ry.toString(),
161
+ stroke: colorToRgb(component.strokeColor),
162
+ strokeOpacity: colorToOpacity(component.strokeColor),
163
+ strokeWidth: component.strokeThickness.toString(),
164
+ fill: colorToRgb(component.fillColor),
165
+ fillOpacity: colorToOpacity(component.fillColor),
166
+ },
167
+ []
168
+ );
169
+ case "polygon":
170
+ return createElement(
171
+ "polygon",
172
+ {
173
+ points: component.points.map((p) => p.x.toString() + "," + p.y.toString()).join(" "),
174
+ stroke: colorToRgb(component.strokeColor),
175
+ strokeOpacity: colorToOpacity(component.strokeColor),
176
+ strokeWidth: component.strokeThickness.toString(),
177
+ fill: colorToRgb(component.fillColor),
178
+ fillOpacity: colorToOpacity(component.fillColor),
179
+ },
180
+ []
181
+ );
182
+ case "rectangle":
183
+ return createElement(
184
+ "rect",
185
+ {
186
+ x: component.topLeft.x.toString(),
187
+ y: component.topLeft.y.toString(),
188
+ width: Math.abs(component.bottomRight.x - component.topLeft.x).toString(),
189
+ height: Math.abs(component.bottomRight.y - component.topLeft.y).toString(),
190
+ stroke: colorToRgb(component.strokeColor),
191
+ strokeOpacity: colorToOpacity(component.strokeColor),
192
+ strokeWidth: component.strokeThickness.toString(),
193
+ fill: colorToRgb(component.fillColor),
194
+ fillOpacity: colorToOpacity(component.fillColor),
195
+ },
196
+ []
197
+ );
198
+ default:
199
+ return "";
200
+ }
201
+ }
202
+
203
+ interface Attributes {
204
+ readonly [key: string]: string;
205
+ }
206
+
207
+ function createElement(elementName: string, attributes: Attributes, innerElements: string[]): string {
208
+ const formattedName = convertUpperToHyphenLower(elementName);
209
+ let element = `<${formattedName}`;
210
+
211
+ if (Object.keys(attributes).length > 0) {
212
+ element = Object.keys(attributes).reduce((previousValue: string, currentValue: string) => {
213
+ if (attributes[currentValue]) {
214
+ return previousValue + ` ${convertUpperToHyphenLower(currentValue)}="${attributes[currentValue]}"`;
215
+ } else {
216
+ return previousValue;
217
+ }
218
+ }, element);
219
+ }
220
+
221
+ element += ">";
222
+
223
+ if (innerElements.length > 0) {
224
+ element = innerElements.reduce((previousValue: string, currentValue: string) => {
225
+ if (!currentValue || currentValue.length < 1) {
226
+ return previousValue;
227
+ } else {
228
+ return previousValue + `${currentValue}`;
229
+ }
230
+ }, element);
231
+ }
232
+
233
+ element += `</${formattedName}>`;
234
+
235
+ return element;
236
+ }
237
+
238
+ function objectToAttributeValue(attributes: Attributes): string {
239
+ if (attributes && Object.keys(attributes).length > 0) {
240
+ return Object.keys(attributes).reduce((previousValue: string, currentValue: string) => {
241
+ if (attributes[currentValue]) {
242
+ return previousValue + `${convertUpperToHyphenLower(currentValue)}:${attributes[currentValue]};`;
243
+ } else {
244
+ return previousValue;
245
+ }
246
+ }, "");
247
+ }
248
+
249
+ return "";
250
+ }
251
+
252
+ function convertUpperToHyphenLower(elementName: string): string {
253
+ function upperToHyphenLower(match: string): string {
254
+ return "-" + match.toLowerCase();
255
+ }
256
+
257
+ return elementName !== "viewBox" ? elementName.replace(/[A-Z]/g, upperToHyphenLower) : elementName;
258
+ }
259
+
260
+ function getBaselineAdjustment(d: AbstractImage.GrowthDirection): number {
261
+ if (d === "up") {
262
+ return 0.0;
263
+ }
264
+ if (d === "uniform") {
265
+ return 0.5;
266
+ }
267
+ if (d === "down") {
268
+ return 1.0;
269
+ }
270
+ throw "Unknown text alignment " + d;
271
+ }
272
+
273
+ function getTextAnchor(d: AbstractImage.GrowthDirection): string {
274
+ if (d === "left") {
275
+ return "end";
276
+ }
277
+ if (d === "uniform") {
278
+ return "middle";
279
+ }
280
+ if (d === "right") {
281
+ return "start";
282
+ }
283
+ throw "Unknown text alignment " + d;
284
+ }
285
+
286
+ function colorToRgb(color: AbstractImage.Color): string {
287
+ return `rgb(${color.r.toString()}, ${color.g.toString()}, ${color.b.toString()})`;
288
+ }
289
+
290
+ function colorToOpacity(color: AbstractImage.Color): string {
291
+ return (color.a / 255).toString();
292
+ }
293
+
294
+ function getImageUrl(format: AbstractImage.BinaryFormat, data: AbstractImage.ImageData): string {
295
+ if (data.type === "url") {
296
+ return data.url;
297
+ } else if (format === "png") {
298
+ const base64 = B64.fromByteArray(data.bytes);
299
+ return `data:image/png;base64,${base64}`;
300
+ } else {
301
+ const svg = String.fromCharCode(...data.bytes).replace('<?xml version="1.0" encoding="utf-8"?>', "");
302
+ const bytes = [];
303
+ for (let i = 0; i < svg.length; ++i) {
304
+ bytes.push(svg.charCodeAt(i));
305
+ }
306
+ const base64 = B64.fromByteArray(new Uint8Array(bytes));
307
+ return `data:image/svg+xml;base64,${base64}`;
308
+ }
309
+ }
package/src/index.ts CHANGED
@@ -1,11 +1,11 @@
1
- export * from "./model/abstract-image";
2
- export * from "./model/color";
3
- export * from "./model/component";
4
- export * from "./model/point";
5
- export * from "./model/size";
6
-
7
- export * from "./exporters/svg-export-image";
8
- export * from "./exporters/png-export-image";
9
- export * from "./exporters/dxf2d-export-image";
10
- export * from "./exporters/react-svg-export-image";
11
- export * from "./exporters/eps-export-image";
1
+ export * from "./model/abstract-image";
2
+ export * from "./model/color";
3
+ export * from "./model/component";
4
+ export * from "./model/point";
5
+ export * from "./model/size";
6
+
7
+ export * from "./exporters/svg-export-image";
8
+ export * from "./exporters/png-export-image";
9
+ export * from "./exporters/dxf2d-export-image";
10
+ export * from "./exporters/react-svg-export-image";
11
+ export * from "./exporters/eps-export-image";