Hello i need to add a custom autocorrect lists from a text file. only option is to add one by one so how to add multiple words to autocorrect.
See <LO user profile>/user/autocorr
; for instance, c:\Users\<username>\AppData\Roaming\LibreOffice\4\user\autocorr
on Windows.
The directory would contain some .dat files (after you’ve added at least one autocorrect entry). Those files are ZIP archives; you need the DocumentList.xml
file in the archive.
So i need to add words list to that xml file. also checked that file it has some other lines with autocorrect words.
It contains the pre-configured list of substitutions that you see when using clean profile; and also whatever you add there. Just format your list to have the required XML format:
<block-list:block block-list:abbreviated-name="word-to-replace" block-list:name="replacement"/>
keeping in mind that some characters (like <
, >
, &
) need escaping in XML to become <
, >
, &
, etc., and append the result to the existing entries.
Is it possible to write a macro? I need to automate this step.
If I type the incorrect > correct word in writer, running a macro should add it to that file.
It is already there.
I am looking for libreoffice - basic (programming language) code to do this. I have written python code, but do not know much about basic.
For a list of many words, I would just use Calc to join everything together and paste into a text editor.
AutoCorrectAddToDocumentList-XML.ods (9.9 KB)
Drag the formula in C down as far as you need, it is only 20 rows in the attached spreadsheet. Language is set to None.
I am sceptical about a pure uno / basic solution. There is the ZipFile Access LibreOffice: ZipFileAccess Service Reference but it provides only read access
which API does it use internally when I add an auto-correct entry using right click?
@shantanuo you can get the DOM of XML from ZIP: How check ODF version - #5 by sokol92
Or here are some macros for ZIP files (I found ones before some years in some blog that not exists any more)
Sub unpackAndAddToZip
dim sPackageURL$, sOutput$
sPackageURL="file:///C:/landic/pokus.zip"
sOutput="file:///c:/landic/new.txt" 'the file from ZIP will be unpacked to this file
GetZipContent(sPackageURL, "DocumentList.xml", sOutput) 'unpack DocumentList.xml to sOutput
PutZipContent(sPackageURL, "NEW/DocuList.txt", sOutput) 'add sOutput to NEW in ZIP
End Sub
Function GetZipContent(sZipURL$, sContentName$, sOutURL$)
dim oZipPkg as object, oSFA as object
dim oContentStream as object, oInput as object, args(1), meta as new com.sun.star.beans.NamedValue
oZipPkg=CreateUnoService("com.sun.star.packages.Package")
args(0)=sZipURL
with meta 'no META-INF&mimetype
.Name="PackageFormat"
.Value=false
end with
args(1)=meta
oZipPkg.initialize(args)
'oZipPkg.initialize(array(sZipURL)) 'if you need META-INF&mimetype in ZIP then .initialize is simplier
if oZipPkg.hasByHierarchicalName(sContentName) then
oContentStream=oZipPkg.getByHierarchicalName(sContentName)
oInput=oContentStream.getInputStream()
oSFA=CreateUnoService("com.sun.star.ucb.SimpleFileAccess")
oSFA.writeFile(sOutURL, oInput)
end if
End Function
Function PutZipContent(sZipURL$, sContentName$, sInputURL$, optional bCompress as boolean)
dim oZipPkg as object, oSFA as object, oContentStream as object, oZipFolder as object, args(1), meta as new com.sun.star.beans.NamedValue
oZipPkg=CreateUnoService("com.sun.star.packages.Package")
args(0)=sZipURL
with meta 'no META-INF&mimetype
.Name="PackageFormat"
.Value=false
end with
args(1)=meta
oZipPkg.initialize(args)
'oZipPkg.initialize(array(sZipURL)) 'if you need META-INF&mimetype in ZIP
oZipFolder=oZipPkg.getByHierarchicalName("/")
oContentStream=oZipPkg.createInstanceWithArguments(array(false))
oSFA=CreateUnoService("com.sun.star.ucb.SimpleFileAccess")
if oSFA.exists(sInputURL) then
oContentStream.setInputStream(oSFA.openFileRead(sInputURL))
if IsMissing(bCompress) then bCompress=True
oContentStream.setPropertyValue("Compressed", bCompress)
if NOT oZipFolder.hasByName(sContentName) then
oZipFolder.insertByName(sContentName, oContentStream)
else
oZipFolder.replaceByName(sContentName, oContentStream)
end if
oZipPkg.commitChanges()
end if
End Function
Sub CreateEmptyZip
dim oZipPkg as object, oSFA as object, oZipFolder as object, args(1), meta as new com.sun.star.beans.NamedValue
const sZipURL="file:///c:/landic/new.zip"
oZipPkg=CreateUnoService("com.sun.star.packages.Package")
args(0)=sZipURL
with meta 'no META-INF&mimetype
.Name="PackageFormat"
.Value=false
end with
args(1)=meta
oZipPkg.initialize(args) 'no META-INF&mimetype in ZIP
'oZipPkg.initialize(array(sZipURL)) 'with META-INF&mimetype in ZIP
oZipPkg.initialize(array(sZipURL))
oZipFolder=oZipPkg.getByHierarchicalName("/")
oZipPkg.commitChanges()
End Sub
Sub CreateEmptyZipFile
dim sZipURL$, sTxt$, i&, nInt&, oPipe as object, oSFA as object
sZipURL="file:///c:/landic/new.zip"
const sEmptyZip="504B0506000000000000000000000000000000000000"
sTxt=sEmptyZip
dim nBytes(Int((Len(sTxt)/2) -1)) as integer
for i=0 to UBound(nBytes)
nInt=Int(CDec("&H" & Mid(sTxt, i*2 +1, 2)))
if nInt > 127 then nInt=nInt - 256
nBytes(i)=nInt
next i
oPipe=CreateUnoService("com.sun.star.io.Pipe")
oPipe.writeBytes(nBytes)
oPipe.flush()
oPipe.closeOutput()
oSFA=CreateUnoService("com.sun.star.ucb.SimpleFileAccess")
oSFA.writeFile(sZipURL, oPipe)
oPipe.closeInput()
End Sub
Sub zipFileAccess
dim sPackageURL$, oZipAcc as object, sNames(), i&, oInput as object
sPackageURL="file:///c:/landic/pokus.zip"
oZipAcc=CreateUnoService("com.sun.star.packages.zip.ZipFileAccess")
oZipAcc.initialize(array(sPackageURL))
sNames=oZipAcc.getElementNames()
for i=0 to UBound(sNames) step 1
oInput=oZipAcc.getByName(sNames(i))
'xray oInput
oInput.closeInput()
next
End Sub
The Package is a service that provides access to a set of files and folders contained within a Package.
And how do I append the file? This does not seem to work.
Open sOutput For Append As #1
Print #1, '<block-list:block block-list:abbreviated-name="teest" block-list:name="test">'
?? Neither ZIP, nor XML can usually just appended. You will need full access and have to find the point, where you can extend entries, then regenerate the “closing” tags for XML or checksums for ZIP (unless your library hides this from you).
@shantanuo You cannot mix single and double quotation marks in LibreBasic :-), single quotation mark is only for comments. And double quotation mark in string is doubled ""
.
Print #1, "<block-list:block block-list:abbreviated-name=""teest"" block-list:name=""test"">"
Here is example how to append to UTF-8 file with the “autodetection” of Enter in file.
Sub appendStringToFile 'if file doesn't exist then it is created
on local error goto bug
dim sEncoding$, sFileName$, sUrl$, oSFA as object, oFileHandler as object, oInputStream as object, oOutputStream as object, s$, sEnter$, data(), sLine$
sFileName="c:\landic\some.txt" 'your file
sEncoding="UTF-8" 'encoding
s="blablabla blebleble glogloglo" 'your string to append
sUrl=ConvertToUrl(sFileName)
oSFA=CreateUnoService("com.sun.star.ucb.SimpleFileAccess")
if NOT oSFA.exists(sUrl) OR (oSFA.exists(sUrl) AND oSFA.getSize(sUrl)=0) then 'file not exists, or has zero size
oFileHandler=oSFA.openFileWrite(sUrl) 'new file for write
oOutputStream=CreateUnoService("com.sun.star.io.TextOutputStream")
oOutputStream.setEncoding(sEncoding)
oOutputStream.setOutputStream(oFileHandler)
oOutputStream.writeString(s) 'write the string
goto closing
end if
oFileHandler=oSFA.openFileReadWrite(sUrl) 'appending need to open for read&write
rem input stream, to set the position for writting to the end of file
oInputStream=CreateUnoService("com.sun.star.io.TextInputStream")
oInputStream.setEncoding(sEncoding)
oInputStream.setInputStream(oFileHandler)
rem detect Enter in TXT file
sLine=oInputStream.readLine()
oInputStream.InputStream.seek(Len(sLine)) 'seek to the end of 1st line
if oInputStream.InputStream.Position<oInputStream.InputStream.Length then 'there is more than 1 line in file
oInputStream.readBytes(data, 1) 'read 1 byte after 1st line to detect Enter
if data(0)=chr(10) then 'LF
sEnter=chr(10) 'linux
else 'suppose CR
sEnter=chr(13) 'suppose mac
if oInputStream.InputStream.Position<oInputStream.InputStream.Length then 'there isn't only 1 line ended by CR
oInputStream.readBytes(data, 1) 'read byte after CR
if data(0)=chr(10) then sEnter=chr(13) & chr(10) 'after CR is LF = windows
end if
end if
end if
if sEnter="" then 'bad detection of Enter, or only one line without Enter in file -> take Enter according to OS
select case getGuiType
case 3 'mac
sEnter=chr(13)
case 4 'linux
sEnter=chr(10)
case 1 'win
sEnter=chr(13) & chr(10)
case else 'something is wrong in detection of Enter according to OS
msgbox("Error in OS detection of Enter :-(, the line isn't append!")
goto closing
end select
end if
oInputStream.InputStream.seek(oInputStream.InputStream.Length) 'set position for writting to end of file -> for Append
rem output stream
oOutputStream=CreateUnoService("com.sun.star.io.TextOutputStream")
oOutputStream.setEncoding(sEncoding)
oOutputStream.setOutputStream(oFileHandler)
oOutputStream.writeString(sEnter & s) 'write the string
rem closing
closing:
if NOT isNull(oOutputStream) then oOutputStream.closeOutput()
if NOT isNull(oInputStream) then oInputStream.closeInput()
oFileHandler.closeOutput()
exit sub
bug:
msgbox("line: " & Erl & chr(13) & Err & ": " & Error, 16, "appendStringToFile")
End Sub
The path is not short.
Change the right side in the filePath assignment to match your locale. And, just in case, make a copy of the acor_xx_YY.dat file.
Sub Test()
Dim oPathSettings As Object, oPackage As Object, oDom As Object, oNode As Object
Dim oPackageFolder As Object, oPackageStream As Object, oOutputStream As Object, oTempFile As Object
Dim filePath As String, DocumentList As String, arr
oPathSettings = GetDefaultContext.getValueByName("/singletons/com.sun.star.util.thePathSettings")
filePath=Split(oPathSettings.AutoCorrect, ";")(1) & "/acor_ru-RU.dat" ' Change ru-RU to your locale!!
oPackage=createUnoService("com.sun.star.packages.Package")
oPackage.initialize Array(filePath)
DocumentList="DocumentList.xml"
oPackageStream=oPackage.getByHierarchicalName(DocumentList)
oDom=createUnoService("com.sun.star.xml.dom.DocumentBuilder").parse(oPackageStream.inputStream)
oNode=oDom.documentElement.appendChild(oDom.createElement("block-list:block"))
oNode.setAttribute("block-list:abbreviated-name", "Teest")
oNode.setAttribute("block-list:name", "Test")
oTempFile=createUnoService("com.sun.star.io.TempFile")
oDom.setOutputStream oTempFile.OutputStream
oDom.start
oTempFile.OutputStream.closeOutput
oPackageFolder=oPackage.getByHierarchicalName("")
oPackageFolder.removeByName DocumentList
oPackageStream=oPackage.createInstanceWithArguments(Array(False))
oPackageStream.SetInputStream(oTempFile.InputStream)
oPackageFolder.insertByName(DocumentList, oPackageStream)
oPackage.commitChanges
End Sub
@sokol92 small modification at the end: .replaceByName instead .removeByName + .insertByName
oPackageFolder=oPackage.getByHierarchicalName("")
oPackageStream=oPackage.createInstanceWithArguments(Array(False))
oPackageStream.SetInputStream(oTempFile.InputStream)
oPackageFolder.replaceByName(DocumentList, oPackageStream)
This works only if my .dat file is placed in the interal path:
/usr/lib/libreoffice/share/autocorr
But my file location is this:
/home/ubuntu/.config/libreoffice/4/user/uno_packages/cache/uno_packages/lu282754f7as.tmp_/with_acor_N_9_0.oxt/autocorr/
(Because it’s installed using an extension)
You know the concrete path…and ask us in which way you would have to change the code???
from pathlib import Path
acorpath = list(Path.home().glob(f".config/libreoffice/4/user/uno_packages/{'*/' * 5}acor*.dat"))[0]
I doubt if it can get past the compiler!
from lxml import etree
from pathlib import Path
from zipfile import ZipFile, ZIP_DEFLATED
from subprocess import check_call
#acorpath = list(Path.home().glob(f".config/libreoffice/4/user/uno_packages/{'*/'*5}acor*.dat"))[0]
acorpath = Path.home() /".config" / "libreoffice" / "4" / "user" / "autocorr" / "acor_de-DE.dat"
def main(xmlfile="DocumentList.xml"):
with ZipFile(acorpath) as acor:
acor.extract(xmlfile)
base = 'http://openoffice.org/2001/block-list'
tree = etree.parse(xmlfile)
nodes = tree.findall('/')
my_dict = {'asdf': 'jklö<',
'ihk': 'Industrie "und" Handelskammer'}
for key, value in my_dict.items():
new = etree.Element(f'{{{base}}}block',
nsmap={'block-list': base})
new.attrib[f'{{{base}}}abbreviated-name'] = key
new.attrib[f'{{{base}}}name'] = value
nodes.append(new)
root = tree.getroot()
root.extend(nodes)
root = root.getroottree()
check_call(["zip", "-d", acorpath, xmlfile ])#ZipFile cannot delete!
root.write(xmlfile)
with ZipFile(acorpath,"a") as acor:
acor.write(xmlfile, compress_type=ZIP_DEFLATED)