js-draw 1.3.1 → 1.4.1

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 (170) hide show
  1. package/README.md +2 -2
  2. package/dist/Editor.css +55 -13
  3. package/dist/bundle.js +2 -2
  4. package/dist/bundledStyles.js +1 -1
  5. package/dist/cjs/Editor.d.ts +36 -3
  6. package/dist/cjs/Editor.js +63 -26
  7. package/dist/cjs/commands/Erase.js +1 -1
  8. package/dist/cjs/commands/UnresolvedCommand.d.ts +1 -1
  9. package/dist/cjs/components/AbstractComponent.d.ts +1 -1
  10. package/dist/cjs/components/AbstractComponent.js +1 -1
  11. package/dist/cjs/components/BackgroundComponent.d.ts +1 -1
  12. package/dist/cjs/components/BackgroundComponent.js +13 -7
  13. package/dist/cjs/components/SVGGlobalAttributesObject.d.ts +2 -2
  14. package/dist/cjs/components/SVGGlobalAttributesObject.js +10 -14
  15. package/dist/cjs/{EditorImage.d.ts → image/EditorImage.d.ts} +30 -8
  16. package/dist/cjs/{EditorImage.js → image/EditorImage.js} +51 -7
  17. package/dist/cjs/image/export/adjustExportedSVGSize.d.ts +6 -0
  18. package/dist/cjs/image/export/adjustExportedSVGSize.js +22 -0
  19. package/dist/cjs/image/export/editorImageToSVG.d.ts +8 -0
  20. package/dist/cjs/image/export/editorImageToSVG.js +63 -0
  21. package/dist/cjs/image/lib.d.ts +1 -0
  22. package/dist/cjs/image/lib.js +8 -0
  23. package/dist/cjs/lib.d.ts +1 -1
  24. package/dist/cjs/lib.js +2 -3
  25. package/dist/cjs/localizations/comments.d.ts +6 -0
  26. package/dist/cjs/localizations/comments.js +10 -0
  27. package/dist/cjs/localizations/es.js +68 -48
  28. package/dist/cjs/rendering/caching/RenderingCache.d.ts +1 -1
  29. package/dist/cjs/rendering/caching/RenderingCacheNode.d.ts +1 -1
  30. package/dist/cjs/rendering/caching/RenderingCacheNode.js +4 -3
  31. package/dist/cjs/rendering/renderers/AbstractRenderer.d.ts +1 -0
  32. package/dist/cjs/rendering/renderers/AbstractRenderer.js +8 -0
  33. package/dist/cjs/rendering/renderers/SVGRenderer.d.ts +28 -1
  34. package/dist/cjs/rendering/renderers/SVGRenderer.js +58 -7
  35. package/dist/cjs/toolbar/AbstractToolbar.d.ts +11 -3
  36. package/dist/cjs/toolbar/AbstractToolbar.js +20 -6
  37. package/dist/cjs/toolbar/EdgeToolbar.js +5 -6
  38. package/dist/cjs/toolbar/IconProvider.d.ts +1 -0
  39. package/dist/cjs/toolbar/IconProvider.js +43 -0
  40. package/dist/cjs/toolbar/widgets/ActionButtonWidget.d.ts +3 -1
  41. package/dist/cjs/toolbar/widgets/ActionButtonWidget.js +19 -1
  42. package/dist/cjs/toolbar/widgets/BaseToolWidget.d.ts +1 -0
  43. package/dist/cjs/toolbar/widgets/BaseToolWidget.js +3 -0
  44. package/dist/cjs/toolbar/widgets/BaseWidget.d.ts +5 -0
  45. package/dist/cjs/toolbar/widgets/BaseWidget.js +30 -2
  46. package/dist/cjs/toolbar/widgets/DocumentPropertiesWidget.js +1 -1
  47. package/dist/cjs/toolbar/widgets/HandToolWidget.d.ts +1 -0
  48. package/dist/cjs/toolbar/widgets/HandToolWidget.js +6 -0
  49. package/dist/cjs/toolbar/widgets/InsertImageWidget.js +1 -1
  50. package/dist/cjs/toolbar/widgets/OverflowWidget.d.ts +1 -0
  51. package/dist/cjs/toolbar/widgets/OverflowWidget.js +3 -0
  52. package/dist/cjs/toolbar/widgets/SaveActionWidget.d.ts +1 -0
  53. package/dist/cjs/toolbar/widgets/SaveActionWidget.js +3 -0
  54. package/dist/cjs/tools/BaseTool.d.ts +3 -0
  55. package/dist/cjs/tools/BaseTool.js +13 -2
  56. package/dist/cjs/tools/FindTool.d.ts +1 -0
  57. package/dist/cjs/tools/FindTool.js +4 -1
  58. package/dist/cjs/tools/PanZoom.d.ts +1 -0
  59. package/dist/cjs/tools/PanZoom.js +4 -0
  60. package/dist/cjs/tools/Pen.d.ts +0 -1
  61. package/dist/cjs/tools/Pen.js +1 -4
  62. package/dist/cjs/tools/PipetteTool.d.ts +1 -0
  63. package/dist/cjs/tools/PipetteTool.js +3 -0
  64. package/dist/cjs/tools/SelectionTool/SelectAllShortcutHandler.d.ts +1 -0
  65. package/dist/cjs/tools/SelectionTool/SelectAllShortcutHandler.js +3 -0
  66. package/dist/cjs/tools/SelectionTool/Selection.d.ts +2 -0
  67. package/dist/cjs/tools/SelectionTool/Selection.js +44 -8
  68. package/dist/cjs/tools/SelectionTool/SelectionHandle.d.ts +14 -6
  69. package/dist/cjs/tools/SelectionTool/SelectionHandle.js +26 -8
  70. package/dist/cjs/tools/SelectionTool/SelectionTool.js +5 -0
  71. package/dist/cjs/tools/SoundUITool.d.ts +1 -0
  72. package/dist/cjs/tools/SoundUITool.js +4 -1
  73. package/dist/cjs/tools/TextTool.js +2 -2
  74. package/dist/cjs/tools/ToolController.d.ts +2 -0
  75. package/dist/cjs/tools/ToolController.js +13 -2
  76. package/dist/cjs/tools/ToolSwitcherShortcut.d.ts +1 -0
  77. package/dist/cjs/tools/ToolSwitcherShortcut.js +3 -0
  78. package/dist/cjs/types.d.ts +9 -4
  79. package/dist/cjs/types.js +4 -3
  80. package/dist/cjs/util/ReactiveValue.d.ts +1 -1
  81. package/dist/cjs/util/ReactiveValue.js +2 -2
  82. package/dist/cjs/version.js +1 -1
  83. package/dist/mjs/Editor.d.ts +36 -3
  84. package/dist/mjs/Editor.mjs +64 -27
  85. package/dist/mjs/Editor.toSVGAsync.test.d.ts +1 -0
  86. package/dist/mjs/commands/Erase.mjs +1 -1
  87. package/dist/mjs/commands/UnresolvedCommand.d.ts +1 -1
  88. package/dist/mjs/components/AbstractComponent.d.ts +1 -1
  89. package/dist/mjs/components/AbstractComponent.mjs +1 -1
  90. package/dist/mjs/components/BackgroundComponent.d.ts +1 -1
  91. package/dist/mjs/components/BackgroundComponent.mjs +13 -7
  92. package/dist/mjs/components/SVGGlobalAttributesObject.d.ts +2 -2
  93. package/dist/mjs/components/SVGGlobalAttributesObject.mjs +10 -14
  94. package/dist/mjs/{EditorImage.d.ts → image/EditorImage.d.ts} +30 -8
  95. package/dist/mjs/{EditorImage.mjs → image/EditorImage.mjs} +51 -7
  96. package/dist/mjs/image/EditorImage.test.d.ts +1 -0
  97. package/dist/mjs/image/export/adjustExportedSVGSize.d.ts +6 -0
  98. package/dist/mjs/image/export/adjustExportedSVGSize.mjs +20 -0
  99. package/dist/mjs/image/export/editorImageToSVG.d.ts +8 -0
  100. package/dist/mjs/image/export/editorImageToSVG.mjs +55 -0
  101. package/dist/mjs/image/lib.d.ts +1 -0
  102. package/dist/mjs/image/lib.mjs +1 -0
  103. package/dist/mjs/lib.d.ts +1 -1
  104. package/dist/mjs/lib.mjs +1 -1
  105. package/dist/mjs/localizations/comments.d.ts +6 -0
  106. package/dist/mjs/localizations/comments.mjs +8 -0
  107. package/dist/mjs/localizations/es.mjs +68 -48
  108. package/dist/mjs/rendering/caching/RenderingCache.d.ts +1 -1
  109. package/dist/mjs/rendering/caching/RenderingCacheNode.d.ts +1 -1
  110. package/dist/mjs/rendering/caching/RenderingCacheNode.mjs +4 -3
  111. package/dist/mjs/rendering/renderers/AbstractRenderer.d.ts +1 -0
  112. package/dist/mjs/rendering/renderers/AbstractRenderer.mjs +8 -0
  113. package/dist/mjs/rendering/renderers/SVGRenderer.d.ts +28 -1
  114. package/dist/mjs/rendering/renderers/SVGRenderer.mjs +58 -7
  115. package/dist/mjs/toolbar/AbstractToolbar.d.ts +11 -3
  116. package/dist/mjs/toolbar/AbstractToolbar.mjs +20 -6
  117. package/dist/mjs/toolbar/EdgeToolbar.mjs +5 -6
  118. package/dist/mjs/toolbar/IconProvider.d.ts +1 -0
  119. package/dist/mjs/toolbar/IconProvider.mjs +43 -0
  120. package/dist/mjs/toolbar/widgets/ActionButtonWidget.d.ts +3 -1
  121. package/dist/mjs/toolbar/widgets/ActionButtonWidget.mjs +21 -2
  122. package/dist/mjs/toolbar/widgets/BaseToolWidget.d.ts +1 -0
  123. package/dist/mjs/toolbar/widgets/BaseToolWidget.mjs +3 -0
  124. package/dist/mjs/toolbar/widgets/BaseWidget.d.ts +5 -0
  125. package/dist/mjs/toolbar/widgets/BaseWidget.mjs +30 -2
  126. package/dist/mjs/toolbar/widgets/DocumentPropertiesWidget.mjs +1 -1
  127. package/dist/mjs/toolbar/widgets/HandToolWidget.d.ts +1 -0
  128. package/dist/mjs/toolbar/widgets/HandToolWidget.mjs +6 -0
  129. package/dist/mjs/toolbar/widgets/InsertImageWidget.mjs +1 -1
  130. package/dist/mjs/toolbar/widgets/OverflowWidget.d.ts +1 -0
  131. package/dist/mjs/toolbar/widgets/OverflowWidget.mjs +3 -0
  132. package/dist/mjs/toolbar/widgets/SaveActionWidget.d.ts +1 -0
  133. package/dist/mjs/toolbar/widgets/SaveActionWidget.mjs +3 -0
  134. package/dist/mjs/tools/BaseTool.d.ts +3 -0
  135. package/dist/mjs/tools/BaseTool.mjs +13 -2
  136. package/dist/mjs/tools/FindTool.d.ts +1 -0
  137. package/dist/mjs/tools/FindTool.mjs +4 -1
  138. package/dist/mjs/tools/PanZoom.d.ts +1 -0
  139. package/dist/mjs/tools/PanZoom.mjs +4 -0
  140. package/dist/mjs/tools/Pen.d.ts +0 -1
  141. package/dist/mjs/tools/Pen.mjs +1 -4
  142. package/dist/mjs/tools/PipetteTool.d.ts +1 -0
  143. package/dist/mjs/tools/PipetteTool.mjs +3 -0
  144. package/dist/mjs/tools/SelectionTool/SelectAllShortcutHandler.d.ts +1 -0
  145. package/dist/mjs/tools/SelectionTool/SelectAllShortcutHandler.mjs +3 -0
  146. package/dist/mjs/tools/SelectionTool/Selection.d.ts +2 -0
  147. package/dist/mjs/tools/SelectionTool/Selection.mjs +45 -9
  148. package/dist/mjs/tools/SelectionTool/SelectionHandle.d.ts +14 -6
  149. package/dist/mjs/tools/SelectionTool/SelectionHandle.mjs +25 -7
  150. package/dist/mjs/tools/SelectionTool/SelectionTool.mjs +5 -0
  151. package/dist/mjs/tools/SoundUITool.d.ts +1 -0
  152. package/dist/mjs/tools/SoundUITool.mjs +4 -1
  153. package/dist/mjs/tools/TextTool.mjs +2 -2
  154. package/dist/mjs/tools/ToolController.d.ts +2 -0
  155. package/dist/mjs/tools/ToolController.mjs +13 -2
  156. package/dist/mjs/tools/ToolSwitcherShortcut.d.ts +1 -0
  157. package/dist/mjs/tools/ToolSwitcherShortcut.mjs +3 -0
  158. package/dist/mjs/types.d.ts +9 -4
  159. package/dist/mjs/types.mjs +4 -3
  160. package/dist/mjs/util/ReactiveValue.d.ts +1 -1
  161. package/dist/mjs/util/ReactiveValue.mjs +2 -2
  162. package/dist/mjs/version.mjs +1 -1
  163. package/package.json +5 -5
  164. package/src/Editor.scss +6 -0
  165. package/src/toolbar/EdgeToolbar.scss +19 -2
  166. package/src/tools/SelectionTool/SelectionTool.scss +74 -0
  167. package/src/tools/tools.scss +1 -1
  168. package/src/tools/SelectionTool/SelectionTool.css +0 -35
  169. /package/dist/cjs/{EditorImage.test.d.ts → Editor.toSVGAsync.test.d.ts} +0 -0
  170. /package/dist/{mjs → cjs/image}/EditorImage.test.d.ts +0 -0
