Attribute VB_Name = "ex_Word" '================ Word helper functions ============= ' Shared module version: 20220911 ' Tested in: TestWord ' Depends on: ' Required reference: Option Private Module Option Explicit Public Function WordIntersectRanges(rng1 As Word.Range, rng2 As Word.Range) As Word.Range If rng1.Document <> rng2.Document Then _ Exit Function If rng1.Start >= rng2.End Or rng2.Start >= rng1.End Then _ Exit Function If rng1.Start >= rng2.Start Then If rng2.End > rng1.Start Then _ Set WordIntersectRanges = rng1.Document.Range(rng1.Start, IIf(rng1.End > rng2.End, rng2.End, rng1.End)) Else If rng1.End > rng2.Start Then _ Set WordIntersectRanges = rng1.Document.Range(rng2.Start, IIf(rng1.End > rng2.End, rng2.End, rng1.End)) End If End Function Public Function WordPutText(sText$, iDoc As Word.Document, nWhere&, Optional aStyle$ = vbNullString) As Word.Range Set WordPutText = iDoc.Range(nWhere, nWhere) With WordPutText Call .InsertAfter(sText) If aStyle <> vbNullString Then _ .Style = aStyle End With End Function Public Function WordAppendTo(sText$, ByRef rWhere As Word.Range, Optional aStyle$ = vbNullString) As Word.Range Const ASCII_NEWLINE = 13 If rWhere.Characters.Count > 0 Then _ If AscW(rWhere.Characters.Last) = ASCII_NEWLINE Then _ Call rWhere.MoveEnd(wdCharacter, -1) Call rWhere.InsertAfter(sText) Dim insertedRng As Word.Range: Set insertedRng = rWhere.Duplicate insertedRng.Start = rWhere.End - VBA.Len(sText) If aStyle <> vbNullString Then _ insertedRng.Style = aStyle Set WordAppendTo = insertedRng End Function Public Function WordAddLine(sText$, rWhere As Word.Range, Optional aStyle$ = vbNullString) As Word.Range Dim nEnd&: nEnd = rWhere.End If rWhere.Tables.Count > 0 Then If rWhere.Cells.Count > 0 Then Dim theLastCell As Word.Cell: Set theLastCell = rWhere.Cells(rWhere.Cells.Count) If nEnd >= theLastCell.Range.End - 1 Then Set WordAddLine = AddLineLastInCell(sText, theLastCell, aStyle) GoTo SAFE_EXIT End If End If End If Dim nParaEnd&: nParaEnd = rWhere.Paragraphs.Last.Range.End If rWhere.End = rWhere.Document.Range.End Then Call rWhere.InsertAfter(vbNewLine) Set WordAddLine = WordPutText(sText, rWhere.Document, rWhere.End - 1, aStyle) Call WordAddLine.MoveEnd(wdCharacter, 1) GoTo SAFE_EXIT End If If nEnd <> nParaEnd Then If rWhere.Next(wdCharacter, 1).End = nParaEnd Then Call rWhere.MoveEnd(wdCharacter, 1) Else Call rWhere.InsertAfter(vbNewLine) End If End If Set WordAddLine = WordPutText(sText & vbNewLine, rWhere.Document, rWhere.End, aStyle) SAFE_EXIT: rWhere.End = nEnd End Function Public Function WordFindAndReplace(iDoc As Word.Document, findText$, ReplaceText$, _ Optional mCase As Boolean = False, _ Optional wildCard As Boolean = False, _ Optional wholeWord As Boolean = True) As Long Dim findRange As Word.Range: Set findRange = iDoc.Range Dim nResult&: nResult = 0 With findRange.Find .Text = findText .MatchCase = mCase .MatchWholeWord = wholeWord .MatchWildcards = wildCard .Replacement.Text = ReplaceText .Format = True .Font.Hidden = False Do While .Execute(Replace:=1) nResult = nResult + 1 Call findRange.Collapse(Direction:=wdCollapseEnd) Loop End With WordFindAndReplace = nResult End Function Public Function WordDeleteHyperlinks(iDoc As Word.Document) As Long On Error Resume Next WordDeleteHyperlinks = 0 Do Until iDoc.Hyperlinks.Count = 0 Call iDoc.Hyperlinks(1).Delete WordDeleteHyperlinks = WordDeleteHyperlinks + 1 Loop End Function Public Function WordStyleExists(iDoc As Word.Document, target$) As Boolean WordStyleExists = True On Error GoTo RET_FALSE Dim aStyle As Word.Style: Set aStyle = iDoc.Styles(target) Exit Function RET_FALSE: WordStyleExists = False End Function ' Requires: wordApp should be Application, responsible for both documents (if any or both are open) Public Function WordCopyStyle(wordApp As Word.Application, sSource$, sDestination$, sStyle$) As Boolean WordCopyStyle = True On Error GoTo RETURN_FALSE Call wordApp.OrganizerCopy(sSource, sDestination, sStyle, wdOrganizerObjectStyles) On Error GoTo 0 Exit Function RETURN_FALSE: WordCopyStyle = False End Function Public Function WordStyleIsUsed(iDoc As Word.Document, aStyle As Word.Style) As Boolean On Error Resume Next Dim findRange As Word.Range: Set findRange = iDoc.Range With findRange.Find .Text = vbNullString .Format = True .Style = aStyle WordStyleIsUsed = .Execute End With End Function Public Function WordAdjustRange(target As Word.Range) As Word.Range Dim rExpansion As Word.Range: Set rExpansion = target.Duplicate If Not rExpansion.Characters.First Like "[" & " " & Chr(160) & "]" Then Call rExpansion.StartOf(wdWord, wdExtend) Call rExpansion.MoveStartWhile("""'«»“”", wdBackward) End If If rExpansion.Start <> rExpansion.End Then _ Call rExpansion.EndOf(wdWord, wdExtend) If Not rExpansion.Characters.Last Like "[" & " " & Chr(160) & "]" Then Call rExpansion.MoveEndWhile("""'«»“”", wdForward) Call rExpansion.MoveEndWhile(" " & Chr(160), wdForward) End If Set WordAdjustRange = rExpansion End Function Public Function AcceptAuthorsRevision(oAuthors As Collection, target As Word.Document) As Long AcceptAuthorsRevision = 0 Dim itemChange As Word.Revision For Each itemChange In target.Revisions Dim sAuthor As Variant For Each sAuthor In oAuthors If sAuthor = itemChange.Author Then Call itemChange.Accept AcceptAuthorsRevision = AcceptAuthorsRevision + 1 GoTo NEXT_ITEM End If Next sAuthor NEXT_ITEM: Next itemChange End Function Public Function AcceptFormatRevision(iDoc As Word.Document) As Long AcceptFormatRevision = 0 Dim itemChange As Word.Revision Dim theType As WdRevisionType For Each itemChange In iDoc.Revisions theType = itemChange.Type If theType = wdRevisionParagraphProperty Or _ theType = wdRevisionStyle Or _ theType = wdRevisionProperty Then itemChange.Accept AcceptFormatRevision = AcceptFormatRevision + 1 End If Next itemChange End Function Public Function StyleByName(sStyle$, iDoc As Word.Document) As Word.Style On Error GoTo RETURN_NOTHING Set StyleByName = iDoc.Styles(sStyle) RETURN_NOTHING: End Function Public Function ExtractCommentText(target As Word.Range) As String If target.Comments.Count <> 0 Then ExtractCommentText = target.Comments(1).Range.Text Exit Function End If Dim theDoc As Word.Document: Set theDoc = target.Document Dim aComment As Word.Comment For Each aComment In theDoc.Comments If target.InRange(aComment.Scope) Then ExtractCommentText = aComment.Range.Text Exit Function End If Next aComment ExtractCommentText = vbNullString End Function ' ===== Private Function AddLineLastInCell(sText$, whereCell As Word.Cell, aStyle$) As Word.Range Dim nWhere&: nWhere = whereCell.Range.End - 1 If whereCell.Range.Characters.Count > 1 Then Call WordPutText(vbNewLine, whereCell.Range.Document, nWhere) nWhere = nWhere + 1 End If Set AddLineLastInCell = WordPutText(sText, whereCell.Range.Document, whereCell.Range.End - 1, aStyle) End Function