Get Image representation of OfficeMath Node via Aspose.Words for .Net?

Good evening,

I was wondering whether it is possible to ‘export’ or convert OfficeMath nodes as an image representation? We have documents in which some equations were added as OLE objects (via Microsoft Equation etc) and these can be extracted as images just perfectly fine, but some equations/formulas have been added as ‘normal’ Office Equations and are identified via Aspose.Words as OfficeMath nodes… but I’d like to get their image representation as well… is that somehow doable?

Thanks,
-JB

Hi Joerg,

Thanks for your inquiry. You may use following code snippet to render any Node to Image. Hopefully it will help you to accomplish the task.

Document doc = new Document(@"Input.docx");
int i = 1;
NodeCollection equations = doc.GetChildNodes(NodeType.OfficeMath, true);
foreach (OfficeMath officemath in equations)
{
    Image image = RenderNode(officemath, new ImageSaveOptions(SaveFormat.Png));
    image.Save(@"Out" + i + ".png");
    i++;
}
/// 
/// Renders any node in a document to the path specified using the image save options.
/// 
public static Image RenderNode(Node node, ImageSaveOptions imageOptions)
{
    // Run some argument checks.
    if (node == null)
        throw new ArgumentException("Node cannot be null");
    // If no image options are supplied, create default options.
    if (imageOptions == null)
        imageOptions = new ImageSaveOptions(SaveFormat.Png);
    // Store the paper color to be used on the final image and change to transparent.
    // This will cause any content around the rendered node to be removed later on.
    Color savePaperColor = imageOptions.PaperColor;
    imageOptions.PaperColor = Color.Transparent;
    // There a bug which affects the cache of a cloned node. To avoid this we instead clone the entire document including all nodes,
    // find the matching node in the cloned document and render that instead.
    Document doc = (Document)node.Document.Clone(true);
    node = doc.GetChild(NodeType.Any, node.Document.GetChildNodes(NodeType.Any, true).IndexOf(node), true);
    // Create a temporary shape to store the target node in. This shape will be rendered to retrieve
    // the rendered content of the node.
    Shape shape = new Shape(doc, ShapeType.TextBox);
    Section parentSection = (Section)node.GetAncestor(NodeType.Section);
    // Assume that the node cannot be larger than the page in size.
    shape.Width = parentSection.PageSetup.PageWidth;
    shape.Height = parentSection.PageSetup.PageHeight;
    shape.FillColor = Color.Transparent; // We must make the shape and paper color transparent.
                                            // Don't draw a surronding line on the shape.
    shape.Stroked = false;
    Node currentNode = node;
    // If the node contains block level nodes then just add a copy of these nodes to the shape.
    if (currentNode is InlineStory || currentNode is Story)
    {
        CompositeNode composite = (CompositeNode)currentNode;
        foreach (Node childNode in composite.ChildNodes)
        {
            shape.AppendChild(childNode.Clone(true));
        }
    }
    else
    {
        // Move up through the DOM until we find node which is suitable to insert into a Shape (a node with a parent can contain paragraph, tables the same as a shape).
        // Each parent node is cloned on the way up so even a descendant node passed to this method can be rendered.
        // Since we are working with the actual nodes of the document we need to clone the target node into the temporary shape.
        while (!(currentNode.ParentNode is InlineStory || currentNode.ParentNode is Story || currentNode.ParentNode is ShapeBase || currentNode.NodeType == NodeType.Paragraph))
        {
            CompositeNode parent = (CompositeNode)currentNode.ParentNode.Clone(false);
            currentNode = currentNode.ParentNode;
            parent.AppendChild(node.Clone(true));
            node = parent; // Store this new node to be inserted into the shape.
        }
        // Add the node to the shape.
        shape.AppendChild(node.Clone(true));
    }
    // We must add the shape to the document tree to have it rendered.
    parentSection.Body.FirstParagraph.AppendChild(shape);
    // Render the shape to stream so we can take advantage of the effects of the ImageSaveOptions class.
    // Retrieve the rendered image and remove the shape from the document.
    MemoryStream stream = new MemoryStream();
    shape.GetShapeRenderer().Save(stream, imageOptions);
    shape.Remove();
    Bitmap croppedImage;
    // Load the image into a new bitmap.
    using (Bitmap renderedImage = new Bitmap(stream))
    {
        // Extract the actual content of the image by cropping transparent space around
        // the rendered shape.
        Rectangle cropRectangle = FindBoundingBoxAroundNode(renderedImage);
        croppedImage = new Bitmap(cropRectangle.Width, cropRectangle.Height);
        croppedImage.SetResolution(imageOptions.HorizontalResolution, imageOptions.VerticalResolution);
        // Create the final image with the proper background color.
        using (Graphics g = Graphics.FromImage(croppedImage))
        {
            g.Clear(savePaperColor);
            g.DrawImage(renderedImage, new Rectangle(0, 0, croppedImage.Width, croppedImage.Height), cropRectangle.X, cropRectangle.Y, cropRectangle.Width, cropRectangle.Height, GraphicsUnit.Pixel);
        }
    }
    return croppedImage;
}
/// 
/// Finds the minimum bounding box around non-transparent pixels in a Bitmap.
/// 
public static Rectangle FindBoundingBoxAroundNode(Bitmap originalBitmap)
{
    Point min = new Point(int.MaxValue, int.MaxValue);
    Point max = new Point(int.MinValue, int.MinValue);
    for (int x = 0; x < originalBitmap.Width; ++x)
    {
        for (int y = 0; y < originalBitmap.Height; ++y)
        {
            // Note that you can speed up this part of the algorithm by using LockBits and unsafe code instead of GetPixel.
            Color pixelColor = originalBitmap.GetPixel(x, y);
            // For each pixel that is not transparent calculate the bounding box around it.
            if (pixelColor.ToArgb() != Color.Empty.ToArgb())
            {
                min.X = Math.Min(x, min.X);
                min.Y = Math.Min(y, min.Y);
                max.X = Math.Max(x, max.X);
                max.Y = Math.Max(y, max.Y);
            }
        }
    }
    // Add one pixel to the width and height to avoid clipping.
    return new Rectangle(min.X, min.Y, (max.X - min.X) + 1, (max.Y - min.Y) + 1);
}

Please feel free to contact us for any further assistance.

Best Regards,

Hi Joerg,

In addition to above reply, you can use GetMathRenderer() method to save OfficeMath Node to Image as well.

Document doc = new Document(@"D:\Downloads\OfficeMath.docx");
int i = 1;
NodeCollection equations = doc.GetChildNodes(NodeType.OfficeMath, true);
foreach (OfficeMath officemath in equations)
{
    officemath.GetMathRenderer().Save("E:/Data/Temp/Out" + i + ".png", new ImageSaveOptions(SaveFormat.Png));
    i++;
}

Best Regards,