@@ -1,11 +1,11 @@
1
- import AbstractRenderer from './rendering/renderers/AbstractRenderer';
2
- import Viewport from './Viewport';
3
- import AbstractComponent from './components/AbstractComponent';
1
+ import AbstractRenderer from '../rendering/renderers/AbstractRenderer';
2
+ import Viewport from '../Viewport';
3
+ import AbstractComponent from '../components/AbstractComponent';
4
4
  import { Rect2 } from '@js-draw/math';
5
- import RenderingCache from './rendering/caching/RenderingCache';
6
- import SerializableCommand from './commands/SerializableCommand';
7
- import EventDispatcher from './EventDispatcher';
8
- import Command from './commands/Command';
5
+ import RenderingCache from '../rendering/caching/RenderingCache';
6
+ import SerializableCommand from '../commands/SerializableCommand';
7
+ import EventDispatcher from '../EventDispatcher';
8
+ import Command from '../commands/Command';
9
9
  export declare const sortLeavesByZIndex: (leaves: Array<ImageNode>) => void;
10
10
  export declare enum EditorImageEventType {
11
11
  ExportViewportChanged = 0,
@@ -14,6 +14,13 @@ export declare enum EditorImageEventType {
14
14
  export type EditorImageNotifier = EventDispatcher<EditorImageEventType, {
15
15
  image: EditorImage;
16
16
  }>;
17
+ /**
18
+ * A callback used to
19
+ * 1. pause the render process
20
+ * 2. observe progress through `componentsProcessed` and `totalComponents`
21
+ * 3. stop the render process early by returning `false`.
22
+ */
23
+ export type PreRenderComponentCallback = (component: AbstractComponent, componentsProcessed: number, totalComponents: number) => Promise<boolean>;
17
24
  export default class EditorImage {
18
25
  private root;
19
26
  private background;
@@ -36,7 +43,21 @@ export default class EditorImage {
36
43
  * the viewport used by the `renderer` (if any).
37
44
  */
38
45
  render(renderer: AbstractRenderer, viewport: Viewport | null): void;
39
- /** Renders all nodes, even ones not within the viewport. @internal */
46
+ /**
47
+ * Like {@link renderAll}, but can be stopped early and paused.
48
+ *
49
+ * **Note**: If the image is being edited during an async rendering, there is no
50
+ * guarantee that all nodes will be rendered correctly (some may be missing).
51
+ *
52
+ * @internal
53
+ */
54
+ renderAllAsync(renderer: AbstractRenderer, preRenderComponent: PreRenderComponentCallback): Promise<boolean>;
55
+ /**
56
+ * Renders all nodes, even ones not within the viewport.
57
+ *
58
+ * This can be slow for large images
59
+ * @internal
60
+ */
40
61
  renderAll(renderer: AbstractRenderer): void;
41
62
  /**
42
63
  * @returns all elements in the image, sorted by z-index. This can be slow for large images.
@@ -136,6 +157,7 @@ export declare class ImageNode {
136
157
  private rebalance;
137
158
  protected removeChild(child: ImageNode): void;
138
159
  remove(): void;
160
+ renderAllAsync(renderer: AbstractRenderer, preRenderComponent: PreRenderComponentCallback): Promise<boolean>;
139
161
  render(renderer: AbstractRenderer, visibleRect?: Rect2): void;
140
162
  renderDebugBoundingBoxes(renderer: AbstractRenderer, visibleRect: Rect2, depth?: number): void;
141
163
  }
@@ -32,13 +32,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
32
32
  var _a, _b, _c;
33
33
  Object.defineProperty(exports, "__esModule", { value: true });
34
34
  exports.RootImageNode = exports.ImageNode = exports.EditorImageEventType = exports.sortLeavesByZIndex = void 0;
35
- const Viewport_1 = __importDefault(require("./Viewport"));
36
- const AbstractComponent_1 = __importStar(require("./components/AbstractComponent"));
35
+ const Viewport_1 = __importDefault(require("../Viewport"));
36
+ const AbstractComponent_1 = __importStar(require("../components/AbstractComponent"));
37
37
  const math_1 = require("@js-draw/math");
38
- const SerializableCommand_1 = __importDefault(require("./commands/SerializableCommand"));
39
- const EventDispatcher_1 = __importDefault(require("./EventDispatcher"));
40
- const assertions_1 = require("./util/assertions");
41
- const Command_1 = __importDefault(require("./commands/Command"));
38
+ const SerializableCommand_1 = __importDefault(require("../commands/SerializableCommand"));
39
+ const EventDispatcher_1 = __importDefault(require("../EventDispatcher"));
40
+ const assertions_1 = require("../util/assertions");
41
+ const Command_1 = __importDefault(require("../commands/Command"));
42
42
  // @internal Sort by z-index, low to high
43
43
  const sortLeavesByZIndex = (leaves) => {
44
44
  leaves.sort((a, b) => a.getContent().getZIndex() - b.getContent().getZIndex());
@@ -117,7 +117,27 @@ class EditorImage {
117
117
  this.background.render(renderer, viewport?.visibleRect);
118
118
  this.root.render(renderer, viewport?.visibleRect);
119
119
  }
120
- /** Renders all nodes, even ones not within the viewport. @internal */
120
+ /**
121
+ * Like {@link renderAll}, but can be stopped early and paused.
122
+ *
123
+ * **Note**: If the image is being edited during an async rendering, there is no
124
+ * guarantee that all nodes will be rendered correctly (some may be missing).
125
+ *
126
+ * @internal
127
+ */
128
+ async renderAllAsync(renderer, preRenderComponent) {
129
+ const stoppedEarly = !(await this.background.renderAllAsync(renderer, preRenderComponent));
130
+ if (!stoppedEarly) {
131
+ return await this.root.renderAllAsync(renderer, preRenderComponent);
132
+ }
133
+ return false;
134
+ }
135
+ /**
136
+ * Renders all nodes, even ones not within the viewport.
137
+ *
138
+ * This can be slow for large images
139
+ * @internal
140
+ */
121
141
  renderAll(renderer) {
122
142
  this.render(renderer, null);
123
143
  }
@@ -640,6 +660,30 @@ class ImageNode {
640
660
  this.content = null;
641
661
  this.children = [];
642
662
  }
663
+ // Creates a (potentially incomplete) async rendering of this image.
664
+ // Returns false if stopped early
665
+ async renderAllAsync(renderer,
666
+ // Used to pause/stop the renderer process
667
+ preRenderComponent) {
668
+ const leaves = this.getLeaves();
669
+ (0, exports.sortLeavesByZIndex)(leaves);
670
+ const totalLeaves = leaves.length;
671
+ for (let leafIndex = 0; leafIndex < totalLeaves; leafIndex++) {
672
+ const leaf = leaves[leafIndex];
673
+ const component = leaf.getContent();
674
+ // Even though leaf was originally a leaf, it might not be any longer --
675
+ // rendering is async and the tree can change during that time.
676
+ if (!component) {
677
+ continue;
678
+ }
679
+ const shouldContinue = await preRenderComponent(component, leafIndex, totalLeaves);
680
+ if (!shouldContinue) {
681
+ return false;
682
+ }
683
+ component.render(renderer, undefined);
684
+ }
685
+ return true;
686
+ }
643
687
  render(renderer, visibleRect) {
644
688
  let leaves;
645
689
  if (visibleRect) {
@@ -0,0 +1,6 @@
1
+ import { Rect2 } from '@js-draw/math';
2
+ export type SVGSizingOptions = {
3
+ minDimension?: number;
4
+ };
5
+ declare const adjustExportedSVGSize: (svg: SVGElement, exportRect: Rect2, options: SVGSizingOptions) => void;
6
+ export default adjustExportedSVGSize;
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const math_1 = require("@js-draw/math");
4
+ // @internal
5
+ const adjustExportedSVGSize = (svg, exportRect, options) => {
6
+ // Adjust the width/height as necessary
7
+ let width = exportRect.w;
8
+ let height = exportRect.h;
9
+ if (options?.minDimension && width < options.minDimension) {
10
+ const newWidth = options.minDimension;
11
+ height *= newWidth / (width || 1);
12
+ width = newWidth;
13
+ }
14
+ if (options?.minDimension && height < options.minDimension) {
15
+ const newHeight = options.minDimension;
16
+ width *= newHeight / (height || 1);
17
+ height = newHeight;
18
+ }
19
+ svg.setAttribute('width', (0, math_1.toRoundedString)(width));
20
+ svg.setAttribute('height', (0, math_1.toRoundedString)(height));
21
+ };
22
+ exports.default = adjustExportedSVGSize;
@@ -0,0 +1,8 @@
1
+ import EditorImage, { PreRenderComponentCallback } from '../EditorImage';
2
+ import { SVGSizingOptions } from './adjustExportedSVGSize';
3
+ export interface SVGExportOptions extends SVGSizingOptions {
4
+ sanitize?: boolean;
5
+ minDimension?: number;
6
+ }
7
+ export declare const editorImageToSVGSync: (image: EditorImage, options: SVGExportOptions) => SVGSVGElement;
8
+ export declare const editorImageToSVGAsync: (image: EditorImage, preRenderComponent: PreRenderComponentCallback, options: SVGExportOptions) => Promise<SVGElement>;
@@ -0,0 +1,63 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.editorImageToSVGAsync = exports.editorImageToSVGSync = void 0;
7
+ const math_1 = require("@js-draw/math");
8
+ const SVGRenderer_1 = __importDefault(require("../../rendering/renderers/SVGRenderer"));
9
+ const SVGLoader_1 = require("../../SVGLoader");
10
+ const adjustExportedSVGSize_1 = __importDefault(require("./adjustExportedSVGSize"));
11
+ const toSVGInternal = (image, renderFunction, options) => {
12
+ const importExportViewport = image.getImportExportViewport().getTemporaryClone();
13
+ // If the rectangle has zero width or height, its size can't be increased
14
+ // -- set its size to the minimum.
15
+ if (options?.minDimension) {
16
+ const originalRect = importExportViewport.visibleRect;
17
+ let rect = originalRect;
18
+ if (rect.w <= 0) {
19
+ rect = new math_1.Rect2(rect.x, rect.y, options.minDimension, rect.h);
20
+ }
21
+ if (rect.h <= 0) {
22
+ rect = new math_1.Rect2(rect.x, rect.y, rect.w, options.minDimension);
23
+ }
24
+ if (!rect.eq(originalRect)) {
25
+ importExportViewport.updateScreenSize(rect.size);
26
+ }
27
+ }
28
+ const { element: result, renderer } = SVGRenderer_1.default.fromViewport(importExportViewport, {
29
+ sanitize: options.sanitize ?? false,
30
+ useViewBoxForPositioning: true,
31
+ });
32
+ // Use a callback rather than async/await to allow this function to create
33
+ // both sync and async render functions
34
+ renderFunction(renderer, () => {
35
+ if (image.getAutoresizeEnabled()) {
36
+ result.classList.add(SVGLoader_1.svgLoaderAutoresizeClassName);
37
+ }
38
+ else {
39
+ result.classList.remove(SVGLoader_1.svgLoaderAutoresizeClassName);
40
+ }
41
+ const exportRect = importExportViewport.visibleRect;
42
+ (0, adjustExportedSVGSize_1.default)(result, exportRect, options);
43
+ return result;
44
+ });
45
+ return result;
46
+ };
47
+ const editorImageToSVGSync = (image, options) => {
48
+ return toSVGInternal(image, (renderer, onComplete) => {
49
+ image.renderAll(renderer);
50
+ onComplete();
51
+ }, options);
52
+ };
53
+ exports.editorImageToSVGSync = editorImageToSVGSync;
54
+ const editorImageToSVGAsync = (image, preRenderComponent, options) => {
55
+ return new Promise(resolve => {
56
+ toSVGInternal(image, async (renderer, onComplete) => {
57
+ await image.renderAllAsync(renderer, preRenderComponent);
58
+ const result = onComplete();
59
+ resolve(result);
60
+ }, options);
61
+ });
62
+ };
63
+ exports.editorImageToSVGAsync = editorImageToSVGAsync;
@@ -0,0 +1 @@
1
+ export { default as EditorImage } from './EditorImage';
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.EditorImage = void 0;
7
+ var EditorImage_1 = require("./EditorImage");
8
+ Object.defineProperty(exports, "EditorImage", { enumerable: true, get: function () { return __importDefault(EditorImage_1).default; } });
package/dist/cjs/lib.d.ts CHANGED
@@ -15,7 +15,7 @@
15
15
  * @packageDocumentation
16
16
  */
17
17
  import Editor, { EditorSettings } from './Editor';
18
- export { default as EditorImage } from './EditorImage';
18
+ export * from './image/lib';
19
19
  export * from './types';
20
20
  export * from './inputEvents';
21
21
  export { default as getLocalizationTable, matchingLocalizationTable } from './localizations/getLocalizationTable';
package/dist/cjs/lib.js CHANGED
@@ -33,11 +33,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
33
33
  return (mod && mod.__esModule) ? mod : { "default": mod };
34
34
  };
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.HTMLToolbar = exports.AbstractToolbar = exports.Editor = exports.__js_draw__version = exports.UndoRedoHistory = exports.PointerDevice = exports.Pointer = exports.EventDispatcher = exports.Viewport = exports.SVGLoader = exports.matchingLocalizationTable = exports.getLocalizationTable = exports.EditorImage = void 0;
36
+ exports.HTMLToolbar = exports.AbstractToolbar = exports.Editor = exports.__js_draw__version = exports.UndoRedoHistory = exports.PointerDevice = exports.Pointer = exports.EventDispatcher = exports.Viewport = exports.SVGLoader = exports.matchingLocalizationTable = exports.getLocalizationTable = void 0;
37
37
  const Editor_1 = __importDefault(require("./Editor"));
38
38
  exports.Editor = Editor_1.default;
39
- var EditorImage_1 = require("./EditorImage");
40
- Object.defineProperty(exports, "EditorImage", { enumerable: true, get: function () { return __importDefault(EditorImage_1).default; } });
39
+ __exportStar(require("./image/lib"), exports);
41
40
  __exportStar(require("./types"), exports);
42
41
  __exportStar(require("./inputEvents"), exports);
43
42
  var getLocalizationTable_1 = require("./localizations/getLocalizationTable");
@@ -0,0 +1,6 @@
1
+ import { EditorLocalization } from '../localization';
2
+ /**
3
+ * Comments to help translators create translations.
4
+ */
5
+ declare const comments: Partial<Record<keyof EditorLocalization, string>>;
6
+ export default comments;
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ /**
4
+ * Comments to help translators create translations.
5
+ */
6
+ const comments = {
7
+ dragAndDropHereOrBrowse: 'Uses {{curly braces}} to denote bold text',
8
+ closeSidebar: 'Currently used as an accessibilty label',
9
+ };
10
+ exports.default = comments;
@@ -1,70 +1,90 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const localization_1 = require("../localization");
4
- // A partial Spanish localization.
4
+ // A partial Spanish localization, created with /scripts/markdownTranslationFormToTs.ts
5
5
  const localization = {
6
6
  ...localization_1.defaultEditorLocalization,
7
- // Strings for the main editor interface
8
- // (see src/localization.ts)
9
- loading: (percentage) => `Cargando: ${percentage}%...`,
10
- imageEditor: 'Editor de dibujos',
11
- undoAnnouncement: (commandDescription) => `${commandDescription} fue deshecho`,
12
- redoAnnouncement: (commandDescription) => `${commandDescription} fue rehecho`,
13
- undo: 'Deshace',
14
- redo: 'Rehace',
15
- // Strings for the toolbar
16
- // (see src/toolbar/localization.ts)
17
7
  pen: 'Lapiz',
18
8
  eraser: 'Borrador',
19
9
  select: 'Selecciona',
20
- thicknessLabel: 'Tamaño',
21
- colorLabel: 'Color',
22
- doneLoading: 'El cargado terminó',
23
- fontLabel: 'Fuente: ',
24
- anyDevicePanning: 'Mover la pantalla con todo dispotivo',
25
- touchPanning: 'Mover la pantalla con un dedo',
26
- touchPanTool: 'Instrumento de mover la pantalla con un dedo',
27
- outlinedRectanglePen: 'Rectángulo con nada más que un borde',
28
- filledRectanglePen: 'Rectángulo sin borde',
29
- linePen: 'Línea',
30
- arrowPen: 'Flecha',
31
- roundedTipPen: 'Lapiz Redondeado',
32
- selectPenTip: 'Forma de dibuja',
33
10
  handTool: 'Mover',
34
- zoom: 'Zoom',
11
+ image: 'Imagen',
12
+ chooseFile: 'Seleccionar archivo',
13
+ cancel: 'Cancelar',
35
14
  resetView: 'Reiniciar vista',
15
+ thicknessLabel: 'Tamaño',
16
+ fontLabel: 'Fuente:',
17
+ textSize: 'Tamaño',
36
18
  resizeImageToSelection: 'Redimensionar la imagen a lo que está seleccionado',
37
19
  deleteSelection: 'Borra la selección',
38
20
  duplicateSelection: 'Duplica la selección',
21
+ exit: 'Salir',
22
+ save: 'Guardar',
23
+ undo: 'Deshace',
24
+ redo: 'Rehace',
25
+ selectPenTip: 'Punta',
26
+ selectShape: 'Forma',
39
27
  pickColorFromScreen: 'Selecciona un color de la pantalla',
40
28
  clickToPickColorAnnouncement: 'Haga un clic en la pantalla para seleccionar un color',
41
- dropdownShown(toolName) {
42
- return `Menú por ${toolName} es visible`;
43
- },
44
- dropdownHidden: function (toolName) {
45
- return `Menú por ${toolName} fue ocultado`;
46
- },
47
- colorChangedAnnouncement: function (color) {
48
- return `Color fue cambiado a ${color}`;
49
- },
50
- keyboardPanZoom: 'Mover la pantalla con el teclado',
51
- penTool: function (penId) {
52
- return `Lapiz ${penId}`;
53
- },
29
+ documentProperties: 'Fondo',
30
+ backgroundColor: 'Color de fondo',
31
+ imageWidthOption: 'Ancho',
32
+ imageHeightOption: 'Alto',
33
+ toggleOverflow: 'Más',
34
+ touchPanning: 'Mover la pantalla con un dedo',
35
+ roundedTipPen: 'Lapiz Redondeado',
36
+ arrowPen: 'Flecha',
37
+ linePen: 'Línea',
38
+ outlinedRectanglePen: 'Rectángulo delineado',
39
+ filledRectanglePen: 'Rectángulo sin borde',
40
+ lockRotation: 'Bloquea rotación',
41
+ paste: 'Pegar',
42
+ closeSidebar: (toolName) => `Close sidebar for ${toolName}`,
43
+ dropdownShown: (toolName) => `Menú por ${toolName} es visible`,
44
+ dropdownHidden: (toolName) => { return `Menú por ${toolName} fue ocultado`; },
45
+ zoomLevel: (zoomPercent) => `Zoom: ${zoomPercent}%`,
46
+ colorChangedAnnouncement: (color) => { return `Color fue cambiado a ${color}`; },
47
+ imageSize: (size, units) => `Tamaño del imagen: ${size} ${units}`,
48
+ imageLoadError: (message) => `Error cargando imagen: ${message}`,
49
+ penTool: (penId) => { return `Lapiz ${penId}`; },
54
50
  selectionTool: 'Selecciona',
55
51
  eraserTool: 'Borrador',
52
+ touchPanTool: 'Instrumento de mover la pantalla con un dedo',
53
+ pipetteTool: 'Seleccione un color de la pantalla',
54
+ keyboardPanZoom: 'Mover la pantalla con el teclado',
56
55
  textTool: 'Texto',
57
56
  enterTextToInsert: 'Entra texto',
58
- textSize: 'Tamaño',
57
+ findLabel: 'Buscar',
58
+ toNextMatch: 'Próxima',
59
+ closeDialog: 'Cerrar',
60
+ focusedFoundText: (matchIdx, totalMatches) => `Viewing match ${matchIdx} of ${totalMatches}`,
61
+ anyDevicePanning: 'Mover la pantalla con todo dispotivo',
62
+ copied: (count, description) => `Copied ${count} ${description}`,
63
+ pasted: (count, description) => `Pasted ${count} ${description}`,
64
+ toolEnabledAnnouncement: (toolName) => `${toolName} enabled`,
65
+ toolDisabledAnnouncement: (toolName) => `${toolName} disabled`,
66
+ transformedElements: (elemCount) => `Transformed ${elemCount} element${elemCount === 1 ? '' : 's'}`,
67
+ resizeOutputCommand: (newSize) => `Resized image to ${newSize.w}x${newSize.h}`,
68
+ addElementAction: (componentDescription) => `Added ${componentDescription}`,
69
+ eraseAction: (componentDescription, numElems) => `Erased ${numElems} ${componentDescription}`,
70
+ duplicateAction: (componentDescription, numElems) => `Duplicated ${numElems} ${componentDescription}`,
71
+ unionOf: (actionDescription, actionCount) => `Union: ${actionCount} ${actionDescription}`,
72
+ inverseOf: (actionDescription) => `Inverse of ${actionDescription}`,
73
+ rotatedBy: (degrees) => `Rotated by ${Math.abs(degrees)} degrees ${degrees < 0 ? 'clockwise' : 'counter-clockwise'}`,
74
+ selectedElements: (count) => `Selected ${count} element${count === 1 ? '' : 's'}`,
75
+ filledBackgroundWithColor: (color) => `Filled background (${color})`,
76
+ text: (text) => `Text object: ${text}`,
77
+ imageNode: (label) => `Image: ${label}`,
78
+ restyledElement: (elementDescription) => `Restyled ${elementDescription}`,
79
+ pathNodeCount: (count) => `There are ${count} visible path objects.`,
80
+ textNodeCount: (count) => `There are ${count} visible text nodes.`,
81
+ imageNodeCount: (nodeCount) => `There are ${nodeCount} visible image nodes.`,
82
+ textNode: (content) => `Text: ${content}`,
59
83
  rerenderAsText: 'Redibuja la pantalla al texto',
60
- lockRotation: 'Bloquea rotación',
61
- image: 'Imagen',
62
- imageSize: (size, units) => `Tamaño del imagen: ${size} ${units}`,
63
- imageLoadError: (message) => `Error cargando imagen: ${message}`,
64
- toggleOverflow: 'Más',
65
- documentProperties: 'Fondo',
66
- imageWidthOption: 'Ancho',
67
- imageHeightOption: 'Alto',
68
- backgroundColor: 'Color de fondo: '
84
+ loading: (percentage) => `Cargando: ${percentage}%...`,
85
+ imageEditor: 'Editor de dibujos',
86
+ doneLoading: 'El cargado terminó',
87
+ undoAnnouncement: (commandDescription) => `${commandDescription} fue deshecho`,
88
+ redoAnnouncement: (commandDescription) => `${commandDescription} fue rehecho`,
69
89
  };
70
90
  exports.default = localization;
@@ -1,4 +1,4 @@
1
- import { ImageNode } from '../../EditorImage';
1
+ import { ImageNode } from '../../image/EditorImage';
2
2
  import Viewport from '../../Viewport';
3
3
  import AbstractRenderer from '../renderers/AbstractRenderer';
4
4
  import { CacheProps } from './types';
@@ -1,4 +1,4 @@
1
- import { ImageNode } from '../../EditorImage';
1
+ import { ImageNode } from '../../image/EditorImage';
2
2
  import { Rect2 } from '@js-draw/math';
3
3
  import Viewport from '../../Viewport';
4
4
  import AbstractRenderer from '../renderers/AbstractRenderer';
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  // A cache record with sub-nodes.
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
- const EditorImage_1 = require("../../EditorImage");
4
+ const EditorImage_1 = require("../../image/EditorImage");
5
5
  const math_1 = require("@js-draw/math");
6
6
  // 3x3 divisions for each node.
7
7
  const cacheDivisionSize = 3;
@@ -44,11 +44,12 @@ class RenderingCacheNode {
44
44
  // Generates children, if missing.
45
45
  generateChildren() {
46
46
  if (this.instantiatedChildren.length === 0) {
47
- const childRects = this.region.divideIntoGrid(cacheDivisionSize, cacheDivisionSize);
48
- if (this.region.size.x === 0 || this.region.size.y === 0) {
47
+ if (this.region.size.x / cacheDivisionSize === 0 || this.region.size.y / cacheDivisionSize === 0) {
49
48
  console.warn('Cache element has zero size! Not generating children.');
50
49
  return;
51
50
  }
51
+ const childRects = this.region.divideIntoGrid(cacheDivisionSize, cacheDivisionSize);
52
+ console.assert(childRects.length === cacheDivisionSize * cacheDivisionSize, 'Warning: divideIntoGrid created the wrong number of subrectangles!');
52
53
  for (const rect of childRects) {
53
54
  const child = new RenderingCacheNode(rect, this.cacheState);
54
55
  child.parent = this;
@@ -73,4 +73,5 @@ export default abstract class AbstractRenderer {
73
73
  getCanvasToScreenTransform(): Mat33;
74
74
  canvasToScreen(vec: Vec2): Vec2;
75
75
  getSizeOfCanvasPixelOnScreen(): number;
76
+ getVisibleRect(): Rect2;
76
77
  }
@@ -160,5 +160,13 @@ class AbstractRenderer {
160
160
  getSizeOfCanvasPixelOnScreen() {
161
161
  return this.getCanvasToScreenTransform().transformVec3(math_1.Vec2.unitX).length();
162
162
  }
163
+ // Returns the region in canvas space that is visible within the viewport this
164
+ // canvas is rendering to.
165
+ //
166
+ // Note that in some cases this might not be the same as the `visibleRect` given
167
+ // to components in their `render` method.
168
+ getVisibleRect() {
169
+ return this.viewport.visibleRect;
170
+ }
163
171
  }
164
172
  exports.default = AbstractRenderer;
@@ -6,6 +6,15 @@ import TextRenderingStyle from '../TextRenderingStyle';
6
6
  import AbstractRenderer, { RenderableImage } from './AbstractRenderer';
7
7
  import RenderablePathSpec from '../RenderablePathSpec';
8
8
  export declare const renderedStylesheetId = "js-draw-style-sheet";
9
+ type FromViewportOptions = {
10
+ sanitize?: boolean;
11
+ /**
12
+ * Rather than having the top left of the `viewBox` set to (0, 0),
13
+ * if `useViewBoxForPositioning` is `true`, the `viewBox`'s top left
14
+ * is based on the top left of the rendering viewport's `visibleRect`.
15
+ */
16
+ useViewBoxForPositioning?: boolean;
17
+ };
9
18
  /**
10
19
  * Renders onto an `SVGElement`.
11
20
  *
@@ -50,8 +59,26 @@ export default class SVGRenderer extends AbstractRenderer {
50
59
  drawPoints(...points: Point2[]): void;
51
60
  drawSVGElem(elem: SVGElement): void;
52
61
  isTooSmallToRender(_rect: Rect2): boolean;
53
- static fromViewport(viewport: Viewport, sanitize?: boolean): {
62
+ private visibleRectOverride;
63
+ /**
64
+ * Overrides the visible region returned by `getVisibleRect`.
65
+ *
66
+ * This is useful when the `viewport`'s transform has been modified,
67
+ * for example, to compensate for storing part of the image's
68
+ * transformation in an SVG property.
69
+ */
70
+ private overrideVisibleRect;
71
+ getVisibleRect(): Rect2;
72
+ /**
73
+ * Creates a new SVG element and `SVGRenerer` with `width`, `height`, `viewBox`,
74
+ * and other metadata attributes set for the given `Viewport`.
75
+ *
76
+ * If `options` is a `boolean`, it is interpreted as whether to sanitize (not add unknown
77
+ * SVG entities to) the output.
78
+ */
79
+ static fromViewport(viewport: Viewport, options?: FromViewportOptions | boolean): {
54
80
  element: SVGSVGElement;
55
81
  renderer: SVGRenderer;
56
82
  };
57
83
  }
84
+ export {};
@@ -42,6 +42,7 @@ class SVGRenderer extends AbstractRenderer_1.default {
42
42
  this.textContainer = null;
43
43
  this.textContainerTransform = null;
44
44
  this.textParentStyle = defaultTextStyle;
45
+ this.visibleRectOverride = null;
45
46
  this.clear();
46
47
  this.addStyleSheet();
47
48
  }
@@ -344,21 +345,71 @@ class SVGRenderer extends AbstractRenderer_1.default {
344
345
  isTooSmallToRender(_rect) {
345
346
  return false;
346
347
  }
347
- // Creates a new SVG element and SVGRenerer with attributes set for the given Viewport.
348
- static fromViewport(viewport, sanitize = true) {
348
+ /**
349
+ * Overrides the visible region returned by `getVisibleRect`.
350
+ *
351
+ * This is useful when the `viewport`'s transform has been modified,
352
+ * for example, to compensate for storing part of the image's
353
+ * transformation in an SVG property.
354
+ */
355
+ overrideVisibleRect(newRect) {
356
+ this.visibleRectOverride = newRect;
357
+ }
358
+ getVisibleRect() {
359
+ return this.visibleRectOverride ?? super.getVisibleRect();
360
+ }
361
+ /**
362
+ * Creates a new SVG element and `SVGRenerer` with `width`, `height`, `viewBox`,
363
+ * and other metadata attributes set for the given `Viewport`.
364
+ *
365
+ * If `options` is a `boolean`, it is interpreted as whether to sanitize (not add unknown
366
+ * SVG entities to) the output.
367
+ */
368
+ static fromViewport(viewport, options = true) {
369
+ let sanitize;
370
+ let useViewBoxForPositioning;
371
+ if (typeof options === 'boolean') {
372
+ sanitize = options;
373
+ useViewBoxForPositioning = false;
374
+ }
375
+ else {
376
+ sanitize = options.sanitize ?? true;
377
+ useViewBoxForPositioning = options.useViewBoxForPositioning ?? false;
378
+ }
349
379
  const svgNameSpace = 'http://www.w3.org/2000/svg';
350
380
  const result = document.createElementNS(svgNameSpace, 'svg');
351
- const rect = viewport.getScreenRectSize();
381
+ const screenRectSize = viewport.getScreenRectSize();
382
+ const visibleRect = viewport.visibleRect;
383
+ let viewBoxComponents;
384
+ if (useViewBoxForPositioning) {
385
+ const exportRect = viewport.visibleRect;
386
+ viewBoxComponents = [
387
+ exportRect.x, exportRect.y, exportRect.w, exportRect.h,
388
+ ];
389
+ // Replace the viewport with a copy that has a modified transform.
390
+ // (Avoids modifying the original viewport).
391
+ viewport = viewport.getTemporaryClone();
392
+ // TODO: This currently discards any rotation information.
393
+ // Render with (0,0) at (0,0) -- the translation is handled by the viewBox.
394
+ viewport.resetTransform(math_1.Mat33.identity);
395
+ }
396
+ else {
397
+ viewBoxComponents = [0, 0, screenRectSize.x, screenRectSize.y];
398
+ }
352
399
  // rect.x -> size of rect in x direction, rect.y -> size of rect in y direction.
353
- result.setAttribute('viewBox', [0, 0, rect.x, rect.y].map(part => (0, math_1.toRoundedString)(part)).join(' '));
354
- result.setAttribute('width', (0, math_1.toRoundedString)(rect.x));
355
- result.setAttribute('height', (0, math_1.toRoundedString)(rect.y));
400
+ result.setAttribute('viewBox', viewBoxComponents.map(part => (0, math_1.toRoundedString)(part)).join(' '));
401
+ result.setAttribute('width', (0, math_1.toRoundedString)(screenRectSize.x));
402
+ result.setAttribute('height', (0, math_1.toRoundedString)(screenRectSize.y));
356
403
  // Ensure the image can be identified as an SVG if downloaded.
357
404
  // See https://jwatt.org/svg/authoring/
358
405
  result.setAttribute('version', '1.1');
359
406
  result.setAttribute('baseProfile', 'full');
360
407
  result.setAttribute('xmlns', svgNameSpace);
361
- return { element: result, renderer: new SVGRenderer(result, viewport, sanitize) };
408
+ const renderer = new SVGRenderer(result, viewport, sanitize);
409
+ if (!visibleRect.eq(viewport.visibleRect)) {
410
+ renderer.overrideVisibleRect(visibleRect);
411
+ }
412
+ return { element: result, renderer };
362
413
  }
363
414
  }
364
415
  exports.default = SVGRenderer;