clientIO / joint

A proven SVG-based JavaScript diagramming library powering exceptional UIs
https://jointjs.com
Mozilla Public License 2.0
4.73k stars 853 forks source link

Visio export not adding images #2698

Closed LeleDallas closed 1 month ago

LeleDallas commented 5 months ago

Hellođź‘‹!

In joint plus I'm trying to export a custom diagram with custom images in vsdx format but all images and styles are blank... How can I solve it?

Steps to reproduce

Code used for exporting data

const archive = await VisioArchive.fromURL(visioUrl);
const { document } = archive;

const [page0] = document.getPages();

await page0.fromPaper(monitorMapPaper.options.paper);

const blob = await archive.toVSDX({ type: 'blob' });
util.downloadBlob(blob, `map_${currentMapId}.vsdx`);

Starting paper

image

Result

image

Version

^4.0.4

What browsers are you seeing the problem on?

Chrome

What operating system are you seeing the problem on?

Windows

Geliogabalus commented 4 months ago

Hello! Can you provide your definition of a shape which contains the image and the image itself? For example that one with a laptop icon.

LeleDallas commented 4 months ago

Thanks for reply! That's how we define in genral the shape:

export class DeviceIconModel extends shapes.standard.Rectangle {
  defaults() {
    return util.defaultsDeep(
      {
        type: 'mapShapes.DeviceIconModel'
      },
      shapes.standard.Rectangle.prototype.defaults
    );
  }
} 

That's how we define the shap you ask

function buildDeviceIconModel(item: Icon, colors: { bg: string; label: string }, itemsInfo?: MapAsset[]) {
  const mapAssetId =...
  const deviceDescription = ...
  const assetEntityId = ...
  const icon = new DeviceIconModel({
    size: {
      width: (item.size.width + item.size.height) / 2,
      height: (item.size.width + item.size.height) / 2
    },
    position: item.position,
    cursor: 'default',
    attrs: {
      root: {
        'data-tooltip': deviceDescription ?? null
      },
      customAttr: {
        mapObjId: item.mapObjId,
        mapObjType: item.mapObjType,
        icon: item.icon,
        mapAssetId: mapAssetId,
        link: item.link,
        deviceType: item.deviceType,
        mapAssetEntityId: assetEntityId
      },
      body: {
        stroke: 'transparent',
        fill: 'transparent',
        position: { x: item.position.x, y: item.position.y },
        strokeWidth: 0,
        cursor: 'default'
      },
      label: {
        text: MapIconsDictionary[item.icon.replace('.gif', '')],
        transform: 'translate(0,5)',
        stroke: 'transparent',
        fill: '#5baadf',
        position: item.position,
        fontFamily: 'VemIcons',
        strokeWidth: 0,
        fontSize: (item.size.width + item.size.height) / 2,
        cursor: 'default'
      }
    }
  });

  return icon;
}

The MapIconsDictionary it's an enum with entries defined like that:

pc: '\ue931' 

These icons are coming from a custom font (VemIcons), could it be that the problem?

Let me know if you need something else

Geliogabalus commented 4 months ago

Hi! Currently, it is impossible to export icons from fonts. I can suggest the following custom shape declaration for your needs that works:

const markup = util.svg`
    <image @selector='icon'></image>
`;

class DeviceIconModel extends dia.Element {
    defaults() {
        return util.defaultsDeep(
            {
                type: 'mapShapes.DeviceIconModel',
                attrs: {
                    icon: {
                        href: 'assets/icons/heart.svg',
                        width: 48,
                        height: 48
                    }
                },
            },
            super.defaults
        );
    }

    preinitialize(): void {
        this.markup = markup;
    }
}

Here I am creating an element with a single image element inside. You can put an image URI in href attribute. Note, that you can't style the image using attributes, so if you want to change its color you should do it in the svg file. Normally, you should be able to access the icon in SVG format from your icon set.

Creating new shape allows you omit body override (you make it transparent) and give you more control over the markup.

Additionally you should not store custom model attributes inside attrs object. attrs object is reserved for presentation attributes (the attributes in the markup). To store custom data you should set model attributes directly. In your case you can create an element like this:

const icon = new DeviceIconModel({
    size: {
      width: (item.size.width + item.size.height) / 2,
      height: (item.size.width + item.size.height) / 2
    },
    position: item.position,
    cursor: 'default',
    // store your data her
    data: {
        mapObjId: item.mapObjId,
        mapObjType: item.mapObjType,
        icon: item.icon,
        mapAssetId: mapAssetId,
        link: item.link,
        deviceType: item.deviceType,
        mapAssetEntityId: assetEntityId
    },
    attrs: {
      root: {
        'data-tooltip': deviceDescription ?? null
      },
      icon: {
        // map the path to the svg image
        href: MapIconsDictionary[item.icon.replace('.gif', '')],
        transform: 'translate(0,5)',
        cursor: 'default'
      }
    }
  });

When you store your data on model you can access it via set()/get() functions or using prop() method for complex property path. You can learn about it in-depth here.

LeleDallas commented 4 months ago

I'll try this solution thanks