Skip to content

Commit 23cc80c

Browse files
authored
Merge pull request #29 from sabir-aspose/main
Add new word features for v25.6.1
2 parents b39da97 + 4684d32 commit 23cc80c

File tree

7 files changed

+248
-10
lines changed

7 files changed

+248
-10
lines changed

Openize.OpenXML-SDK.csproj

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,17 @@
33
<PropertyGroup>
44
<TargetFramework>netcoreapp3.1</TargetFramework>
55
<LangVersion>9.0</LangVersion>
6-
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
7-
<GenerateTargetFrameworkAttribute>false</GenerateTargetFrameworkAttribute>
6+
<GenerateAssemblyInfo>true</GenerateAssemblyInfo>
7+
<GenerateTargetFrameworkAttribute>true</GenerateTargetFrameworkAttribute>
8+
<!-- DLL version info -->
9+
<AssemblyVersion>25.6.1.0</AssemblyVersion>
10+
<FileVersion>25.6.1.0</FileVersion>
11+
<InformationalVersion>25.6.1</InformationalVersion>
12+
<!-- NuGet and metadata -->
813
<Description>A .NET library that simplifies working with OpenXML documents. Create, read, and manipulate Wordprocessing documents (Docx), Excel spreadsheets (Xlsx), and PowerPoint presentations (Pptx) effortlessly with Openize.OpenXML SDK. Licensed under MIT.</Description>
9-
<ReleaseVersion>25.4.1</ReleaseVersion>
14+
<ReleaseVersion>25.6.1</ReleaseVersion>
1015
<SynchReleaseVersion>false</SynchReleaseVersion>
11-
<PackageVersion>25.4.1</PackageVersion>
16+
<PackageVersion>25.6.1</PackageVersion>
1217
<Authors>Openize Pty Ltd</Authors>
1318
<Copyright>2024-2025</Copyright>
1419
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ var document = new Openize.Words.Document();
2626
document.Save("word.docx");
2727
Console.WriteLine("Empty word document created !!!");
2828
```
29-
For more details please check [word readme](Word/README.md).
29+
For more details please check [word readme](https://github.com/openize-com/openize-open-xml-sdk-net/blob/main/Word/README.md).
3030

3131
### Create an Empty Excel Spreadsheet
3232
```csharp

Word/OoxmData.cs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ internal class OoxmlDocData
1616
{
1717
private static ConcurrentDictionary<int, WordprocessingDocument> _staticDocDict =
1818
new ConcurrentDictionary<int, WordprocessingDocument>();
19+
private static Dictionary<int, WP.Table> _staticTableDict =
20+
new Dictionary<int, WP.Table>();
1921
private static int _staticDocCount = 0;
2022
private OwDocument _ooxmlDoc;
2123
private readonly object _lockObject = new object();
@@ -48,12 +50,16 @@ internal static OoxmlDocData CreateInstance()
4850
return new OoxmlDocData();
4951
}
5052

51-
5253
internal static string ConstructMessage(Exception ex, string operation)
5354
{
5455
return $"Error in operation {operation} at OpenXML.Words.Data : {ex.Message} \n Inner Exception: {ex.InnerException?.Message ?? "N/A"}";
5556
}
5657

58+
internal static void MapTable(int elementID,WP.Table wpTable)
59+
{
60+
_staticTableDict.TryAdd(elementID, wpTable);
61+
}
62+
5763
internal void Insert(FF.IElement newElement, int position, Document doc)
5864
{
5965
lock (_lockObject)
@@ -81,7 +87,6 @@ internal void Insert(FF.IElement newElement, int position, Document doc)
8187
elements.ElementAt(position).InsertBeforeSelf(wpPara);
8288
break;
8389

84-
8590
case FF.Table ffTable:
8691
var wpTable = OoxmlTable.CreateInstance(
8792
_ooxmlDoc.IDs, _ooxmlDoc.NumberingPart).CreateTable(ffTable);
@@ -152,8 +157,12 @@ internal void Update(FF.IElement newElement, int position, Document doc)
152157
break;
153158

154159
case FF.Table ffTable:
160+
_staticTableDict.TryGetValue(ffTable.ElementId, out WP.Table wpOldTable);
161+
//var wpTable = OoxmlTable.CreateInstance(
162+
// _ooxmlDoc.IDs, _ooxmlDoc.NumberingPart).CreateTable(ffTable);
155163
var wpTable = OoxmlTable.CreateInstance(
156-
_ooxmlDoc.IDs, _ooxmlDoc.NumberingPart).CreateTable(ffTable);
164+
_ooxmlDoc.IDs, _ooxmlDoc.NumberingPart).
165+
UpdateTable(ffTable, wpOldTable);
157166
enumerable1.ElementAt(position).InsertBeforeSelf(wpTable);
158167
break;
159168
case FF.Image ffImage:

