js-draw 1.0.1 → 1.0.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 (182) hide show
  1. package/LICENSE +21 -0
  2. package/dist/bundle.js +1 -1
  3. package/dist/cjs/version.js +1 -1
  4. package/dist/mjs/version.mjs +1 -1
  5. package/docs/img/readme-images/js-draw.jpg +0 -0
  6. package/docs/img/readme-images/unsupported-elements--in-editor.png +0 -0
  7. package/package.json +5 -4
  8. package/dist-test/test_imports/package-lock.json +0 -13
  9. package/dist-test/test_imports/package.json +0 -12
  10. package/dist-test/test_imports/test-imports.js +0 -11
  11. package/dist-test/test_imports/test-require.cjs +0 -14
  12. package/src/Editor.loadFrom.test.ts +0 -24
  13. package/src/Editor.test.ts +0 -107
  14. package/src/Editor.toSVG.test.ts +0 -294
  15. package/src/Editor.ts +0 -1443
  16. package/src/EditorImage.test.ts +0 -117
  17. package/src/EditorImage.ts +0 -609
  18. package/src/EventDispatcher.test.ts +0 -123
  19. package/src/EventDispatcher.ts +0 -72
  20. package/src/Pointer.ts +0 -183
  21. package/src/SVGLoader.test.ts +0 -114
  22. package/src/SVGLoader.ts +0 -672
  23. package/src/UndoRedoHistory.test.ts +0 -34
  24. package/src/UndoRedoHistory.ts +0 -102
  25. package/src/Viewport.ts +0 -322
  26. package/src/bundle/bundled.ts +0 -7
  27. package/src/commands/Command.ts +0 -45
  28. package/src/commands/Duplicate.ts +0 -75
  29. package/src/commands/Erase.ts +0 -95
  30. package/src/commands/SerializableCommand.ts +0 -49
  31. package/src/commands/UnresolvedCommand.ts +0 -37
  32. package/src/commands/invertCommand.ts +0 -58
  33. package/src/commands/lib.ts +0 -16
  34. package/src/commands/localization.ts +0 -47
  35. package/src/commands/uniteCommands.test.ts +0 -23
  36. package/src/commands/uniteCommands.ts +0 -140
  37. package/src/components/AbstractComponent.transformBy.test.ts +0 -23
  38. package/src/components/AbstractComponent.ts +0 -383
  39. package/src/components/BackgroundComponent.test.ts +0 -44
  40. package/src/components/BackgroundComponent.ts +0 -348
  41. package/src/components/ImageComponent.ts +0 -176
  42. package/src/components/RestylableComponent.ts +0 -161
  43. package/src/components/SVGGlobalAttributesObject.ts +0 -79
  44. package/src/components/Stroke.test.ts +0 -137
  45. package/src/components/Stroke.ts +0 -294
  46. package/src/components/TextComponent.test.ts +0 -202
  47. package/src/components/TextComponent.ts +0 -429
  48. package/src/components/UnknownSVGObject.test.ts +0 -10
  49. package/src/components/UnknownSVGObject.ts +0 -60
  50. package/src/components/builders/ArrowBuilder.ts +0 -106
  51. package/src/components/builders/CircleBuilder.ts +0 -100
  52. package/src/components/builders/FreehandLineBuilder.test.ts +0 -24
  53. package/src/components/builders/FreehandLineBuilder.ts +0 -210
  54. package/src/components/builders/LineBuilder.ts +0 -77
  55. package/src/components/builders/PressureSensitiveFreehandLineBuilder.ts +0 -453
  56. package/src/components/builders/RectangleBuilder.ts +0 -73
  57. package/src/components/builders/types.ts +0 -15
  58. package/src/components/lib.ts +0 -31
  59. package/src/components/localization.ts +0 -24
  60. package/src/components/util/StrokeSmoother.ts +0 -302
  61. package/src/components/util/describeComponentList.ts +0 -18
  62. package/src/dialogs/makeAboutDialog.ts +0 -82
  63. package/src/inputEvents.ts +0 -143
  64. package/src/lib.ts +0 -91
  65. package/src/localization.ts +0 -34
  66. package/src/localizations/de.ts +0 -146
  67. package/src/localizations/en.ts +0 -8
  68. package/src/localizations/es.ts +0 -74
  69. package/src/localizations/getLocalizationTable.test.ts +0 -27
  70. package/src/localizations/getLocalizationTable.ts +0 -74
  71. package/src/rendering/Display.ts +0 -247
  72. package/src/rendering/RenderablePathSpec.ts +0 -88
  73. package/src/rendering/RenderingStyle.test.ts +0 -68
  74. package/src/rendering/RenderingStyle.ts +0 -55
  75. package/src/rendering/TextRenderingStyle.ts +0 -55
  76. package/src/rendering/caching/CacheRecord.test.ts +0 -48
  77. package/src/rendering/caching/CacheRecord.ts +0 -76
  78. package/src/rendering/caching/CacheRecordManager.ts +0 -71
  79. package/src/rendering/caching/RenderingCache.test.ts +0 -43
  80. package/src/rendering/caching/RenderingCache.ts +0 -66
  81. package/src/rendering/caching/RenderingCacheNode.ts +0 -404
  82. package/src/rendering/caching/testUtils.ts +0 -35
  83. package/src/rendering/caching/types.ts +0 -34
  84. package/src/rendering/lib.ts +0 -8
  85. package/src/rendering/localization.ts +0 -20
  86. package/src/rendering/renderers/AbstractRenderer.ts +0 -232
  87. package/src/rendering/renderers/CanvasRenderer.ts +0 -312
  88. package/src/rendering/renderers/DummyRenderer.test.ts +0 -41
  89. package/src/rendering/renderers/DummyRenderer.ts +0 -142
  90. package/src/rendering/renderers/SVGRenderer.ts +0 -434
  91. package/src/rendering/renderers/TextOnlyRenderer.test.ts +0 -34
  92. package/src/rendering/renderers/TextOnlyRenderer.ts +0 -68
  93. package/src/shortcuts/KeyBinding.test.ts +0 -61
  94. package/src/shortcuts/KeyBinding.ts +0 -257
  95. package/src/shortcuts/KeyboardShortcutManager.test.ts +0 -95
  96. package/src/shortcuts/KeyboardShortcutManager.ts +0 -163
  97. package/src/shortcuts/lib.ts +0 -3
  98. package/src/testing/createEditor.ts +0 -11
  99. package/src/testing/getUniquePointerId.ts +0 -18
  100. package/src/testing/lib.ts +0 -3
  101. package/src/testing/sendPenEvent.ts +0 -36
  102. package/src/testing/sendTouchEvent.ts +0 -71
  103. package/src/toolbar/AbstractToolbar.ts +0 -542
  104. package/src/toolbar/DropdownToolbar.ts +0 -220
  105. package/src/toolbar/EdgeToolbar.test.ts +0 -54
  106. package/src/toolbar/EdgeToolbar.ts +0 -543
  107. package/src/toolbar/IconProvider.ts +0 -861
  108. package/src/toolbar/constants.ts +0 -1
  109. package/src/toolbar/lib.ts +0 -6
  110. package/src/toolbar/localization.ts +0 -136
  111. package/src/toolbar/types.ts +0 -13
  112. package/src/toolbar/widgets/ActionButtonWidget.ts +0 -39
  113. package/src/toolbar/widgets/BaseToolWidget.ts +0 -81
  114. package/src/toolbar/widgets/BaseWidget.ts +0 -495
  115. package/src/toolbar/widgets/DocumentPropertiesWidget.ts +0 -250
  116. package/src/toolbar/widgets/EraserToolWidget.ts +0 -84
  117. package/src/toolbar/widgets/HandToolWidget.ts +0 -239
  118. package/src/toolbar/widgets/InsertImageWidget.ts +0 -248
  119. package/src/toolbar/widgets/OverflowWidget.ts +0 -92
  120. package/src/toolbar/widgets/PenToolWidget.ts +0 -369
  121. package/src/toolbar/widgets/SelectionToolWidget.ts +0 -195
  122. package/src/toolbar/widgets/TextToolWidget.ts +0 -149
  123. package/src/toolbar/widgets/components/makeColorInput.ts +0 -184
  124. package/src/toolbar/widgets/components/makeFileInput.ts +0 -128
  125. package/src/toolbar/widgets/components/makeGridSelector.ts +0 -179
  126. package/src/toolbar/widgets/components/makeSeparator.ts +0 -17
  127. package/src/toolbar/widgets/components/makeThicknessSlider.ts +0 -62
  128. package/src/toolbar/widgets/keybindings.ts +0 -19
  129. package/src/toolbar/widgets/layout/DropdownLayoutManager.ts +0 -262
  130. package/src/toolbar/widgets/layout/EdgeToolbarLayoutManager.ts +0 -71
  131. package/src/toolbar/widgets/layout/types.ts +0 -74
  132. package/src/toolbar/widgets/lib.ts +0 -13
  133. package/src/tools/BaseTool.ts +0 -169
  134. package/src/tools/Eraser.test.ts +0 -103
  135. package/src/tools/Eraser.ts +0 -173
  136. package/src/tools/FindTool.test.ts +0 -67
  137. package/src/tools/FindTool.ts +0 -153
  138. package/src/tools/InputFilter/FunctionMapper.ts +0 -17
  139. package/src/tools/InputFilter/InputMapper.ts +0 -41
  140. package/src/tools/InputFilter/InputPipeline.test.ts +0 -41
  141. package/src/tools/InputFilter/InputPipeline.ts +0 -34
  142. package/src/tools/InputFilter/InputStabilizer.ts +0 -254
  143. package/src/tools/InputFilter/StrokeKeyboardControl.ts +0 -104
  144. package/src/tools/PanZoom.test.ts +0 -339
  145. package/src/tools/PanZoom.ts +0 -525
  146. package/src/tools/PasteHandler.ts +0 -94
  147. package/src/tools/Pen.test.ts +0 -260
  148. package/src/tools/Pen.ts +0 -284
  149. package/src/tools/PipetteTool.ts +0 -84
  150. package/src/tools/SelectionTool/SelectAllShortcutHandler.ts +0 -29
  151. package/src/tools/SelectionTool/Selection.ts +0 -647
  152. package/src/tools/SelectionTool/SelectionHandle.ts +0 -142
  153. package/src/tools/SelectionTool/SelectionTool.test.ts +0 -370
  154. package/src/tools/SelectionTool/SelectionTool.ts +0 -510
  155. package/src/tools/SelectionTool/TransformMode.ts +0 -112
  156. package/src/tools/SelectionTool/types.ts +0 -11
  157. package/src/tools/SoundUITool.ts +0 -221
  158. package/src/tools/TextTool.ts +0 -339
  159. package/src/tools/ToolController.ts +0 -224
  160. package/src/tools/ToolEnabledGroup.ts +0 -14
  161. package/src/tools/ToolSwitcherShortcut.ts +0 -39
  162. package/src/tools/ToolbarShortcutHandler.ts +0 -39
  163. package/src/tools/UndoRedoShortcut.test.ts +0 -62
  164. package/src/tools/UndoRedoShortcut.ts +0 -24
  165. package/src/tools/keybindings.ts +0 -85
  166. package/src/tools/lib.ts +0 -22
  167. package/src/tools/localization.ts +0 -76
  168. package/src/types.ts +0 -151
  169. package/src/util/ReactiveValue.test.ts +0 -168
  170. package/src/util/ReactiveValue.ts +0 -241
  171. package/src/util/assertions.ts +0 -55
  172. package/src/util/fileToBase64.ts +0 -18
  173. package/src/util/guessKeyCodeFromKey.ts +0 -36
  174. package/src/util/listPrefixMatch.ts +0 -19
  175. package/src/util/stopPropagationOfScrollingWheelEvents.ts +0 -20
  176. package/src/util/untilNextAnimationFrame.ts +0 -9
  177. package/src/util/waitForAll.ts +0 -18
  178. package/src/util/waitForTimeout.ts +0 -9
  179. package/src/version.test.ts +0 -12
  180. package/src/version.ts +0 -3
  181. package/tools/allLocales.js +0 -4
  182. package/tools/copyREADME.ts +0 -62
