Problems with export to svg

Hi. I have a diagram with pictures and text in the node. The property ‘imageStretch’ from the picture element is set to ‘UniformToFill’. The diagram shows the picture correctly. But when I export the diagram to SVG, the pictures are not stretched to ‘UniformToFill’. You can see it in the attached picture. I marked it with a red frame. The margin of the picture element is not exported. You can see it in the black frame. The text is not correctly exportet. You can see it in the yellow frame.

Thank you for the helpful report, we’ll fix this and get back to you soon.

In trying to fix this we noticed a bug with Blink’s SVG rendering, where it treats a clip-path referencing a url (that points to a path) differently from simply specifying that same path. We may need to wait for them to address this before this issue is fully fixed.

However we’ve improved things a bit, can you try this build: [EDIT: deleted, due to the release 9f v2.3.6]

And see if it improves the issue in your case? Also, what browsers are you using?

We use all browsers. Chrome, Edge, Safari …

Sorry, but I don`t know how can I test the ‘go236a.js’ file. We use Angular.

Do you notice a difference between Firefox and Chrome?

You could temporarily replace the contents of the go.js file that your system uses.

We’ve just released GoJS 2.3.6 which should fix this issue.

Hi. I test the fix. The images are correct now. But I have new problems with the new version. Please check the attached picture. Some texts are not exported and the expand and collapse image are not exported. I use an own expander button with images for collapse and expand.

Thanks for the report. We’ll investigate and fix it.

The export to jpg or png works correctly.

Could I see your Node template? I’m having trouble reproducing the issue.

Hi. My node template is very complex. We have many settings to change the layout at runtime.
Here is the layout for one template with picture (Angular + TypeScript):

const nodePictureTemplate =
  $(go.Node, 'Spot', this.nodeSpotProperties($),
    $(go.Panel, 'Auto', nodePictureTemplateProperties,
      this.nodeRectangle($, false),
      this.nodeHeader($),
      this.nodeBodyWithPicture($, true, marginBodyWidthHeader),
      this.nodeMenuButtons($)
    ),
    new go.Binding('isTreeExpanded').makeTwoWay(),
    this.nodeExpanderButton($)
  );

private nodeSpotProperties(maker: Function, isSelectorRounded = false): any {
return [{
contextClick: (e) => this.showNodeContextMenu(e.diagram.lastInput),
selectionObjectName: ‘BODY’,
selectionAdornmentTemplate: this.nodeSelector(maker, isSelectorRounded),
click: (e, o) => {
this.onNodeClick(o.part as go.Node);
},
doubleClick: (e, o) => this.onNodeDblClick(o.part as go.Node),
mouseDragEnter: this.nodeMouseDragEnter,
mouseDragLeave: this.nodeMouseDragLeave,
mouseDrop: this.nodeMouseDrop,
toolTip: this.nodeTooltip(maker)
},
new go.Binding(‘layerName’, ‘isSelected’, sel => sel ? ‘Foreground’ : ‘’).ofObject()
];
}

private nodeTooltip(maker: Function): any {
return maker(
go.Adornment, ‘Auto’,
new go.Binding(‘visible’, ‘’, (adorn) => {
const titleTextBlock = adorn.adornedPart.findObject(‘TITLE’);
const subtitleTextBlock = adorn.adornedPart.findObject(‘SUBTITLE’);
return (titleTextBlock.text !== titleTextBlock.metrics.arrText[0] || subtitleTextBlock.text !== subtitleTextBlock.metrics.arrText[0]);
}).ofObject(),
maker(go.Shape, { fill: ‘white’ }),
maker(go.Panel, ‘Table’, {
stretch: go.GraphObject.Fill,
margin: new go.Margin(7, 7, 4, 7)
},
maker(go.RowColumnDefinition, { row: 0, alignment: go.Spot.Center }),
maker(go.RowColumnDefinition, { row: 1, alignment: go.Spot.Center }),
maker(go.TextBlock, { row: 0 }, new go.Binding(‘text’, nameof(‘title’))),
maker(go.TextBlock, { row: 1 }, new go.Binding(‘text’, nameof(‘subtitle’)))
)
);
}

private nodeRectangle(maker: any, isRounded: boolean): any {
return maker(
go.Shape,
(isRounded ? ‘RoundedRectangle’ : ‘Rectangle’), {
name: ‘SHAPE’,
strokeWidth: 1,
stretch: go.GraphObject.Fill,
stroke: OrgChartConstants.nodeRectColor
},
new go.Binding(‘fill’, ‘’, (e, o) => this.getNodeBackgroundColor(o.part as go.Node)),
);
}

private nodeHeader(maker: any): any {
return maker(
go.Shape,
‘Rectangle’, {
fill: OrgChartConstants.nodeHeaderColor,
alignment: go.Spot.Top,
height: 2,
stretch: go.GraphObject.Horizontal,
strokeWidth: 0
}
);
}

private nodeBody(maker: any, titleBold: boolean, margin: go.Margin) {
return this.nodeTable(maker, titleBold);
}

private nodeBodyWithPicture(maker: any, titleBold: boolean, margin: go.Margin) {
return maker(go.Panel, ‘Table’, {
margin: margin,
stretch: go.GraphObject.Fill
},
maker(go.RowColumnDefinition, {
column: 0,
width: (this.pictureLocation === ‘left’ ? OrgChartConstants.pictureWidth : NaN),
stretch: (this.pictureLocation === ‘left’ ? go.GraphObject.None : go.GraphObject.Horizontal)
},
new go.Binding(‘width’, ‘’, () => (this.pictureLocation === ‘left’ ? OrgChartConstants.pictureWidth : NaN)),
new go.Binding(‘stretch’, ‘’, () => (this.pictureLocation === ‘left’ ? go.GraphObject.None : go.GraphObject.Horizontal)),
),
maker(go.RowColumnDefinition, {
column: 1,
width: (this.pictureLocation === ‘left’ ? NaN : OrgChartConstants.pictureWidth),
stretch: (this.pictureLocation === ‘left’ ? go.GraphObject.Horizontal : go.GraphObject.None)
},
new go.Binding(‘width’, ‘’, () => (this.pictureLocation === ‘left’ ? NaN : OrgChartConstants.pictureWidth)),
new go.Binding(‘stretch’, ‘’, () => (this.pictureLocation === ‘left’ ? go.GraphObject.Horizontal : go.GraphObject.None)),
),
this.nodePicture(maker),
this.nodeTable(maker, titleBold),
);
}

private nodePicture(maker: any): any {
return maker(go.Picture, {
name: ‘PICTURE’,
column: (this.pictureLocation === ‘left’ ? 0 : 1),
rowSpan: 4,
stretch: go.GraphObject.Fill,
source: ‘assets/images/OrgChartPicEmpty.png’,
imageStretch: go.GraphObject.UniformToFill,
},
new go.Binding(‘column’, ‘’, (value, obj) => this.pictureLocation === ‘left’ ? 0 : 1),
new go.Binding(‘source’, nameof(‘picture’), (e, o) => this.convertPicture(e)),
);
}

private nodeTable(maker: any, titleBold = false) {
return maker(go.Panel, ‘Table’, {
column: (!this.showPicture || this.pictureLocation === ‘right’ ? 0 : 1),
stretch: go.GraphObject.Fill,
margin: new go.Margin(8, 6, 5, 6)
},
new go.Binding(‘column’, ‘’, (val, obj) => (!this.showPicture || this.pictureLocation === ‘right’ || isNullOrEmpty(obj.part.data.picture) ? 0 : 1)),
maker(go.RowColumnDefinition, { row: 0, alignment: go.Spot.Top, height: 12 }),
maker(go.RowColumnDefinition, { row: 1, alignment: go.Spot.Center }),
maker(go.RowColumnDefinition, { row: 2, alignment: go.Spot.Center }),
maker(go.RowColumnDefinition, { row: 3, alignment: go.Spot.Bottom, height: 12 }),
this.nodeRow0(maker),
this.nodeTitle(maker, titleBold),
this.nodeSubtitle(maker),
this.nodeRow3(maker),
);
}

private nodeRow0(maker) {
return maker(go.Panel, ‘Auto’, {
row: 0,
stretch: go.GraphObject.Fill
},
maker(go.Panel, ‘Table’,
maker(go.RowColumnDefinition, { column: 0, alignment: go.Spot.Left }),
maker(go.RowColumnDefinition, { column: 2, alignment: go.Spot.Right }),
this.nodeUpperLeft(maker),
this.nodeUpperRight(maker),
), this.nodeKey(maker));
}

private nodeRow3(maker) {
return maker(go.Panel, ‘Table’, {
row: 3,
stretch: go.GraphObject.Fill
},
maker(go.RowColumnDefinition, { column: 0, alignment: go.Spot.Left }),
maker(go.RowColumnDefinition, { column: 1, alignment: go.Spot.Right }),
this.nodeLowerLeft(maker),
this.nodeLowerRight(maker)
);
}

private nodeMenuButtons(maker) {
return maker(
go.TextBlock, {
name: ‘MENU_BUTTONS’,
editable: false,
isMultiline: false,
wrap: go.TextBlock.None,
overflow: go.TextBlock.OverflowEllipsis,
alignment: go.Spot.LeftCenter,
font: OrgChartConstants.menuButtonsFontSize + ’ ’ + OrgChartConstants.fonts,
text: ‘…’,
stroke: OrgChartConstants.titleForeColor,
visible: false,
angle: 90,
cursor: ‘pointer’,
click: (e, o) => this.showNodeContextMenu(o.diagram.lastInput)
},
new go.Binding(‘visible’, ‘’, () => this.isEditMode),
new go.Binding(‘margin’, ‘’, (e, o) => {
if (this.orientation === ‘top’ || this.orientation === ‘bottom’) {
if (this.showPicture && this.pictureLocation === ‘left’) {
return new go.Margin(0, -8, 0, 0);
} else {
return new go.Margin(0, 0, 0, 0);
}
} else {
if (this.showPicture) {
if (this.pictureLocation === ‘left’) {
return new go.Margin(0, 0, 2, 42);
} else {
return new go.Margin(0, 0, 2, -42);
}
} else {
return new go.Margin(0, 0, 2, 0);
}
}
}),
new go.Binding(‘alignment’, ‘’, (e, o) => {
if (this.orientation === ‘top’ || this.orientation === ‘bottom’) {
if (this.showPicture && this.pictureLocation === ‘left’) {
return go.Spot.RightCenter;
} else {
return go.Spot.LeftCenter;
}
} else {
return go.Spot.BottomCenter;
}
}),
new go.Binding(‘angle’, ‘’, (e, o) => {
if (this.orientation === ‘top’ || this.orientation === ‘bottom’) {
return 90;
} else {
return 0;
}
})
);
}

private nodeKey(maker: any): any {
return maker(
go.TextBlock, {
name: ‘KEY’,
row: 0,
column: 1,
editable: false,
isMultiline: false,
textValidation: this.validateKey,
wrap: go.TextBlock.None,
overflow: go.TextBlock.OverflowEllipsis,
alignment: go.Spot.Center,
font: OrgChartConstants.keyFontSize + ’ ’ + OrgChartConstants.fonts,
visible: false
},
new go.Binding(‘text’, ‘’, this.keyConverter).makeTwoWay(this.keyBackConverter),
new go.Binding(‘visible’, ‘’, () => this.isEditMode || this.showAlwaysId),
new go.Binding(‘editable’, ‘’, () => this.isEditMode)
);
}

private nodeUpperLeft(maker: any): any {
return maker(go.Panel, ‘Horizontal’, {
row: 0,
column: 0,
alignment: go.Spot.Left
},
maker(
go.TextBlock, {
name: ‘CHILDREN_COUNT_UPPER_LEFT’,
font: OrgChartConstants.upperLowerFontSize + ’ ’ + OrgChartConstants.fonts,
margin: new go.Margin(0, 5, 0, 0)
},
new go.Binding(‘text’, nameof(‘childrenCount’), this.childrenCountConverter),
new go.Binding(‘visible’, ‘’, (e, o) =>
this.childrenCountLocation === ‘upperLeft’ && ((o.part as go.Node).data as OrgChartNode).childrenCount > 0)
),
maker(
go.TextBlock, {
editable: false,
isMultiline: false,
font: OrgChartConstants.upperLowerFontSize + ’ ’ + OrgChartConstants.fonts
},
new go.Binding(‘text’, ‘’, (e, o) => ((o.part as go.Node).data as OrgChartNode)?.upperLeft).makeTwoWay((value, data, model) => model.setDataProperty(data, nameof(‘upperLeft’), value)),
new go.Binding(‘editable’, ‘’, () => this.isEditMode)
)
);
}

private nodeUpperRight(maker: any): any {
return maker(go.Panel, ‘Horizontal’, {
row: 0,
column: 1,
alignment: go.Spot.Right
},
maker(
go.TextBlock, {
editable: false,
isMultiline: false,
font: OrgChartConstants.upperLowerFontSize + ’ ’ + OrgChartConstants.fonts
},
new go.Binding(‘text’, ‘’, (e, o) => ((o.part as go.Node).data as OrgChartNode)?.upperRight).makeTwoWay((value, data, model) => model.setDataProperty(data, nameof(‘upperRight’), value)),
new go.Binding(‘editable’, ‘’, () => this.isEditMode)
),
maker(
go.TextBlock, {
name: ‘CHILDREN_COUNT_UPPER_RIGHT’,
font: OrgChartConstants.upperLowerFontSize + ’ ’ + OrgChartConstants.fonts,
margin: new go.Margin(0, 0, 0, 5)
},
new go.Binding(‘text’, ‘childrenCount’, this.childrenCountConverter),
new go.Binding(‘visible’, ‘’, (e, o) =>
this.childrenCountLocation === ‘upperRight’ && ((o.part as go.Node).data as OrgChartNode).childrenCount > 0)
),
);
}

private nodeLowerLeft(maker: any): any {
return maker(go.Panel, ‘Horizontal’, {
row: 0,
column: 0,
alignment: go.Spot.Left
},
maker(
go.TextBlock, {
name: ‘CHILDREN_COUNT_LOWER_LEFT’,
font: OrgChartConstants.upperLowerFontSize + ’ ’ + OrgChartConstants.fonts,
margin: new go.Margin(0, 5, 0, 0)
},
new go.Binding(‘text’, ‘childrenCount’, this.childrenCountConverter),
new go.Binding(‘visible’, ‘’, (e, o) =>
this.childrenCountLocation === ‘lowerLeft’ && ((o.part as go.Node).data as OrgChartNode).childrenCount > 0)
),
maker(
go.TextBlock, {
editable: false,
isMultiline: false,
font: OrgChartConstants.upperLowerFontSize + ’ ’ + OrgChartConstants.fonts,
},
new go.Binding(‘text’, ‘’, (e, o) => ((o.part as go.Node).data as OrgChartNode)?.lowerLeft).makeTwoWay((value, data, model) => model.setDataProperty(data, nameof(‘lowerLeft’), value)),
new go.Binding(‘editable’, ‘’, () => this.isEditMode)
)
);
}

private nodeLowerRight(maker: any): any {
return maker(go.Panel, ‘Horizontal’, {
row: 0,
column: 1,
alignment: go.Spot.Right
},
maker(
go.TextBlock, {
editable: false,
isMultiline: false,
font: OrgChartConstants.upperLowerFontSize + ’ ’ + OrgChartConstants.fonts,
},
new go.Binding(‘text’, ‘’, (e, o) => ((o.part as go.Node).data as OrgChartNode)?.lowerRight).makeTwoWay((value, data, model) => model.setDataProperty(data, nameof(‘lowerRight’), value)),
new go.Binding(‘editable’, ‘’, () => this.isEditMode)
),
maker(
go.TextBlock, {
name: ‘CHILDREN_COUNT_LOWER_RIGHT’,
font: OrgChartConstants.upperLowerFontSize + ’ ’ + OrgChartConstants.fonts,
margin: new go.Margin(0, 0, 0, 5)
},
new go.Binding(‘text’, ‘childrenCount’, this.childrenCountConverter),
new go.Binding(‘visible’, ‘’, (e, o) =>
this.childrenCountLocation === ‘lowerRight’ && ((o.part as go.Node).data as OrgChartNode).childrenCount > 0)
),
);
}

private nodeTitle(maker: any, bold = false): any {
return maker(
go.TextBlock, {
name: ‘TITLE’,
row: 1,
column: 0,
editable: false,
isMultiline: false,
maxSize: new go.Size(OrgChartConstants.textBlockMaxWidth, NaN),
wrap: go.TextBlock.None,
overflow: go.TextBlock.OverflowEllipsis,
alignment: go.Spot.BottomCenter,
font: (bold ? OrgChartConstants.titleBoldFontSize : OrgChartConstants.titleFontSize) + ’ ’ + OrgChartConstants.fonts,
stroke: OrgChartConstants.titleForeColor,
},
new go.Binding(‘text’, ‘’, (e, o) => ((o.part as go.Node).data as OrgChartNode)?.title).makeTwoWay((value, data, model) => model.setDataProperty(data, nameof(‘title’), value)),
new go.Binding(‘editable’, ‘’, () => this.isEditMode),
new go.Binding(‘maxSize’, ‘’, () => this.isEditMode ? new go.Size(178, NaN) : new go.Size(OrgChartConstants.textBlockMaxWidth, NaN))
);
}

private nodeSubtitle(maker: any): any {
return maker(
go.TextBlock, {
name: ‘SUBTITLE’,
row: 2,
column: 0,
editable: false,
isMultiline: false,
maxSize: new go.Size(OrgChartConstants.textBlockMaxWidth, NaN),
wrap: go.TextBlock.None,
overflow: go.TextBlock.OverflowEllipsis,
alignment: go.Spot.TopCenter,
font: OrgChartConstants.subtitleFontSize + ’ ’ + OrgChartConstants.fonts,
},
new go.Binding(‘text’, ‘’, (e, o) => ((o.part as go.Node).data as OrgChartNode)?.subtitle).makeTwoWay((value, data, model) => model.setDataProperty(data, nameof(‘subtitle’), value)),
new go.Binding(‘editable’, ‘’, () => this.isEditMode),
new go.Binding(‘maxSize’, ‘’, () => this.isEditMode ? new go.Size(178, NaN) : new go.Size(OrgChartConstants.textBlockMaxWidth, NaN))
);
}

private nodeSelector(maker: any, isRounded = false): any {
return maker(go.Adornment, ‘Spot’,
maker(go.Panel, ‘Auto’,
maker(go.Shape, (isRounded ? ‘RoundedRectangle’ : ‘Rectangle’), {
fill: null,
stroke: OrgChartConstants.nodeAdornmentColor,
strokeWidth: OrgChartConstants.nodeAdornmentStrokeWidth,
margin: isRounded ? 2 : 1
}),
maker(go.Placeholder)
),
this.nodeExpanderButton(maker, true, isRounded)
);
}

private nodeExpanderButton(maker: any, isSelector = false, isRounded = false): any {
return maker(‘Button’, new go.Binding(‘visible’, ‘’, (e, o) => this.getNodeFromGraphObject(o).findTreeChildrenNodes().count > 0).ofObject(), {
‘ButtonBorder.figure’: ‘Circle’,
‘ButtonBorder.stroke’: (isSelector ? OrgChartConstants.nodeAdornmentColor : OrgChartConstants.nodeRectColor),
‘ButtonBorder.strokeWidth’: (isSelector ? OrgChartConstants.nodeAdornmentStrokeWidth : 1)
},
isSelector ? { ‘_buttonStrokeOver’: OrgChartConstants.nodeAdornmentExpanderStroke } : { },
isSelector ? { ‘_buttonStrokePressed’: OrgChartConstants.nodeAdornmentExpanderStroke } : { },
new go.Binding(‘alignment’, ‘’, (e, o) => {
if (this.orientation === ‘top’) {
return new go.Spot(0.5, 1, 0, isSelector ? (isRounded ? -4 : -2) : 0);
} else if (this.orientation === ‘bottom’) {
return new go.Spot(0.5, 0, 0, isSelector ? (isRounded ? 4 : 1) : 0);
} else if (this.orientation === ‘left’) {
return new go.Spot(1, 0.5, isSelector ? (isRounded ? -4 : -2) : 0, 0);
} else {
return new go.Spot(0, 0.5, isSelector ? (isRounded ? 4 : 1) : 0, 0);
}
}), {
width: isSelector ? OrgChartConstants.nodeExpanderWidth + 1 : OrgChartConstants.nodeExpanderWidth,
height: isSelector ? OrgChartConstants.nodeExpanderHeight + 1 : OrgChartConstants.nodeExpanderHeight,
click: (e, o) => {
e.handled = true;
const node = this.getNodeFromGraphObject(o);
(node.isTreeExpanded ? this.diagram.commandHandler.collapseTree(node) : this.diagram.commandHandler.expandTree(node));
}
},
maker(go.Picture, { width: 16, height: 16 }, new go.Binding(‘source’, ‘’, (e, o) => this.getNodeFromGraphObject(o).isTreeExpanded ? ‘assets/images/S/OrgChartCollapse.svg’ : ‘assets/images/S/OrgChartExpand.svg’))
);
}

I have built a new version that I think might fix these issues. Would you mind testing it? You would need to copy these files:

[EDIT: deleted due to release of v2.3.7]

And replace the ones in your node_modules/gojs/release/ folder. Then you might need to rebuild your Angular project fully, which probably means deleting something like the node_modules/.bin folder (wherever Angular temporarily bundles things).

Hi.I tried it with the new files but I become the same result. With version 2.3.7 too.

Could you please show us the problem(s)?