Word/OoxmlTable.cs

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using WP = DocumentFormat.OpenXml.Wordprocessing;
66
using FF = Openize.Words.IElements;
77
using OWD = OpenXML.Words.Data;
8+
using DocumentFormat.OpenXml;
89

910
namespace OpenXML.Words
1011
{
@@ -61,7 +62,8 @@ internal WP.Table CreateTable(FF.Table ffTable)
6162
foreach (var ffPara in ffCell.Paragraphs)
6263
{
6364
//CreateParagraph(ffPara));
64-
wpCell.Append(OoxmlParagraph.CreateInstance(_IDs, _numberingPart).CreateParagraph(ffPara));
65+
wpCell.Append(OoxmlParagraph.CreateInstance(_IDs, _numberingPart).
66+
CreateParagraph(ffPara));
6567
}
6668

6769
wpRow.Append(wpCell);
@@ -79,6 +81,30 @@ internal WP.Table CreateTable(FF.Table ffTable)
7981
}
8082
}
8183
}
84+
85+
internal WP.Table UpdateTable(FF.Table ffTable,WP.Table wpTable)
86+
{
87+
var wpRows = wpTable.Elements<WP.TableRow>().ToList();
88+
for (int i = 0; i < ffTable.Rows.Count && i < wpRows.Count; i++)
89+
{
90+
var ffRow = ffTable.Rows[i];
91+
var wpRow = wpRows[i];
92+
var wpCells = wpRow.Elements<WP.TableCell>().ToList();
93+
for (int j = 0; j < ffRow.Cells.Count && j < wpCells.Count; j++)
94+
{
95+
var ffCell = ffRow.Cells[j];
96+
var wpCell = wpCells[j];
97+
wpCell.RemoveAllChildren<WP.Paragraph>();
98+
foreach (var para in ffCell.Paragraphs)
99+
{
100+
wpCell.Append(OoxmlParagraph.CreateInstance(_IDs, _numberingPart).
101+
CreateParagraph(para));
102+
}
103+
}
104+
}
105+
return wpTable;
106+
}
107+
82108
internal FF.Table LoadTable(WP.Table wpTable, int id)
83109
{
84110
lock (_lockObject)
@@ -127,6 +153,8 @@ internal FF.Table LoadTable(WP.Table wpTable, int id)
127153
ffTable.Style = tableStyle.Val;
128154
}
129155

156+
OWD.OoxmlDocData.MapTable(id, wpTable);
157+
130158
return ffTable;
131159
}
132160
catch (Exception ex)

Word/OpenizeElements.cs

Lines changed: 109 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1-
using System.Collections.Generic;
1+
using System;
2+
using System.Collections.Generic;
23
using System.Linq;
4+
using System.Text.RegularExpressions;
5+
36
namespace Openize.Words.IElements
47
{
58
/// <summary>
@@ -226,6 +229,111 @@ internal void UpdateText()
226229
{
227230
Text = string.Join("", Runs.Select(run => run.Text));
228231
}
232+
233+
internal void ReplaceText(string search, string replacement, bool useRegex = false)
234+
{
235+
var matches = useRegex
236+
? Regex.Matches(this.Text, search)
237+
: Regex.Matches(this.Text, Regex.Escape(search));
238+
239+
if (matches.Count == 0) return;
240+
241+
var newRuns = new List<Run>();
242+
int globalIndex = 0;
243+
244+
foreach (Match match in matches)
245+
{
246+
int matchStart = match.Index;
247+
int matchEnd = match.Index + match.Length;
248+
249+
// Track through original runs
250+
int currentIndex = 0;
251+
foreach (var run in this.Runs)
252+
{
253+
var text = run.Text;
254+
int runStart = currentIndex;
255+
int runEnd = currentIndex + text.Length;
256+
257+
if (runEnd <= matchStart || runStart >= matchEnd)
258+
{
259+
newRuns.Add(CloneRun(run));
260+
}
261+
else
262+
{
263+
// Overlaps with the match
264+
int relativeStart = Math.Max(0, matchStart - currentIndex);
265+
int relativeEnd = Math.Min(text.Length, matchEnd - currentIndex);
266+
267+
if (relativeStart > 0)
268+
{
269+
newRuns.Add(new Run
270+
{
271+
Text = text.Substring(0, relativeStart),
272+
FontFamily = run.FontFamily,
273+
FontSize = run.FontSize,
274+
Color = run.Color,
275+
Bold = run.Bold,
276+
Italic = run.Italic,
277+
Underline = run.Underline
278+
});
279+
}
280+
281+
if (runStart <= matchStart && runEnd >= matchEnd)
282+
{
283+
newRuns.Add(new Run
284+
{
285+
Text = replacement,
286+
FontFamily = run.FontFamily,
287+
FontSize = run.FontSize,
288+
Color = run.Color,
289+
Bold = run.Bold,
290+
Italic = run.Italic,
291+
Underline = run.Underline
292+
});
293+
}
294+
295+
if (relativeEnd < text.Length)
296+
{
297+
newRuns.Add(new Run
298+
{
299+
Text = text.Substring(relativeEnd),
300+
FontFamily = run.FontFamily,
301+
FontSize = run.FontSize,
302+
Color = run.Color,
303+
Bold = run.Bold,
304+
Italic = run.Italic,
305+
Underline = run.Underline
306+
});
307+
}
308+
}
309+
310+
currentIndex += run.Text.Length;
311+
312+
if (currentIndex >= matchEnd)
313+
break;
314+
}
315+
316+
// Replace only the first match at a time to avoid index shifting
317+
break;
318+
}
319+
320+
this.Runs.Clear();
321+
foreach (var r in newRuns)
322+
{
323+
this.AddRun(r);
324+
}
325+
}
326+
private static Run CloneRun(Run r) =>
327+
new Run
328+
{
329+
Text = r.Text,
330+
FontFamily = r.FontFamily,
331+
FontSize = r.FontSize,
332+
Color = r.Color,
333+
Bold = r.Bold,
334+
Italic = r.Italic,
335+
Underline = r.Underline
336+
};
229337
}
230338

