Inserting images in docx

Hi Aspose Team,

I need to insert some images in specific parts of a documents. When the image is inserted the lower parts of the documents should move acoordingly to the bottom. I was thinking in using find and replce to find the places where the pictures need to be insert or maybe and html table and inside the image. What do you recommend to accomplish this or how can I achieve this?

Hi Alfredo,

Thanks for your inquiry. Could you please attach your input Word document and expected output document here for our reference? Please manually create your expected Word document using Microsoft Word and attach it here for our reference. We will investigate how you want your final Word output be generated like. We will then provide you more information on this along with code.

Hi Tahir,

I’m attaching the base document. Look for the strings %image%, that is where I need to insert images, in somes cases there is no photo so I Should replace the string with empty spaces.

Regards,

Alfredo

Hi Alfredo,

Thanks for sharing the detail.

You can achieve your requirement by implementing IReplacingCallback interface.
Please use the same approach shared at following documentation link to
find text and insert the image.
https://docs.aspose.com/words/java/find-and-replace/

Please read the following article about ‘Find and Replace’ and check the following code example for your kind reference.
https://docs.aspose.com/words/java/find-and-replace/

Hope this helps you. Please let us know if you have any more queries.

Document doc = new Document(MyDir + "in.docx");
doc.Range.Replace(new Regex(@"\%IMAGE1\%"), new ReplaceEvaluatorImage(MyDir + "in.jpg"), false);
doc.Save(MyDir + "Out.docx");
public class ReplaceEvaluatorImage : IReplacingCallback
{
    String imagepath;
    public ReplaceEvaluatorImage(String path)
    {
        imagepath = path;
    }
    /// 
    /// This method is called by the Aspose.Words find and replace engine for each match.
    /// This method highlights the match string, even if it spans multiple runs.
    /// 
    ReplaceAction IReplacingCallback.Replacing(ReplacingArgs e)
    {
        // This is a Run node that contains either the beginning or the complete match.
        Node currentNode = e.MatchNode;
        // The first (and may be the only) run can contain text before the match,
        // in this case it is necessary to split the run.
        if (e.MatchOffset > 0)
            currentNode = SplitRun((Run)currentNode, e.MatchOffset);
        // This array is used to store all nodes of the match for further removing.
        ArrayList runs = new ArrayList();
        // Find all runs that contain parts of the match string.
        int remainingLength = e.Match.Value.Length;
        while (
        (remainingLength > 0) &&
        (currentNode != null) &&
        (currentNode.GetText().Length <= remainingLength))
        {
            runs.Add(currentNode);
            remainingLength = remainingLength - currentNode.GetText().Length;
            // Select the next Run node.
            // Have to loop because there could be other nodes such as BookmarkStart etc.
            do
            {
                currentNode = currentNode.NextSibling;
            }
            while ((currentNode != null) && (currentNode.NodeType != NodeType.Run));
        }
        // Split the last run that contains the match if there is any text left.
        if ((currentNode != null) && (remainingLength > 0))
        {
            SplitRun((Run)currentNode, remainingLength);
            runs.Add(currentNode);
        }
        // Create Document Buidler 
        DocumentBuilder builder = new DocumentBuilder(e.MatchNode.Document as Document);
        builder.MoveTo((Run)runs[runs.Count - 1]);
        builder.InsertImage(imagepath);
        // Now remove all runs in the sequence.
        foreach (Run run in runs)
            run.Remove();
        // Signal to the replace engine to do nothing because we have already done all what we wanted.
        return ReplaceAction.Skip;
    }
    private static Run SplitRun(Run run, int position)
    {
        Run afterRun = (Run)run.Clone(true);
        afterRun.Text = run.Text.Substring(position);
        run.Text = run.Text.Substring(0, position);
        run.ParentNode.InsertAfter(afterRun, run);
        return afterRun;
    }
}

Hi Tahir,

Sorry for the late response, I still have problem trying to insert the image. I Implemented the interface but everytime I called the method:

doc.Range.Replace(new Regex(@"%IMAGE1%"), new ReplaceEvaluatorImage(MyDir + "in.jpg"), false);

It only enters in the constructor and do not find and insert the image.

Code attached: Main Class Documento.cs method hacerMerge.

Regards,

Alfredo.

Base document

Hi Alfredo,

Thanks for your inquiry. Your document contain the IMAGE1. In this case, please use following code snippet. The first parameter of Range.Replace Method (Regex, IReplacingCallback, Boolean) is Regex, a regular expression pattern used to find matches. Please create the Regex according to your requirements.

doc.Range.Replace(new Regex(@"IMAGE1"), new ReplaceEvaluatorImage(MyDir + "in.jpg"), false);

Hi Tahir,

Thanks for your response, you were right, the Regex was wrong. Now it’s working. But I’m having a issue later. I have several etiquets where pictures could or not be located on the document, after setting the pictures I do a:

