Bug 526775 - Problems saving diagram to image
Summary: Problems saving diagram to image
Status: UNCONFIRMED
Alias: None
Product: Graphiti
Classification: Modeling
Component: Core (show other bugs)
Version: 0.12.0   Edit
Hardware: PC Windows 7
: P3 major (vote)
Target Milestone: ---   Edit
Assignee: Project Inbox CLA
QA Contact:
URL: https://www.eclipse.org/forums/index....
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2017-11-02 11:55 EDT by Ignacio Ibanez CLA
Modified: 2017-11-04 12:25 EDT (History)
2 users (show)

See Also:


Attachments
Differences from original diagram to generated Image (42.23 KB, image/png)
2017-11-02 11:55 EDT, Ignacio Ibanez CLA
no flags Details

Note You need to log in before you can comment on or make changes to this bug.
Description Ignacio Ibanez CLA 2017-11-02 11:55:12 EDT
Created attachment 271302 [details]
Differences from original diagram to generated Image

When using the SaveImageFeature, right after saving a diagram in the editor, in some situations the generated image is missing some elements to draw. Those situations look pseudo-random. This is what I´ve found after a few dozen tests:

-It will not draw any pictogram inside the container shape (it will keep text), inclduing decorators.
-Everything else seems to get drawn correctly.
-It looks to happen randomly. Doing the same thing twice will sometimes trigger it sometimes not. Maybe there is an underlying race condition?
-It seems to happen more often when removing than adding elements
-It seems to happen more often when removing connections than other elements.
-If you create the image again, changing nothing in the diagram, it generates the image properly.

Please find examples attached as images.

Code:

This is the creation of the image, using a implementation of org.eclipse.graphiti.features.ISaveImageFeature

Code:
...
IDiagramTypeProvider dtp;
		dtp = GraphitiUi
				.getExtensionManager()
				.createDiagramTypeProvider(diagram,
						"TypeProvider"); //$NON-NLS-1$
		ISaveDocImageFeature flowSaveImageFeature = ((FlowFeatureProvider) dtp.getFeatureProvider()).getSaveDocImageFeature();
		SaveImageContext context = new SaveImageContext();
		context.putProperty(ISaveDocImageFeature.EDITOR_PROPERTY_ID, fde);
		flowSaveImageFeature.execute(context);
...


Big chunk of the definition of the Yellow element (dialog activity) which little screen inside is not getting drawn. Part of the "cascaded elements" are failing to get drawn.

