abstract-image 3.3.2 → 3.4.0

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 (118) hide show
  1. package/CHANGELOG.md +54 -38
  2. package/LICENSE +21 -21
  3. package/README.md +73 -73
  4. package/lib/_tests_/exporters/svg-export-image.test.d.ts.map +1 -0
  5. package/lib/_tests_/exporters/svg-export-image.test.js.map +1 -0
  6. package/lib/exporters/__tests__/dxf2d-export-image/test-defs/dxf2d-ellipse.js +379 -379
  7. package/lib/exporters/__tests__/dxf2d-export-image/test-defs/dxf2d-group.js +123 -123
  8. package/lib/exporters/__tests__/dxf2d-export-image/test-defs/dxf2d-line.js +55 -55
  9. package/lib/exporters/__tests__/dxf2d-export-image/test-defs/dxf2d-polygon.js +89 -89
  10. package/lib/exporters/__tests__/dxf2d-export-image/test-defs/dxf2d-polyline.js +79 -79
  11. package/lib/exporters/__tests__/dxf2d-export-image/test-defs/dxf2d-rectangle.js +99 -99
  12. package/lib/exporters/__tests__/dxf2d-export-image/test-defs/dxf2d-text-growth-directions.js +135 -135
  13. package/lib/exporters/__tests__/dxf2d-export-image/test-defs/dxf2d-text.js +63 -63
  14. package/lib/exporters/__tests__/eps-export-image/export.test.js +1 -1
  15. package/lib/exporters/__tests__/eps-export-image/test-defs/eps-ellipse.js +24 -24
  16. package/lib/exporters/__tests__/eps-export-image/test-defs/eps-empty-text.js +26 -26
  17. package/lib/exporters/__tests__/eps-export-image/test-defs/eps-group.js +31 -31
  18. package/lib/exporters/__tests__/eps-export-image/test-defs/eps-line.js +20 -20
  19. package/lib/exporters/__tests__/eps-export-image/test-defs/eps-polygon.js +34 -34
  20. package/lib/exporters/__tests__/eps-export-image/test-defs/eps-polyline.js +26 -26
  21. package/lib/exporters/__tests__/eps-export-image/test-defs/eps-rectangle.js +20 -20
  22. package/lib/exporters/__tests__/eps-export-image/test-defs/eps-text-growth-directions.js +59 -59
  23. package/lib/exporters/__tests__/eps-export-image/test-defs/eps-text-iso-latin1-encoding.d.ts +3 -0
  24. package/lib/exporters/__tests__/eps-export-image/test-defs/eps-text-iso-latin1-encoding.d.ts.map +1 -0
  25. package/lib/exporters/__tests__/eps-export-image/test-defs/eps-text-iso-latin1-encoding.js +71 -0
  26. package/lib/exporters/__tests__/eps-export-image/test-defs/eps-text-iso-latin1-encoding.js.map +1 -0
  27. package/lib/exporters/__tests__/eps-export-image/test-defs/eps-text.js +26 -26
  28. package/lib/exporters/eps-export-image.d.ts +2 -0
  29. package/lib/exporters/eps-export-image.d.ts.map +1 -1
  30. package/lib/exporters/eps-export-image.js +113 -32
  31. package/lib/exporters/eps-export-image.js.map +1 -1
  32. package/package.json +4 -2
  33. package/src/__stories__/react-svg-export/example-1.stories.tsx +54 -54
  34. package/src/__stories__/svg-export/example-1.stories.tsx +42 -42
  35. package/src/exporters/__tests__/dxf2d-export-image/export-test-def.ts +11 -11
  36. package/src/exporters/__tests__/dxf2d-export-image/export.test.tsx +13 -13
  37. package/src/exporters/__tests__/dxf2d-export-image/test-defs/dxf2d-ellipse.ts +405 -405
  38. package/src/exporters/__tests__/dxf2d-export-image/test-defs/dxf2d-group.ts +166 -166
  39. package/src/exporters/__tests__/dxf2d-export-image/test-defs/dxf2d-line.ts +80 -80
  40. package/src/exporters/__tests__/dxf2d-export-image/test-defs/dxf2d-polygon.ts +114 -114
  41. package/src/exporters/__tests__/dxf2d-export-image/test-defs/dxf2d-polyline.ts +103 -103
  42. package/src/exporters/__tests__/dxf2d-export-image/test-defs/dxf2d-rectangle.ts +125 -125
  43. package/src/exporters/__tests__/dxf2d-export-image/test-defs/dxf2d-text-growth-directions.ts +214 -214
  44. package/src/exporters/__tests__/dxf2d-export-image/test-defs/dxf2d-text.ts +97 -97
  45. package/src/exporters/__tests__/eps-export-image/export-test-def.ts +11 -11
  46. package/src/exporters/__tests__/eps-export-image/export.test.tsx +13 -13
  47. package/src/exporters/__tests__/eps-export-image/test-defs/eps-ellipse.ts +50 -50
  48. package/src/exporters/__tests__/eps-export-image/test-defs/eps-empty-text.ts +60 -60
  49. package/src/exporters/__tests__/eps-export-image/test-defs/eps-group.ts +74 -74
  50. package/src/exporters/__tests__/eps-export-image/test-defs/eps-line.ts +45 -45
  51. package/src/exporters/__tests__/eps-export-image/test-defs/eps-polygon.ts +65 -65
  52. package/src/exporters/__tests__/eps-export-image/test-defs/eps-polyline.ts +58 -58
  53. package/src/exporters/__tests__/eps-export-image/test-defs/eps-rectangle.ts +46 -46
  54. package/src/exporters/__tests__/eps-export-image/test-defs/eps-text-growth-directions.ts +138 -138
  55. package/src/exporters/__tests__/eps-export-image/test-defs/eps-text-iso-latin1-encoding.ts +71 -0
  56. package/src/exporters/__tests__/eps-export-image/test-defs/eps-text.ts +60 -60
  57. package/src/exporters/__tests__/exception/png-unsupported.test.tsx +25 -25
  58. package/src/exporters/__tests__/exception/react-svg-direction-exception.test.tsx +65 -65
  59. package/src/exporters/__tests__/exception/svg-direction-exception.test.tsx +65 -65
  60. package/src/exporters/__tests__/png-export-image/export-test-def.ts +11 -11
  61. package/src/exporters/__tests__/png-export-image/export.test.tsx +13 -13
  62. package/src/exporters/__tests__/png-export-image/test-defs/png-createPNG.tsx +26 -26
  63. package/src/exporters/__tests__/react-svg-export-image/export-test-def.tsx +13 -13
  64. package/src/exporters/__tests__/react-svg-export-image/export.test.tsx +13 -13
  65. package/src/exporters/__tests__/react-svg-export-image/test-defs/react-svg-binary-png.tsx +26 -26
  66. package/src/exporters/__tests__/react-svg-export-image/test-defs/react-svg-binary-url.tsx +26 -26
  67. package/src/exporters/__tests__/react-svg-export-image/test-defs/react-svg-binary.tsx +25 -25
  68. package/src/exporters/__tests__/react-svg-export-image/test-defs/react-svg-callback.tsx +60 -60
  69. package/src/exporters/__tests__/react-svg-export-image/test-defs/react-svg-ellipse.tsx +28 -28
  70. package/src/exporters/__tests__/react-svg-export-image/test-defs/react-svg-empty-text.tsx +35 -35
  71. package/src/exporters/__tests__/react-svg-export-image/test-defs/react-svg-group.tsx +44 -44
  72. package/src/exporters/__tests__/react-svg-export-image/test-defs/react-svg-line.tsx +26 -26
  73. package/src/exporters/__tests__/react-svg-export-image/test-defs/react-svg-polygon.tsx +32 -32
  74. package/src/exporters/__tests__/react-svg-export-image/test-defs/react-svg-polyline.tsx +33 -33
  75. package/src/exporters/__tests__/react-svg-export-image/test-defs/react-svg-rectangle.tsx +27 -27
  76. package/src/exporters/__tests__/react-svg-export-image/test-defs/react-svg-subimage.tsx +36 -36
  77. package/src/exporters/__tests__/react-svg-export-image/test-defs/react-svg-text-bold.tsx +50 -50
  78. package/src/exporters/__tests__/react-svg-export-image/test-defs/react-svg-text-growth-directions.tsx +80 -80
  79. package/src/exporters/__tests__/react-svg-export-image/test-defs/react-svg-text-italic.tsx +65 -65
  80. package/src/exporters/__tests__/react-svg-export-image/test-defs/react-svg-text-sub.tsx +35 -35
  81. package/src/exporters/__tests__/react-svg-export-image/test-defs/react-svg-text.tsx +35 -35
  82. package/src/exporters/__tests__/svg-export-image/export-test-def.ts +11 -11
  83. package/src/exporters/__tests__/svg-export-image/export.test.tsx +13 -13
  84. package/src/exporters/__tests__/svg-export-image/test-defs/svg-binary.tsx +25 -25
  85. package/src/exporters/__tests__/svg-export-image/test-defs/svg-ellipse.ts +27 -27
  86. package/src/exporters/__tests__/svg-export-image/test-defs/svg-empty-text.ts +34 -34
  87. package/src/exporters/__tests__/svg-export-image/test-defs/svg-group.ts +44 -44
  88. package/src/exporters/__tests__/svg-export-image/test-defs/svg-line.ts +26 -26
  89. package/src/exporters/__tests__/svg-export-image/test-defs/svg-polygon.ts +32 -32
  90. package/src/exporters/__tests__/svg-export-image/test-defs/svg-polyline.ts +33 -33
  91. package/src/exporters/__tests__/svg-export-image/test-defs/svg-rectangle.ts +27 -27
  92. package/src/exporters/__tests__/svg-export-image/test-defs/svg-text-bold.ts +50 -50
  93. package/src/exporters/__tests__/svg-export-image/test-defs/svg-text-growth-directions.ts +80 -80
  94. package/src/exporters/__tests__/svg-export-image/test-defs/svg-text-italic.ts +65 -65
  95. package/src/exporters/__tests__/svg-export-image/test-defs/svg-text.ts +35 -35
  96. package/src/exporters/dxf2d-export-image.ts +218 -218
  97. package/src/exporters/eps-export-image.ts +233 -154
  98. package/src/exporters/index.ts +3 -3
  99. package/src/exporters/png-export-image.ts +12 -12
  100. package/src/exporters/react-svg-export-image.tsx +315 -315
  101. package/src/exporters/svg-export-image.ts +309 -309
  102. package/src/index.ts +11 -11
  103. package/src/model/__tests__/color/export-test-def.ts +13 -13
  104. package/src/model/__tests__/color/export.test.tsx +14 -14
  105. package/src/model/__tests__/color/test-defs/color-from-string.ts +46 -46
  106. package/src/model/__tests__/color/test-defs/color-to-string.ts +35 -35
  107. package/src/model/__tests__/color/test-defs/color-undefined-2.ts +8 -8
  108. package/src/model/__tests__/color/test-defs/color-undefined.ts +8 -8
  109. package/src/model/abstract-image.ts +25 -25
  110. package/src/model/color.ts +52 -52
  111. package/src/model/component.ts +279 -279
  112. package/src/model/index.ts +5 -5
  113. package/src/model/point.ts +11 -11
  114. package/src/model/size.ts +11 -11
  115. package/lib/exporters/__tests__/svg-export-image.test.d.ts.map +0 -1
  116. package/lib/exporters/__tests__/svg-export-image.test.js.map +0 -1
  117. /package/lib/{exporters/__tests__ → _tests_/exporters}/svg-export-image.test.d.ts +0 -0
  118. /package/lib/{exporters/__tests__ → _tests_/exporters}/svg-export-image.test.js +0 -0
@@ -1,309 +1,309 @@
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
- }
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";
@@ -1,13 +1,13 @@
1
- /**
2
- * Defines a data-type that represents a single test for the Docx render function
3
- */
4
-
5
- import * as AbstractImage from "../../../../src/index";
6
-
7
- export type ExportTestDef = {
8
- readonly name: string;
9
- readonly only?: boolean;
10
- readonly skip?: boolean;
11
- readonly abstractColor: {} | AbstractImage.Color | undefined;
12
- readonly expectedColor: {} | undefined;
13
- };
1
+ /**
2
+ * Defines a data-type that represents a single test for the Docx render function
3
+ */
4
+
5
+ import * as AbstractImage from "../../../../src/index";
6
+
7
+ export type ExportTestDef = {
8
+ readonly name: string;
9
+ readonly only?: boolean;
10
+ readonly skip?: boolean;
11
+ readonly abstractColor: {} | AbstractImage.Color | undefined;
12
+ readonly expectedColor: {} | undefined;
13
+ };