I’m in the middle of reviewing a ~13,000 word paper written in Microsoft Word. One of the things I needed to do was keep track of when a figure or table appeared, and had no reference in the paragraphs. This could be done with pen and paper, but I wanted to do it digitally.
LibreOffice Writer’s default search module cannot find this kind of cross-reference.
LibreOffice Writer has no functionality to expose “this field has no references”.
The Alternative Search and Replace extension for LibreOffice Writer cannot find this kind of cross-reference either. It can do a lot of things, but not this.
I do not know anything about programming in UNO – LibreOffice’s BASIC macro language. ChatGPT does though, so long as it’s put in High mode and given the instruction to validate the work before providing code. The following, placed as a macro in the Standard library, will do a search when run is executed. Searches like “9”, or “Figure 9”, or “Appendix 4” will find the text in the paragraphs matching that string, but only if it’s an internal cross-reference.
This made it easy to confirm that something like Figure 35 in Appendix 6 was actually referenced somewhere in the body of the paper, and was done as a field cross-reference (which means updating field references in the document would flow any changes).
Option Explicit
Sub FindCrossReferences
Dim oDoc As Variant
Dim oEnum As Variant
Dim oField As Variant
Dim sFilter As String
Dim sSourceType As String
Dim sDetails As String
Dim sHaystack As String
Dim nCount As Long
Dim nFound As Long
Dim ans As Integer
oDoc = ThisComponent
If Not oDoc.supportsService( _
"com.sun.star.text.TextDocument") Then
MsgBox "This is not a Writer document.", _
48, "Find Cross-References"
Exit Sub
End If
sFilter = InputBox( _
"Optional filter." & Chr(13) & Chr(13) & _
"Matches target name or displayed text." & Chr(13) & _
"Leave blank to find every cross-reference.", _
"Find Cross-References", _
"")
' ============================================================
' PASS 1: count matching GetReference fields
' ============================================================
nCount = 0
oEnum = oDoc.TextFields.createEnumeration()
Do While oEnum.hasMoreElements()
oField = oEnum.nextElement()
If oField.supportsService( _
"com.sun.star.text.textfield.GetReference") Then
sHaystack = _
CStr(oField.SourceName) & Chr(10) & _
CStr(oField.CurrentPresentation)
If sFilter = "" Or _
InStr(1, sHaystack, sFilter, 1) > 0 Then
nCount = nCount + 1
End If
End If
Loop
If nCount = 0 Then
If sFilter = "" Then
MsgBox _
"No cross-reference fields found.", _
64, _
"Find Cross-References"
Else
MsgBox _
"No cross-reference fields matching """ & _
sFilter & """ found.", _
64, _
"Find Cross-References"
End If
Exit Sub
End If
ans = MsgBox( _
nCount & " cross-reference field(s) found." & _
Chr(13) & Chr(13) & _
"Step through them?", _
36, _
"Find Cross-References")
If ans <> 6 Then Exit Sub
' ============================================================
' PASS 2: step through matching references
' ============================================================
oEnum = oDoc.TextFields.createEnumeration()
nFound = 0
Do While oEnum.hasMoreElements()
oField = oEnum.nextElement()
If oField.supportsService( _
"com.sun.star.text.textfield.GetReference") Then
sHaystack = _
CStr(oField.SourceName) & Chr(10) & _
CStr(oField.CurrentPresentation)
If sFilter = "" Or _
InStr(1, sHaystack, sFilter, 1) > 0 Then
nFound = nFound + 1
' ------------------------------------------------
' Determine source type
' ------------------------------------------------
Select Case oField.ReferenceFieldSource
Case 0
sSourceType = "Reference mark"
Case 1
sSourceType = "Sequence field"
Case 2
sSourceType = "Bookmark"
Case 3
sSourceType = "Footnote"
Case 4
sSourceType = "Endnote"
Case 5
sSourceType = "Style"
Case Else
sSourceType = _
"Unknown (" & _
oField.ReferenceFieldSource & ")"
End Select
' ------------------------------------------------
' Jump to the CROSS-REFERENCE occurrence itself.
'
' This is the text in the prose displaying e.g.
' "Figure 8", not the target caption/bookmark.
' ------------------------------------------------
GotoCrossReference oDoc, oField
' ------------------------------------------------
' Show details
' ------------------------------------------------
sDetails = _
"Reference " & nFound & _
" of " & nCount & Chr(13) & Chr(13) & _
"Displayed as: """ & _
oField.CurrentPresentation & """" & _
Chr(13) & _
"Target: " & _
oField.SourceName & Chr(13) & _
"Source type: " & _
sSourceType & Chr(13) & _
"Source value: " & _
oField.ReferenceFieldSource & Chr(13) & _
"Reference part: " & _
oField.ReferenceFieldPart & Chr(13) & _
"Sequence/ID: " & _
oField.SequenceNumber & _
Chr(13) & Chr(13) & _
"Yes = next reference" & Chr(13) & _
"No = stop"
ans = MsgBox( _
sDetails, _
36, _
"Find Cross-References")
If ans <> 6 Then Exit Sub
End If
End If
Loop
End Sub
Sub GotoCrossReference( _
oDoc As Variant, _
oField As Variant)
Dim oViewCursor As Variant
Dim oAnchor As Variant
Dim nLen As Long
oViewCursor = _
oDoc.CurrentController.getViewCursor()
oAnchor = oField.Anchor
' Jump to where the cross-reference field occurs
' in the document.
oViewCursor.gotoRange(oAnchor, False)
' Try to make the location visually obvious by
' selecting the displayed xref text.
nLen = Len(CStr(oField.CurrentPresentation))
If nLen > 0 Then
oViewCursor.goRight(nLen, True)
End If
End Sub