@@ -1,142 +0,0 @@
1
- import { Mat33, Rect2, Point2, Vec2, Vec3 } from '@js-draw/math';
2
- import Viewport from '../../Viewport';
3
- import RenderingStyle from '../RenderingStyle';
4
- import TextRenderingStyle from '../TextRenderingStyle';
5
- import AbstractRenderer, { RenderableImage } from './AbstractRenderer';
6
-
7
- // Renderer that outputs almost nothing. Useful for automated tests.
8
- export default class DummyRenderer extends AbstractRenderer {
9
- // Variables that track the state of what's been rendered
10
- public clearedCount: number = 0;
11
- public renderedPathCount: number = 0;
12
- public lastFillStyle: RenderingStyle|null = null;
13
- public lastPoint: Point2|null = null;
14
- public objectNestingLevel: number = 0;
15
- public lastText: string|null = null;
16
- public lastImage: RenderableImage|null = null;
17
-
18
- // List of points drawn since the last clear.
19
- public pointBuffer: Point2[] = [];
20
-
21
- public constructor(viewport: Viewport) {
22
- super(viewport);
23
- }
24
-
25
- public displaySize(): Vec2 {
26
- // Do we have a stored viewport size?
27
- const viewportSize = this.getViewport().getScreenRectSize();
28
-
29
- // Don't use a 0x0 viewport — DummyRenderer is often used
30
- // for tests that run without a display, so pretend we have a
31
- // reasonable-sized display.
32
- if (viewportSize.x === 0 || viewportSize.y === 0) {
33
- return Vec2.of(640, 480);
34
- }
35
-
36
- return viewportSize;
37
- }
38
-
39
- public clear() {
40
- this.clearedCount ++;
41
- this.renderedPathCount = 0;
42
- this.pointBuffer = [];
43
- this.lastText = null;
44
- this.lastImage = null;
45
-
46
- // Ensure all objects finished rendering
47
- if (this.objectNestingLevel > 0) {
48
- throw new Error(
49
- `Within an object while clearing! Nesting level: ${this.objectNestingLevel}`
50
- );
51
- }
52
- }
53
-
54
- protected beginPath(startPoint: Vec3) {
55
- this.lastPoint = startPoint;
56
- this.pointBuffer.push(startPoint);
57
- }
58
-
59
- protected endPath(style: RenderingStyle) {
60
- this.renderedPathCount++;
61
- this.lastFillStyle = style;
62
- }
63
-
64
- protected lineTo(point: Vec3) {
65
- point = this.canvasToScreen(point);
66
-
67
- this.lastPoint = point;
68
- this.pointBuffer.push(point);
69
- }
70
-
71
- protected moveTo(point: Point2) {
72
- point = this.canvasToScreen(point);
73
-
74
- this.lastPoint = point;
75
- this.pointBuffer.push(point);
76
- }
77
-
78
- protected traceCubicBezierCurve(p1: Vec3, p2: Vec3, p3: Vec3) {
79
- p1 = this.canvasToScreen(p1);
80
- p2 = this.canvasToScreen(p2);
81
- p3 = this.canvasToScreen(p3);
82
-
83
- this.lastPoint = p3;
84
- this.pointBuffer.push(p1, p2, p3);
85
- }
86
-
87
- protected traceQuadraticBezierCurve(controlPoint: Vec3, endPoint: Vec3) {
88
- controlPoint = this.canvasToScreen(controlPoint);
89
- endPoint = this.canvasToScreen(endPoint);
90
-
91
- this.lastPoint = endPoint;
92
- this.pointBuffer.push(controlPoint, endPoint);
93
- }
94
-
95
- public drawPoints(..._points: Vec3[]) {
96
- // drawPoints is intended for debugging.
97
- // As such, it is unlikely to be the target of automated tests.
98
- }
99
-
100
-
101
- public drawText(text: string, _transform: Mat33, _style: TextRenderingStyle): void {
102
- this.lastText = text;
103
- }
104
-
105
- public drawImage(image: RenderableImage) {
106
- this.lastImage = image;
107
- }
108
-
109
- public override startObject(boundingBox: Rect2, _clip: boolean) {
110
- super.startObject(boundingBox);
111
-
112
- this.objectNestingLevel += 1;
113
- }
114
-
115
- public override endObject() {
116
- super.endObject();
117
-
118
- this.objectNestingLevel -= 1;
119
- }
120
-
121
- public isTooSmallToRender(_rect: Rect2): boolean {
122
- return false;
123
- }
124
-
125
-
126
- public override canRenderFromWithoutDataLoss(other: AbstractRenderer) {
127
- return other instanceof DummyRenderer;
128
- }
129
-
130
- public override renderFromOtherOfSameType(transform: Mat33, other: AbstractRenderer): void {
131
- if (!(other instanceof DummyRenderer)) {
132
- throw new Error(`${other} cannot be rendered onto ${this}`);
133
- }
134
-
135
- this.renderedPathCount += other.renderedPathCount;
136
- this.lastFillStyle = other.lastFillStyle;
137
- this.lastPoint = other.lastPoint;
138
- this.pointBuffer.push(...other.pointBuffer.map(point => {
139
- return transform.transformVec2(point);
140
- }));
141
- }
142
- }
@@ -1,434 +0,0 @@
1
- import { LoadSaveDataTable } from '../../components/AbstractComponent';
2
- import { Mat33, Rect2, Point2, Vec2, toRoundedString } from '@js-draw/math';
3
- import { svgAttributesDataKey, svgLoaderAttributeContainerID, SVGLoaderUnknownAttribute, SVGLoaderUnknownStyleAttribute, svgStyleAttributesDataKey } from '../../SVGLoader';
4
- import Viewport from '../../Viewport';
5
- import RenderingStyle, { stylesEqual } from '../RenderingStyle';
6
- import TextRenderingStyle from '../TextRenderingStyle';
7
- import AbstractRenderer, { RenderableImage } from './AbstractRenderer';
8
- import RenderablePathSpec, { pathFromRenderable } from '../RenderablePathSpec';
9
- import listPrefixMatch from '../../util/listPrefixMatch';
10
-
11
- export const renderedStylesheetId = 'js-draw-style-sheet';
12
-
13
- const svgNameSpace = 'http://www.w3.org/2000/svg';
14
-
15
- const defaultTextStyle: Partial<TextRenderingStyle> = {
16
- fontWeight: '400',
17
- fontStyle: 'normal',
18
- };
19
-
20
- /**
21
- * Renders onto an `SVGElement`.
22
- *
23
- * @see {@link Editor.toSVG}
24
- */
25
- export default class SVGRenderer extends AbstractRenderer {
26
- private lastPathStyle: RenderingStyle|null = null;
27
- private lastPathString: string[] = [];
28
- private lastContainerIDList: string[] = [];
29
-
30
- // Elements that make up the current object (as created by startObject)
31
- // if any.
32
- private objectElems: SVGElement[]|null = null;
33
-
34
- private overwrittenAttrs: Record<string, string|null> = {};
35
-
36
- /**
37
- * Creates a renderer that renders onto `elem`. If `sanitize`, don't render potentially untrusted data.
38
- *
39
- * `viewport` is used to determine the translation/rotation/scaling/output size of the rendered
40
- * data.
41
- */
42
- public constructor(private elem: SVGSVGElement, viewport: Viewport, private sanitize: boolean = false) {
43
- super(viewport);
44
- this.clear();
45
-
46
- this.addStyleSheet();
47
- }
48
-
49
- private addStyleSheet() {
50
- if (!this.elem.querySelector(`#${renderedStylesheetId}`)) {
51
- // Default to rounded strokes.
52
- const styleSheet = document.createElementNS('http://www.w3.org/2000/svg', 'style');
53
- styleSheet.innerHTML = `
54
- path {
55
- stroke-linecap: round;
56
- stroke-linejoin: round;
57
- }
58
-
59
- text {
60
- white-space: pre;
61
- }
62
- `.replace(/\s+/g, '');
63
- styleSheet.setAttribute('id', renderedStylesheetId);
64
- this.elem.appendChild(styleSheet);
65
- }
66
- }
67
-
68
- // Sets an attribute on the root SVG element.
69
- public setRootSVGAttribute(name: string, value: string|null) {
70
- if (this.sanitize) {
71
- return;
72
- }
73
-
74
- // Make the original value of the attribute restorable on clear
75
- if (!(name in this.overwrittenAttrs)) {
76
- this.overwrittenAttrs[name] = this.elem.getAttribute(name);
77
- }
78
-
79
- if (value !== null) {
80
- this.elem.setAttribute(name, value);
81
- } else {
82
- this.elem.removeAttribute(name);
83
- }
84
- }
85
-
86
- public displaySize(): Vec2 {
87
- return Vec2.of(this.elem.clientWidth, this.elem.clientHeight);
88
- }
89
-
90
- public clear() {
91
- this.lastPathString = [];
92
- this.lastContainerIDList = [];
93
-
94
- if (!this.sanitize) {
95
- // Restore all all attributes
96
- for (const attrName in this.overwrittenAttrs) {
97
- const value = this.overwrittenAttrs[attrName];
98
-
99
- if (value) {
100
- this.elem.setAttribute(attrName, value);
101
- } else {
102
- this.elem.removeAttribute(attrName);
103
- }
104
- }
105
- this.overwrittenAttrs = {};
106
- }
107
- }
108
-
109
- // Push `this.fullPath` to the SVG. Returns the path added to the SVG, if any.
110
- // @internal
111
- protected addPathToSVG() {
112
- if (!this.lastPathStyle || this.lastPathString.length === 0) {
113
- return null;
114
- }
115
-
116
- const pathElem = document.createElementNS(svgNameSpace, 'path');
117
- pathElem.setAttribute('d', this.lastPathString.join(' '));
118
-
119
- const style = this.lastPathStyle;
120
- if (style.fill.a > 0) {
121
- pathElem.setAttribute('fill', style.fill.toHexString());
122
- } else {
123
- pathElem.setAttribute('fill', 'none');
124
- }
125
-
126
- if (style.stroke) {
127
- pathElem.setAttribute('stroke', style.stroke.color.toHexString());
128
- pathElem.setAttribute('stroke-width', toRoundedString(style.stroke.width));
129
- }
130
-
131
- this.elem.appendChild(pathElem);
132
- this.objectElems?.push(pathElem);
133
-
134
- return pathElem;
135
- }
136
-
137
- public override drawPath(pathSpec: RenderablePathSpec) {
138
- const style = pathSpec.style;
139
- const path = pathFromRenderable(pathSpec).transformedBy(this.getCanvasToScreenTransform());
140
-
141
- // Try to extend the previous path, if possible
142
- if (
143
- this.lastPathString.length === 0 || !this.lastPathStyle || !stylesEqual(this.lastPathStyle, style)
144
- ) {
145
- this.addPathToSVG();
146
- this.lastPathStyle = style;
147
- this.lastPathString = [];
148
- }
149
- this.lastPathString.push(path.toString());
150
- }
151
-
152
- // Apply [elemTransform] to [elem]. Uses both a `matrix` and `.x`, `.y` properties if `setXY` is true.
153
- // Otherwise, just uses a `matrix`.
154
- private transformFrom(elemTransform: Mat33, elem: SVGElement, inCanvasSpace: boolean = false, setXY: boolean = true) {
155
- let transform = !inCanvasSpace ? this.getCanvasToScreenTransform().rightMul(elemTransform) : elemTransform;
156
- const translation = transform.transformVec2(Vec2.zero);
157
-
158
- if (setXY) {
159
- transform = transform.rightMul(Mat33.translation(translation.times(-1)));
160
- }
161
-
162
- if (!transform.eq(Mat33.identity)) {
163
- elem.style.transform = `matrix(
164
- ${transform.a1}, ${transform.b1},
165
- ${transform.a2}, ${transform.b2},
166
- ${transform.a3}, ${transform.b3}
167
- )`;
168
- } else {
169
- elem.style.transform = '';
170
- }
171
-
172
- if (setXY) {
173
- elem.setAttribute('x', `${toRoundedString(translation.x)}`);
174
- elem.setAttribute('y', `${toRoundedString(translation.y)}`);
175
- }
176
- }
177
-
178
- private textContainer: SVGTextElement|null = null;
179
- private textContainerTransform: Mat33|null = null;
180
- private textParentStyle: Partial<TextRenderingStyle>|null = defaultTextStyle;
181
- public drawText(text: string, transform: Mat33, style: TextRenderingStyle): void {
182
- const applyTextStyles = (elem: SVGTextElement|SVGTSpanElement, style: TextRenderingStyle) => {
183
- if (style.fontFamily !== this.textParentStyle?.fontFamily) {
184
- elem.style.fontFamily = style.fontFamily;
185
- }
186
- if (style.fontVariant !== this.textParentStyle?.fontVariant) {
187
- elem.style.fontVariant = style.fontVariant ?? '';
188
- }
189
- if (style.fontWeight !== this.textParentStyle?.fontWeight) {
190
- elem.style.fontWeight = style.fontWeight ?? '';
191
- }
192
- if (style.fontStyle !== this.textParentStyle?.fontStyle) {
193
- elem.style.fontStyle = style.fontStyle ?? '';
194
- }
195
- if (style.size !== this.textParentStyle?.size) {
196
- elem.style.fontSize = style.size + 'px';
197
- }
198
-
199
- const fillString = style.renderingStyle.fill.toHexString();
200
- // TODO: Uncomment at some future major version release --- currently causes incompatibility due
201
- // to an SVG parsing bug in older versions.
202
- //const parentFillString = this.textParentStyle?.renderingStyle?.fill?.toHexString();
203
- //if (fillString !== parentFillString) {
204
- elem.style.fill = fillString;
205
- //}
206
-
207
- if (style.renderingStyle.stroke) {
208
- const strokeStyle = style.renderingStyle.stroke;
209
- elem.style.stroke = strokeStyle.color.toHexString();
210
- elem.style.strokeWidth = strokeStyle.width + 'px';
211
- }
212
- };
213
- transform = this.getCanvasToScreenTransform().rightMul(transform);
214
-
215
- if (!this.textContainer) {
216
- const container = document.createElementNS(svgNameSpace, 'text');
217
- container.appendChild(document.createTextNode(text));
218
-
219
- // Don't set .x/.y properties (just use .style.transform).
220
- // Child nodes aren't translated by .x/.y properties, but are by .style.transform.
221
- const setXY = false;
222
- this.transformFrom(transform, container, true, setXY);
223
- applyTextStyles(container, style);
224
-
225
- this.elem.appendChild(container);
226
- this.objectElems?.push(container);
227
- if (this.objectLevel > 0) {
228
- this.textContainer = container;
229
- this.textContainerTransform = transform;
230
- this.textParentStyle = { ...defaultTextStyle, ...style };
231
- }
232
- } else {
233
- const elem = document.createElementNS(svgNameSpace, 'tspan');
234
- elem.appendChild(document.createTextNode(text));
235
- this.textContainer.appendChild(elem);
236
-
237
- // Make .x/.y relative to the parent.
238
- transform = this.textContainerTransform!.inverse().rightMul(transform);
239
-
240
- // .style.transform does nothing to tspan elements. As such, we need to set x/y:
241
- const translation = transform.transformVec2(Vec2.zero);
242
- elem.setAttribute('x', `${toRoundedString(translation.x)}`);
243
- elem.setAttribute('y', `${toRoundedString(translation.y)}`);
244
-
245
- applyTextStyles(elem, style);
246
- }
247
- }
248
-
249
- public drawImage(image: RenderableImage) {
250
- let label = image.label ?? image.image.getAttribute('aria-label') ?? '';
251
- if (label === '') {
252
- label = image.image.getAttribute('alt') ?? '';
253
- }
254
-
255
- const svgImgElem = document.createElementNS(svgNameSpace, 'image');
256
- svgImgElem.setAttribute('href', image.base64Url);
257
- svgImgElem.setAttribute('width', image.image.getAttribute('width') ?? '');
258
- svgImgElem.setAttribute('height', image.image.getAttribute('height') ?? '');
259
- svgImgElem.setAttribute('aria-label', label);
260
- this.transformFrom(image.transform, svgImgElem);
261
-
262
- this.elem.appendChild(svgImgElem);
263
- this.objectElems?.push(svgImgElem);
264
- }
265
-
266
- public override startObject(boundingBox: Rect2) {
267
- super.startObject(boundingBox);
268
-
269
- // Only accumulate a path within an object
270
- this.lastPathString = [];
271
- this.lastPathStyle = null;
272
- this.textContainer = null;
273
- this.textParentStyle = defaultTextStyle;
274
- this.objectElems = [];
275
- }
276
-
277
- public override endObject(loaderData?: LoadSaveDataTable, elemClassNames?: string[]) {
278
- super.endObject(loaderData);
279
-
280
- // Don't extend paths across objects
281
- this.addPathToSVG();
282
-
283
- // If empty/not an object, stop.
284
- if (!this.objectElems) {
285
- return;
286
- }
287
-
288
- if (loaderData && !this.sanitize) {
289
- // Restore any attributes unsupported by the app.
290
- for (const elem of this.objectElems) {
291
- const attrs = loaderData[svgAttributesDataKey] as SVGLoaderUnknownAttribute[]|undefined;
292
- const styleAttrs = loaderData[svgStyleAttributesDataKey] as SVGLoaderUnknownStyleAttribute[]|undefined;
293
-
294
- if (attrs) {
295
- for (const [ attr, value ] of attrs) {
296
- elem.setAttribute(attr, value);
297
- }
298
- }
299
-
300
- if (styleAttrs) {
301
- for (const attr of styleAttrs) {
302
- elem.style.setProperty(attr.key, attr.value, attr.priority);
303
- }
304
- }
305
- }
306
-
307
- // Update the parent
308
- const containerIDData = loaderData[svgLoaderAttributeContainerID];
309
- let containerIDList: string[] = [];
310
- if (containerIDData && containerIDData[0]) {
311
- // If a string list,
312
- if ((containerIDData[0] as any).length) {
313
- containerIDList = containerIDData[0] as string[];
314
- }
315
- }
316
-
317
- if (
318
- containerIDList.length > 0
319
- // containerIDList must share a prefix with the last ID list
320
- // otherwise, the z order of elements may have been changed from
321
- // the original image.
322
- // In the case that the z order has been changed, keep the current
323
- // element as a child of the root to preserve z order.
324
- && listPrefixMatch(this.lastContainerIDList, containerIDList)
325
-
326
- // The component can add at most one more parent than the previous item.
327
- && this.lastContainerIDList.length >= containerIDList.length - 1
328
- ) {
329
- // Select the last
330
- const containerID = containerIDList[containerIDList.length - 1];
331
-
332
- const containerCandidates = this.elem.querySelectorAll(`g#${containerID}`);
333
- if (containerCandidates.length >= 1) {
334
- const container = containerCandidates[0];
335
-
336
- // If this is the first time we're entering the group, the
337
- // group should be empty.
338
- // Otherwise, this may be a case that would break z-ordering.
339
- if (container.children.length === 0 || this.lastContainerIDList.length >= containerIDList.length) {
340
- // Move all objectElems to the found container
341
- for (const elem of this.objectElems) {
342
- elem.remove();
343
- container.appendChild(elem);
344
- }
345
- } else {
346
- containerIDList = [];
347
- }
348
- }
349
- } else {
350
- containerIDList = [];
351
- }
352
-
353
- this.lastContainerIDList = containerIDList;
354
- }
355
-
356
- // Add class names to the object, if given.
357
- if (elemClassNames && this.objectElems) {
358
- if (this.objectElems.length === 1) {
359
- this.objectElems[0].classList.add(...elemClassNames);
360
- } else {
361
- const wrapper = document.createElementNS(svgNameSpace, 'g');
362
- wrapper.classList.add(...elemClassNames);
363
-
364
- for (const elem of this.objectElems) {
365
- elem.remove();
366
- wrapper.appendChild(elem);
367
- }
368
-
369
- this.elem.appendChild(wrapper);
370
- }
371
- }
372
- }
373
-
374
- // Not implemented -- use drawPath instead.
375
- private unimplementedMessage() { throw new Error('Not implemenented!'); }
376
- protected beginPath(_startPoint: Point2) { this.unimplementedMessage(); }
377
- protected endPath(_style: RenderingStyle) { this.unimplementedMessage(); }
378
- protected lineTo(_point: Point2) { this.unimplementedMessage(); }
379
- protected moveTo(_point: Point2) { this.unimplementedMessage(); }
380
- protected traceCubicBezierCurve(
381
- _controlPoint1: Point2, _controlPoint2: Point2, _endPoint: Point2
382
- ) { this.unimplementedMessage(); }
383
- protected traceQuadraticBezierCurve(_controlPoint: Point2, _endPoint: Point2) { this.unimplementedMessage(); }
384
-
385
- public drawPoints(...points: Point2[]) {
386
- points.map(point => {
387
- const elem = document.createElementNS(svgNameSpace, 'circle');
388
- elem.setAttribute('cx', `${point.x}`);
389
- elem.setAttribute('cy', `${point.y}`);
390
- elem.setAttribute('r', '15');
391
- this.elem.appendChild(elem);
392
- });
393
- }
394
-
395
- // Renders a **copy** of the given element.
396
- public drawSVGElem(elem: SVGElement) {
397
- if (this.sanitize) {
398
- return;
399
- }
400
-
401
- // Don't add multiple copies of the default stylesheet.
402
- if (elem.tagName.toLowerCase() === 'style' && elem.getAttribute('id') === renderedStylesheetId) {
403
- return;
404
- }
405
-
406
- const elemToDraw = elem.cloneNode(true) as SVGElement;
407
- this.elem.appendChild(elemToDraw);
408
- this.objectElems?.push(elemToDraw);
409
- }
410
-
411
- public isTooSmallToRender(_rect: Rect2): boolean {
412
- return false;
413
- }
414
-
415
- // Creates a new SVG element and SVGRenerer with attributes set for the given Viewport.
416
- public static fromViewport(viewport: Viewport, sanitize: boolean = true) {
417
- const svgNameSpace = 'http://www.w3.org/2000/svg';
418
- const result = document.createElementNS(svgNameSpace, 'svg');
419
-
420
- const rect = viewport.getScreenRectSize();
421
- // rect.x -> size of rect in x direction, rect.y -> size of rect in y direction.
422
- result.setAttribute('viewBox', [0, 0, rect.x, rect.y].map(part => toRoundedString(part)).join(' '));
423
- result.setAttribute('width', toRoundedString(rect.x));
424
- result.setAttribute('height', toRoundedString(rect.y));
425
-
426
- // Ensure the image can be identified as an SVG if downloaded.
427
- // See https://jwatt.org/svg/authoring/
428
- result.setAttribute('version', '1.1');
429
- result.setAttribute('baseProfile', 'full');
430
- result.setAttribute('xmlns', svgNameSpace);
431
-
432
- return { element: result, renderer: new SVGRenderer(result, viewport, sanitize) };
433
- }
434
- }
@@ -1,34 +0,0 @@
1
- import { ImageComponent, Mat33 } from '../../lib';
2
- import createEditor from '../../testing/createEditor';
3
- import TextOnlyRenderer from './TextOnlyRenderer';
4
-
5
- describe('TextOnlyRenderer', () => {
6
- it('should summarize the number of visible image nodes', () => {
7
- const editor = createEditor();
8
-
9
- const htmlImage = new Image();
10
- htmlImage.width = 500;
11
- htmlImage.height = 200;
12
-
13
- const image = new ImageComponent({
14
- transform: Mat33.identity,
15
- image: htmlImage,
16
- base64Url: '',
17
- label: 'Testing...',
18
- });
19
- editor.dispatch(editor.image.addElement(image));
20
-
21
- const textRenderer = new TextOnlyRenderer(editor.viewport, editor.localization);
22
- editor.image.render(textRenderer, editor.viewport);
23
-
24
- // Should contian number of image nodes and the image description
25
- expect(textRenderer.getDescription()).toContain('Testing...');
26
- expect(textRenderer.getDescription()).toContain('1');
27
-
28
- textRenderer.clear();
29
-
30
- // After clearing, should not contain description of the image/image count
31
- expect(textRenderer.getDescription()).not.toContain('Testing...');
32
- expect(textRenderer.getDescription()).not.toContain('1');
33
- });
34
- });
@@ -1,68 +0,0 @@
1
- import { Vec2, Vec3, Rect2, Mat33 } from '@js-draw/math';
2
- import Viewport from '../../Viewport';
3
- import { TextRendererLocalization } from '../localization';
4
- import RenderingStyle from '../RenderingStyle';
5
- import TextRenderingStyle from '../TextRenderingStyle';
6
- import AbstractRenderer, { RenderableImage } from './AbstractRenderer';
7
-
8
- // Outputs a description of what was rendered.
9
- export default class TextOnlyRenderer extends AbstractRenderer {
10
- private descriptionBuilder: string[] = [];
11
- private pathCount: number = 0;
12
- private textNodeCount: number = 0;
13
- private imageNodeCount: number = 0;
14
-
15
- public constructor(viewport: Viewport, private localizationTable: TextRendererLocalization) {
16
- super(viewport);
17
- }
18
-
19
- public displaySize(): Vec3 {
20
- // We don't have a graphical display, export a reasonable size.
21
- return Vec2.of(500, 500);
22
- }
23
-
24
- public clear(): void {
25
- this.descriptionBuilder = [];
26
- this.pathCount = 0;
27
- this.textNodeCount = 0;
28
- this.imageNodeCount = 0;
29
- }
30
-
31
- public getDescription(): string {
32
- return [
33
- this.localizationTable.pathNodeCount(this.pathCount),
34
- ...(this.textNodeCount > 0 ? [this.localizationTable.textNodeCount(this.textNodeCount)] : []),
35
- ...(this.imageNodeCount > 0 ? [this.localizationTable.imageNodeCount(this.imageNodeCount)] : []),
36
- ...this.descriptionBuilder
37
- ].join('\n');
38
- }
39
-
40
- protected beginPath(_startPoint: Vec3): void {
41
- }
42
- protected endPath(_style: RenderingStyle): void {
43
- this.pathCount ++;
44
- }
45
- protected lineTo(_point: Vec3): void {
46
- }
47
- protected moveTo(_point: Vec3): void {
48
- }
49
- protected traceCubicBezierCurve(_p1: Vec3, _p2: Vec3, _p3: Vec3): void {
50
- }
51
- protected traceQuadraticBezierCurve(_controlPoint: Vec3, _endPoint: Vec3): void {
52
- }
53
- public drawText(text: string, _transform: Mat33, _style: TextRenderingStyle): void {
54
- this.descriptionBuilder.push(this.localizationTable.textNode(text));
55
- this.textNodeCount ++;
56
- }
57
- public drawImage(image: RenderableImage) {
58
- const label = image.label ? this.localizationTable.imageNode(image.label) : this.localizationTable.unlabeledImageNode;
59
-
60
- this.descriptionBuilder.push(label);
61
- this.imageNodeCount ++;
62
- }
63
- public isTooSmallToRender(rect: Rect2): boolean {
64
- return rect.maxDimension < 15 / this.getSizeOfCanvasPixelOnScreen();
65
- }
66
- public drawPoints(..._points: Vec3[]): void {
67
- }
68
- }