Compare commits

...

2 Commits

Author SHA1 Message Date
El Mau aac5b910a5 Initial version test 2022-04-19 23:07:26 -05:00
El Mau f033f0e2c4 Initial version 2022-04-19 22:03:45 -05:00
35 changed files with 18591 additions and 1 deletions

3
.gitignore vendored
View File

@ -1,4 +1,7 @@
# ---> Python
nerd-dictation/
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]

View File

@ -1,3 +1,21 @@
# zaz-talk2me
Dictation support for LibreOffice
Dictation support for LibreOffice
**Important**: Only for Linux and LibreOffice install from distribute repositories.
Support for English and Spanish.
Dependencies:
Ubuntu
```
sudo apt install xdotool
pip install --user vosk
```
Arch
```
sudo pacman -S xdotool
pip install --user vosk
```

1
VERSION Normal file
View File

@ -0,0 +1 @@
0.1.0

441
conf.py Normal file
View File

@ -0,0 +1,441 @@
#!/usr/bin/env python3
# ~ This file is part of ZAZ.
# ~ ZAZ is free software: you can redistribute it and/or modify
# ~ it under the terms of the GNU General Public License as published by
# ~ the Free Software Foundation, either version 3 of the License, or
# ~ (at your option) any later version.
# ~ ZAZ is distributed in the hope that it will be useful,
# ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
# ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# ~ GNU General Public License for more details.
# ~ You should have received a copy of the GNU General Public License
# ~ along with ZAZ. If not, see <https://www.gnu.org/licenses/>.
import logging
# ~ Type extension:
# ~ 1 = normal extension
# ~ 2 = new component
# ~ 3 = Calc addin
TYPE_EXTENSION = 1
# ~ Your great extension name, not used spaces
NAME = 'ZazTalk2Me'
# ~ https://semver.org/
VERSION = '0.1.0'
# ~ Should be unique, used URL inverse
ID = 'net.elmau.zaz.talk'
# ~ If you extension will be multilanguage set: True
# ~ This feature used gettext, set pythonpath and easymacro in True
# ~ You can used PoEdit for edit PO files and generate MO files.
# ~ https://poedit.net/
USE_LOCALES = True
DOMAIN = 'base'
PATH_LOCALES = 'locales'
PATH_PYGETTEXT = '/usr/lib/python3.10/Tools/i18n/pygettext.py'
# ~ You can use PoEdit for update locales too
PATH_MSGMERGE = 'msgmerge'
# ~ Show in extension manager
PUBLISHER = {
'en': {'text': 'El Mau', 'link': 'https://git.cuates.net/elmau/zaz-talk2me'},
'es': {'text': 'El Mau', 'link': 'https://git.cuates.net/elmau/zaz-talk2me'},
}
# ~ Name in this folder for copy
ICON = 'images/logo.png'
# ~ Name inside extensions
ICON_EXT = f'{NAME.lower()}.png'
DEPENDENCIES_MINIMAL = ''
# ~ Change for you favorite license
LICENSE_EN = f"""This file is part of {NAME}.
{NAME} is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
{NAME} is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with {NAME}. If not, see <https://www.gnu.org/licenses/>.
"""
LICENSE_ES = LICENSE_EN
INFO = {
'en': {
'display_name': 'Zaz Talk2Me',
'description': 'Dictation support for LibreOffice',
'license': LICENSE_EN,
},
'es': {
'display_name': 'Zaz Hablame',
'description': 'Soporte para dictado en LibreOffice',
'license': LICENSE_ES,
},
}
# ~ Menus, only for TYPE_EXTENSION = 1
# ~ Parent can be: AddonMenu or OfficeMenuBar
# ~ For icons con name: NAME_16.bmp, used only NAME
# ~ PARENT = ''
# ~ MENU_MAIN = {}
# ~ Shortcut: Key + "Modifier Keys"
# ~ Important: Not used any shortcuts used for LibreOffice
# ~ SHIFT is mapped to Shift on all platforms.
# ~ MOD1 is mapped to Ctrl on Windows/Linux, while it is mapped to Cmd on Mac.
# ~ MOD2 is mapped to Alt on all platforms.
# ~ For example: Shift+Ctrl+Alt+T -> T_SHIFT_MOD1_MOD2
PARENT = 'OfficeMenuBar'
MENU_MAIN = {
'en': 'Zaz Talk2Me',
'es': 'Zaz Hablame',
}
MENUS = (
{
'title': {'en': 'Start', 'es': 'Iniciar'},
'argument': 'start',
'context': 'writer',
'icon': 'start',
'toolbar': True,
'shortcut': 'S_SHIFT_MOD1_MOD2',
},
{
'title': {'en': 'End', 'es': 'Terminar'},
'argument': 'end',
'context': 'writer',
'icon': 'end',
'toolbar': True,
'shortcut': 'E_SHIFT_MOD1_MOD2',
},
{
'title': {'en': 'Config...', 'es': 'Configurar...'},
'argument': 'config',
'context': 'writer',
'icon': 'config',
'toolbar': True,
'shortcut': '',
},
)
# ~ Functions, only for TYPE_EXTENSION = 3
FUNCTIONS = {
'test': {
'displayname': {'en': 'test', 'es': 'prueba'},
'description': {'en': 'My test', 'es': 'Mi prueba'},
'parameters': {
'value': {
'displayname': {'en': 'value', 'es': 'valor'},
'description': {'en': 'The value', 'es': 'El valor'},
},
},
},
}
# ~ FUNCTIONS = {}
EXTENSION = {
'version': VERSION,
'name': NAME,
'id': ID,
'icon': (ICON, ICON_EXT),
'languages': tuple(INFO.keys())
}
# ~ If used more libraries set python path in True and copy inside
# ~ If used easymacro pythonpath always is True, recommended
DIRS = {
'meta': 'META-INF',
'source': 'source',
'description': 'description',
'images': 'images',
'registration': 'registration',
'files': 'extension',
'office': 'Office',
'locales': PATH_LOCALES,
'pythonpath': True,
}
FILES = {
'oxt': f'{NAME}_v{VERSION}.oxt',
'py': f'{NAME}.py',
'ext_desc': 'desc_{}.txt',
'manifest': 'manifest.xml',
'description': 'description.xml',
'idl': f'X{NAME}.idl',
'addons': 'Addons.xcu',
'urd': f'X{NAME}.urd',
'rdb': f'X{NAME}.rdb',
'update': f'{NAME.lower()}.update.xml',
'addin': 'CalcAddIn.xcu',
'shortcut': 'Accelerators.xcu',
'easymacro': True,
}
# ~ URLs for update for example
# ~ URL_XML_UPDATE = 'https://gitlab.com/USER/PROYECT/raw/BRANCH/FOLDERs/FILE_NAME'
URL_XML_UPDATE = ''
URL_OXT = ''
# ~ Default program for test: --calc, --writer, --draw
PROGRAM = '--writer'
# ~ Path to file for test
FILE_TEST = ''
PATHS = {
'idlc': '/usr/lib/libreoffice/sdk/bin/idlc',
'include': '/usr/share/idl/libreoffice',
'regmerge': '/usr/lib/libreoffice/program/regmerge',
'soffice': ('soffice', PROGRAM, FILE_TEST),
'install': ('unopkg', 'add', '-v', '-f', '-s'),
'profile': '/home/mau/.config/libreoffice/4/user',
'gettext': PATH_PYGETTEXT,
'msgmerge': PATH_MSGMERGE,
}
SERVICES = {
'job': "('com.sun.star.task.Job',)",
'addin': "('com.sun.star.sheet.AddIn',)",
}
FORMAT = '%(asctime)s - %(levelname)s - %(message)s'
DATE = '%d/%m/%Y %H:%M:%S'
LEVEL_ERROR = logging.getLevelName(logging.ERROR)
LEVEL_INFO = logging.getLevelName(logging.INFO)
logging.addLevelName(logging.ERROR, f'\033[1;41m{LEVEL_ERROR}\033[1;0m')
logging.addLevelName(logging.INFO, f'\x1b[32m{LEVEL_INFO}\033[1;0m')
logging.basicConfig(level=logging.DEBUG, format=FORMAT, datefmt=DATE)
log = logging.getLogger(NAME)
def _methods():
template = """ def {0}(self, {1}):
print({1})
return 'ok'\n"""
functions = ''
for k, v in FUNCTIONS.items():
args = ','.join(v['parameters'].keys())
functions += template.format(k, args)
return functions
SRV = SERVICES['job']
XSRV = 'XJobExecutor'
SRV_IMPORT = f'from com.sun.star.task import {XSRV}'
METHODS = """ def trigger(self, args='pyUNO'):
print('Hello World', args)
return\n"""
if TYPE_EXTENSION > 1:
MENUS = ()
XSRV = f'X{NAME}'
SRV_IMPORT = f'from {ID} import {XSRV}'
if TYPE_EXTENSION == 2:
SRV = f"('{ID}',)"
METHODS = """ def test(self, args='pyUNO'):
print('Hello World', args)
return\n"""
elif TYPE_EXTENSION == 3:
SRV = SERVICES['addin']
METHODS = _methods()
DATA_PY = f"""import uno
import unohelper
{SRV_IMPORT}
ID_EXTENSION = '{ID}'
SERVICE = {SRV}
class {NAME}(unohelper.Base, {XSRV}):
def __init__(self, ctx):
self.ctx = ctx
{METHODS}
g_ImplementationHelper = unohelper.ImplementationHelper()
g_ImplementationHelper.addImplementation({NAME}, ID_EXTENSION, SERVICE)
"""
def _functions():
a = '[in] any {}'
t = ' any {}({});'
f = ''
for k, v in FUNCTIONS.items():
args = ','.join([a.format(k) for k, v in v['parameters'].items()])
f += t.format(k, args)
return f
FILE_IDL = ''
if TYPE_EXTENSION > 1:
id_ext = ID.replace('.', '_')
interface = f'X{NAME}'
module = ''
for i, P in enumerate(ID.split('.')):
module += f'module {P} {{ '
close_module = '}; ' * (i + 1)
functions = ' void test([in] any argument);'
if TYPE_EXTENSION == 3:
functions = _functions()
FILE_IDL = f"""#ifndef __{id_ext}_idl__
#define __{id_ext}_idl__
#include <com/sun/star/uno/XInterface.idl>
{module}
interface {interface} : com::sun::star::uno::XInterface
{{
{functions}
}};
service {P} {{
interface {interface};
}};
{close_module}
#endif
"""
def _parameters(args):
NODE = """ <node oor:name="{name}" oor:op="replace">
<prop oor:name="DisplayName">
{displayname}
</prop>
<prop oor:name="Description">
{description}
</prop>
</node>"""
line = '{}<value xml:lang="{}">{}</value>'
node = ''
for k, v in args.items():
displayname = '\n'.join(
[line.format(' ' * 16, k, v) for k, v in v['displayname'].items()])
description = '\n'.join(
[line.format(' ' * 16, k, v) for k, v in v['description'].items()])
values = {
'name': k,
'displayname': displayname,
'description': description,
}
node += NODE.format(**values)
return node
NODE_FUNCTIONS = ''
if TYPE_EXTENSION == 3:
tmp = '{}<value xml:lang="{}">{}</value>'
NODE_FUNCTION = """ <node oor:name="{name}" oor:op="replace">
<prop oor:name="DisplayName">
{displayname}
</prop>
<prop oor:name="Description">
{description}
</prop>
<prop oor:name="Category">
<value>Add-In</value>
</prop>
<prop oor:name="CompatibilityName">
<value xml:lang="en">AutoAddIn.{name}</value>
</prop>
<node oor:name="Parameters">
{parameters}
</node>
</node>"""
for k, v in FUNCTIONS.items():
displayname = '\n'.join(
[tmp.format(' ' * 12, k, v) for k, v in v['displayname'].items()])
description = '\n'.join(
[tmp.format(' ' * 12, k, v) for k, v in v['description'].items()])
parameters = _parameters(v['parameters'])
values = {
'name': k,
'displayname': displayname,
'description': description,
'parameters': parameters,
}
NODE_FUNCTIONS += NODE_FUNCTION.format(**values)
FILE_ADDIN = f"""<?xml version="1.0" encoding="UTF-8"?>
<oor:component-data xmlns:oor="http://openoffice.org/2001/registry" xmlns:xs="http://www.w3.org/2001/XMLSchema" oor:name="CalcAddIns" oor:package="org.openoffice.Office">
<node oor:name="AddInInfo">
<node oor:name="{ID}" oor:op="replace">
<node oor:name="AddInFunctions">
{NODE_FUNCTIONS}
</node>
</node>
</node>
</oor:component-data>"""
DATA_MANIFEST = [FILES['py'], f"Office/{FILES['shortcut']}", 'Addons.xcu']
if TYPE_EXTENSION > 1:
DATA_MANIFEST.append(FILES['rdb'])
if TYPE_EXTENSION == 3:
DATA_MANIFEST.append('CalcAddIn.xcu')
DATA_DESCRIPTION = {
'identifier': {'value': ID},
'version': {'value': VERSION},
'display-name': {k: v['display_name'] for k, v in INFO.items()},
'icon': ICON_EXT,
'publisher': PUBLISHER,
'update': URL_XML_UPDATE,
'minimal': DEPENDENCIES_MINIMAL,
}
DATA_ADDONS = {
'parent': PARENT,
'images': DIRS['images'],
'main': MENU_MAIN,
'menus': MENUS,
}
DATA = {
'py': DATA_PY,
'manifest': DATA_MANIFEST,
'description': DATA_DESCRIPTION,
'addons': DATA_ADDONS,
'update': URL_OXT,
'idl': FILE_IDL,
'addin': FILE_ADDIN,
}
with open('VERSION', 'w') as f:
f.write(VERSION)
# ~ LICENSE_ACCEPT_BY = 'user' # or admin
# ~ LICENSE_SUPPRESS_ON_UPDATE = True