Code: 
...
 @Override
    public ContainerShape getShape(ContainerShape targetContainer, ISprayStyle sprayStyle) {
        // Create a ContainerShape for this Shape
        Diagram diagram = peService.getDiagramForShape(targetContainer);
        ContainerShape containerShape = peCreateService.createContainerShape(targetContainer, true);
        SprayLayoutService.setId(containerShape, "FlowShapeDialogActivity.containerShape");

        // define general layout for ContainerShape
        sprayStyle = new com.wincornixdorf.tools.flow.spray.styles.FlowYellow();
        sprayStyle.getStyle(diagram).setProportional(false);
        sprayStyle.getStyle(diagram).setStretchH(false);
        sprayStyle.getStyle(diagram).setStretchV(false);

        // layout data
        SprayLayoutType containerLayout = SprayLayoutType.FIT;
        SprayLayoutService.setLayoutManager(containerShape, containerLayout, 0, 0, true);
        SprayLayoutService.getLayoutData(containerShape).setVisible(true);

        createCascadedElements(diagram, containerShape, sprayStyle);
        createAnchorPoints(diagram, containerShape);

        // Fix the broken coordinate syaten for not active container shapes
        SprayAbstractLayoutManager.fixOffset(containerShape);

        return containerShape;
    }

    // START GENERATING CASCADED ELEMENTS
    protected void createCascadedElements(Diagram diagram, ContainerShape containerShape, ISprayStyle sprayStyle) {
        IDirectEditingInfo directEditingInfo = getFeatureProvider().getDirectEditingInfo();
        directEditingInfo.setMainPictogramElement(containerShape);
        directEditingInfo.setPictogramElement(containerShape);

        GraphicsAlgorithm element_0 = gaService.createInvisibleRectangle(containerShape);
        element_0.setStyle(sprayStyle.getStyle(diagram));
        SprayLayoutService.setShapeFromDsl(containerShape, true);
        gaService.setLocationAndSize(element_0, 0, 0, 150, 65);

        createElement_1(diagram, containerShape, sprayStyle);

        // Set start values for height and width as properties on the element for Layout Feature
        SprayLayoutManager.setSizePictogramProperties(containerShape);
    }

    protected Shape createElement_2(Diagram diagram, ContainerShape parentShape, ISprayStyle sprayStyle) {
        Shape shape_2 = peCreateService.createShape(parentShape, false);
        SprayLayoutService.setId(shape_2, "FlowShapeDialogActivity.shape_2");
        Text element_2 = gaService.createPlainText(shape_2);
        ISprayStyle style_2 = new com.wincornixdorf.tools.flow.spray.styles.FlowTextBold();
        element_2.setStyle(style_2.getStyle(diagram));
        gaService.setLocationAndSize(element_2, 5, 0, 130, 8);
        SprayLayoutService.setLayoutData(shape_2, 130, 8, true);
        element_2.setHorizontalAlignment(Orientation.ALIGNMENT_CENTER);
        element_2.setVerticalAlignment(Orientation.ALIGNMENT_BOTTOM);
        peService.setPropertyValue(element_2, ISprayConstants.TEXT_ID, TextIds.activityName.name());
        peService.setPropertyValue(shape_2, ISprayConstants.TEXT_ID, TextIds.activityName.name());
        element_2.setValue("");
        getFeatureProvider().getDirectEditingInfo().setGraphicsAlgorithm(element_2);
        return shape_2;
    }

    protected Shape createElement_3(Diagram diagram, ContainerShape parentShape, ISprayStyle sprayStyle) {
        List<Point> pointList_3 = new ArrayList<Point>();
        pointList_3.add(gaService.createPoint(1, 20, 0, 0));
        pointList_3.add(gaService.createPoint(149, 20, 0, 0));
        Shape shape_3 = peCreateService.createShape(parentShape, false);
        SprayLayoutService.setId(shape_3, "FlowShapeDialogActivity.shape_3");
        Polyline element_3 = gaService.createPlainPolyline(shape_3, pointList_3);
        ISprayStyle style_3 = sprayStyle;
        element_3.setStyle(style_3.getStyle(diagram));
        return shape_3;
    }

    protected Shape createElement_4(Diagram diagram, ContainerShape parentShape, ISprayStyle sprayStyle) {
        Shape shape_4 = peCreateService.createShape(parentShape, false);
        SprayLayoutService.setId(shape_4, "FlowShapeDialogActivity.shape_4");
        Text element_4 = gaService.createPlainText(shape_4);
        ISprayStyle style_4 = new com.wincornixdorf.tools.flow.spray.styles.FlowText();
        element_4.setStyle(style_4.getStyle(diagram));
        gaService.setLocationAndSize(element_4, 5, 16, 6, 8);
        SprayLayoutService.setLayoutData(shape_4, 6, 8, true);
        element_4.setHorizontalAlignment(Orientation.ALIGNMENT_LEFT);
        element_4.setVerticalAlignment(Orientation.ALIGNMENT_BOTTOM);
        peService.setPropertyValue(element_4, ISprayConstants.TEXT_ID, TextIds.activityActionLabel.name());
        peService.setPropertyValue(shape_4, ISprayConstants.TEXT_ID, TextIds.activityActionLabel.name());
        element_4.setValue("");
        getFeatureProvider().getDirectEditingInfo().setGraphicsAlgorithm(element_4);
        return shape_4;
    }

    protected Shape createElement_5(Diagram diagram, ContainerShape parentShape, ISprayStyle sprayStyle) {
        Shape shape_5 = peCreateService.createShape(parentShape, false);
        SprayLayoutService.setId(shape_5, "FlowShapeDialogActivity.shape_5");
        Text element_5 = gaService.createPlainText(shape_5);
        ISprayStyle style_5 = new com.wincornixdorf.tools.flow.spray.styles.FlowText();
        element_5.setStyle(style_5.getStyle(diagram));
        gaService.setLocationAndSize(element_5, 65, 16, 80, 8);
        SprayLayoutService.setLayoutData(shape_5, 80, 8, true);
        element_5.setHorizontalAlignment(Orientation.ALIGNMENT_LEFT);
        element_5.setVerticalAlignment(Orientation.ALIGNMENT_BOTTOM);
        peService.setPropertyValue(element_5, ISprayConstants.TEXT_ID, TextIds.activityAction.name());
        peService.setPropertyValue(shape_5, ISprayConstants.TEXT_ID, TextIds.activityAction.name());
        element_5.setValue("");
        getFeatureProvider().getDirectEditingInfo().setGraphicsAlgorithm(element_5);
        return shape_5;
    }

    protected Shape createElement_6(Diagram diagram, ContainerShape parentShape, ISprayStyle sprayStyle) {
        Shape shape_6 = peCreateService.createShape(parentShape, false);
        SprayLayoutService.setId(shape_6, "FlowShapeDialogActivity.shape_6");
        Text element_6 = gaService.createPlainText(shape_6);
        ISprayStyle style_6 = new com.wincornixdorf.tools.flow.spray.styles.FlowText();
        element_6.setStyle(style_6.getStyle(diagram));
        gaService.setLocationAndSize(element_6, 5, 30, 60, 8);
        SprayLayoutService.setLayoutData(shape_6, 60, 8, true);
        element_6.setHorizontalAlignment(Orientation.ALIGNMENT_LEFT);
        element_6.setVerticalAlignment(Orientation.ALIGNMENT_BOTTOM);
        peService.setPropertyValue(element_6, ISprayConstants.TEXT_ID, TextIds.activityParameterLabel.name());
        peService.setPropertyValue(shape_6, ISprayConstants.TEXT_ID, TextIds.activityParameterLabel.name());
        element_6.setValue("");
        getFeatureProvider().getDirectEditingInfo().setGraphicsAlgorithm(element_6);
        return shape_6;
    }

    protected Shape createElement_7(Diagram diagram, ContainerShape parentShape, ISprayStyle sprayStyle) {
        Shape shape_7 = peCreateService.createShape(parentShape, false);
        SprayLayoutService.setId(shape_7, "FlowShapeDialogActivity.shape_7");
        Text element_7 = gaService.createPlainText(shape_7);
        ISprayStyle style_7 = new com.wincornixdorf.tools.flow.spray.styles.FlowText();
        element_7.setStyle(style_7.getStyle(diagram));
        gaService.setLocationAndSize(element_7, 65, 30, 80, 8);
        SprayLayoutService.setLayoutData(shape_7, 80, 8, true);
        element_7.setHorizontalAlignment(Orientation.ALIGNMENT_LEFT);
        element_7.setVerticalAlignment(Orientation.ALIGNMENT_BOTTOM);
        peService.setPropertyValue(element_7, ISprayConstants.TEXT_ID, TextIds.activityParameter.name());
        peService.setPropertyValue(shape_7, ISprayConstants.TEXT_ID, TextIds.activityParameter.name());
        element_7.setValue("");
        getFeatureProvider().getDirectEditingInfo().setGraphicsAlgorithm(element_7);
        return shape_7;
    }

    protected Shape createElement_1(Diagram diagram, ContainerShape parentShape, ISprayStyle sprayStyle) {
        ContainerShape shape_1 = peCreateService.createContainerShape(parentShape, false);
        SprayLayoutService.setId(shape_1, "FlowShapeDialogActivity.shape_1");
        RoundedRectangle element_1 = gaService.createPlainRoundedRectangle(shape_1, 15, 15);
        ISprayStyle style_1 = new com.wincornixdorf.tools.flow.spray.styles.FlowYellow();
        element_1.setStyle(style_1.getStyle(diagram));
        gaService.setLocationAndSize(element_1, 0, 0, 150, 65);
        SprayLayoutService.setLayoutData(shape_1, 150, 65, true);
        createElement_2(diagram, shape_1, style_1);
        createElement_3(diagram, shape_1, style_1);
        createElement_4(diagram, shape_1, style_1);
        createElement_5(diagram, shape_1, style_1);
        createElement_6(diagram, shape_1, style_1);
        createElement_7(diagram, shape_1, style_1);
        return shape_1;
    }

    // STOP GENERATING CASCADED ELEMENTS

    protected void createAnchorPoints(Diagram diagram, ContainerShape containerShape) {
        peCreateService.createChopboxAnchor(containerShape);
    }

    public SprayLayoutManager getShapeLayout() {
...
    }