doc.Range.Replace(detalleImagen.frWord, “”,false,true);

In order to replace the Strings where pictures weren´t found but when saving the doc the strings are left in the documents instead of been replaced.

I’m attaching you the code:

=================================

if (documento.enabledInsercionImagenes.Equals(ConfigurationManager.AppSettings["valorEnabled"].ToString()))
{
    foreach (BeanQueryImagen detalleImagen in documento.queryImagenes)
    {
        Console.WriteLine("Insercion de imagenes");
        log.LogMessage("Insercion de Imagenes");
        // TODOInsercion de imagenes
        try
        {
            String rutaImagen = String.Empty;
            // Reemplazar parte del string que dice :RequestID
            String tempQuery = detalleImagen.queryImagen + " " + detalleImagen.whereImagen;
            detalleImagen.queryImagen = tempQuery.Replace(":RequestID", documento.request);

            bool recImagen = acceso.recuperarBlob(detalleImagen.queryImagen, ConfigurationManager.AppSettings["folderTMP"].ToString(), ref rutaImagen);
            if (recImagen && ConfigurationManager.AppSettings["formatoImagen"].ToString().IndexOf(Path.GetExtension(rutaImagen).ToUpper()) >= 0)
            {
                doc.Range.Replace(new Regex(@detalleImagen.frWord), new ReplaceEvaluatorImage(rutaImagen), true);
            }
            // Quita las cadenas que quedaron sin imagen
            doc.Range.Replace(detalleImagen.frWord, "", false, true);
        }
        catch (Exception e)
        {
            Console.WriteLine("Fallo recuperacion de imagen: " + e.Message);
            log.LogMessage("Fallo recuperacion de imagen: " + e.Message);
        }
    }
}

Hi Alfredo,

Thanks for your inquiry. You may use the same approach (shared in this post - implement IReplacingCallback) to replace the matched text with empty string.

In your this scenario, please share following detail for investigation purposes.

  • Please attach your input Word document.
  • Please attach the output Word file that shows the undesired behavior.
  • Please
    attach your target Word document showing the desired behavior. You can
    use Microsoft Word to create your target Word document. I will
    investigate as to how you are expecting your final document be generated
    like.

As soon as you get these pieces of information to
us we’ll start our investigation into your issue.

Hi Tahir,

I Thought using doc.Range.Replace(detalleImagen.frWord, “”,false,true); would’nt need a IReplacingCallback interface like you previously suggest for inserting the images.

In my case not all iterations for the document will have images, so when the images are not inserted in the document I need to replace the strings with empty characters. I’m attaching you the base document and the .pdf resulting after the process finished.

As you might see, the Strings IMAGE3 is not replaced after the picture for that String is not set. The idea is removing that string and maybe you can suggest maybe how can I remove the entire line. That would be even better.

Regards

Alfredo

Hi Alfredo,

Thanks for your inquiry. In your case, I suggest you please use the last parameter value as false in Range.Replace method. This will fix the issue which you are facing.

Document doc = new Document(MyDir + "DOSSIER.docx");
doc.Range.Replace("##EXHIBIT1##", "", false, false);
doc.Range.Replace("##EXHIBIT2##", "", false, false);
doc.Range.Replace("##EXHIBIT3##", "", false, false);
doc.Save(MyDir + "Out.docx");
doc.Save(MyDir + "Out.pdf");

If you can modify your template document, I suggest you please use mail merge feature to insert image. In that case, you need to implement IFieldMergingCallback interface.
Please specify a field name prefix like **Image:**MyFieldName in the document to be able to directly merge images during Mail Merge.

There are three properties available ImageFileName, ImageStream and Image to specify where the image must be taken from. Set only one of these properties. Please see the following highlighted code snippet.

Document doc = new Document(MyDir + "in.docx");
// Your code…
// Your code...
doc.MailMerge.FieldMergingCallback = new HandleMergeImageField();
doc.MailMerge.Execute(datatable); //Pass the datatable in Execute method
doc.Save(MyDir + "Out.docx");
private class HandleMergeImageField : IFieldMergingCallback
{
    void IFieldMergingCallback.FieldMerging(FieldMergingArgs args)
    {
        // Do nothing.
    }
    /// 
    /// This is called when mail merge engine encounters Image:XXX merge field in the document.
    /// You have a chance to return an Image object, file name or a stream that contains the image.
    /// 
    void IFieldMergingCallback.ImageFieldMerging(ImageFieldMergingArgs e)
    {
        MemoryStream stream = (MemoryStream)e.FieldValue;
        stream.Position = 0;
        // Now the mail merge engine will retrieve the image from the stream.
        e.ImageStream = stream;
    }
}