7731
easymacro.py Normal file

File diff suppressed because it is too large Load Diff

Binary file not shown.

BIN
images/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

1
requirements.txt Normal file
View File

@ -0,0 +1 @@
vosk

130
source/Addons.xcu Normal file
View File

@ -0,0 +1,130 @@
<?xml version="1.0" encoding="utf-8"?>
<oor:component-data xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:oor="http://openoffice.org/2001/registry" oor:name="Addons" oor:package="org.openoffice.Office">
<node oor:name="AddonUI">
<node oor:name="OfficeMenuBar">
<node oor:name="net.elmau.zaz.talk" oor:op="replace">
<prop oor:name="Title" oor:type="xs:string">
<value xml:lang="en">Zaz Talk2Me</value>
<value xml:lang="es">Zaz Hablame</value>
</prop>
<prop oor:name="Target" oor:type="xs:string">
<value>_self</value>
</prop>
<node oor:name="Submenu">
<node oor:name="m0" oor:op="replace">
<prop oor:name="Title" oor:type="xs:string">
<value xml:lang="en">Start</value>
<value xml:lang="es">Iniciar</value>
</prop>
<prop oor:name="Context" oor:type="xs:string">
<value>com.sun.star.text.TextDocument</value>
</prop>
<prop oor:name="URL" oor:type="xs:string">
<value>service:net.elmau.zaz.talk?start</value>
</prop>
<prop oor:name="Target" oor:type="xs:string">
<value>_self</value>
</prop>
<prop oor:name="ImageIdentifier" oor:type="xs:string">
<value>%origin%/images/start</value>
</prop>
</node>
<node oor:name="m1" oor:op="replace">
<prop oor:name="Title" oor:type="xs:string">
<value xml:lang="en">End</value>
<value xml:lang="es">Terminar</value>
</prop>
<prop oor:name="Context" oor:type="xs:string">
<value>com.sun.star.text.TextDocument</value>
</prop>
<prop oor:name="URL" oor:type="xs:string">
<value>service:net.elmau.zaz.talk?end</value>
</prop>
<prop oor:name="Target" oor:type="xs:string">
<value>_self</value>
</prop>
<prop oor:name="ImageIdentifier" oor:type="xs:string">
<value>%origin%/images/end</value>
</prop>
</node>
<node oor:name="m2" oor:op="replace">
<prop oor:name="Title" oor:type="xs:string">
<value xml:lang="en">Config...</value>
<value xml:lang="es">Configurar...</value>
</prop>
<prop oor:name="Context" oor:type="xs:string">
<value>com.sun.star.text.TextDocument</value>
</prop>
<prop oor:name="URL" oor:type="xs:string">
<value>service:net.elmau.zaz.talk?config</value>
</prop>
<prop oor:name="Target" oor:type="xs:string">
<value>_self</value>
</prop>
<prop oor:name="ImageIdentifier" oor:type="xs:string">
<value>%origin%/images/config</value>
</prop>
</node>
</node>
</node>
</node>
<node oor:name="OfficeToolBar">
<node oor:name="net.elmau.zaz.talk" oor:op="replace">
<node oor:name="t0" oor:op="replace">
<prop oor:name="Title" oor:type="xs:string">
<value xml:lang="en">Start</value>
<value xml:lang="es">Iniciar</value>
</prop>
<prop oor:name="Context" oor:type="xs:string">
<value>com.sun.star.text.TextDocument</value>
</prop>
<prop oor:name="URL" oor:type="xs:string">
<value>service:net.elmau.zaz.talk?start</value>
</prop>
<prop oor:name="Target" oor:type="xs:string">
<value>_self</value>
</prop>
<prop oor:name="ImageIdentifier" oor:type="xs:string">
<value>%origin%/images/start</value>
</prop>
</node>
<node oor:name="t1" oor:op="replace">
<prop oor:name="Title" oor:type="xs:string">
<value xml:lang="en">End</value>
<value xml:lang="es">Terminar</value>
</prop>
<prop oor:name="Context" oor:type="xs:string">
<value>com.sun.star.text.TextDocument</value>
</prop>
<prop oor:name="URL" oor:type="xs:string">
<value>service:net.elmau.zaz.talk?end</value>
</prop>
<prop oor:name="Target" oor:type="xs:string">
<value>_self</value>
</prop>
<prop oor:name="ImageIdentifier" oor:type="xs:string">
<value>%origin%/images/end</value>
</prop>
</node>
<node oor:name="t2" oor:op="replace">
<prop oor:name="Title" oor:type="xs:string">
<value xml:lang="en">Config...</value>
<value xml:lang="es">Configurar...</value>
</prop>
<prop oor:name="Context" oor:type="xs:string">
<value>com.sun.star.text.TextDocument</value>
</prop>
<prop oor:name="URL" oor:type="xs:string">
<value>service:net.elmau.zaz.talk?config</value>
</prop>
<prop oor:name="Target" oor:type="xs:string">
<value>_self</value>
</prop>
<prop oor:name="ImageIdentifier" oor:type="xs:string">
<value>%origin%/images/config</value>
</prop>
</node>
</node>
</node>
</node>
</oor:component-data>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest:manifest xmlns:manifest="urn:oasis:names:tc:opendocument:xmlns:manifest:1.0" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" manifest:version="1.2">
<manifest:file-entry manifest:full-path="ZazTalk2Me.py" manifest:media-type="application/vnd.sun.star.uno-component;type=Python"/>
<manifest:file-entry manifest:full-path="Office/Accelerators.xcu" manifest:media-type="application/vnd.sun.star.configuration-data"/>
<manifest:file-entry manifest:full-path="Addons.xcu" manifest:media-type="application/vnd.sun.star.configuration-data"/>
</manifest:manifest>

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<oor:component-data xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:oor="http://openoffice.org/2001/registry" oor:name="Accelerators" oor:package="org.openoffice.Office">
<node oor:name="PrimaryKeys">
<node oor:name="Modules">
<node oor:name="com.sun.star.text.TextDocument">
<node oor:name="S_SHIFT_MOD1_MOD2" oor:op="fuse">
<prop oor:name="Command">
<value xml:lang="en-US">service:net.elmau.zaz.talk?start</value>
</prop>
</node>
</node>
<node oor:name="com.sun.star.text.TextDocument">
<node oor:name="E_SHIFT_MOD1_MOD2" oor:op="fuse">
<prop oor:name="Command">
<value xml:lang="en-US">service:net.elmau.zaz.talk?end</value>
</prop>
</node>
</node>
</node>
</node>
</oor:component-data>