Comment 1 Michael Wenz CLA 2017-11-03 09:23:09 EDT
Ignacio,

what is the ISaveDocImageFeature  and its implementation? Can you share the code?

Michael
Comment 2 Ignacio Ibanez CLA 2017-11-04 12:25:23 EDT
Hi Michael,

Sure thing. I will provide you with some bigger snippets of components that play a role in this. Hopefully this can help. If you need any other info please let me know!

ISaveDocImageFeature is quite empty:

--------
...
import org.eclipse.graphiti.features.ISaveImageFeature;
...

public interface ISaveDocImageFeature extends ISaveImageFeature {

    /**
     * The property id.
     */
    String EDITOR_PROPERTY_ID = "editor"; //$NON-NLS-1$
    
    void setProject(IProject project);
}
...
-------

What might be more interesting for you is a save image feature definition and the save conf used.

-------
/**
 * Feature to save an image file of Graphiti based editor.
 * 
 * @author ignacio.ibanez
 *
 */
public class SaveDocImageFeature extends DefaultSaveImageFeature implements ICustomFeature, ISaveDocImageFeature {

    private IProject project;
    /**
     * Creates a new SaveDocImage Feature.
     * 
     * @param fp The feature provider for the SaveDocImage feature
     */
    public SaveDocImageFeature(final IFeatureProvider fp) {
        super(fp);
    }

