|
| 1 | +import sys |
| 2 | +from enum import Enum, auto |
| 3 | +from pathlib import Path |
| 4 | + |
| 5 | + |
| 6 | +class OfficeAppType(Enum): |
| 7 | + """Enum representing Microsoft Office application types.""" |
| 8 | + |
| 9 | + WORD = auto() |
| 10 | + EXCEL = auto() |
| 11 | + POWERPOINT = auto() |
| 12 | + |
| 13 | + |
| 14 | +def open_document_in_office(file_path: Path, app_type: OfficeAppType) -> tuple[object, object]: |
| 15 | + """ |
| 16 | + Opens a document at the specified path in Word, Excel, or PowerPoint. |
| 17 | + Creates and saves the document if it doesn't exist. |
| 18 | +
|
| 19 | + Args: |
| 20 | + file_path: Path to the document |
| 21 | + app_type: The type of Office application to use |
| 22 | +
|
| 23 | + Returns: |
| 24 | + A tuple containing (app_instance, document_instance) |
| 25 | +
|
| 26 | + Raises: |
| 27 | + EnvironmentError: If not running on Windows |
| 28 | + ValueError: If an invalid app_type is provided |
| 29 | + """ |
| 30 | + if sys.platform != "win32": |
| 31 | + raise EnvironmentError("This function only works on Windows.") |
| 32 | + |
| 33 | + import win32com.client as win32 |
| 34 | + |
| 35 | + # Map app types to their COM identifiers and document properties |
| 36 | + app_config = { |
| 37 | + OfficeAppType.WORD: {"app_name": "Word.Application", "collection_name": "Documents", "file_ext": ".docx"}, |
| 38 | + OfficeAppType.EXCEL: {"app_name": "Excel.Application", "collection_name": "Workbooks", "file_ext": ".xlsx"}, |
| 39 | + OfficeAppType.POWERPOINT: { |
| 40 | + "app_name": "PowerPoint.Application", |
| 41 | + "collection_name": "Presentations", |
| 42 | + "file_ext": ".pptx", |
| 43 | + }, |
| 44 | + } |
| 45 | + |
| 46 | + if app_type not in app_config: |
| 47 | + raise ValueError(f"Unsupported app type: {app_type}") |
| 48 | + |
| 49 | + config = app_config[app_type] |
| 50 | + app_name = config["app_name"] |
| 51 | + collection_name = config["collection_name"] |
| 52 | + |
| 53 | + # Ensure path is a Path object and resolve to absolute path |
| 54 | + doc_path = file_path.resolve() |
| 55 | + |
| 56 | + # Add default extension if no extension is present |
| 57 | + if not doc_path.suffix: |
| 58 | + doc_path = doc_path.with_suffix(config["file_ext"]) |
| 59 | + |
| 60 | + # Create parent directories if they don't exist |
| 61 | + doc_path.parent.mkdir(parents=True, exist_ok=True) |
| 62 | + |
| 63 | + # First try to get an existing app instance |
| 64 | + try: |
| 65 | + app = win32.GetActiveObject(app_name) |
| 66 | + # Check if the document is already open in this instance |
| 67 | + collection = getattr(app, collection_name) |
| 68 | + |
| 69 | + # Handle different collection indexing methods for different Office apps |
| 70 | + if app_type == OfficeAppType.WORD or app_type == OfficeAppType.EXCEL: |
| 71 | + for i in range(1, collection.Count + 1): |
| 72 | + open_doc = collection(i) |
| 73 | + if open_doc.FullName.lower() == str(doc_path).lower(): |
| 74 | + # Document is already open, just return it and its parent app |
| 75 | + return app, open_doc |
| 76 | + elif app_type == OfficeAppType.POWERPOINT: |
| 77 | + # PowerPoint uses a different approach for accessing presentations |
| 78 | + for open_doc in collection: |
| 79 | + if hasattr(open_doc, "FullName") and open_doc.FullName.lower() == str(doc_path).lower(): |
| 80 | + return app, open_doc |
| 81 | + except Exception: |
| 82 | + # No running app instance found, create a new one |
| 83 | + app = win32.Dispatch(app_name) |
| 84 | + |
| 85 | + # Make app visible |
| 86 | + app.Visible = True |
| 87 | + |
| 88 | + # If the file exists, open it, otherwise create a new document and save it |
| 89 | + collection = getattr(app, collection_name) |
| 90 | + |
| 91 | + if doc_path.exists(): |
| 92 | + doc = collection.Open(str(doc_path)) |
| 93 | + else: |
| 94 | + doc = collection.Add() |
| 95 | + doc.SaveAs(str(doc_path)) |
| 96 | + |
| 97 | + return app, doc |
0 commit comments