Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.9.34622.214
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Replace-MergeField-with-Bookmark-Hyperlink", "Replace-MergeField-with-Bookmark-Hyperlink\Replace-MergeField-with-Bookmark-Hyperlink.csproj", "{ED3D612A-7416-4DB4-9C33-B772BEAF52A6}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{ED3D612A-7416-4DB4-9C33-B772BEAF52A6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{ED3D612A-7416-4DB4-9C33-B772BEAF52A6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{ED3D612A-7416-4DB4-9C33-B772BEAF52A6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{ED3D612A-7416-4DB4-9C33-B772BEAF52A6}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {4353CBCF-E886-4543-982B-CBB8018082D2}
EndGlobalSection
EndGlobal
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
using Syncfusion.DocIO.DLS;
using Syncfusion.DocIO;

namespace Replace_MergeField_with_Bookmark_Hyperlink
{
internal class Program
{
static void Main(string[] args)
{
// Open the existing Word document using a FileStream.
using (FileStream docStream = new FileStream(Path.GetFullPath(@"Data/Template.docx"), FileMode.Open, FileAccess.Read))
{
// Load the Word document into the WordDocument object.
using (WordDocument document = new WordDocument(docStream, FormatType.Docx))
{
// Find the first merge field with the name "Test" in the document.
WMergeField mergeField = document.FindItemByProperty(EntityType.MergeField, "FieldName", "Test") as WMergeField;

// Convert the merge field to a placeholder and get the placeholder text.
string textToFind = ConvertMergeFieldToPlaceHolder(mergeField);

// Replace the placeholder text with "2" in the document.
document.Replace(textToFind, "2", false, true);

// Create a new TextBodyPart to hold the bookmark hyperlink.
TextBodyPart textBodyPart = new TextBodyPart(document);

// Create a new paragraph to add the hyperlink.
WParagraph paragraphHyperlink = new WParagraph(document);
textBodyPart.BodyItems.Add(paragraphHyperlink);

// Append a hyperlink to the paragraph that links to the bookmark "Bookmark1" with display text "Syncfusion".
paragraphHyperlink.AppendHyperlink("Bookmark1", "Syncfusion", HyperlinkType.Bookmark);

// Replace the text "2" with the bookmark hyperlink in the document.
document.Replace("2", textBodyPart, false, true);

// Save the modified document to a new file.
using (FileStream outputStream = new FileStream(Path.GetFullPath(@"Output/Result.docx"), FileMode.Create, FileAccess.Write))
{
document.Save(outputStream, FormatType.Docx);
}
}
}
}

/// <summary>
/// Converts the specified merge field into a placeholder text by extracting
/// its displayed content and replacing the merge field with a text range.
/// </summary>
/// <param name="field">The merge field to be converted.</param>
/// <returns>
/// The text extracted from the merge field that is used as a placeholder.
/// </returns>
private static string ConvertMergeFieldToPlaceHolder(WMergeField field)
{
// Get the paragraph that contains the merge field.
WParagraph paragraph = field.OwnerParagraph;

// Find the index of the merge field in the paragraph's child entities.
int itemIndex = paragraph.ChildEntities.IndexOf(field);

// Create a new WTextRange to hold the text that will replace the merge field.
WTextRange textRange = new WTextRange(paragraph.Document);

// Get the text from the merge field (which may be a hyperlink field).
textRange.Text = GetMergeFieldText(itemIndex, paragraph);

// Remove the merge field from the paragraph's child entities.
paragraph.ChildEntities.RemoveAt(itemIndex);

// Insert the text that replaces the merge field at the same position.
paragraph.ChildEntities.Insert(itemIndex, textRange);

// Return the text that will replace the merge field.
return textRange.Text;
}

/// <summary>
/// Extracts the displayed text from a merge field by traversing its field
/// elements and handling nested fields when present.
/// </summary>
/// <param name="hyperlinkIndex">
/// The index of the merge field within the paragraph child entities collection.
/// </param>
/// <param name="paragraph">
/// The paragraph containing the merge field.
/// </param>
/// <returns>
/// The text displayed by the merge field.
/// </returns>
private static string GetMergeFieldText(int hyperlinkIndex, WParagraph paragraph)
{
string text = string.Empty;

// Stack to handle nested fields while iterating through paragraph child entities.
Stack<Entity> fieldStack = new Stack<Entity>();
fieldStack.Push(paragraph.ChildEntities[hyperlinkIndex]);

// Flag to control whether to collect text or skip field code sections.
bool isFieldCode = true;
int i = (hyperlinkIndex + 1);

// Iterate through the paragraph's items (child entities) to collect text between field separator and field end.
while (i < paragraph.Items.Count)
{
Entity item = paragraph.ChildEntities[i];

// Check if the current item is a field and handle it by adding to the stack.
if (item is WField)
{
fieldStack.Push(item);
isFieldCode = true; // Set flag to skip text in field code section.
}
// Check if the current item is a field separator and enable collecting text.
else if (item is WFieldMark && ((WFieldMark)item).Type == FieldMarkType.FieldSeparator)
{
isFieldCode = false;
}
// Check if the current item is a field end and handle field stack.
else if (item is WFieldMark && ((WFieldMark)item).Type == FieldMarkType.FieldEnd)
{
// If it's the end of the outermost field, return the collected text.
if (fieldStack.Count == 1)
{
fieldStack.Clear();
return text;
}
else
{
fieldStack.Pop();
}
}
// If not in field code section and the item is a text range, collect the text.
else if (!isFieldCode && item is WTextRange)
{
text += ((WTextRange)item).Text;
}

i++;
}

return text;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<RootNamespace>Replace_MergeField_with_Bookmark_Hyperlink</RootNamespace>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Syncfusion.DocIO.Net.Core" Version="*" />
</ItemGroup>

<ItemGroup>
<None Update="Data\Template.docx">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="Output\.gitkeep">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
Loading