231339
/// <summary>

Word/OpenizeWord.cs

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -925,6 +925,88 @@ public bool Remove(int elementId)
925925
}
926926
}
927927
}
928+
929+
/// <summary>
930+
/// Replaces all occurrences of a specified string or regular expression pattern in the text content of
931+
/// paragraphs and table cells throughout the document.
932+
/// </summary>
933+
/// <param name="search">
934+
/// The text or regular expression pattern to search for within the document's paragraphs.
935+
/// </param>
936+
/// <param name="replacement">
937+
/// The text to replace each occurrence of the <paramref name="search"/> pattern with.
938+
/// </param>
939+
/// <param name="useRegex">
940+
/// If <c>true</c>, interprets <paramref name="search"/> as a regular expression pattern;
941+
/// otherwise, treats it as plain text. Default is <c>false</c>.
942+
/// </param>
943+
/// <remarks>
944+
/// This method scans both top-level paragraphs and paragraphs within tables. If a match is found in a paragraph,
945+
/// the text is replaced using the provided <paramref name="replacement"/> value. Changes are tracked and applied
946+
/// through the <c>Update</c> method to maintain document structure integrity.
947+
/// </remarks>
948+
/// <example>
949+
/// <code>
950+
/// // Replace all occurrences of "foo" with "bar"
951+
/// document.ReplaceText("foo", "bar");
952+
///
953+
/// // Replace using a regular expression to match digits
954+
/// document.ReplaceText(@"\d+", "#", useRegex: true);
955+
/// </code>
956+
/// </example>
957+
public void ReplaceText(string search, string replacement, bool useRegex = false)
958+
{
959+
try
960+
{
961+
var updatedElements = new List<IElement>();
962+
var regex = useRegex
963+
? new System.Text.RegularExpressions.Regex(search)
964+
: new System.Text.RegularExpressions.Regex(System.Text.RegularExpressions.Regex.Escape(search));
965+
foreach (var element in _lstStructure)
966+
{
967+
if (element is Paragraph para)
968+
{
969+
if (regex.IsMatch(para.Text))
970+
{
971+
para.ReplaceText(search, replacement, useRegex);
972+
updatedElements.Add(para);
973+
}
974+
}
975+
else if (element is Table table)
976+
{
977+
var isMatched = false;
978+
foreach (var row in table.Rows)
979+
{
980+
foreach (var cell in row.Cells)
981+
{
982+
foreach (var cellPara in cell.Paragraphs)
983+
{
984+
if (regex.IsMatch(cellPara.Text))
985+
{
986+
cellPara.ReplaceText(search, replacement, useRegex);
987+
isMatched = true;
988+
}
989+
}
990+
}
991+
}
992+
if (isMatched)
993+
{
994+
updatedElements.Add(table);
995+
}
996+
}
997+
}
998+
foreach (var element in updatedElements)
999+
{
1000+
this.Update(element.ElementId, element);
1001+
}
1002+
}
1003+
catch (Exception ex)
1004+
{
1005+
var errorMessage = ConstructMessage(ex, "Replace Text");
1006+
throw new OpenizeException(errorMessage, ex);
1007+
}
1008+
}
1009+
9281010
/// <summary>
9291011
/// Dispose off all managed and unmanaged resources.
9301012
/// </summary>

Word/README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,4 +151,10 @@ var oval = new Openize.Words.IElements.Shape(300, 0, 200, 200,
151151
var groupShape = new Openize.Words.IElements.GroupShape(diamond, oval);
152152
body.AppendChild(groupShape);
153153
doc.Save("WordGroupShape.docx");
154+
```
155+
## Word Find and Replace Text
156+
```csharp
157+
var doc = new Openize.Words.Document("sample.docx");
158+
doc.ReplaceText("Original","Replaced");
159+
doc.Save("sample-updated.docx");
154160
```

0 commit comments

Comments
 (0)