32
source/ZazTalk2Me.py Normal file
View File

@ -0,0 +1,32 @@
import uno
import unohelper
from com.sun.star.task import XJobExecutor
import easymacro as app
import nerd_dictation as nd
ID_EXTENSION = 'net.elmau.zaz.talk'
SERVICE = ('com.sun.star.task.Job',)
class ZazTalk2Me(unohelper.Base, XJobExecutor):
def __init__(self, ctx):
self.ctx = ctx
@app.run_in_thread
def trigger(self, args):
path_ext = app.paths.from_id(ID_EXTENSION)
path_model = app.paths.join(path_ext, 'model')
if args=='start':
nd.main_begin(vosk_model_dir=path_model, config_override=None)
app.info('Start')
elif args=='end':
nd.main_end()
app.info('End')
return
g_ImplementationHelper = unohelper.ImplementationHelper()
g_ImplementationHelper.addImplementation(ZazTalk2Me, ID_EXTENSION, SERVICE)

26
source/description.xml Normal file
View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<description xmlns="http://openoffice.org/extensions/description/2006" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:d="http://openoffice.org/extensions/description/2006" xmlns:l="http://libreoffice.org/extensions/description/2011">
<identifier value="net.elmau.zaz.talk"/>
<version value="0.1.0"/>
<display-name>
<name lang="en">Zaz Talk2Me</name>
<name lang="es">Zaz Hablame</name>
</display-name>
<extension-description>
<src lang="en" xlink:href="description/desc_en.txt"/>
<src lang="es" xlink:href="description/desc_es.txt"/>
</extension-description>
<icon>
<default xlink:href="images/zaztalk2me.png"/>
</icon>
<publisher>
<name xlink:href="https://git.cuates.net/elmau/zaz-talk2me" lang="en">El Mau</name>
<name xlink:href="https://git.cuates.net/elmau/zaz-talk2me" lang="es">El Mau</name>
</publisher>
<registration>
<simple-license accept-by="user" suppress-on-update="true">
<license-text xlink:href="registration/license_en.txt" lang="en"/>
<license-text xlink:href="registration/license_es.txt" lang="es"/>
</simple-license>
</registration>
</description>

View File

@ -0,0 +1 @@
Dictation support for LibreOffice

View File

@ -0,0 +1 @@
Soporte para dictado en LibreOffice

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

10
source/model/README Normal file
View File

@ -0,0 +1,10 @@
Copyright 2022-2050 Alpha Cephei Inc
Small Spanish model for Vosk
WER
%WER 18.51 [ 28200 / 152364, 3732 ins, 5395 del, 19073 sub ] exp/chain_a/tdnn/decode_test_cv_look_fast/wer_10_0.0
LOG (nnet3-latgen-faster-lookahead[5.5.916~6-7f57e]:main():nnet3-latgen-faster-lookahead.cc:215) Time taken 797.251s: real-time factor assuming 100 frames/sec is 0.0845848
%WER 20.39 [ 2939 / 14416, 495 ins, 745 del, 1699 sub ] exp/chain_a/tdnn/decode_test_mtedx_look_fast/wer_8_0.0
LOG (nnet3-latgen-faster-lookahead[5.5.916~6-7f57e]:main():nnet3-latgen-faster-lookahead.cc:215) Time taken 34.4051s: real-time factor assuming 100 frames/sec is 0.0877592

BIN
source/model/am/final.mdl Normal file

Binary file not shown.

View File

@ -0,0 +1,7 @@
--use-energy=false
--num-mel-bins=40
--num-ceps=40
--low-freq=20
--high-freq=7600
--allow-upsample=true
--allow-downsample=true

View File

@ -0,0 +1,10 @@
--min-active=200
--max-active=7000
--beam=13.0
--lattice-beam=4.0
--acoustic-scale=1.0
--frame-subsampling-factor=3
--endpoint.silence-phones=1:2:3:4:5:6:7:8:9:10
--endpoint.rule2.min-trailing-silence=0.5
--endpoint.rule3.min-trailing-silence=1.0
--endpoint.rule4.min-trailing-silence=2.0

BIN
source/model/graph/Gr.fst Normal file

Binary file not shown.

BIN
source/model/graph/HCLr.fst Normal file

Binary file not shown.

View File

@ -0,0 +1,10 @@
7837
7838
7839
7840
7841
7842
7843
7844
7845
7846

View File

