@@ -31,15 +31,15 @@ const patch = init([
3131 eventListenersModule , // attaches event listeners
3232] ) ;
3333
34- interface SaveImageAction extends Action {
34+ interface ExportAction extends Action {
3535 saveType : "svg" | "pdf" ;
3636 selectionOnly : boolean ;
3737}
3838
39- export namespace SaveImageAction {
39+ export namespace ExportAction {
4040 export const KIND = "save-image" ;
4141
42- export function create ( saveType : "svg" | "pdf" , selectionOnly : boolean ) : SaveImageAction {
42+ export function create ( saveType : "svg" | "pdf" , selectionOnly : boolean ) : ExportAction {
4343 return {
4444 kind : KIND ,
4545 saveType,
@@ -54,12 +54,15 @@ interface SVGResult {
5454 height : number ;
5555}
5656
57- export class SaveImageCommand extends Command {
58- static readonly KIND = SaveImageAction . KIND ;
57+ /**
58+ * Exports the diagram as either a svg or pdf
59+ */
60+ export class ExportCommand extends Command {
61+ static readonly KIND = ExportAction . KIND ;
5962 private static readonly PADDING = 5 ;
6063
6164 constructor (
62- @inject ( TYPES . Action ) private readonly action : SaveImageAction ,
65+ @inject ( TYPES . Action ) private readonly action : ExportAction ,
6366 @inject ( FileName ) private readonly fileName : FileName ,
6467 @inject ( TYPES . ViewRegistry ) private readonly viewRegistry : ViewRegistry ,
6568 @multiInject ( TYPES . IVNodePostprocessor ) private readonly postProcessors : IVNodePostprocessor [ ] ,
@@ -98,6 +101,11 @@ export class SaveImageCommand extends Command {
98101 return context . root ;
99102 }
100103
104+ /**
105+ * Generates saveable svg code
106+ * @param dom A dummy root element attached to the Dom
107+ * @returns The generated svg
108+ */
101109 getSVG ( context : CommandExecutionContext , dom : HTMLElement ) : SVGResult | undefined {
102110 // render diagram virtually
103111 if ( this . action . selectionOnly ) {
@@ -147,11 +155,11 @@ export class SaveImageCommand extends Command {
147155 if ( ! holderG . data ) holderG . data = { } ;
148156 if ( ! holderG . data . attrs ) holderG . data . attrs = { } ;
149157 holderG . data . attrs [ "transform" ] =
150- `translate(${ - minTranslate . x + SaveImageCommand . PADDING } ,${ - minTranslate . y + SaveImageCommand . PADDING } )` ;
158+ `translate(${ - minTranslate . x + ExportCommand . PADDING } ,${ - minTranslate . y + ExportCommand . PADDING } )` ;
151159 if ( ! svg . data ) svg . data = { } ;
152160 if ( ! svg . data . attrs ) svg . data . attrs = { } ;
153- const width = maxSize . x - minTranslate . x + 2 * SaveImageCommand . PADDING ;
154- const height = maxSize . y - minTranslate . y + 2 * SaveImageCommand . PADDING ;
161+ const width = maxSize . x - minTranslate . x + 2 * ExportCommand . PADDING ;
162+ const height = maxSize . y - minTranslate . y + 2 * ExportCommand . PADDING ;
155163 svg . data . attrs . width = width ;
156164 svg . data . attrs . height = height ;
157165 svg . data . attrs . viewBox = `0 0 ${ width } ${ height } ` ;
@@ -189,6 +197,11 @@ export class SaveImageCommand extends Command {
189197 link . click ( ) ;
190198 }
191199
200+ /**
201+ * Recursively removes the selected class
202+ * Should be called before rendering
203+ * @param v Root VNode
204+ */
192205 private removeSelectedClass ( v : VNode ) {
193206 if ( v . data ?. class ?. selected ) {
194207 v . data . class . selected = false ;
@@ -201,6 +214,12 @@ export class SaveImageCommand extends Command {
201214 }
202215 }
203216
217+ /**
218+ * Recursively removes the routing handles of edges.
219+ * Needs to happen before removing classes
220+ * @param v
221+ * @returns
222+ */
204223 private removeRoutingHandles ( v : VNode ) {
205224 if ( ! v . children ) return ;
206225 v . children = v . children ?. filter ( ( c ) => {
@@ -213,6 +232,10 @@ export class SaveImageCommand extends Command {
213232 }
214233 }
215234
235+ /**
236+ * Recursively transforms the computed style of the html elements to properties on the VNode
237+ * @param v The current VNode
238+ */
216239 private transformStyleToAttributes ( v : VNode ) {
217240 if ( ! v . elm ) return ;
218241
@@ -263,6 +286,10 @@ export class SaveImageCommand extends Command {
263286 }
264287 }
265288
289+ /**
290+ * Recursively removes html attributes the svg file does not need.
291+ * @param v The current VNode
292+ */
266293 private removeUnusedAttributes ( v : VNode ) {
267294 if ( ! v . data ) v . data = { } ;
268295 if ( v . data . attrs ) {
@@ -282,6 +309,13 @@ export class SaveImageCommand extends Command {
282309 }
283310 }
284311
312+ /**
313+ * Recursively iterates the VNodes an centers the text position manually.
314+ * This should happen after transforming the style to attributes
315+ * @param v Current VNode
316+ * @param maxSiblingSize biggest size of siblings
317+ * @param maxSiblingX biggest x of siblings
318+ */
285319 private centerText ( v : VNode , maxSiblingSize : number = 0 , maxSiblingX : number = 0 ) {
286320 if ( getVNodeSVGType ( v ) == "text" ) {
287321 if ( ! v . data ) v . data = { } ;
@@ -341,7 +375,6 @@ function getMinTranslate(e: VNode, parentOffset: { x: number; y: number } = { x:
341375
342376/**
343377 * Calculates the absolute translation of an element relative to the svg.
344- * If the element has no translation, the offset of the parent is returned.
345378 * @param e the element to get the translation from
346379 * @param parentOffset Offset of the containing element
347380 * @returns Offset of the child relative to the svg
@@ -363,6 +396,13 @@ function getTranslate(
363396 return { x : newX , y : newY } ;
364397}
365398
399+ /**
400+ * Gets the maximum size the canvas needs by adding its position and size and finding the maximum of this among children.
401+ * This is done by recursively.
402+ * @param e the root element for the sizing
403+ * @param parentOffset Offset of the containing element
404+ * @returns Required canvas size
405+ */
366406function getMaxRequiredCanvasSize (
367407 e : VNode ,
368408 parentOffset : { x : number ; y : number } = { x : 0 , y : 0 } ,
@@ -380,6 +420,12 @@ function getMaxRequiredCanvasSize(
380420 return maxSize ;
381421}
382422
423+ /**
424+ * Calculates the size the canvas needs to be to accommodate the given element
425+ * @param e the element to calculate for
426+ * @param parentOffset Offset of the containing element
427+ * @returns The size required for the element
428+ */
383429function getRequiredCanvasSize (
384430 e : VNode ,
385431 parentOffset : { x : number ; y : number } = { x : 0 , y : 0 } ,
@@ -397,6 +443,10 @@ function getVNodeSVGType(v: VNode): string | undefined {
397443 return v . sel ?. split ( / # | \. / ) [ 0 ] ;
398444}
399445
446+ /**
447+ * @param v VNode to check
448+ * @returns The relevant style properties for the node type
449+ */
400450function getRelevantStyleProps ( v : VNode ) : string [ ] {
401451 const type = getVNodeSVGType ( v ) ;
402452 switch ( type ) {
@@ -418,6 +468,10 @@ function getRelevantStyleProps(v: VNode): string[] {
418468 }
419469}
420470
471+ /**
472+ * @param key CSS key
473+ * @returns The default value for a given CSS key
474+ */
421475function getDefaultPropertyValues ( key : string ) {
422476 switch ( key ) {
423477 case "stroke-dasharray" :
@@ -433,6 +487,9 @@ function getDefaultPropertyValues(key: string) {
433487 }
434488}
435489
490+ /**
491+ * VNodePostprocessor removing all non-selected elements
492+ */
436493class SelectionPostProcessor implements IVNodePostprocessor {
437494 decorate ( v : VNode , element : SModelElementImpl ) : VNode {
438495 let shouldRender = this . isSelected ( element ) ;
0 commit comments