From 7797405a56f4663cc4b1024b69169960dcdaf53f Mon Sep 17 00:00:00 2001 From: Lennart Regebro Date: Fri, 12 Jan 2024 14:10:04 +0100 Subject: [PATCH] Adda an --input-filter argument (#100) Also change the --filter argument to --output-filter, but keep --filter as an alias. Also add the possibility to use file extensions etc as filter names, if they are specified (which they rarely are). Co-authored-by: Lennart Regebro --- CHANGES.rst | 11 ++++++- src/unoserver/client.py | 10 +++++- src/unoserver/comparer.py | 4 ++- src/unoserver/converter.py | 64 ++++++++++++++++++++++++++++++++++---- src/unoserver/server.py | 2 ++ 5 files changed, 82 insertions(+), 9 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index c944c68..bf2d925 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,7 +1,16 @@ 2.0.2 (unreleased) ------------------ -- Nothing changed yet. +- Add a --input-filter argument to specify a different file type than the + one LibreOffice will guess. + +- For consistency renamed --filter to --output-filter, but the --filter + will remain for backwards compatibility. + +- If you specify a non-existent filter, the list of filters is now alphabetical. + +- You can now use both the LibreOffice name, but also internal shorter names + and sometimes even file suffices to specify the filter. 2.0.1 (2024-01-12) diff --git a/src/unoserver/client.py b/src/unoserver/client.py index 4ed9a92..4c84933 100644 --- a/src/unoserver/client.py +++ b/src/unoserver/client.py @@ -48,6 +48,7 @@ def convert( filtername=None, filter_options=[], update_index=True, + infiltername=None, ): """Converts a file from one type to another @@ -92,6 +93,7 @@ def convert( filtername, filter_options, update_index, + infiltername, ) if result is not None: # We got the file back over xmlrpc: @@ -192,6 +194,11 @@ def converter_main(): help="The file type/extension of the output file (ex pdf). Required when using stdout", ) parser.add_argument( + "--input-filter", + help="The LibreOffice input filter to use (ex 'writer8'), if autodetect fails", + ) + parser.add_argument( + "--output-filter", "--filter", default=None, help="The export filter to use when converting. It is selected automatically if not specified.", @@ -250,9 +257,10 @@ def converter_main(): indata=indata, outpath=args.outfile, convert_to=args.convert_to, - filtername=args.filter, + filtername=args.output_filter, filter_options=args.filter_options, update_index=args.update_index, + infiltername=args.input_filter, ) if args.outfile is None: diff --git a/src/unoserver/comparer.py b/src/unoserver/comparer.py index b3858d8..8a44f94 100644 --- a/src/unoserver/comparer.py +++ b/src/unoserver/comparer.py @@ -247,7 +247,9 @@ def compare( ) logger.info(f"Exporting to {outpath}") - logger.info(f"Using {filtername} export filter") + logger.info( + f"Using {filtername} export filter from {new_type} to {export_type}" + ) output_props = ( PropertyValue(Name="FilterName", Value=filtername), diff --git a/src/unoserver/converter.py b/src/unoserver/converter.py index 90f34b9..facda82 100644 --- a/src/unoserver/converter.py +++ b/src/unoserver/converter.py @@ -102,6 +102,17 @@ def find_filter(self, import_type, export_type): # No filter found return None + def get_available_import_filters(self): + # List import filters. You can only search on module, iflags and eflags, + # so the import and export types we have to test in a loop + import_filters = self.filter_service.createSubSetEnumerationByQuery( + "getSortedFilterList():iflags=1" + ) + + while import_filters.hasMoreElements(): + # Filter DocumentService here + yield prop2dict(import_filters.nextElement()) + def get_available_export_filters(self): # List export filters. You can only search on module, iflags and eflags, # so the import and export types we have to test in a loop @@ -113,8 +124,20 @@ def get_available_export_filters(self): # Filter DocumentService here yield prop2dict(export_filters.nextElement()) - def get_available_filter_names(self): - return [filter["Name"] for filter in self.get_available_export_filters()] + def get_filter_names(self, filters): + names = {} + for flt in filters: + # Add all names and exstensions, etc in a mapping to the internal + # Libreoffice name, so we can map it. + # The actual name: + names[flt["Name"]] = flt["Name"] + # UserData sometimes has file extensions, etc. + # Skip empty data, and those weird file paths, and "true"... + for name in filter( + lambda x: x and x != "true" and "." not in x, flt["UserData"] + ): + names[name] = flt["Name"] + return names def convert( self, @@ -125,6 +148,7 @@ def convert( filtername=None, filter_options=[], update_index=True, + infiltername=None, ): """Converts a file from one type to another @@ -139,12 +163,25 @@ def convert( filtername: The name of the export filter to use for conversion. If None, it is auto-detected. + filter_options: A list of output filter options as strings, in a "OptionName=Value" format. + update_index: Updates the index before conversion + infiltername: The name of the input filter, ie "writer8", "PowerPoint 3", etc. + You must specify the inpath or the indata, and you must specify and outpath or a convert_to. """ - input_props = (PropertyValue(Name="ReadOnly", Value=True),) + if infiltername: + infilters = self.get_filter_names(self.get_available_import_filters()) + if infiltername in infilters: + input_props += ( + PropertyValue(Name="FilterName", Value=infilters[infiltername]), + ) + else: + raise ValueError( + f"There is no '{infiltername}' import filter. Available filters: {sorted(infilters.keys())}" + ) if inpath: # TODO: Verify that inpath exists and is openable, and that outdir exists, because uno's @@ -171,6 +208,17 @@ def convert( import_path, "_default", 0, input_props ) + if document is None: + # Could not load document, fail + if not inpath: + inpath = "" + if not infiltername: + infiltername = "default" + + error = f"Could not load document {inpath} using the {infiltername} filter." + logger.error(error) + raise RuntimeError(error) + if update_index: # Update document indexes for ii in range(2): @@ -216,10 +264,12 @@ def convert( ) if filtername is not None: - available_filter_names = self.get_available_filter_names() + available_filter_names = self.get_filter_names( + self.get_available_export_filters() + ) if filtername not in available_filter_names: raise RuntimeError( - f"'{filtername}' is not a valid filter name. Valid filters are {available_filter_names}" + f"There is no '{filtername}' export-filter. Available filters: {sorted(available_filter_names)}" ) else: filtername = self.find_filter(import_type, export_type) @@ -229,7 +279,9 @@ def convert( ) logger.info(f"Exporting to {outpath}") - logger.info(f"Using {filtername} export filter") + logger.info( + f"Using {filtername} export filter from {infiltername} to {export_type}" + ) filter_data = [] for option in filter_options: diff --git a/src/unoserver/server.py b/src/unoserver/server.py index 2126e41..01cb912 100644 --- a/src/unoserver/server.py +++ b/src/unoserver/server.py @@ -100,6 +100,7 @@ def convert( filtername=None, filter_options=[], update_index=True, + infiltername=None, ): if indata is not None: indata = indata.data @@ -114,6 +115,7 @@ def convert( filtername, filter_options, update_index, + infiltername, ) return result