@ -0,0 +1,110 @@
1 nonword
2 begin
3 end
4 internal
5 singleton
6 nonword
7 begin
8 end
9 internal
10 singleton
11 begin
12 end
13 internal
14 singleton
15 begin
16 end
17 internal
18 singleton
19 begin
20 end
21 internal
22 singleton
23 begin
24 end
25 internal
26 singleton
27 begin
28 end
29 internal
30 singleton
31 begin
32 end
33 internal
34 singleton
35 begin
36 end
37 internal
38 singleton
39 begin
40 end
41 internal
42 singleton
43 begin
44 end
45 internal
46 singleton
47 begin
48 end
49 internal
50 singleton
51 begin
52 end
53 internal
54 singleton
55 begin
56 end
57 internal
58 singleton
59 begin
60 end
61 internal
62 singleton
63 begin
64 end
65 internal
66 singleton
67 begin
68 end
69 internal
70 singleton
71 begin
72 end
73 internal
74 singleton
75 begin
76 end
77 internal
78 singleton
79 begin
80 end
81 internal
82 singleton
83 begin
84 end
85 internal
86 singleton
87 begin
88 end
89 internal
90 singleton
91 begin
92 end
93 internal
94 singleton
95 begin
96 end
97 internal
98 singleton
99 begin
100 end
101 internal
102 singleton
103 begin
104 end
105 internal
106 singleton
107 begin
108 end
109 internal
110 singleton

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,3 @@
[
5.206991e+10 -2.007229e+09 -4.328604e+09 5.49911e+07 -7.643476e+09 -9.501782e+09 -7.916147e+09 -5.964982e+09 -3.417672e+09 -2.90296e+09 -1.729187e+09 -2.994376e+09 -3.811278e+09 -9.768101e+08 -3.268531e+09 -2.405868e+09 -1.514416e+09 -8.461341e+08 -1.018629e+09 -2.062752e+08 -2.70429e+08 -1.184466e+08 -4.470086e+07 5591588 1.14964e+08 1.34052e+08 2.581257e+08 6.844078e+07 1.854583e+08 7597449 1.244031e+08 1.31236e+08 2.400246e+08 2.442383e+08 3.396472e+08 2.301846e+08 1.53252e+08 -9.70687e+07 -1.643316e+08 -1.051184e+08 5.0121e+08
5.548082e+12 1.901654e+11 2.493659e+11 2.369916e+11 3.321238e+11 4.469359e+11 3.488889e+11 2.948305e+11 2.37128e+11 2.259571e+11 1.850742e+11 2.219626e+11 1.79272e+11 1.295488e+11 1.300027e+11 9.150627e+10 6.573611e+10 4.872693e+10 3.349526e+10 1.837348e+10 9.650796e+09 3.471324e+09 4.574948e+08 1.280425e+08 1.622987e+09 4.072197e+09 6.965214e+09 9.330767e+09 1.133664e+10 1.261221e+10 1.321959e+10 1.292789e+10 1.348439e+10 1.409859e+10 1.181245e+10 9.12475e+09 8.949862e+09 7.389243e+09 5.498101e+09 3.763717e+09 0 ]

View File

@ -0,0 +1 @@
# configuration file for apply-cmvn-online, used in the script ../local/run_online_decoding.sh

View File

@ -0,0 +1,2 @@
--left-context=3
--right-context=3

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,14 @@
This file is part of ZazTalk2Me.
ZazTalk2Me is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ZazTalk2Me is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with ZazTalk2Me. If not, see <https://www.gnu.org/licenses/>.

View File

@ -0,0 +1,14 @@
This file is part of ZazTalk2Me.
ZazTalk2Me is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ZazTalk2Me is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with ZazTalk2Me. If not, see <https://www.gnu.org/licenses/>.

837
zaz.py Executable file
View File

