linking citation to bibliography
Following a previous macro to do this in libreoffice written in basic, I rewrote the program in python and improved it. I would appreciate any improvements, especially to the regex searches.
# coding: utf-8
import uno
import unohelper
import sys
import re
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 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 findFirstWord (oString):
(firstWord, *_) = oString.split(maxsplit=1)
return firstWord
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 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 modStyles():
modifyStyle("Internet Link", "CharacterStyles", "Times New Roman", 12, 0x0000FF)
modifyStyle("Visited Internet Link", "CharacterStyles", "Times New Roman", 12, 0x0000FF)
def linkNumberedReferences():
modStyles()
insertZoteroBookmarks(True)
refSearch("([1-9][0-9][0-9]|[1-9][0-9]|[0-9])", True)
def linkAuthorDateReferences():
modStyles()
insertZoteroBookmarks(False)
refSearch("\p{Lu}[\p{Lu}\p{Ll}'-]*( et al.)(,?) [0-9]{4}([a-z]?)", False)
refSearch("(\p{Lu}[\p{Lu}\p{Ll}'-]* and )?(\p{Lu}[\p{Lu}\p{Ll}'-]*)(,?) [0-9]{4}([a-z]?)", False)
refSearch("(\p{Lu}[\p{Lu}\p{Ll}'-]*, \p{Lu}[\p{Lu}\p{Ll}'-]*, and \p{Lu}[\p{Lu}\p{Ll}'-]*)(,?) [0-9]{4}([a-z]?)", False)
def refSearch(sSearchString, bNumbered):
oSearch = document.createSearchDescriptor()
oSearch.SearchRegularExpression = True
oSearch.SearchString = sSearchString
oFound = document.findFirst(oSearch)
while oFound:
oCursor = oFound.Text.createTextCursorByRange(oFound)
if oCursor.Start.ReferenceMark:
if bNumbered:
bm = "#Ref_" + oCursor.String
else:
bm = "#Ref_" + findFirstWord (oCursor.getString()) + "_" + literatureDate(oCursor.getString())
oCursor.HyperLinkURL = bm
oFound = document.findNext(oFound, oSearch)
g_exportedScripts = linkNumberedReferences, linkAuthorDateReferences,
I also have a similar version in vba for word.
By the way, the code works for numeric references and author-date with disambiguation using year-letter (eg. Smith 2010a, Smith 2010b).
Function firstWord(oStr)
firstWord = Split(oStr, " ")(0)
End Function
Function getYear(oStr, oExtra)
Dim searchString As String
searchString = "\b[0-9]{4,}[a-z]?" & oExtra
Set re = New RegExp
re.Global = True
re.IgnoreCase = False
re.Pattern = searchString
getYear = re.Execute(oStr)(0)
End Function
Sub insertZoteroLiteratureBookmarks(bmNumbered)
Dim oRange As Range
Dim oPara As Paragraph
Dim oRangePara As Range
Dim bmtext As String
For i = 1 To ActiveDocument.Fields.Count
If InStr(ActiveDocument.Fields(i).Code, "ADDIN ZOTERO_BIBL") Then
Set oRange = ActiveDocument.Fields(i).Result
Exit For
End If
Next
For Each oBookMark In oRange.Bookmarks
oBookMark.Delete
Next
iCount = 0
For Each oPara In oRange.Paragraphs
Set oRangePara = oPara.Range
Set bmRange = oRangePara
iCount = iCount + 1
If bmNumbered Then
bmtext = "Ref_" + CStr(iCount)
Else
oYear = getYear(oRangePara.Text, "[;:.,\)]")
bmtext = "Ref_" + oRangePara.Words(1) + "_" + oYear
bmtext = multipleReplace(bmtext, ":;.,) " & Chr(39) & ChrW(8217) & Chr(13))
End If
bmRange.MoveEnd unit:=wdCharacter, Count:=-1
ActiveDocument.Bookmarks.Add Name:=bmtext, Range:=bmRange
bmRange.Collapse wdCollapseEnd
Next
End Sub
Sub insertCitationToZoteroLiteratue(bmNumbered)
Dim i As Long
Dim oRng As Range
startingFieldCount = ActiveDocument.Fields.Count
For i = startingFieldCount To 1 Step -1
If InStr(ActiveDocument.Fields(i).Code, "ADDIN ZOTERO_ITEM") Then
Set oRange = ActiveDocument.Fields(i).Result
With oRange.Find
.MatchWildcards = True
If bmNumbered Then
oRange.Collapse wdCollapseStart
Do While .Execute("[0-9]{1,}") And oRange.InRange(ActiveDocument.Fields(i).Result)
bmtext = "Ref_" & oRange.Text
Set oRng = oRange
ActiveDocument.Hyperlinks.Add Anchor:=oRng, Address:="", _
SubAddress:=bmtext, ScreenTip:="", TextToDisplay:=""
oRange.Collapse wdCollapseEnd
Loop
Else
Do While .Execute("<*([0-9]{4}[a-z,,.;/)])") And oRange.InRange(ActiveDocument.Fields(i).Result)
bmtext = "Ref_" & firstWord(oRange.Text) & "_" & getYear(oRange.Text, "")
oRange.MoveEnd unit:=wdCharacter, Count:=-1
ActiveDocument.Hyperlinks.Add Anchor:=oRange, Address:="", _
SubAddress:=bmtext, ScreenTip:="", TextToDisplay:=""
oRange.Collapse wdCollapseEnd
Loop
End If
End With
End If
Next i
End Sub
Sub ZoteroLinkNumberedCitations()
insertZoteroLiteratureBookmarks (True)
insertCitationToZoteroLiteratue (True)
End Sub
Sub ZoteroLinkAuthorDateCitations()
insertZoteroLiteratureBookmarks (False)
insertCitationToZoteroLiteratue (False)
End Sub
import uno
import re
from ast import literal_eval as to_dict
document = XSCRIPTCONTEXT.getDocument()
semicolon = re.compile(r"\d\w?;")
comma = re.compile(r"\d\w?,")
colon = re.compile(r"\d\w?:")
def external_reference_links():
search_descriptor = document.createSearchDescriptor()
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]
citation_items_list = data_dictionary["citationItems"]
for citation_items in citation_items_list:
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_ok= [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_ok:
search_descriptor.SearchString = cite
found_range = document.findNext(text_cursor_start, search_descriptor)
if found_range.HyperLinkURL == "":
found = True
break
else:
text_cursor_start = found_range.getEnd()
if found == False:
break
text_cursor_by_range = found_range.Text.createTextCursorByRange(found_range)
if "DOI" in item_data.keys():
DOI = item_data["DOI"]
else:
DOI = ""
if "source" in item_data.keys():
source = item_data["source"]
else:
source = ""
if "note" in item_data.keys():
note = item_data["note"]
else:
note = ""
if DOI:
text_cursor_by_range.HyperLinkURL = "https://doi.org/" + DOI
elif source == "PubMed":
_, n = note.split(": ")
text_cursor_by_range.HyperLinkURL = "https://pubmed.ncbi.nlm.nih.gov/" + n
else:
pass
else:
continue
There is no such function in vba and you haven't defined one
Function multipleReplace(sBmtext, sList)
Dim iCounter As Integer
For iCounter = 1 To Len(sList)
sBmtext = Replace(sBmtext, Mid(sList, iCounter, 1), "")
Next
multipleReplace = sBmtext
End Function
Hope this solves it.