#!/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 . import logging # ~ Type extension: # ~ 1 = normal extension # ~ 2 = new component # ~ 3 = Calc addin TYPE_EXTENSION = 1 # ~ https://semver.org/ VERSION = '0.6.0' # ~ Your great extension name, not used spaces NAME = 'ZAZBarCode' # ~ Should be unique, used URL inverse ID = 'net.elmau.zaz.BarCode' # ~ If you extension will be multilanguage set: True # ~ This feature used gettext, set pythonpath and easymacro in True # ~ Yu can used PoEdit for edit PO files and generate MO files. # ~ https://poedit.net/ USE_LOCALES = True DOMAIN = 'base' PATH_LOCALES = 'locales' # ~ locate pygettext.py PATH_PYGETTEXT = '/usr/lib/python3.7/Tools/i18n/pygettext.py' PATH_MSGMERGE = 'msgmerge' # ~ Show in extension manager PUBLISHER = { 'en': {'text': 'El Mau', 'link': 'https://gitlab.com/mauriciobaeza'}, 'es': {'text': 'El Mau', 'link': 'https://gitlab.com/mauriciobaeza'}, } # ~ Name in this folder for copy ICON = 'images/logo.png' # ~ Name inside extensions ICON_EXT = f'{NAME.lower()}.png' # ~ For example # ~ DEPENDENCIES_MINIMAL = '6.0' 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 . """ LICENSE_ES = LICENSE_EN INFO = { 'en': { 'display_name': 'ZAZ Bar Code', 'description': 'Generate and insert barcode in documents', 'license': LICENSE_EN, }, 'es': { 'display_name': 'ZAZ Códigos de Barras', 'description': 'Genera e inserta codigos de barras en documentos', 'license': LICENSE_ES, }, } 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', } # ~ 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 BarCode', 'es': 'ZAZ BarCode', } MENUS = ( { 'title': {'en': 'Insert BarCode', 'es': 'Insertar Código'}, 'argument': 'ask', 'context': 'calc,writer,impress,draw', 'icon': 'barcode', 'toolbar': False, 'shortcut': 'I_SHIFT_MOD1_MOD2', }, { 'title': {'en': 'Insert CODE39', 'es': 'Insertar CODE39'}, 'argument': 'code39', 'context': 'calc,writer', 'icon': 'barcode', 'toolbar': False, }, { 'title': {'en': 'Insert CODE128', 'es': 'Insertar CODE128'}, 'argument': 'code128', 'context': 'calc,writer', 'icon': 'barcode', 'toolbar': False, }, { 'title': {'en': 'Insert EAN', 'es': 'Insertar EAN'}, 'argument': 'ean', 'context': 'calc,writer', 'icon': 'barcode', 'toolbar': False, }, { 'title': {'en': 'Insert EAN8', 'es': 'Insertar EAN8'}, 'argument': 'ean8', 'context': 'calc,writer', 'icon': 'barcode', 'toolbar': False, }, { 'title': {'en': 'Insert EAN13', 'es': 'Insertar EAN13'}, 'argument': 'ean13', 'context': 'calc,writer', 'icon': 'barcode', 'toolbar': False, }, { 'title': {'en': 'Insert GS1', 'es': 'Insertar GS1'}, 'argument': 'gs1', 'context': 'calc,writer', 'icon': 'barcode', 'toolbar': False, }, { 'title': {'en': 'Insert GTIN', 'es': 'Insertar GTIN'}, 'argument': 'gtin', 'context': 'calc,writer', 'icon': 'barcode', 'toolbar': False, }, { 'title': {'en': 'Insert ISBN', 'es': 'Insertar ISBN'}, 'argument': 'isbn', 'context': 'calc,writer', 'icon': 'barcode', 'toolbar': False, }, { 'title': {'en': 'Insert ISBN10', 'es': 'Insertar ISBN10'}, 'argument': 'isbn10', 'context': 'calc,writer', 'icon': 'barcode', 'toolbar': False, }, { 'title': {'en': 'Insert ISBN13', 'es': 'Insertar ISBN13'}, 'argument': 'isbn13', 'context': 'calc,writer', 'icon': 'barcode', 'toolbar': False, }, { 'title': {'en': 'Insert ISSN', 'es': 'Insertar ISSN'}, 'argument': 'issn', 'context': 'calc,writer', 'icon': 'barcode', 'toolbar': False, }, { 'title': {'en': 'Insert JAN', 'es': 'Insertar JAN'}, 'argument': 'jan', 'context': 'calc,writer', 'icon': 'barcode', 'toolbar': False, }, { 'title': {'en': 'Insert PZN', 'es': 'Insertar PZN'}, 'argument': 'pzn', 'context': 'calc,writer', 'icon': 'barcode', 'toolbar': False, }, { 'title': {'en': 'Insert UPC', 'es': 'Insertar UPC'}, 'argument': 'upc', 'context': 'calc,writer', 'icon': 'barcode', 'toolbar': False, }, { 'title': {'en': 'Insert UPCA', 'es': 'Insertar UPCA'}, 'argument': 'upca', 'context': 'calc,writer', 'icon': 'barcode', 'toolbar': False, }, { 'title': {'en': 'Insert QRCODE', 'es': 'Insertar QRCODE'}, 'argument': 'qrcode', 'context': 'calc,writer', 'icon': 'barcode', 'toolbar': False, }, ) # ~ 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': 'files', '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_BASE = 'https://gitlab.com/mauriciobaeza/zaz-barcode/raw/master/files/' URL_XML_UPDATE = URL_BASE + FILES['update'] URL_OXT = URL_BASE + FILES['oxt'] # ~ Default program for test: --calc, --writer, --draw PROGRAM = '--calc' # ~ 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() FILE_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) """ tmp = ' {}' node = [tmp.format(k, v['display_name']) for k, v in INFO.items()] NODE_DISPLAY_NAME = '\n'.join(node) tmp = ' ' node = [tmp.format(k) for k, v in INFO.items()] NODE_EXTENSION_DESCRIPTION = '\n'.join(node) NODE_ICON = '' if ICON: NODE_ICON = f' ' NODE_PUBLISHER = '' if PUBLISHER: tmp = ' {}' node = [tmp.format(v['link'], k, v['text']) for k, v in PUBLISHER.items()] NODE_PUBLISHER = '\n'.join(node) NODE_DEPENDENCIES_MINIMAL = '' if DEPENDENCIES_MINIMAL: NODE_DEPENDENCIES_MINIMAL = f"""\n """ tmp = ' ' node = [tmp.format(DIRS['registration'], k) for k in INFO.keys()] NODE_LICENSE = '\n'.join(node) NODE_UPDATE = '' if URL_XML_UPDATE: NODE_UPDATE = f""" """ FILE_DESCRIPTION = f""" {NODE_DISPLAY_NAME} {NODE_EXTENSION_DESCRIPTION} {NODE_ICON} {NODE_PUBLISHER} {NODE_LICENSE} {NODE_DEPENDENCIES_MINIMAL}{NODE_UPDATE} """ NODE_MENU = """ {titles} service:{id}?{argument} _self {context} %origin%/{folder}/{icon} """ opt = 'fuse' if PARENT == 'OfficeMenuBar': opt = 'replace' def _get_context(args): if not args: return '' c = [] for v in args.split(','): c.append(CONTEXT[v]) return ','.join(c) menus = [] toolbar = [] tmp = ' {}' for i, m in enumerate(MENUS): titles = [tmp.format(k, v) for k, v in m['title'].items()] values = { 'id': ID, 'index': i+101, 'opt': opt, 'titles': '\n'.join(titles), 'argument': m['argument'], 'context': _get_context(m['context']), 'folder': DIRS['images'], 'icon': m['icon'], } menus.append(NODE_MENU.format(**values)) if m['toolbar']: values['index'] = f't{i+1}' toolbar.append(NODE_MENU.format(**values)) NODE_TOOLBAR = '' NODE_MENUS = '' if TYPE_EXTENSION == 1: if PARENT == 'AddonMenu': NODE_MENUS = '\n'.join(menus) elif PARENT == 'OfficeMenuBar': tmp = ' {}' titles = '\n'.join([tmp.format(k, v) for k, v in MENU_MAIN.items()]) SUBMENUS = '\n ' + '\n'.join(menus) + '\n ' NODE_MENUS = f""" {titles} _self {SUBMENUS} """ if toolbar: node_toolbars = '\n'.join(toolbar) NODE_TOOLBAR = f""" {node_toolbars} """ FILE_ADDONS = f""" {NODE_MENUS} {NODE_TOOLBAR} """ FILE_UPDATE = '' if URL_XML_UPDATE: FILE_UPDATE = f""" """ 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 {module} interface {interface} : com::sun::star::uno::XInterface {{ {functions} }}; service {P} {{ interface {interface}; }}; {close_module} #endif """ def _parameters(args): NODE = """ {displayname} {description} """ line = '{}{}' 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 = '{}{}' NODE_FUNCTION = """ {displayname} {description} Add-In AutoAddIn.{name} {parameters} """ 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""" {NODE_FUNCTIONS} """ NODE_SHORTCUT = """ {0} {0} {0}service:{2}?{3} {0} {0} """ NODE_SHORTCUTS = '' if TYPE_EXTENSION == 1: node_global = [] node_module = {} for m in MENUS: if not m.get('shortcut', ''): continue if m['context']: for c in m['context'].split(','): if not c in node_module: node_module[c] = [] node = NODE_SHORTCUT.format(' ', m['shortcut'], ID, m['argument']) node_module[c].append(node) continue node = NODE_SHORTCUT.format('', m['shortcut'], ID, m['argument']) node_global.append(node) if node_global: NODE_SHORTCUTS = ' \n' NODE_SHORTCUTS += '\n'.join(node_global) NODE_SHORTCUTS += ' ' if node_module: NODE_SHORTCUTS += ' \n' for c, n in node_module.items(): NODE_SHORTCUTS += ' \n'.format(CONTEXT[c]) NODE_SHORTCUTS += '\n'.join(n) NODE_SHORTCUTS += ' \n' NODE_SHORTCUTS += ' ' FILE_SHORTCUTS = f""" {NODE_SHORTCUTS} """ 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 = { 'py': FILE_PY, 'manifest': DATA_MANIFEST, 'description': FILE_DESCRIPTION, 'addons': FILE_ADDONS, 'update': FILE_UPDATE, 'idl': FILE_IDL, 'addin': FILE_ADDIN, 'shortcut': FILE_SHORTCUTS, } # ~ LICENSE_ACCEPT_BY = 'user' # or admin # ~ LICENSE_SUPPRESS_ON_UPDATE = True