    @Override
    public String getName() {
        return ToolsMessages.SaveDocImageFeature_Title;
    }

    @Override
    public void setProject(IProject project) {
        this.project = project;
    }
    
    @Override
    public void save(ISaveImageContext context) {

        // Get viewer containing the diagram to print (by default the one
        // contained in the diagram editor that starts this feature
        GraphicalViewer viewer = getGraphicalViewer(context);

        // Configure and open dialog
        SaveDocImageConfiguration saveAsImageConfiguration = getSaveAsImageConfiguration(viewer);
        saveAsImageConfiguration.saveScaledImage();
        IFolder ifolder = GraphitiUtil.getDocImageFolder(getDiagram(), project);
        
        String finalFilePath = GraphitiUtil.getDocImageFolderPath(getDiagram());
        if (project != null) { 
            
            ifolder = project.getFolder(ifolder.getProjectRelativePath());
            IFolder imagesFolder = project.getFolder(ifolder.getProjectRelativePath());
            finalFilePath = imagesFolder.getRawLocation().toOSString();
        }
        
        String fileName = FilenameUtils.removeExtension(EMFFunctionUtil.EOBJ2IFILE.apply(getDiagram()).getName());
        File folder = new File(finalFilePath);

        folder.mkdirs();
        Shell shell = Display.getCurrent().getActiveShell();
        try {
            CreateResourcesUtil.createFolders(ifolder, null);

            // Add extension to filename (if none exists)
            fileName = addFileExtension(
                    saveAsImageConfiguration.getFormattedFileExtension(),
                    fileName);
            finalFilePath = finalFilePath + System.getProperty("file.separator") //$NON-NLS-1$
                    + fileName; // $NON-NLS-1$

            finalFilePath = addFileExtension(
                    saveAsImageConfiguration.getFormattedFileExtension(),
                    finalFilePath);

            IFile iFile = ifolder.getFile(fileName);

            // If the image is set as read/only we change it to non-read/only
            if (iFile.exists() && iFile.isReadOnly()) {
                ResourceAttributes resAttr = iFile.getResourceAttributes();
                resAttr.setReadOnly(false);
                iFile.setResourceAttributes(resAttr);
            }
            // Create the save as image operation ...
            IRunnableWithProgress operation = getSaveAsImageOperation(
                    saveAsImageConfiguration, finalFilePath);

            // ... and start save as image
            new ProgressMonitorDialog(shell).run(false, false, operation);
        } catch (Exception e) {
            String message = "Cannot save image: "; //$NON-NLS-1$
            MessageDialog.openError(shell,
                    "Cannot save image ", message + e.getMessage()); //$NON-NLS-1$
        }
    }

