Skip to content

Instantly share code, notes, and snippets.

@poggenpower
Last active October 5, 2025 09:25
Show Gist options
  • Select an option

  • Save poggenpower/37d6073d74f43e9f7999eac9000d6e11 to your computer and use it in GitHub Desktop.

Select an option

Save poggenpower/37d6073d74f43e9f7999eac9000d6e11 to your computer and use it in GitHub Desktop.
LibreOffice replace url by QR Code
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()
# 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