Dear,
I have a docs document in which I want to replace a collection of words with images.
For each word, that I want to replace, I have the corresponding image.
The image should replace the word. The dimensions of the image should be respected when inserting the image.
There should not be any new paragraphbreak or pagebreak etc be inserted.
Can you provide me with a working algorithm for this.
Many thanks
Patrick Vanbrabant
@natasjadegroot
To replace a collection of words with images in a Word document while maintaining the image dimensions and avoiding any new paragraph or page breaks, follow this algorithm:
-
Cause: You need to replace specific words in a Word document with images to enhance visual representation while preserving the document’s formatting.
-
Solution: Use Aspose.Words to automate the process of searching for words and replacing them with images. This ensures that the images maintain the same dimensions as the original words and are inserted inline to prevent any formatting issues.
-
Next Steps: Implement the following C# code snippet to achieve the desired outcome:
using Aspose.Words;
using Aspose.Words.Drawing;
// Load the document
Document doc = new Document("input.docx");
// Define the words and their corresponding images
Dictionary<string, string> replacements = new Dictionary<string, string>
{
{ "word1", "image1.png" },
{ "word2", "image2.png" }
};
// Iterate through each word to replace
foreach (var replacement in replacements)
{
string word = replacement.Key;
string imagePath = replacement.Value;
// Find all runs containing the word
foreach (Run run in doc.GetChildNodes(NodeType.Run, true))
{
if (run.Text.Contains(word))
{
// Replace the word with an image
Shape image = new Shape(doc, ShapeType.Image);
image.ImageData.SetImage(imagePath);
image.Width = run.Font.Size * 10; // Adjust width as needed
image.Height = run.Font.Size * 10; // Adjust height as needed
image.WrapType = WrapType.Inline;
// Insert the image at the run's location
run.ParentParagraph.InsertBefore(image, run);
run.Text = run.Text.Replace(word, string.Empty);
}
}
}
// Save the modified document
doc.Save("output.docx");
- Closing Line: This code will effectively replace the specified words with images while maintaining the document’s formatting. If you have any further questions or need additional assistance, feel free to ask!
This piece of code will not maintain the dimensions of the image acoording to me. As the code is setting the width and the height of the image based on teh font size,. Can you provide the correct code to also set the image height and width according to the height and width of the replacement image.
What about text which needs to be repalced, which is inside a cell in a table.
@natasjadegroot You can easily achieve this using IReplacingCallback
. For example see the following code:
Document doc = new Document(@"C:\Temp\in.docx");
FindReplaceOptions opt = new FindReplaceOptions();
opt.ReplacingCallback = new ReplacingCallbackReplaceWithImage();
doc.Range.Replace("[image]", @"C:\Temp\test.png", opt);
doc.Save(@"C:\Temp\out.docx");
internal class ReplacingCallbackReplaceWithImage : IReplacingCallback
{
public ReplaceAction Replacing(ReplacingArgs args)
{
Document doc = (Document)args.MatchNode.Document;
List<Run> matchedRuns = GetMatchedRuns(args);
// Create DocumentBuilder to insert the image.
DocumentBuilder builder = new DocumentBuilder(doc);
// Move builder to the first run.
builder.MoveTo(matchedRuns.First());
// Insert Image.
builder.InsertImage(args.Replacement);
// Delete matched runs
foreach (Run run in matchedRuns)
run.Remove();
// Signal to the replace engine to do nothing because we have already done all what we wanted.
return ReplaceAction.Skip;
}
private static List<Run> GetMatchedRuns(ReplacingArgs args)
{
// This is a Run node that contains either the beginning or the complete match.
Node currentNode = args.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 (args.MatchOffset > 0)
currentNode = SplitRun((Run)currentNode, args.MatchOffset);
// This array is used to store all nodes of the match for further deleting.
List<Run> runs = new List<Run>();
// Find all runs that contain parts of the match string.
int remainingLength = args.Match.Value.Length;
while (
remainingLength > 0 &&
currentNode != null &&
currentNode.GetText().Length <= remainingLength)
{
runs.Add((Run)currentNode);
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((Run)currentNode);
}
return runs;
}
private static Run SplitRun(Run run, int position)
{
Run afterRun = (Run)run.Clone(true);
run.ParentNode.InsertAfter(afterRun, run);
afterRun.Text = run.Text.Substring(position);
run.Text = run.Text.Substring((0), (0) + (position));
return afterRun;
}
}
Also, you can consider using LINQ Reporting Engine to fill the document with data:
https://docs.aspose.com/words/net/inserting-images-dynamically/
Hi Alexy,
Thanks for your reply. However I’m using java programming language. Not so sure about the LINQ capabilities.
Will Builder.InsertImage respect the original dimensions of the image?
ANd if I have a collection of strings to be replaced, do I have to call doc.Range.replace for each of the strings or is there a way to pass the complete collection in there.
Also 'm a bit worried about the performance of this piece of code when I have quite a lot of replacements to do. Lets say 1000
KR
Patrick Vanbrabant
@natasjadegroot Here is java version of the code:
Document doc = new Document("C:\\Temp\\in.docx");
FindReplaceOptions opt = new FindReplaceOptions();
opt.setReplacingCallback(new ReplacingCallbackReplaceWithImage());
doc.getRange().replace("[image]", "C:\\Temp\\test.png", opt);
doc.save("C:\\Temp\\out.docx");
public class ReplacingCallbackReplaceWithImage implements IReplacingCallback {
/**
* This method is called by the Aspose.Words find and replace engine for each match.
*/
@Override
public int replacing(ReplacingArgs args) throws Exception {
Document doc = (Document)args.getMatchNode().getDocument();
ArrayList<Run> matchedRuns = GetMatchedRuns(args);
// Create DocumentBuilder to insert the image.
DocumentBuilder builder = new DocumentBuilder(doc);
// Move builder to the first run.
builder.moveTo(matchedRuns.get(0));
// Insert Image.
builder.insertImage(args.getReplacement());
// Delete matched runs
for (Run run : matchedRuns)
run.remove();
// Signal to the replace engine to do nothing because we have already done all what we wanted.
return ReplaceAction.SKIP;
}
private static ArrayList<Run> GetMatchedRuns(ReplacingArgs args)
{
// This is a Run node that contains either the beginning or the complete match.
Node currentNode = args.getMatchNode();
// 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 (args.getMatchOffset() > 0)
currentNode = splitRun((Run)currentNode, args.getMatchOffset());
// This array is used to store all nodes of the match for further deleting.
ArrayList<Run> runs = new ArrayList<Run>();
// Find all runs that contain parts of the match string.
int remainingLength = args.getMatch().group().length();
while (
remainingLength > 0 &&
currentNode != null &&
currentNode.getText().length() <= remainingLength)
{
runs.add((Run)currentNode);
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.getNextSibling();
} while (currentNode != null && currentNode.getNodeType() != 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((Run)currentNode);
}
return runs;
}
private static Run splitRun(Run run, int position)
{
Run afterRun = (Run)run.deepClone(true);
run.getParentNode().insertAfter(afterRun, run);
afterRun.setText(run.getText().substring(position));
run.setText(run.getText().substring(0, position));
return afterRun;
}
}
Yes, DocumentBuilder.insertImage
respects the original image dimensions.
You are right, find/replace is not the best way to fill the document with data. That is why I have proposed to soncider using LINQ Reporting Engine.
Is LINQ reporting engine also available for Java. I have never used that component.
Are there some easy and more complex examples available somewhere
Hi Aleey.
I used the code you provided in the following program
Document doc = new Document("D:\\Temp\\Replacement\\source.docx");
FindReplaceOptions opt = new FindReplaceOptions();
opt.setReplacingCallback(new ReplacingCallbackReplaceWithImage());
doc.getRange().replace("#@LWLYGL@#", "D:\\Temp\\Replacement\\LWLYGL.PNG", opt);
doc.getRange().replace("#@YGLGWF@#", "D:\\Temp\\Replacement\\YGLGWF.PNG", opt);
doc.getRange().replace("#@WGLWLG@#", "D:\\Temp\\Replacement\\WGLWLG.PNG", opt);
doc.getRange().replace("#@FYTGGL@#", "D:\\Temp\\Replacement\\FYTGGL.PNG", opt);
doc.getRange().replace("#@TFWFGW@#", "D:\\Temp\\Replacement\\TFWFGW.PNG", opt);
doc.getRange().replace("#@YWWWLG@#", "D:\\Temp\\Replacement\\YWWWLG.PNG", opt);
doc.save("D:\\Temp\\Replacement\\request.docx");
In the attached zip file you can find all relevant files.
Please look at the result document (request.docx) and check the size of each of the images. They are certainly not the original size of the replacement image. They are significantly larger.
The aspect ratio however is the same.
Could you please have a look at this.
Many thanks
Patrick
replacing.zip (167.3 KB)
@natasjadegroot Size of the images is the same as original images:

If you insert one of your images into the document using MS Word, the result will be exactly the same. Here is the document created in MS Word by inserting your images.
test.docx (82.0 KB)