    @Override
    protected SaveDocImageConfiguration getSaveAsImageConfiguration(
            GraphicalViewer viewer) {
        Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
        SaveDocImageConfiguration saveAsImageDialog = new SaveDocImageConfiguration(
                shell, viewer);

        // Add exporters
        saveAsImageDialog.addExporters(getDiagramExporters());
        return saveAsImageDialog;
    }

    /**
     * Gets the diagram editor.
     * 
     * @param context the save image context.
     * @return the diagram editor.
     */
    private DiagramEditor getEditor(ISaveImageContext context) {
        return (DiagramEditor) context.getProperty(EDITOR_PROPERTY_ID); // $NON-NLS-1$
    }

    @Override
    protected GraphicalViewer getGraphicalViewer(ISaveImageContext context) {
        IDiagramContainerUI diagramContainer = getEditor(context).getDiagramBehavior().getDiagramContainer();

        if (diagramContainer != null) {
            return diagramContainer.getAdapter(GraphicalViewer.class);
        } else {
            return null;
        }
    }

    @Override
    public String getImageId() {
        return null;
    }

    @Override
    public boolean canExecute(ICustomContext context) {
        return true;
    }

    @Override
    public void execute(ICustomContext context) { //NOSONAR needed for ICustomFeature
        super.execute(context);

    }
}
-------
public class SaveDocImageConfiguration extends AbstractConfiguration implements ISaveAsImageConfiguration {

    private static final String[] IMAGE_FILE_EXTENSIONS = new String[] { "BMP", "GIF", "JPG", "PNG", "RLE" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$

    private static final int[] IMAGE_FILE_TYPES = new int[] { SWT.IMAGE_BMP, SWT.IMAGE_GIF, SWT.IMAGE_JPEG, SWT.IMAGE_PNG, SWT.IMAGE_BMP_RLE };

    private static final int DEFAULT_IMAGE_INDEX = 2; // PNG

    private static final double DEFAULT_UPPER_BOUND = 10000.0D;

    // selected values
    private int formatIndex = DEFAULT_IMAGE_INDEX;

    /**
     * Creates a new ExportDiagramDialog.
     * 
     * @param shell
     *            The Shell of this dialog.
     * @param graphicalViewer
     *            The GraphicalViewer, which to save.
     */
    public SaveDocImageConfiguration(final Shell shell, final GraphicalViewer graphicalViewer) {
        super(shell, graphicalViewer);
    }

    @Override
    public int configure() {
        return open();
    }

    @Override
    public void addExporters(Map<String, Boolean> diagramExporterTypes) {
        // nothing to add here
    }

    /**
     * Saves the scaled image.
     */
    public void saveScaledImage() {
        if (getImageFormat() == SWT.IMAGE_PNG) {
            setScaledImage(getImageScaleFactor(), DEFAULT_UPPER_BOUND);
        } else {
            setScaledImage(getImageScaleFactor());
        }
    }

    @Override
    public final int getImageFormat() {
        if (formatIndex >= IMAGE_FILE_TYPES.length) {
            // Custom file type provided via extension
            return -1;
        }
        return IMAGE_FILE_TYPES[formatIndex];
    }

    @Override
    public final String getFormattedFileExtension() {
        return IMAGE_FILE_EXTENSIONS[formatIndex].toLowerCase(Locale.ENGLISH);
    }

    @Override
    public final String getFileExtension() {
        return IMAGE_FILE_EXTENSIONS[formatIndex];
    }

    @Override
    public double getImageScaleFactor() {
        return 1.0;
    }
}
-------