-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathMailMergeExResultHandler.cls
210 lines (164 loc) · 6.22 KB
/
MailMergeExResultHandler.cls
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
VERSION 1.0 CLASS
BEGIN
MultiUse = -1 'True
END
Attribute VB_Name = "MailMergeExResultHandler"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
Option Explicit
Public Doc As Document
Public Tag_StartOfDocument As String
Public Tag_FieldStart As String
Public Tag_FieldEnd As String
Public Tag_FieldNameDelimiter As String
Public Tag_NewLineSubstitute As String
Sub Class_Initialize()
Tag_StartOfDocument = MailMergeEx.Tag_StartOfDocument
Tag_FieldStart = MailMergeEx.Tag_FieldStart
Tag_FieldEnd = MailMergeEx.Tag_FieldEnd
Tag_FieldNameDelimiter = MailMergeEx.Tag_FieldNameDelimiter
Tag_ValueDelimiter = MailMergeEx.Tag_ValueDelimiter
Tag_NewLineSubstitute = MailMergeEx.Tag_NewLineSubstitute
End Sub
Public Sub CleanMerged()
' Foreach sub-document
Dim subdoc As Range
Set subdoc = SubDocAfter(StartOfDocumentRange)
Do Until subdoc Is Nothing
Dim analysis As Collection
Set analysis = AnalyzeSubDoc(subdoc)
Dim fieldGroup As Collection
Dim field As FieldInfo
For Each fieldGroup In analysis
If fieldGroup.Count = 1 Then
' Simple substitution
Set field = fieldGroup(1)
field.Range.Text = field.Values(0)
Else
' Complex substitution
Dim r As Range
Set r = Doc.Range(fieldGroup(1).Range.Start, fieldGroup(fieldGroup.Count).Range.End)
Set r = Doc.Range(r.Paragraphs.First.Range.Start, r.Paragraphs.Last.Range.End)
If Asc(Right(r, 1)) <> 13 Then ' Special case when not standard paragraph (i.e. table)
r.End = r.End + 1
End If
r.Copy
r.Collapse
' Replace in reverse order
ReplaceFieldWithValues fieldGroup, UBound(fieldGroup(1).Values), r
Dim i As Integer
For i = UBound(fieldGroup(1).Values) - 1 To LBound(fieldGroup(1).Values) Step -1
r.Paste
ReplaceFieldWithValues fieldGroup, i, r
Next
End If
Next
Set subdoc = SubDocAfter(subdoc)
Loop
RemoveStartOfDocumentTags
End Sub
Sub RemoveStartOfDocumentTags()
Doc.Range.Find.Execute Tag_StartOfDocument, ReplaceWith:=""
End Sub
Sub ReplaceFieldWithValues(fieldGroup As Collection, idx As Integer, r As Range)
Dim f As FieldInfo
For Each f In fieldGroup
Dim r2 As Range
Set r2 = r.Duplicate
r2.Find.Execute Tag_FieldStart & f.Name & Tag_FieldNameDelimiter & "*" & Tag_FieldEnd, _
MatchWildcards:=True
r2.Text = f.Values(idx)
Next
End Sub
Function AnalyzeSubDoc(subdoc As Range) As Collection
Dim r As Range
Dim field As Range
Dim items As New Collection
Set r = subdoc.Duplicate
' While finding field
Do While r.Find.Execute(Tag_FieldStart & "*" & Tag_FieldEnd, MatchWildcards:=True)
' that are in the current document
If r.Start > subdoc.End Then
Exit Do
End If
items.Add AnalyseField(r)
Loop
' Groupe by non-empty GroupName
Dim groupedItems As New Collection
Dim item As FieldInfo
Dim currentGroup As Collection
Dim currentGroupName As String
currentGroupName = ""
For Each item In items
If item.GroupName <> currentGroupName Or item.GroupName = "" Then
Set currentGroup = New Collection
groupedItems.Add currentGroup
currentGroupName = item.GroupName
End If
currentGroup.Add item
Next
Set AnalyzeSubDoc = groupedItems
End Function
Function AnalyseField(field As Range) As FieldInfo
Dim item As New FieldInfo
Set item.Range = field.Duplicate
' Cut start and end tags
Dim r As Range
Set r = field.Duplicate
r.Start = r.Start + Len(Tag_FieldStart)
r.End = r.End - Len(Tag_FieldEnd)
' Find name delimiter
Dim nameDelimiterRange As Range
Set nameDelimiterRange = r.Duplicate
nameDelimiterRange.Find.Execute Tag_FieldNameDelimiter
' Extract name
Dim nameRange As Range
Set nameRange = Doc.Range(r.Start, nameDelimiterRange.Start)
item.Name = nameRange.Text
' Extract values
Dim valueRange As Range
Dim valueStrings As Variant
Set valueRange = Doc.Range(nameDelimiterRange.End, r.End)
valueStrings = Split(valueRange.Text, Tag_ValueDelimiter)
Dim i As Integer
For i = LBound(valueStrings) To UBound(valueStrings)
valueStrings(i) = EscapeFieldValue(valueStrings(i))
Next
item.Values = valueStrings
item.InitGroupName
Set AnalyseField = item
End Function
Function EscapeFieldValue(ByVal str As String) As String
EscapeFieldValue = Replace(str, Tag_NewLineSubstitute, Chr(13))
End Function
Function SubDocAfter(location As Range)
Dim sod As Range
Dim nextSod As Range
Dim found As Boolean
' If at the end of the document
If location.End >= Doc.Range.End Then
Set SubDocAfter = Nothing
Exit Function
End If
' Searching start tag
Set sod = Doc.Range(location.End, Doc.Range.End)
found = sod.Find.Execute(Tag_StartOfDocument)
If Not found Then
Set SubDocAfter = Nothing
Exit Function
End If
' Searching end tag
Set nextSod = Doc.Range(sod.End, Doc.Range.End)
found = nextSod.Find.Execute(Tag_StartOfDocument)
If found Then ' Standard case
Set SubDocAfter = Doc.Range(sod.Start, nextSod.Start)
Else ' When at end of document
Set SubDocAfter = Doc.Range(sod.Start, Doc.Range.End)
End If
End Function
' ----------------
Function StartOfDocumentRange() As Range
Set StartOfDocumentRange = Doc.Range(Doc.Range.Start, Doc.Range.Start)
End Function