Libreoffice/openoffice linking citations to references
I have written the following macros for libreoffice odt files to link citations to a bookmark for the references. The bookmark (called References) has to be currently set manually.
Sub linkReferencesNumbered
refSearch("([0-9]|[1-9][0-9]|[1-9][0-9][0-9])")
End Sub
Sub linkReferencesAuthorDate
refSearch("\p{Lu}[\p{Lu}\p{Ll}'-]*( et al.) [0-9]{4}")
refSearch("\p{Lu}[\p{Lu}\p{Ll}'-]* [0-9]{4}")
refSearch("\p{Lu}[\p{Lu}\p{Ll}'-]* and \p{Lu}[\p{Lu}\p{Ll}'-]* [0-9]{4}")
End Sub
Sub refSearch(sSearchString)
oDoc = ThisComponent
oCursor = oDoc.Text.createTextCursor
oSearch = oDoc.createSearchDescriptor
oSearch.SearchRegularExpression = True
oSearch.SearchString = sSearchString
oFound = oDoc.findFirst(oSearch)
While Not IsNull(oFound)
oCursor = oFound.Text.createTextCursorByRange(oFound)
if isObject(oCursor.Start.ReferenceMark) Then oCursor.HyperLinkURL = "#References"
oFound = oDoc.findNext(oFound, oSearch)
Wend
End Sub
Sub linkReferencesNumbered
refSearch("([0-9]|[1-9][0-9]|[1-9][0-9][0-9])")
End Sub
Sub linkReferencesAuthorDate
refSearch("\p{Lu}[\p{Lu}\p{Ll}'-]*( et al.) [0-9]{4}")
refSearch("\p{Lu}[\p{Lu}\p{Ll}'-]* [0-9]{4}")
refSearch("\p{Lu}[\p{Lu}\p{Ll}'-]* and \p{Lu}[\p{Lu}\p{Ll}'-]* [0-9]{4}")
End Sub
Sub refSearch(sSearchString)
oDoc = ThisComponent
oCursor = oDoc.Text.createTextCursor
oSearch = oDoc.createSearchDescriptor
oSearch.SearchRegularExpression = True
oSearch.SearchString = sSearchString
oFound = oDoc.findFirst(oSearch)
While Not IsNull(oFound)
oCursor = oFound.Text.createTextCursorByRange(oFound)
if isObject(oCursor.Start.ReferenceMark) Then oCursor.HyperLinkURL = "#References"
oFound = oDoc.findNext(oFound, oSearch)
Wend
End Sub
Sub linkReferencesNumbered
refSearch("([1-9][0-9][0-9]|[1-9][0-9]|[0-9])")
End Sub
Sub linkNumberedReferences
insertZoteroBookmarks(TRUE)
refSearchnumbered("([1-9][0-9][0-9]|[1-9][0-9]|[0-9])")
End Sub
Sub refSearchnumbered(sSearchString)
oDoc = ThisComponent
oCursor = oDoc.Text.createTextCursor
oSearch = oDoc.createSearchDescriptor
oSearch.SearchRegularExpression = True
oSearch.SearchString = sSearchString
oFound = oDoc.findFirst(oSearch)
While Not IsNull(oFound)
oCursor = oFound.Text.createTextCursorByRange(oFound)
if isObject(oCursor.Start.ReferenceMark) Then
bm = "#_Ref_" & oCursor.String
oCursor.HyperLinkURL = bm
End If
oFound = oDoc.findNext(oFound, oSearch)
Wend
End Sub
Sub linkAuthorDateReferences
insertZoteroBookmarks(FALSE)
refSearch("\p{Lu}[\p{Lu}\p{Ll}'-]*( et al.)(,?) [0-9]{4}")
refSearch("(\p{Lu}[\p{Lu}\p{Ll}'-]* and )?(\p{Lu}[\p{Lu}\p{Ll}'-]*)(,?) [0-9]{4}")
refSearch("(\p{Lu}[\p{Lu}\p{Ll}'-]*, \p{Lu}[\p{Lu}\p{Ll}'-]*, and \p{Lu}[\p{Lu}\p{Ll}'-]*)(,?) [0-9]{4}")
End Sub
Sub refSearch(sSearchString)
oDoc = ThisComponent
oCursor = oDoc.Text.createTextCursor
oSearch = oDoc.createSearchDescriptor
oSearch.SearchRegularExpression = True
oSearch.SearchString = sSearchString
oFound = oDoc.findFirst(oSearch)
While Not IsNull(oFound)
oCursor = oFound.Text.createTextCursorByRange(oFound)
if isObject(oCursor.Start.ReferenceMark) Then
bm = "#_Ref_" & findFirstWord (oCursor.string)
oCursor.HyperLinkURL = bm
End If
oFound = oDoc.findNext(oFound, oSearch)
Wend
End Sub
Function findFirstWord (oString)
oReturnString = Split (oString, " ")
findFirstWord = Replace (oReturnString(0), ",", "")
End Function
Function Replace(Source As String, Search As String, NewPart As String)
Dim Result As String
Result = join(split(Source, Search), NewPart)
Replace = Trim(Result)
End Function
Sub insertZoteroBookmarks(bNumbered As boolean)
deleteRefBookmarks
Dim vSections
Dim sEventNames 'Array of event types
Dim sNames
oDoc = ThisComponent
oCursor = oDoc.Text.createTextCursor
vSections = ThisComponent.TextSections()
sEventNames = vSections.getElementNames()
For Each sEventName In sEventNames
If instr (sEventName, "ZOTERO") Then
oSection = oDoc.getTextSections().getByName(sEventName)
oSectionAnchor = oSection.getAnchor
oViewCursor = oDoc.CurrentController.getViewCursor()
oViewCursor.gotoRange(oSectionAnchor, false)
oSelection = oDoc.getCurrentSelection (oViewCursor)
oSel = oSelection.getbyIndex(0)
iCount=0
oPE= oSel.createEnumeration()
Do While oPE.hasMoreElements()
oPar = oPE.nextElement()
If oPar.supportsService("com.sun.star.text.Paragraph") Then
oSecEnum = oPar.createEnumeration()
Do While oSecEnum.hasMoreElements()
oParSection = oSecEnum.nextElement()
If oParSection.TextPortionType = "Text" Then
bm = ThisComponent.createInstance("com.sun.star.text.Bookmark")
oCurs = oParSection.getText().createTextCursorByRange(oParSection)
If bNumbered Then
iCount = iCount +1
bm.Name = "_Ref_" & iCount
Else
bm.Name = "_Ref_" & findFirstWord (oCurs.string)
End if
oDoc.Text.insertTextContent(oCurs, bm, True)
End If
Loop
End If
Loop
End If
Next
End Sub
Sub deleteRefBookmarks
Dim i As Integer, oBookmarks, sBookMarkNames()
If HasUnoInterfaces( ThisComponent, "com.sun.star.text.XBookmarksSupplier" ) Then
oBookmarks = ThisComponent.getBookmarks()
sBookMarkNames = oBookmarks.getElementNames()
For i = 0 To Ubound( sBookMarkNames )
If instr(sBookMarkNames(i), "_Ref_") Then
oBookmarks.getByName( sBookMarkNames( i ) ).dispose()
End if
Next
End If
End Sub
Sub linkNumberedReferences
insertZoteroBookmarks(TRUE)
refSearchnumbered("([1-9][0-9][0-9]|[1-9][0-9]|[0-9])")
End Sub
Sub refSearchnumbered(sSearchString)
oDoc = ThisComponent
oSearch = oDoc.createSearchDescriptor
oSearch.SearchRegularExpression = True
oSearch.SearchString = sSearchString
oFound = oDoc.findFirst(oSearch)
While Not IsNull(oFound)
oCursor = oFound.Text.createTextCursorByRange(oFound)
if isObject(oCursor.Start.ReferenceMark) Then
bm = "#_Ref_" & oCursor.String
oCursor.HyperLinkURL = bm
End If
oFound = oDoc.findNext(oFound, oSearch)
Wend
End Sub
Sub linkAuthorDateReferences
insertZoteroBookmarks(FALSE)
refSearch("\p{Lu}[\p{Lu}\p{Ll}'-]*( et al.)(,?) [0-9]{4}")
refSearch("(\p{Lu}[\p{Lu}\p{Ll}'-]* and )?(\p{Lu}[\p{Lu}\p{Ll}'-]*)(,?) [0-9]{4}")
refSearch("(\p{Lu}[\p{Lu}\p{Ll}'-]*, \p{Lu}[\p{Lu}\p{Ll}'-]*, and \p{Lu}[\p{Lu}\p{Ll}'-]*)(,?) [0-9]{4}")
End Sub
Sub refSearch(sSearchString)
oDoc = ThisComponent
oSearch = oDoc.createSearchDescriptor
oSearch.SearchRegularExpression = True
oSearch.SearchString = sSearchString
oFound = oDoc.findFirst(oSearch)
While Not IsNull(oFound)
oCursor = oFound.Text.createTextCursorByRange(oFound)
if isObject(oCursor.Start.ReferenceMark) Then
bm = "#_Ref_" & findFirstWord (oCursor.string)
oCursor.HyperLinkURL = bm
End If
oFound = oDoc.findNext(oFound, oSearch)
Wend
End Sub
Function findFirstWord (oString)
oReturnString = Split (oString, " ")
findFirstWord = Replace (Replace (oReturnString(0), ",", ""), Chr(10), "")
End Function
Function Replace(Source As String, Search As String, NewPart As String)
Dim Result As String
Result = join(split(Source, Search), NewPart)
Replace = Trim(Result)
End Function
Sub insertZoteroBookmarks(bNumbered As boolean)
deleteRefBookmarks
Dim vSections
Dim sEventNames 'Array of event types
Dim sNames
oDoc = ThisComponent
vSections = ThisComponent.TextSections()
sEventNames = vSections.getElementNames()
For Each sEventName In sEventNames
If instr (sEventName, "ZOTERO") Then Exit For
Next
oSection = oDoc.getTextSections().getByName(sEventName)
oSectionAnchor = oSection.getAnchor
oCursor = oSectionAnchor.getText().createTextCursorByRange(oSectionAnchor)
oPE = oCursor.createEnumeration()
iCount = 0
Do While oPE.hasMoreElements()
oPar = oPE.nextElement()
If oPar.supportsService("com.sun.star.text.Paragraph") Then
iCount = iCount +1
bm = ThisComponent.createInstance("com.sun.star.text.Bookmark")
oCurs = oPar.getText().createTextCursorByRange(oPar)
If bNumbered Then
bm.Name = "_Ref_" & iCount
Else
bm.Name = "_Ref_" & findFirstWord (oCurs.string)
End if
If Not oDoc.getBookmarks().hasByName(bm.Name) Then oDoc.Text.insertTextContent(oCurs, bm, True)
End If
Loop
End sub
Sub deleteRefBookmarks
Dim i As Integer, oBookmarks, sBookMarkNames()
If HasUnoInterfaces( ThisComponent, "com.sun.star.text.XBookmarksSupplier" ) Then
oBookmarks = ThisComponent.getBookmarks()
sBookMarkNames = oBookmarks.getElementNames()
For i = 0 To Ubound( sBookMarkNames )
If instr(sBookMarkNames(i), "_Ref_") Then
oBookmarks.getByName( sBookMarkNames( i ) ).dispose()
End if
Next
End If
End Sub
as I am not familiar with LO Basic nor with Macros at all, it took me some time to get the things working. Let me drop here some comments for potential other „noobs“:
-citations in the document have to be as ReferenceMarks (option under „Set Document Preferences“ button: „ReferenceMarks“/„Bookmarks“)
-Out of all the „routines“/„Subs“ that appear in the list of macros, run only one: „linkNumberedReferences“ or „linkAuthorDateReferences“, based on the actual citation style used in Your document (other „Subs“ are helping functions for organizing the source code)
-After Macro is run, the numbers marking the references in text (e.g. [27]) should get underlined to symbolise that they became hyperlinks. But I observed also that nothing visible happened which is some bug of my LO distribution (6.2.8.2. x64). Try to export PDF to see, whether the references in text changed into clickable hyperlinks
-The references does not become clickable hyperlinks in your ODT, it works only in exported PDF (when you hover your mouse over the reference in our ODT, you still can see the „ZOTERO“ system ToolTipText)
-You don't do the Unlink Citations as it would break also the result of the Macro
Finally, many thanks to the author of this macro!
I have updated the libreoffice macro, mainly by converting from the regex searches to find the citations.
import uno
import re
from ast import literal_eval as to_dict
#alter this for citations divided by comma
semicolon = re.compile(r"\d\w?;")
comma = re.compile(r"\d\w?,")
colon = re.compile(r"\d\w?:")
document = XSCRIPTCONTEXT.getDocument()
def modifyStyle(cStyleName, cStyleFamily, oFont, oSize, oColor):
oStyleFamily = document.getStyleFamilies().getByName( cStyleFamily )
oStyle = oStyleFamily.getByName( cStyleName )
oStyle.setPropertyValue("CharFontName", oFont)
oStyle.setPropertyValue("CharColor", oColor)
oStyle.setPropertyValue("CharHeight", oSize)
oStyle.setPropertyValue("CharUnderline", 0)
oStyle.setPropertyValue("CharNoHyphenation", True)
def modStyles():
modifyStyle("Internet Link", "CharacterStyles", "Times New Roman", 12, 0x0000FF)
modifyStyle("Visited Internet Link", "CharacterStyles", "Times New Roman", 12, 0x0000FF)
def deleteRefBookmarks():
bookmarks = document.getBookmarks()
bmList = []
for bookmark in bookmarks:
if "Ref_" in bookmark.Name:
bmList.append(bookmark.Name)
for bm in bmList:
bookmarks.getByName(bm).dispose()
def insertZoteroBookmarks(bNumbered):
deleteRefBookmarks()
vSections = document.getTextSections()
sEventNames = vSections.getElementNames()
for sEventName in sEventNames:
if "ZOTERO" in sEventName:
oSection = document.getTextSections().getByName(sEventName)
oSectionTextRange = oSection.getAnchor()
oPE = oSectionTextRange.createEnumeration()
iCount = 0
while oPE.hasMoreElements():
oPar = oPE.nextElement()
if oPar.supportsService("com.sun.star.text.Paragraph"):
iCount = iCount +1
bm = document.createInstance("com.sun.star.text.Bookmark")
oCurs = oPar.getText().createTextCursorByRange(oPar)
if bNumbered:
bm.Name = "Ref_" + str(iCount)
else:
bm.Name = "Ref_" + findFirstWord (oCurs.getString()) + "_" + literatureDate(oCurs.getString())
if not document.getBookmarks().hasByName(bm.Name):
document.Text.insertTextContent(oCurs, bm, True)
def findFirstWord (oString):
#finds first capitilzed name! updated 2021-09-24
notFound = True
while notFound:
(firstWord, nextString) = oString.split(maxsplit=1)
if firstWord[0].isupper():
notFound = False
else:
oString = nextString
return firstWord.rstrip(",")
def literatureDate(oString):
year = re.search(r'((19|2[0-9])\d{2}[a-z]?)', oString).group(1)
# year numbers from 1900 to 2999 and optional a-z
return year
def linkAuthorDateReferences():
modStyles()
insertZoteroBookmarks(False)
author_date_references()
def author_date_references():
search_descriptor = document.createSearchDescriptor()
search_descriptor.SearchRegularExpression = True
text_cursor = document.getText().createTextCursor()
reference_marks = document.ReferenceMarks
for reference_mark in reference_marks:
reference_mark_content = reference_mark.Name
reference_mark_content, _ = reference_mark_content.rsplit(" ", 1)
_,_, reference_mark_data = reference_mark_content.split(" ", 2)
data_dictionary = to_dict(reference_mark_data)
plain_cite = data_dictionary["properties"]["plainCitation"]
plain_cite = plain_cite.replace("(", "").replace(")", "")
if semicolon.search(plain_cite):
names = list(plain_cite.split(";"))
elif comma.search(plain_cite):
names = list(plain_cite.split(","))
elif colon.search(plain_cite):
names = list(plain_cite.split(":"))
else:
names = [plain_cite]
for citation_items in data_dictionary["citationItems"]:
if "itemData" in citation_items.keys():
item_data = citation_items["itemData"]
author = item_data["author"][0]["family"]
date_year = item_data["issued"]["date-parts"][0][0]
citation = list(filter(lambda x: author in x,names))
citation_year_duplicates= [c for c in citation if (date_year in c)]
text_cursor = reference_mark.getAnchor()
text_cursor_start = text_cursor.getStart()
found = False
for cite in citation_year_duplicates:
search_descriptor.SearchString = cite
found_range = document.findNext(text_cursor_start, search_descriptor)
if found_range.HyperLinkURL == "":
found = True
if found_range.String[-1].isalpha():
date_year = date_year + found_range.String[-1]
break
else:
text_cursor_start = found_range.getEnd()
if found == False:
break
text_cursor_by_range = found_range.Text.createTextCursorByRange(found_range)
text_cursor_by_range.HyperLinkURL = "#Ref_" + author + "_" + date_year
def linkNumberedReferences():
modStyles()
insertZoteroBookmarks(True)
numbered_references()
def numbered_references():
search_descriptor = document.createSearchDescriptor()
search_descriptor.SearchRegularExpression = True
text_cursor = document.getText().createTextCursor()
reference_marks = document.ReferenceMarks
for reference_mark in reference_marks:
reference_mark_content = reference_mark.Name
reference_mark_content, _ = reference_mark_content.rsplit(" ", 1)
_,_, reference_mark_data = reference_mark_content.split(" ", 2)
data_dictionary = to_dict(reference_mark_data)
plain_cite = data_dictionary["properties"]["plainCitation"]
plain_cite = plain_cite.replace("(", "").replace(")", "").replace("-", ",").replace("–", ",")
numbers = list(plain_cite.split(","))
for number in numbers:
text_cursor = reference_mark.getAnchor()
text_cursor_start = text_cursor.getStart()
search_descriptor.SearchString = number
found_range = document.findNext(text_cursor_start, search_descriptor)
text_cursor_by_range = found_range.Text.createTextCursorByRange(found_range)
text_cursor_by_range.HyperLinkURL = "#Ref_" + number
g_exportedScripts = linkNumberedReferences, linkAuthorDateReferences,
Also, you should be able to use the hyperlinks in libreoffice. You may need to press ctrl at the same time as the mouse button, depends on your setting.
Now i can run both python macro and Basic macro. The python macro didn't change the file (no new bookmarks) while the Basic macro can successfully generate bookmarks for each reference. Would it be possible to hyperlink the citations in the paper content with each bookmark in the reference section?
I'm not entirely certain whether the macro is intended to perform this function. Would you please be able to advise me on how to resolve this issue?
Because the video was not useful at all, here is how install this macro (LibreOffice version 7.3):
1. On Ubuntu, an extra package should be installed to manage Python macros:
sudo apt install libreoffice-script-provider-python
This will create the menu entry "Python" under "Tools > Macros > Manage Macros"
2. Copy the script into $HOME/.config/libreoffice/4/user/Scripts/python/. For other OS, see here: https://help.libreoffice.org/7.3/en-US/text/sbasic/python/python_locations.html
Now the script shows up in "run macro" window in "My macros".
Some comments on the code: the function 'ModStyles' to update character formatting of category "Internet Link" failed, because my interface is in French so I think it should be "Lien Internet". Anyway I removed this function call and edited this style in the normal LibreOffice interface.
It would be better to use a new specific style for bibliography links, because we want to style them separately from actual internet links.
here is a 2024 update:
The line
from ast import literal_eval as to_dict
should be replaced by:
from json import loads as to_dict
because the "reference marks" are in json format, which causes the original macro to fail on some input (for example if you manually edit some citations).
Would you mind if I put the latest version on github?
I did rewrite to automatically detect numbered or author-date references. I am including it here with your update.
I also wrote something similar for word (https://forums.zotero.org/discussion/86621/linking-citation-to-bibliography#latest) - please note the additional routine I managed to miss off the code I presented in the last comment.
import uno
import re
import json
from json import loads as to_dict
# alter this for citations divided by comma
semicolon = re.compile(r"\d\w?;")
comma = re.compile(r"\d\w?,")
colon = re.compile(r"\d\w?:")
document = XSCRIPTCONTEXT.getDocument()
def modifyStyle(cStyleName, cStyleFamily, oFont, oSize, oColor):
oStyleFamily = document.getStyleFamilies().getByName(cStyleFamily)
oStyle = oStyleFamily.getByName(cStyleName)
oStyle.setPropertyValue("CharFontName", oFont)
oStyle.setPropertyValue("CharColor", oColor)
oStyle.setPropertyValue("CharHeight", oSize)
oStyle.setPropertyValue("CharUnderline", 0)
oStyle.setPropertyValue("CharNoHyphenation", True)
def modStyles():
modifyStyle("Internet Link", "CharacterStyles", "Times New Roman", 12, 0x0000FF)
modifyStyle("Visited Internet Link", "CharacterStyles", "Times New Roman", 12, 0x0000FF)
def deleteRefBookmarks():
bookmarks = document.getBookmarks()
bmList = []
for bookmark in bookmarks:
if "Ref_" in bookmark.Name:
bmList.append(bookmark.Name)
for bm in bmList:
bookmarks.getByName(bm).dispose()
def insertZoteroBookmarks(bNumbered):
deleteRefBookmarks()
vSections = document.getTextSections()
sEventNames = vSections.getElementNames()
for sEventName in sEventNames:
if "ZOTERO" in sEventName:
oSection = document.getTextSections().getByName(sEventName)
oSectionTextRange = oSection.getAnchor()
oPE = oSectionTextRange.createEnumeration()
iCount = 0
while oPE.hasMoreElements():
oPar = oPE.nextElement()
if oPar.supportsService("com.sun.star.text.Paragraph"):
iCount = iCount + 1
bm = document.createInstance("com.sun.star.text.Bookmark")
oCurs = oPar.getText().createTextCursorByRange(oPar)
if bNumbered:
bm.Name = "Ref_" + str(iCount)
else:
bm.Name = "Ref_" + findFirstWord(oCurs.getString()) + "_" + literatureDate(oCurs.getString())
if not document.getBookmarks().hasByName(bm.Name):
document.Text.insertTextContent(oCurs, bm, True)
def findFirstWord(oString):
# finds first capitilzed name! updated 2021-09-24
notFound = True
while notFound:
(firstWord, nextString) = oString.split(maxsplit=1)
if firstWord[0].isupper():
notFound = False
else:
oString = nextString
return firstWord.rstrip(",")
def literatureDate(oString):
year = re.search(r'((19|2[0-9])\d{2}[a-z]?)', oString).group(1)
# year numbers from 1900 to 2999 and optional a-z
return year
def linkReferences():
modStyles()
search_descriptor = document.createSearchDescriptor()
search_descriptor.SearchRegularExpression = True
text_cursor = document.getText().createTextCursor()
reference_marks = document.ReferenceMarks
for reference_mark in reference_marks:
reference_mark_content = reference_mark.Name
reference_mark_content, _ = reference_mark_content.rsplit(" ", 1)
_, _, reference_mark_data = reference_mark_content.split(" ", 2)
# data_dictionary = to_dict(reference_mark_data)
data_dictionary = json.loads(reference_mark_data, strict=False)
plain_cite = data_dictionary["properties"]["plainCitation"]
plain_cite = plain_cite.replace("(", "").replace(")", "").replace("-", ",").replace("–", ",")
if plain_cite[0] in "0123456789":
insertZoteroBookmarks(True)
plain_cite = plain_cite.replace("-", ",").replace("–", ",")
numbers = list(plain_cite.split(","))
for number in numbers:
text_cursor = reference_mark.getAnchor()
text_cursor_start = text_cursor.getStart()
search_descriptor.SearchString = number
found_range = document.findNext(text_cursor_start, search_descriptor)
text_cursor_by_range = found_range.Text.createTextCursorByRange(found_range)
text_cursor_by_range.HyperLinkURL = "#Ref_" + number
else:
insertZoteroBookmarks(False)
if semicolon.search(plain_cite):
names = list(plain_cite.split(";"))
elif comma.search(plain_cite):
names = list(plain_cite.split(","))
elif colon.search(plain_cite):
names = list(plain_cite.split(":"))
else:
names = [plain_cite]
for citation_items in data_dictionary["citationItems"]:
if "itemData" in citation_items.keys():
item_data = citation_items["itemData"]
author = item_data["author"][0]["family"]
date_year = item_data["issued"]["date-parts"][0][0]
citation = list(filter(lambda x: author in x, names))
citation_year_duplicates = [c for c in citation if (date_year in c)]
text_cursor = reference_mark.getAnchor()
text_cursor_start = text_cursor.getStart()
found = False
for cite in citation_year_duplicates:
search_descriptor.SearchString = cite
found_range = document.findNext(text_cursor_start, search_descriptor)
if found_range.HyperLinkURL == "":
found = True
if found_range.String[-1].isalpha():
date_year = date_year + found_range.String[-1]
break
else:
text_cursor_start = found_range.getEnd()
if found is False:
break
text_cursor_by_range = found_range.Text.createTextCursorByRange(found_range)
text_cursor_by_range.HyperLinkURL = "#Ref_" + author + "_" + date_year
g_exportedScripts = linkReferences,
Just had to use it, and learned a little on how to debug a Libreoffice macro!
I had to tweak it because some citations caused it to crash:
found_range
isNone
when the search text is not found so this causes an AttributeError on the next line.I fixed some problems, such as when the citation was edited (in the editor of the "classic" view of zotero's plugin). I put the code at https://gitlab.com/GullumLuvl/bibloids/-/blob/master/LO-zotero-hyperlink-citations.py (Note that I removed the styling parts that I don't use).
For example, now a citation modified to look like “Smith (1974)” instead of the default “(Smith 1974)” will be processed.
Remaining problematic citations are:
- Multiple dates for one author, such as “Smith 1974, 1975”;
- Manually edited citation text (edited from the main document interface). Then this causes an attribute 'dontUpdate: "true"' to be set, which could be handled.
- if a prefix was added and is separated by comma, this can trigger comma-splitting a single “author, date” group
- most often, edited citations cause problems
Would also be nice to take into account the 'prefix' and 'suffix' fields and keep them out of the hyperlink. Things for my todo list :)Thanks again!
if not citation_year_duplicates:
citation_year_duplicates = [plain_cite]
However, this does add them to the hyperlink.
Insert this instead which seems to do deal with suffixes, prefixes and page locators (only in English!).
This should be inserted between
insertZoteroBookmarks(False)
and
if semicolon.search(plain_cite):
# added Nov 2024 need to remove suffixes etc first
for citation_items in data_dictionary["citationItems"]:
label = ""
if "label" in citation_items:
label = citation_items["label"]
locator = ""
if "locator" in citation_items:
locator = ", p. " + citation_items["locator"]
prefix = ""
if "prefix" in citation_items:
prefix = citation_items["prefix"] + " "
suffix = ""
if "suffix" in citation_items:
suffix = " " + citation_items["suffix"]
plain_cite = plain_cite.replace(prefix, "", 1)
plain_cite = plain_cite.replace(suffix, "", 1)
plain_cite = plain_cite.replace(locator, "", 1)