@ -0,0 +1,837 @@
#!/usr/bin/env python3
# == Rapid Develop Macros in LibreOffice ==
# ~ This file is part of ZAZ.
# ~ https://git.elmau.net/elmau/zaz
# ~ ZAZ is free software: you can redistribute it and/or modify
# ~ it under the terms of the GNU General Public License as published by
# ~ the Free Software Foundation, either version 3 of the License, or
# ~ (at your option) any later version.
# ~ ZAZ is distributed in the hope that it will be useful,
# ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
# ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# ~ GNU General Public License for more details.
# ~ You should have received a copy of the GNU General Public License
# ~ along with ZAZ. If not, see <https://www.gnu.org/licenses/>.
import argparse
import os
import py_compile
import re
import sys
import zipfile
from datetime import datetime
from pathlib import Path
from shutil import copyfile
from subprocess import call
from xml.etree import ElementTree as ET
from xml.dom.minidom import parseString
from conf import (
DATA,
DIRS,
DOMAIN,
EXTENSION,
FILES,
INFO,
PATHS,
TYPE_EXTENSION,
USE_LOCALES,
log)
EASYMACRO = 'easymacro.py'
class LiboXML(object):
CONTEXT = {
'calc': 'com.sun.star.sheet.SpreadsheetDocument',
'writer': 'com.sun.star.text.TextDocument',
'impress': 'com.sun.star.presentation.PresentationDocument',
'draw': 'com.sun.star.drawing.DrawingDocument',
'base': 'com.sun.star.sdb.OfficeDatabaseDocument',
'math': 'com.sun.star.formula.FormulaProperties',
'basic': 'com.sun.star.script.BasicIDE',
}
TYPES = {
'py': 'application/vnd.sun.star.uno-component;type=Python',
'pyc': 'application/binary',
'zip': 'application/binary',
'xcu': 'application/vnd.sun.star.configuration-data',
'rdb': 'application/vnd.sun.star.uno-typelibrary;type=RDB',
'xcs': 'application/vnd.sun.star.configuration-schema',
'help': 'application/vnd.sun.star.help',
'component': 'application/vnd.sun.star.uno-components',
}
NS_MANIFEST = {
'manifest_version': '1.2',
'manifest': 'urn:oasis:names:tc:opendocument:xmlns:manifest:1.0',
'xmlns:loext': 'urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0',
}
xmlns = 'http://openoffice.org/extensions/description/2006'
xmlns_xlink = 'http://www.w3.org/1999/xlink'
xmlns_l = 'http://libreoffice.org/extensions/description/2011'
NS_DESCRIPTION = {
'xmlns': xmlns,
'xmlns:xlink': xmlns_xlink,
'xmlns:d': xmlns,
'xmlns:l': xmlns_l,
}
NS_ADDONS = {
'xmlns:xs': 'http://www.w3.org/2001/XMLSchema',
'xmlns:oor': 'http://openoffice.org/2001/registry',
}
NS_UPDATE = {
'xmlns': 'http://openoffice.org/extensions/update/2006',
'xmlns:d': 'http://openoffice.org/extensions/description/2006',
'xmlns:xlink': 'http://www.w3.org/1999/xlink',
}
def __init__(self):
self._manifest = None
self._paths = []
self._path_images = ''
self._toolbars = []
def _save_path(self, attr):
self._paths.append(attr['{{{}}}full-path'.format(self.NS_MANIFEST['manifest'])])
return
def _clean(self, name, nodes):
has_words = re.compile('\\w')
if not re.search(has_words, str(nodes.tail)):
nodes.tail = ''
if not re.search(has_words, str(nodes.text)):
nodes.text = ''
for node in nodes:
if name == 'manifest':
self._save_path(node.attrib)
if not re.search(has_words, str(node.tail)):
node.tail = ''
if not re.search(has_words, str(node.text)):
node.text = ''
return
def new_manifest(self, data):
attr = {
'manifest:version': self.NS_MANIFEST['manifest_version'],
'xmlns:manifest': self.NS_MANIFEST['manifest'],
'xmlns:loext': self.NS_MANIFEST['xmlns:loext'],
}
self._manifest = ET.Element('manifest:manifest', attr)
return self.add_data_manifest(data)
def parse_manifest(self, data):
ET.register_namespace('manifest', self.NS_MANIFEST['manifest'])
self._manifest = ET.fromstring(data)
attr = {'xmlns:loext': self.NS_MANIFEST['xmlns:loext']}
self._manifest.attrib.update(**attr)
self._clean('manifest', self._manifest)
return
def add_data_manifest(self, data):
node_name = 'manifest:file-entry'
attr = {
'manifest:full-path': '',
'manifest:media-type': '',
}
for path in data:
if path in self._paths:
continue
ext = path.split('.')[-1]
attr['manifest:full-path'] = path
attr['manifest:media-type'] = self.TYPES.get(ext, '')
ET.SubElement(self._manifest, node_name, attr)
return self._get_xml(self._manifest)
def new_description(self, data):
doc = ET.Element('description', self.NS_DESCRIPTION)
key = 'identifier'
ET.SubElement(doc, key, data[key])
key = 'version'
ET.SubElement(doc, key, data[key])
key = 'minimal'
if data[key]:
node = ET.SubElement(doc, 'dependencies')
attr = {'value': data[key], 'l:name': f'LibreOffice {data[key]}'}
ET.SubElement(node, 'LibreOffice-minimal-version', attr)
key = 'display-name'
node = ET.SubElement(doc, key)
for k, v in data[key].items():
sn = ET.SubElement(node, 'name', {'lang': k})
sn.text = v
node = ET.SubElement(doc, 'extension-description')
for k in data[key].keys():
attr = {
'lang': k,
'xlink:href': f'description/desc_{k}.txt',
}
ET.SubElement(node, 'src', attr)
key = 'icon'
node = ET.SubElement(doc, key)
attr = {'xlink:href': f"images/{data[key]}"}
ET.SubElement(node, 'default', attr)
key = 'publisher'
node = ET.SubElement(doc, key)
for k, v in data[key].items():
attr = {
'xlink:href': v['link'],
'lang': k,
}
sn = ET.SubElement(node, 'name', attr)
sn.text = v['text']
key = 'display-name'
node = ET.SubElement(doc, 'registration')
attr = {
'accept-by': 'user',
'suppress-on-update': 'true',
}
node = ET.SubElement(node, 'simple-license', attr)
for k in data[key].keys():
attr = {
'xlink:href': f"{DIRS['registration']}/license_{k}.txt",
'lang': k
}
ET.SubElement(node, 'license-text', attr)
if data['update']:
node = ET.SubElement(doc, 'update-information')
ET.SubElement(node, 'src', {'xlink:href': data['update']})
return self._get_xml(doc)
def _get_context(self, args):
if not args:
return ''
context = ','.join([self.CONTEXT[v] for v in args.split(',')])
return context
def _add_node_value(self, node, name, value='_self'):
attr = {'oor:name': name, 'oor:type': 'xs:string'}
sn = ET.SubElement(node, 'prop', attr)
sn = ET.SubElement(sn, 'value')
sn.text = value
return
def _add_menu(self, id_extension, node, index, menu, in_menu_bar=True):
if in_menu_bar:
attr = {
'oor:name': index,
'oor:op': 'replace',
}
subnode = ET.SubElement(node, 'node', attr)
else:
subnode = node
attr = {'oor:name': 'Title', 'oor:type': 'xs:string'}
sn1 = ET.SubElement(subnode, 'prop', attr)
for k, v in menu['title'].items():
sn2 = ET.SubElement(sn1, 'value', {'xml:lang': k})
sn2.text = v
value = self._get_context(menu['context'])
self._add_node_value(subnode, 'Context', value)
if 'submenu' in menu:
sn = ET.SubElement(subnode, 'node', {'oor:name': 'Submenu'})
for i, m in enumerate(menu['submenu']):
self._add_menu(id_extension, sn, f'{index}.s{i}', m)
if m.get('toolbar', False):
self._toolbars.append(m)
return
value = f"service:{id_extension}?{menu['argument']}"
self._add_node_value(subnode, 'URL', value)
self._add_node_value(subnode, 'Target')
value = f"%origin%/{self._path_images}/{menu['icon']}"
self._add_node_value(subnode, 'ImageIdentifier', value)
return
def new_addons(self, id_extension, data):
in_menu_bar = data['parent'] == 'OfficeMenuBar'
self._path_images = data['images']
attr = {
'oor:name': 'Addons',
'oor:package': 'org.openoffice.Office',
}
attr.update(self.NS_ADDONS)
doc = ET.Element('oor:component-data', attr)
parent = ET.SubElement(doc, 'node', {'oor:name': 'AddonUI'})
node = ET.SubElement(parent, 'node', {'oor:name': data['parent']})
op = 'fuse'
if in_menu_bar:
op = 'replace'
attr = {'oor:name': id_extension, 'oor:op': op}
node = ET.SubElement(node, 'node', attr)
if in_menu_bar:
attr = {'oor:name': 'Title', 'oor:type': 'xs:string'}
subnode = ET.SubElement(node, 'prop', attr)
for k, v in data['main'].items():
sn = ET.SubElement(subnode, 'value', {'xml:lang': k})
sn.text = v
self._add_node_value(node, 'Target')
node = ET.SubElement(node, 'node', {'oor:name': 'Submenu'})
for i, menu in enumerate(data['menus']):
self._add_menu(id_extension, node, f'm{i}', menu, in_menu_bar)
if menu.get('toolbar', False):
self._toolbars.append(menu)
if self._toolbars:
attr = {'oor:name': 'OfficeToolBar'}
toolbar = ET.SubElement(parent, 'node', attr)
attr = {'oor:name': id_extension, 'oor:op': 'replace'}
toolbar = ET.SubElement(toolbar, 'node', attr)
for t, menu in enumerate(self._toolbars):
self._add_menu(id_extension, toolbar, f't{t}', menu)
return self._get_xml(doc)
def _add_shortcut(self, node, key, id_extension, arg):
attr = {'oor:name': key, 'oor:op': 'fuse'}
subnode = ET.SubElement(node, 'node', attr)
subnode = ET.SubElement(subnode, 'prop', {'oor:name': 'Command'})
subnode = ET.SubElement(subnode, 'value', {'xml:lang': 'en-US'})
subnode.text = f"service:{id_extension}?{arg}"
return
def _get_acceleartors(self, menu):
if 'submenu' in menu:
for m in menu['submenu']:
return self._get_acceleartors(m)
if not menu.get('shortcut', ''):
return ''
return menu
def new_accelerators(self, id_extension, menus):
attr = {
'oor:name': 'Accelerators',
'oor:package': 'org.openoffice.Office',
}
attr.update(self.NS_ADDONS)
doc = ET.Element('oor:component-data', attr)
parent = ET.SubElement(doc, 'node', {'oor:name': 'PrimaryKeys'})
data = []
for m in menus:
info = self._get_acceleartors(m)
if info:
data.append(info)
node_global = None
node_modules = None
for m in data:
if m['context']:
if node_modules is None:
node_modules = ET.SubElement(
parent, 'node', {'oor:name': 'Modules'})
for app in m['context'].split(','):
node = ET.SubElement(
node_modules, 'node', {'oor:name': self.CONTEXT[app]})
self._add_shortcut(
node, m['shortcut'], id_extension, m['argument'])
else:
if node_global is None:
node_global = ET.SubElement(
parent, 'node', {'oor:name': 'Global'})
self._add_shortcut(
node_global, m['shortcut'], id_extension, m['argument'])
return self._get_xml(doc)
def new_update(self, extension, url_oxt):
doc = ET.Element('description', self.NS_UPDATE)
ET.SubElement(doc, 'identifier', {'value': extension['id']})
ET.SubElement(doc, 'version', {'value': extension['version']})
node = ET.SubElement(doc, 'update-download')
ET.SubElement(node, 'src', {'xlink:href': url_oxt})
node = ET.SubElement(doc, 'release-notes')
return self._get_xml(doc)
def _get_xml(self, doc):
xml = parseString(ET.tostring(doc, encoding='utf-8'))
return xml.toprettyxml(indent=' ', encoding='utf-8').decode('utf-8')
def _exists(path):
return os.path.exists(path)
def _join(*paths):
return os.path.join(*paths)
def _mkdir(path):
return Path(path).mkdir(parents=True, exist_ok=True)
def _save(path, data):
with open(path, 'w') as f:
f.write(data)
return
def _get_files(path, filters=''):
paths = []
if filters in ('*', '*.*'):
filters = ''
for folder, _, files in os.walk(path):
if filters:
pattern = re.compile(r'\.(?:{})$'.format(filters), re.IGNORECASE)
paths += [_join(folder, f) for f in files if pattern.search(f)]
else:
paths += files
return paths
def _compress_oxt():
log.info('Compress OXT extension...')
path_oxt = _join(DIRS['files'], FILES['oxt'])
z = zipfile.ZipFile(path_oxt, 'w', compression=zipfile.ZIP_DEFLATED)
root_len = len(os.path.abspath(DIRS['source']))
for root, dirs, files in os.walk(DIRS['source']):
relative = os.path.abspath(root)[root_len:]
for f in files:
fullpath = _join(root, f)
file_name = _join(relative, f)
if file_name == FILES['idl']:
continue
z.write(fullpath, file_name, zipfile.ZIP_DEFLATED)
z.close()
log.info('Extension OXT created successfully...')
return
def _install_and_test():
path_oxt = (_join(DIRS['files'], FILES['oxt']),)
call(PATHS['install'] + path_oxt)
log.info('Install extension successfully...')
log.info('Start LibreOffice...')
call(PATHS['soffice'])
return
def _validate_new():
path_source = DIRS['source']
if not _exists(path_source):
return True
msg = f'Path: {path_source}, exists, delete first'
log.error(msg)
return False
def _create_new_directories():
path_source = DIRS['source']
_mkdir(path_source)
path = _join(path_source, DIRS['meta'])
_mkdir(path)
path = _join(path_source, DIRS['description'])
_mkdir(path)
path = _join(path_source, DIRS['images'])
_mkdir(path)
path = _join(path_source, DIRS['registration'])
_mkdir(path)
path = _join(path_source, DIRS['office'])
_mkdir(path)
if FILES['easymacro'] or DIRS['pythonpath']:
path = _join(path_source, 'pythonpath')
_mkdir(path)
path = DIRS['files']
if not _exists(path):
_mkdir(path)
msg = 'Created directories...'
log.info(msg)
return
def _create_new_files():
path_source = DIRS['source']
for k, v in INFO.items():
file_name = f'license_{k}.txt'
path = _join(path_source, DIRS['registration'], file_name)
_save(path, v['license'])
if TYPE_EXTENSION > 1:
path = _join(path_source, FILES['idl'])
_save(path, DATA['idl'])
path = _join(path_source, FILES['py'])
_save(path, DATA['py'])
msg = 'Created files...'
log.info(msg)
return
def _validate_update():
if TYPE_EXTENSION == 1:
return True
if not _exists(PATHS['idlc']):
msg = 'Binary: "idlc" not found'
log.error(msg)
return False
if not _exists(PATHS['include']):
msg = 'Directory: "include" not found'
log.error(msg)
return False
if not _exists(PATHS['regmerge']):
msg = 'Binary: "regmerge" not found'
log.error(msg)
return False
path = _join(DIRS['source'], FILES['idl'])
if not _exists(path):
msg = f'File: "{FILES["idl"]}" not found'
log.error(msg)
return False
return True
def _compile_idl():
if TYPE_EXTENSION == 1:
return
log.info('Compilate IDL...')
path_rdb = _join(DIRS['source'], FILES['rdb'])
path_urd = _join(DIRS['source'], FILES['urd'])
path = _join(DIRS['source'], FILES['idl'])
call([PATHS['idlc'], '-I', PATHS['include'], path])
call([PATHS['regmerge'], path_rdb, '/UCR', path_urd])
os.remove(path_urd)
log.info('Compilate IDL successfully...')
return
def _update_files():
path_files = DIRS['files']
if not _exists(path_files):
_mkdir(path_files)
path_source = DIRS['source']
for k, v in INFO.items():
file_name = FILES['ext_desc'].format(k)
path = _join(path_source, DIRS['description'], file_name)
_save(path, v['description'])
path_logo = EXTENSION['icon'][0]
if _exists(path_logo):
file_name = EXTENSION['icon'][1]
path = _join(path_source, DIRS['images'], file_name)
copyfile(path_logo, path)
files = os.listdir(DIRS['images'])
for f in files:
if f[-3:].lower() == 'bmp':
source = _join(DIRS['images'], f)
target = _join(path_source, DIRS['images'], f)
copyfile(source, target)
if FILES['easymacro']:
source = EASYMACRO
target = _join(path_source, 'pythonpath', source)
copyfile(source, target)
xml = LiboXML()
path = _join(path_source, DIRS['meta'], FILES['manifest'])
data = xml.new_manifest(DATA['manifest'])
_save(path, data)
path = _join(path_source, FILES['description'])
data = xml.new_description(DATA['description'])
_save(path, data)
if TYPE_EXTENSION == 1:
path = _join(path_source, FILES['addons'])
data = xml.new_addons(EXTENSION['id'], DATA['addons'])
_save(path, data)
path = _join(path_source, DIRS['office'])
_mkdir(path)
path = _join(path_source, DIRS['office'], FILES['shortcut'])
data = xml.new_accelerators(EXTENSION['id'], DATA['addons']['menus'])
_save(path, data)
if TYPE_EXTENSION == 3:
path = _join(path_source, FILES['addin'])
_save(path, DATA['addin'])
if USE_LOCALES:
msg = "Don't forget generate DOMAIN.pot for locales"
for lang in EXTENSION['languages']:
path = _join(path_source, DIRS['locales'], lang, 'LC_MESSAGES')
Path(path).mkdir(parents=True, exist_ok=True)
log.info(msg)
if DATA['update']:
path_xml = _join(path_files, FILES['update'])
data = xml.new_update(EXTENSION, DATA['update'])
_save(path_xml, data)
_compile_idl()
return
def _create():
if not _validate_new():
return
_create_new_directories()
_create_new_files()
_update_files()
msg = f"New extension: {EXTENSION['name']} make sucesfully...\n"
msg += '\tNow, you can install and test: zaz.py -i'
log.info(msg)
return
def _get_info_path(path):
path, filename = os.path.split(path)
name, extension = os.path.splitext(filename)
return (path, filename, name, extension)
def _zip_embed(source, files):
PATH = 'Scripts/python/'
FILE_PYC = 'easymacro.pyc'
p, f, name, e = _get_info_path(source)
now = datetime.now().strftime('_%Y%m%d_%H%M%S')
path_source = _join(p, name + now + e)
copyfile(source, path_source)
target = source
py_compile.compile(EASYMACRO, FILE_PYC)
xml = LiboXML()
path_easymacro = PATH + FILE_PYC
names = [f[1] for f in files] + [path_easymacro]
nodes = []
with zipfile.ZipFile(target, 'w', compression=zipfile.ZIP_DEFLATED) as zt:
with zipfile.ZipFile(path_source, compression=zipfile.ZIP_DEFLATED) as zs:
for name in zs.namelist():
if FILES['manifest'] in name:
path_manifest = name
xml_manifest = zs.open(name).read()
elif name in names:
continue
else:
zt.writestr(name, zs.open(name).read())
data = []
for path, name in files:
data.append(name)
zt.write(path, name)
zt.write(FILE_PYC, path_easymacro)
data.append(path_easymacro)
xml.parse_manifest(xml_manifest)
xml_manifest = xml.add_data_manifest(data)
zt.writestr(path_manifest, xml_manifest)
os.unlink(FILE_PYC)
return
def _embed(args):
PATH = 'Scripts/python'
PYTHONPATH = 'pythonpath'
doc = args.document
if not doc:
msg = '-d/--document Path file to embed is mandatory'
log.error(msg)
return
if not _exists(doc):
msg = 'Path file not exists'
log.error(msg)
return
files = []
if args.files:
files = args.files.split(',')
source = _join(PATHS['profile'], PATH)
content = os.listdir(source)
if PYTHONPATH in content:
content.remove(PYTHONPATH)
if files:
files = [(_join(source, f), _join(PATH, f)) for f in files if f in content]
else:
files = [(_join(source, f), _join(PATH, f)) for f in content]
_zip_embed(doc, files)
log.info('Embedded macros successfully...')
return
def _locales(args):
if args.files:
files = args.files.split(',')
else:
files = _get_files(DIRS['source'], 'py')
paths = ' '.join([f for f in files if not EASYMACRO in f])
path_pot = _join(DIRS['source'], DIRS['locales'], '{}.pot'.format(DOMAIN))
call([PATHS['gettext'], '-o', path_pot, paths])
log.info('POT generate successfully...')
return
def _update():
path_locales = _join(DIRS['source'], DIRS['locales'])
path_pot = _join(DIRS['source'], DIRS['locales'], '{}.pot'.format(DOMAIN))
if not _exists(path_pot):
log.error('Not exists file POT...')
return
files = _get_files(path_locales, 'po')
if not files:
log.error('First, generate files PO...')
return
for f in files:
call([PATHS['msgmerge'], '-U', f, path_pot])
log.info('\tUpdate: {}'.format(f))
log.info('Locales update successfully...')
return
def _new(args):
if not args.target:
msg = 'Add argument target: -t PATH_TARGET'
log.error(msg)
return
if not args.name:
msg = 'Add argument name: -n name-new-extension'
log.error(msg)
return
path = _join(args.target, args.name)
_mkdir(path)
_mkdir(_join(path, 'extension'))
_mkdir(_join(path, 'images'))
path_logo = 'images/pymacros.png'
copyfile(path_logo, _join(path, 'images/logo.png'))
copyfile('zaz.py', _join(path, 'zaz.py'))
copyfile(EASYMACRO, _join(path, 'easymacro.py'))
copyfile('conf.py.example', _join(path, 'conf.py'))
msg = 'Folders and files copy successfully for new extension.'
log.info(msg)
msg = f'Change to folder: {path}'
log.info(msg)
return
def main(args):
if args.new:
_new(args)
return
if args.update:
_update()
return
if args.locales:
_locales(args)
return
if args.embed:
_embed(args)
return
if args.create:
_create()
return
if not _validate_update():
return
if not args.only_compress:
_update_files()
_compress_oxt()
if args.install:
_install_and_test()
log.info('Extension make successfully...')
return
def _process_command_line_arguments():
parser = argparse.ArgumentParser(description='Make LibreOffice extensions')
parser.add_argument('-new', '--new', dest='new', action='store_true',
default=False, required=False)
parser.add_argument('-t', '--target', dest='target', default='')
parser.add_argument('-n', '--name', dest='name', default='', required=False)
parser.add_argument('-c', '--create', dest='create', action='store_true',
default=False, required=False)
parser.add_argument('-i', '--install', dest='install', action='store_true',
default=False, required=False)
parser.add_argument('-e', '--embed', dest='embed', action='store_true',
default=False, required=False)
parser.add_argument('-d', '--document', dest='document', default='')
parser.add_argument('-f', '--files', dest='files', default='')
parser.add_argument('-l', '--locales', dest='locales', action='store_true',
default=False, required=False)
parser.add_argument('-u', '--update', dest='update', action='store_true',
default=False, required=False)
parser.add_argument('-oc', '--only_compress', dest='only_compress',
action='store_true', default=False, required=False)
return parser.parse_args()
if __name__ == '__main__':
args = _process_command_line_arguments()
main(args)