Last active
October 5, 2025 09:25
-
-
Save poggenpower/37d6073d74f43e9f7999eac9000d6e11 to your computer and use it in GitHub Desktop.
LibreOffice replace url by QR Code
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import uno | |
| import os | |
| import tempfile | |
| import sys | |
| from com.sun.star.text.TextContentAnchorType import AS_CHARACTER | |
| # this script is looking for all URLs matching URL_SEARCH_PREFIX and replace it with a QR code. | |
| # See notes for dependencies. | |
| # Define the link prefix to search for (RegEx search) | |
| URL_SEARCH_PREFIX = "https://guest.schmu.net:8000" | |
| # --- External Library Dependency --- | |
| # NOTE: The 'qrcode' library MUST be installed in the Python environment that LibreOffice uses. | |
| # If this dependency is missing, the script will fail at the 'import qrcode' line. | |
| # Example installation (may vary by OS/LO version): /usr/lib/libreoffice/program/python-core-3.x/bin/pip install qrcode | |
| # See hacks.py to install packages on macOS, it is hacky, but works ... ;-) | |
| try: | |
| import qrcode | |
| QR_CODE_AVAILABLE = True | |
| except ImportError: | |
| QR_CODE_AVAILABLE = False | |
| print("WARNING: 'qrcode' Python library not found. QR code generation will be skipped.") | |
| # ----------------------------------- | |
| def generate_qr_code_image(link_url): | |
| """ | |
| Generates a QR code image file for the given URL and returns the file path. | |
| Returns None if the QR code library is not available. | |
| """ | |
| if not QR_CODE_AVAILABLE: | |
| return None | |
| try: | |
| # Create a temporary file path | |
| temp_dir = tempfile.gettempdir() | |
| file_name = f"qr_code_{os.urandom(8).hex()}.png" | |
| file_path = os.path.join(temp_dir, file_name) | |
| # Generate the QR Code | |
| qr = qrcode.QRCode( | |
| version=1, | |
| error_correction=qrcode.constants.ERROR_CORRECT_L, | |
| box_size=5, # Adjust size as needed | |
| border=0 | |
| ) | |
| qr.add_data(link_url) | |
| qr.make(fit=True) | |
| img = qr.make_image(fill_color="black", back_color="white") | |
| img.save(file_path) | |
| # LibreOffice needs file URLs (e.g., file:///path/to/file.png) | |
| # Use uno.systemPathToFileUrl for OS-independent path conversion | |
| return uno.systemPathToFileUrl(file_path) | |
| except Exception as e: | |
| print(f"Error generating QR code for {link_url}: {e}") | |
| return None | |
| def find_and_replace_links_with_qr(document, url_prefix, placeholder_string): | |
| """ | |
| Searches the document for the URL, extracts the URL text, then searches for | |
| the following placeholder string and replaces the placeholder with the | |
| QR code of the extracted URL. | |
| """ | |
| doc_text = document.getText() | |
| # 1. Setup Search Descriptor for the URL (RegEx) | |
| url_search_descriptor = document.createSearchDescriptor() | |
| url_regex_pattern = url_prefix + r'[^\s]*' | |
| url_search_descriptor.setSearchString(url_regex_pattern) | |
| url_search_descriptor.SearchRegularExpression = True | |
| # # 2. Setup Search Descriptor for the Placeholder (Literal) | |
| # placeholder_search_descriptor = document.createSearchDescriptor() | |
| # placeholder_search_descriptor.setSearchString(placeholder_string) | |
| # placeholder_search_descriptor.SearchRegularExpression = False | |
| # Start the search from the beginning of the document | |
| found_url_range = document.findFirst(url_search_descriptor) | |
| replace_count = 0 | |
| while found_url_range: | |
| # Get the full URL text from the found range | |
| full_link_url = found_url_range.getString() | |
| try: | |
| # 3. Search for the placeholder starting immediately after the found URL | |
| search_start_range = found_url_range.getEnd() | |
| # 4. Create a cursor that spans the placeholder text | |
| # cursor = doc_text.createTextCursorByRange(found_placeholder_range) | |
| # xText is the parent text container | |
| xText = found_url_range.getText() | |
| # Create a cursor spanning the whole text (or parent) | |
| cursor = xText.createTextCursor() | |
| # Move cursor to cover the placeholder range | |
| cursor.gotoRange(found_url_range, False) # True = expand to cover range | |
| cursor.setString("") | |
| # Placeholder found. Proceed with QR code generation and replacement. | |
| qr_url = generate_qr_code_image(full_link_url) | |
| if qr_url: | |
| # The cursor is now a collapsed range (an insertion point) | |
| # Create and configure the graphic object | |
| graphic = document.createInstance("com.sun.star.text.GraphicObject") | |
| graphic.GraphicURL = qr_url | |
| graphic.AnchorType = AS_CHARACTER | |
| # Set margins to 0.16 cm (160 units of 1/100th mm) | |
| margin_units = 160 | |
| graphic.LeftMargin = margin_units | |
| graphic.RightMargin = margin_units | |
| graphic.TopMargin = margin_units | |
| graphic.BottomMargin = margin_units | |
| # Set size (in 1/100th mm) | |
| graphic.Width = 3730 | |
| graphic.Height = 3730 | |
| # Insert the graphic using the doc_text object (guaranteed XText interface) | |
| # Anchor is the collapsed cursor; Before is False (insert after the anchor point) | |
| xText.insertTextContent(cursor, graphic, False) | |
| # --------------------------------------------------- | |
| replace_count += 1 | |
| else: | |
| print(f"Skipping replacement for URL {full_link_url} due to missing QR library or generation error.") | |
| # 5. Set the starting point for the NEXT URL search | |
| # to the position AFTER the graphic was inserted. | |
| next_search_start = cursor.getEnd() | |
| except Exception as e: | |
| # Log the error but continue searching for the next URL | |
| print(f"An error occurred during processing URL '{full_link_url}': {e}") | |
| next_search_start = found_url_range.getEnd() # Advance the search past the troubled spot | |
| pass | |
| # Find the next occurrence of the URL, starting from the calculated point | |
| found_url_range = document.findNext(next_search_start, url_search_descriptor) | |
| return replace_count | |
| def g_main(context=None): | |
| """ | |
| Main entry point for the LibreOffice Python macro. | |
| """ | |
| desktop = None | |
| try: | |
| # Get the component context and desktop | |
| local_context = uno.get # Use the default context mechanism | |
| smgr = local_context.ServiceManager | |
| desktop = smgr.createInstanceWithContext("com.sun.star.frame.Desktop", local_context) | |
| except: | |
| # Fallback for older LO versions or specific environments | |
| local_context = uno.getComponentContext() | |
| smgr = local_context.ServiceManager | |
| desktop = smgr.createInstanceWithContext("com.sun.star.frame.Desktop", local_context) | |
| model = desktop.getCurrentComponent() | |
| if not hasattr(model, "getText"): | |
| print("This macro must be run on a text document (Writer or Mail Merge document).") | |
| return | |
| print(f"Starting search for URLs matching: {URL_SEARCH_PREFIX}") | |
| # Process the document | |
| count = find_and_replace_links_with_qr(model, URL_SEARCH_PREFIX) | |
| if count > 0: | |
| msg = f"Successfully replaced {count} instances with QR codes encoding the preceding URL." | |
| else: | |
| msg = f"No URLs matching '{URL_SEARCH_PREFIX}' were found." | |
| print(msg) | |
| # The message is printed to the LO Python console. | |
| return 0 # Success code | |
| # Standard boilerplate to run the main function | |
| if __name__ == '__main__': | |
| g_main() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # run this to install required dependencies in your LibreOffice Python. | |
| def install_PIL(): | |
| import pip._internal | |
| import importlib | |
| # Install Pillow if missing | |
| try: | |
| import PIL | |
| except ImportError: | |
| pip._internal.main(["install", "Pillow"]) | |
| importlib.invalidate_caches() | |
| import PIL | |
| import PIL.Image | |
| try: | |
| import qrcode | |
| except ImportError: | |
| pip._internal.main(["install", "qrcode"]) | |
| importlib.invalidate_caches() | |
| import qrcode | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment