How to inline images with text by using aspose.words for .NET

Could you please check this code ??? I am calling the overlapIssue() method in my main method (Convert()). I think my method calling is wrong. because when I call the method (OverlapIssue(document);), I think I should pass the saved word document. Could you please help me to do that? How can I pass the saved word document to the method (OverlapIssue(document);??? I am not allowed to get the document from the machine. Do you have another way to get saved document???

///Main Method

public List<byte[]> Convert(ContentsDto content, IDictionary<string,string> options, System.Func<DependentContent, Task<ContentsDto>> GetDependency=null)
        {          
            License htmlLicense1 = new License(); 
            htmlLicense1.SetLicense("Aspose.Words.NET.lic");

            HtmlDocument htmlDocument = new HtmlDocument();
            using (var htmlStream = new MemoryStream(content.Data))
            {
                htmlDocument.Load(htmlStream);
            }
           
            var byteArray = new List<byte[]>();
           
            using (var dataStream = new MemoryStream(content.Data))
            {
                HtmlLoadOptions docOptions = new HtmlLoadOptions();

                docOptions.ResourceLoadingCallback = new AsposeWordsAuthHandler(_settingsProvider, _authSettings, _logger);
                var document = new Aspose.Words.Document(dataStream, docOptions);
               
                using (var outputStream = new MemoryStream())
                {          
                  
                    document.Save(outputStream, SaveFormat.Docx);
                    //in here i am calling your method.
                    OverlapIssue(document);
                    byteArray.Add(outputStream.ToArray());
                }
            }
            return byteArray;
        }
//Method , which adjust the overlap issue
 private static void OverlapIssue(Document doc)
        {
           
            LayoutCollector collector = new LayoutCollector(doc);
            LayoutEnumerator enumerator = new LayoutEnumerator(doc);

            NodeCollection shapes = doc.GetChildNodes(NodeType.Shape, true);
            foreach (Shape s in shapes)
            {
                // LayoutCollector and LayoutEnumerator do not work with nodes in header and footer.
                // Skip them.
                if (s.GetAncestor(NodeType.HeaderFooter) != null)
                    continue;

                PageSetup ps = ((Section)s.GetAncestor(NodeType.Section)).PageSetup;
                // Rectangle inside page margin.
                double top = ps.TopMargin + ps.HeaderDistance;
                double bottom = ps.BottomMargin + ps.FooterDistance;
                float width = (float)(ps.PageWidth - ps.LeftMargin - ps.RightMargin);
                float height = (float)(ps.PageHeight - top - bottom);
                RectangleF rect = new RectangleF((float)ps.LeftMargin, (float)top, width, height);

                // Get shape rectangle on the page.
                enumerator.Current = collector.GetEntity(s);
                RectangleF shapeRect = enumerator.Rectangle;

                // Update shape position to place it inside page margins.
                if (shapeRect.Left < rect.Left)
                    s.Left += (rect.Left - shapeRect.Left);

                if (shapeRect.Right > rect.Right)
                    s.Left -= (shapeRect.Right - rect.Right);

                if (shapeRect.Top < rect.Top)
                    s.Top += (rect.Top - shapeRect.Top);

                if (shapeRect.Bottom > rect.Bottom)
                    s.Top -= (shapeRect.Bottom - rect.Bottom);

            }   
        }

@nethmi The problem occurs because you make changes after saving the document to stream, so the changes are not reflected in in the output stream. You should modify code like:

//in here i am calling your method.
OverlapIssue(document);

using (var outputStream = new MemoryStream())
{
    document.Save(outputStream, SaveFormat.Docx);
    byteArray.Add(outputStream.ToArray());
}

I put that method before the outputStream. still getting the same issue. I think that method is only working with the saved word document. See this one. in the second method i am using the saved word document from the machine and calling that method in the main method before the outputStream. this is working. But the pervious one is not working.

public List<byte[]> Convert(ContentsDto content, IDictionary<string,string> options, System.Func<DependentContent, Task<ContentsDto>> GetDependency=null)
        {          
            License htmlLicense1 = new License(); 
            htmlLicense1.SetLicense("Aspose.Words.NET.lic");

            HtmlDocument htmlDocument = new HtmlDocument();
            using (var htmlStream = new MemoryStream(content.Data))
            {
                htmlDocument.Load(htmlStream);
            }
           
            var byteArray = new List<byte[]>();
           
            using (var dataStream = new MemoryStream(content.Data))
            {
                HtmlLoadOptions docOptions = new HtmlLoadOptions();

                docOptions.ResourceLoadingCallback = new AsposeWordsAuthHandler(_settingsProvider, _authSettings, _logger);
                var document = new Aspose.Words.Document(dataStream, docOptions);

                //in here i am calling your method.
                    OverlapIssue();

                using (var outputStream = new MemoryStream())
                {          
                  
                    document.Save(outputStream, SaveFormat.Docx);
                    byteArray.Add(outputStream.ToArray());
                }
            }
            return byteArray;
        }

//
//Method , which adjust the overlap issue
 private static void OverlapIssue(Document doc)
        {
           Document doc = new Document(@"D:\TestOutput\test.docx");
            LayoutCollector collector = new LayoutCollector(doc);
            LayoutEnumerator enumerator = new LayoutEnumerator(doc);

            NodeCollection shapes = doc.GetChildNodes(NodeType.Shape, true);
            foreach (Shape s in shapes)
            {
                // LayoutCollector and LayoutEnumerator do not work with nodes in header and footer.
                // Skip them.
                if (s.GetAncestor(NodeType.HeaderFooter) != null)
                    continue;

                PageSetup ps = ((Section)s.GetAncestor(NodeType.Section)).PageSetup;
                // Rectangle inside page margin.
                double top = ps.TopMargin + ps.HeaderDistance;
                double bottom = ps.BottomMargin + ps.FooterDistance;
                float width = (float)(ps.PageWidth - ps.LeftMargin - ps.RightMargin);
                float height = (float)(ps.PageHeight - top - bottom);
                RectangleF rect = new RectangleF((float)ps.LeftMargin, (float)top, width, height);

                // Get shape rectangle on the page.
                enumerator.Current = collector.GetEntity(s);
                RectangleF shapeRect = enumerator.Rectangle;

                // Update shape position to place it inside page margins.
                if (shapeRect.Left < rect.Left)
                    s.Left += (rect.Left - shapeRect.Left);

                if (shapeRect.Right > rect.Right)
                    s.Left -= (shapeRect.Right - rect.Right);

                if (shapeRect.Top < rect.Top)
                    s.Top += (rect.Top - shapeRect.Top);

                if (shapeRect.Bottom > rect.Bottom)
                    s.Top -= (shapeRect.Bottom - rect.Bottom);

            } doc.Save(@"D:\TestOutput\out.docx");
    }

I am not allowed to use this.
Document doc = new Document(@“D:\TestOutput\test.docx”);

Do you have any method to get the saved document??? Because I think It’s only working with saved word document. Do you have any suggestions?

@nethmi Thank you for additional information. The suggested method should be used together with the approach suggested earlier. First you should change the appropriate shapes WrapType and then process the document with the code that uses LayoutCollector and LayoutEnumerator.
Also, it is not required to use Document doc = new Document(@"D:\TestOutput\test.docx");. This line of code is used for demonstration purposes to check the output produced by the code.

You code should look like this:

public List<byte[]> Convert(ContentsDto content, IDictionary<string, string> options, System.Func<DependentContent, Task<ContentsDto>> GetDependency = null)
{
    License htmlLicense1 = new License();
    htmlLicense1.SetLicense("Aspose.Words.NET.lic");

    HtmlDocument htmlDocument = new HtmlDocument();
    using (var htmlStream = new MemoryStream(content.Data))
    {
        htmlDocument.Load(htmlStream);
    }

    var byteArray = new List<byte[]>();

    using (var dataStream = new MemoryStream(content.Data))
    {
        HtmlLoadOptions docOptions = new HtmlLoadOptions();

        docOptions.ResourceLoadingCallback = new AsposeWordsAuthHandler(_settingsProvider, _authSettings, _logger);

        var document = new Aspose.Words.Document(dataStream, docOptions);

        using (var outputStream = new MemoryStream())
        {
            using (var htmlStream = new MemoryStream(content.Data))
            {
                htmlDocument.Load(htmlStream);
            }

            ApplyImageFormatting(document, htmlDocument);
            FormatOutput(GetDependency, document, htmlDocument);
            
            // Here overlap issue is fixed by layout code.
            OverlapIssue(document);

            document.Save(outputStream, SaveFormat.Docx);
            byteArray.Add(outputStream.ToArray());
        }
    }
    return byteArray;
}

done. still getting the same issue.

@nethmi Could you please crate a simple console application and attach it here, so I can run it on my side and debug it? I will check what is going wrong and provide you more information.
Also, please modify OverlapIssue method like this:

//Method , which adjust the overlap issue
 private static void OverlapIssue(Document doc)
        {
            LayoutCollector collector = new LayoutCollector(doc);
            LayoutEnumerator enumerator = new LayoutEnumerator(doc);

            NodeCollection shapes = doc.GetChildNodes(NodeType.Shape, true);
            foreach (Shape s in shapes)
            {
                // LayoutCollector and LayoutEnumerator do not work with nodes in header and footer.
                // Skip them.
                if (s.GetAncestor(NodeType.HeaderFooter) != null)
                    continue;

                PageSetup ps = ((Section)s.GetAncestor(NodeType.Section)).PageSetup;
                // Rectangle inside page margin.
                double top = ps.TopMargin + ps.HeaderDistance;
                double bottom = ps.BottomMargin + ps.FooterDistance;
                float width = (float)(ps.PageWidth - ps.LeftMargin - ps.RightMargin);
                float height = (float)(ps.PageHeight - top - bottom);
                RectangleF rect = new RectangleF((float)ps.LeftMargin, (float)top, width, height);

                // Get shape rectangle on the page.
                enumerator.Current = collector.GetEntity(s);
                RectangleF shapeRect = enumerator.Rectangle;

                // Update shape position to place it inside page margins.
                if (shapeRect.Left < rect.Left)
                    s.Left += (rect.Left - shapeRect.Left);

                if (shapeRect.Right > rect.Right)
                    s.Left -= (shapeRect.Right - rect.Right);

                if (shapeRect.Top < rect.Top)
                    s.Top += (rect.Top - shapeRect.Top);

                if (shapeRect.Bottom > rect.Bottom)
                    s.Top -= (shapeRect.Bottom - rect.Bottom);
            } 
    }

sure I will.
Could you please try OverlapIssue(Document doc) method, with these two word documents???

If you can see a difference please let me know. I think both overlap images in the two documents run under same if condition. I think, That’s why we can’t get correct output for one document.

sample1.docx (29.6 KB)

sample2.docx (30.0 KB)

any update??

@nethmi Thank you for additional information. I have modified the code a bit more and now it gives better result. But please note that fonts used in the document must be available on the machine where code is used, because layout code requires fonts to build layout properly. Could you please check on your side:

private static void OverlapIssue(Document doc)
{
    LayoutCollector collector = new LayoutCollector(doc);
    LayoutEnumerator enumerator = new LayoutEnumerator(doc);

    NodeCollection shapes = doc.GetChildNodes(NodeType.Shape, true);
    foreach (Shape s in shapes)
    {
        // LayoutCollector and LayoutEnumerator do not work with nodes in header and footer.
        // Skip them.
        if (s.GetAncestor(NodeType.HeaderFooter) != null || !s.IsTopLevel)
            continue;

        PageSetup ps = ((Section)s.GetAncestor(NodeType.Section)).PageSetup;
        // Rectangle inside page margin.
        float width = (float)(ps.PageWidth - ps.LeftMargin - ps.RightMargin);
        float height = (float)(ps.PageHeight - ps.TopMargin - ps.BottomMargin);
        RectangleF rect = new RectangleF((float)ps.LeftMargin, (float)ps.TopMargin, width, height);

        // Get shape rectangle on the page.
        enumerator.Current = collector.GetEntity(s);
        RectangleF shapeRect = enumerator.Rectangle;

        double left = 0;
        double top = 0;

        // Update shape position to place it inside page margins.
        if (shapeRect.Left < rect.Left)
            left = rect.Left;

        if (shapeRect.Right > rect.Right)
            left = rect.Right - shapeRect.Width;

        if (shapeRect.Top < rect.Top)
            top = rect.Top;

        if (shapeRect.Bottom > rect.Bottom)
            top = rect.Bottom - shapeRect.Height;

        if (!IsZero(left) || !IsZero(top))
        {
            // Set relative shape position to page.
            s.RelativeHorizontalPosition = RelativeHorizontalPosition.Page;
            s.RelativeVerticalPosition = RelativeVerticalPosition.Page;
            s.Top = Math.Max(top, rect.Top);
            s.Left = Math.Max(left, rect.Left);
            doc.UpdatePageLayout();
        }
    }
}

public static bool IsZero(double value)
{
    return (Math.Abs(value) < Double.Epsilon);
}
1 Like

Hii

This is working. I checked several scenarios with this code. It’s working. Thank you very much for your support and effort. I really appreciate it. :slightly_smiling_face:

1 Like

A post was split to a new topic: How to change image wrap type using Aspose.Pdf?

Hii

I am using below code to wrap my images.

private static void SetImageLayout(Document document, HtmlDocument htmlDocument)
    {
        HtmlNodeCollection images = htmlDocument.DocumentNode.SelectNodes("//img");
        NodeCollection shapes = document.GetChildNodes(NodeType.Shape, true);

        if (images != null)
        {
            var imgIndex = 0;
            foreach (Shape shape in shapes)
            {
                var image = images.ElementAt(imgIndex);

                if (image.HasClass("fr-fil"))
                {
                    shape.WrapType = WrapType.Square;
                    shape.Top += shape.Height + 10;
                
                }
                else
                {
                    shape.HorizontalAlignment = HorizontalAlignment.Center;
                }
                shape.AllowOverlap = false;
                imgIndex++;
            }
        }
    }

but sometimes it doesn’t give the expected outcome.( When I reduce content between two images -you can understand this problem by using my expected and current outcomes. It has two wrapped images.)

Expected outcome expected.PNG.jpg (163.7 KB)

current outcome(this is happening when I reduce paragraph length between images.)current.PNG.jpg (161.8 KB)

HTML.zip (2.9 KB)

I can’t provide exact information because of the privacy reasons. So I have sent you a dummy HTML . My HTML is like that. You can check my issue with this HTML by changing paragraph length between those two images. images are in the third page.

Could you please help me to solve this issue?

@nethmi I am checking the issue and get back to you soon.

1 Like

@nethmi I have checked your HTML and code on my side and unfortunately, I cannot reproduce the same output document you have shared. Could you please attach your output DOCX document with the problem?
I suspect the shapes are anchored to the same paragraph and as a solution you can check whether the shapes are in the same paragraph and placed on the same page, if so you can adjust position of such shapes and place them one above another. However, this in only my guess. Once I have your real problematic document I can analyze the issue closer.

ok i’ll send you

1 Like

Hiii

HTML MYHTML.zip (2.1 KB)
word document document.docx (17.5 KB)
used Image picture.jpg (5.7 KB)

In the 3rd page you can see the two wrapped images.
current outcomecurrent.PNG.jpg (135.9 KB)

expected outcome expected.PNG.jpg (163.7 KB)

shapes are in different paragraphs

@nethmi Thank you for additional information. I have create a code example that demonstrates the basic technique of adjusting shapes positions to get the required output. But note, the code demonstrates only the basic technique and does not guaranty to work with more complicated cases. For more complicated cases, you have to implement more complicated logic to adjust positions of shapes on the page. Implementation of such logic is out of Aspose.Words scope.

Document doc = new Document(@"C:\Temp\in.docx");
CorrectShapesPosition(doc);
doc.Save(@"C:\Temp\out.docx");
private static void CorrectShapesPosition(Document doc)
{
    LayoutCollector collector = new LayoutCollector(doc);
    LayoutEnumerator enumerator = new LayoutEnumerator(doc);

    // Get all shapes in the document.
    NodeCollection shapes = doc.GetChildNodes(NodeType.Shape, true);

    // Collect shapes per page.
    Dictionary<int, List<ShapeRect>> shapesPerPage = new Dictionary<int, List<ShapeRect>>();
    foreach (Shape s in shapes)
    {
        enumerator.Current = collector.GetEntity(s);
        if (!shapesPerPage.ContainsKey(enumerator.PageIndex))
            shapesPerPage.Add(enumerator.PageIndex, new List<ShapeRect>());

        shapesPerPage[enumerator.PageIndex].Add(new ShapeRect(s, enumerator.Rectangle));
    }

    foreach (int page in shapesPerPage.Keys)
    {
        List<ShapeRect> shapesOnPage = shapesPerPage[page];
        // If there is only one shape on the page no actiona is required.
        if (shapesOnPage.Count == 1)
            continue;

        // Adjust vertical position of shapes to avoid overlapping.
        // The code demonstrates the basic technique and does not guaranly to work in all case.
        // More complicated cases requires implemeting core complicated logic.
        for (int i = 0; i < shapesOnPage.Count - 1; i++)
        {
            ShapeRect current = shapesOnPage[i];
            ShapeRect next = shapesOnPage[i + 1];
            if (current.Rectangle.Bottom > next.Rectangle.Top)
                current.Shape.Top -= current.Rectangle.Bottom - next.Rectangle.Top;
        }
    }
}

private class ShapeRect
{
    public ShapeRect(Shape shape, RectangleF rect)
    {
        mShape = shape;
        mRect = rect;
    }

    public Shape Shape { get { return mShape; } }
    public RectangleF Rectangle { get { return mRect; } }

    private Shape mShape;
    private RectangleF mRect;
}

here is the output document produced on my side: out.docx (17.5 KB)

1 Like

got it. Thank you very much for your effort.

1 Like