If you want to pass image file name(path of image), please use the ImageFieldMergingArgs.ImageFileName property. This property sets the file name of the image that the mail merge engine must insert into the document.Please check the following code example.

Hope this helps you. Please let us know if you have any more queries.

Document doc = new Document(MyDir + "in.docx");
doc.MailMerge.FieldMergingCallback = new MailMergeInsertImage();
doc.MailMerge.Execute(
    new string[] { "MyFieldName" },
    new object[] { MyDir + "image.jpg" });
doc.Save(MyDir + "Out.docx");
public class MailMergeInsertImage : IFieldMergingCallback
{
    void IFieldMergingCallback.FieldMerging(FieldMergingArgs args)
    {
    }
    void IFieldMergingCallback.ImageFieldMerging(ImageFieldMergingArgs e)
    {
        e.ImageFileName = e.FieldValue.ToString();
    }
}

Hi Tahir,

Thanks for your examples of using images with MergeFields, since I can modify the template I add your recomendation as follow:

========================================================

if (documento.enabledInsercionImagenes.Equals(ConfigurationManager.AppSettings["valorEnabled"].ToString()))
{
    doc.MailMerge.FieldMergingCallback = new MailMergeInsertImage();
    foreach (BeanQueryImagen detalleImagen in documento.queryImagenes)
    {
        Console.WriteLine("Insercion de imagenes");
        log.LogMessage("Insercion de Imagenes");
        // TODOInsercion de imagenes
        try
        {
            String rutaImagen = String.Empty;
            // Reemplazar parte del string que dice :RequestID
            String tempQuery = detalleImagen.queryImagen + " " + detalleImagen.whereImagen;
            tempQuery = tempQuery.Replace(":RequestID", documento.request);

            bool recImagen = acceso.recuperarBlob(tempQuery, ConfigurationManager.AppSettings["folderTMP"].ToString(), ref rutaImagen);
            if (recImagen && ConfigurationManager.AppSettings["formatoImagen"].ToString().IndexOf(Path.GetExtension(rutaImagen).ToUpper()) >= 0)
            {

                columnas = new String[] { detalleImagen.frWord };
                valores = new object[] { rutaImagen };
                doc.MailMerge.Execute(columnas, valores);
            }

        }
        catch (Exception e)
        {
            Console.WriteLine("Fallo recuperacion de imagen: " + e.Message);
            log.LogMessage("Fallo recuperacion de imagen: " + e.Message);
        }
    }
}
// Se crea un dataset en blanco para remover los merge
DataSet data = new DataSet();
// Se elige el tipo de limpieza de merge.
doc.MailMerge.CleanupOptions = MailMergeCleanupOptions.RemoveUnusedRegions;
doc.MailMerge.ExecuteWithRegions(data);

==========================================================

But everytime the execute is done I get the following error:

“object reference not set to an instance of an object” Like if somethng was missed to initialize.

But if manage with try the merge insert the image.

Another weird thing is at the end I do a cleaning of MergeFields but this is not working with Image Tags.

Hi Tahir,

I modified the IFieldMergingCallback since was calling itself two or three times sometimes, thats why was causing the Object not instance error.

public class MailMergeInsertImage : IFieldMergingCallback
{
    void IFieldMergingCallback.FieldMerging(FieldMergingArgs args)
    {
    }

    void IFieldMergingCallback.ImageFieldMerging(ImageFieldMergingArgs e)
    {
        if (e.FieldValue != null)
        {
            e.ImageFileName = e.FieldValue.ToString();
        }
    }
}

But I’m still having problems cleaning the mergefields not used.

DataSet data = new DataSet();
// Se elige el tipo de limpieza de merge.
doc.MailMerge.CleanupOptions = MailMergeCleanupOptions.RemoveUnusedRegions;
doc.MailMerge.ExecuteWithRegions(data);

Regards,
Alfredo

Sorry Tahir,

I wasn’t using the right cleaning method. Mistake corrected.

Regards,

Alfredo

Hi Alfredo,

Thanks for your inquiry. It seems that your problem has been solved. Please let us know if you still face any issue.

Following code snippet shows how to
instruct the mail merge engine to use multiple cleanup options.

doc.MailMerge.CleanupOptions |=
Aspose.Words.Reporting.MailMergeCleanupOptions.RemoveEmptyParagraphs;
doc.MailMerge.CleanupOptions |= Aspose.Words.Reporting.MailMergeCleanupOptions.RemoveUnusedRegions;
doc.MailMerge.CleanupOptions |= Aspose.Words.Reporting.MailMergeCleanupOptions.RemoveUnusedFields;
doc.MailMerge.CleanupOptions |= Aspose.Words.Reporting.MailMergeCleanupOptions.RemoveContainingFields;
doc.MailMerge.ExecuteWithRegions(DataSet);
doc.Save(MyDir + "Out.docx");