|
| 1 | +""" |
| 2 | +Utility module for generating file viewer HTML content. |
| 3 | +""" |
| 4 | + |
| 5 | +import base64 |
| 6 | +import mimetypes |
| 7 | +import os |
| 8 | + |
| 9 | + |
| 10 | +def generate_file_viewer_html(file_path: str) -> str: |
| 11 | + """ |
| 12 | + Generate HTML content for viewing different file types. |
| 13 | +
|
| 14 | + Args: |
| 15 | + file_path: The absolute path to the file |
| 16 | +
|
| 17 | + Returns: |
| 18 | + str: HTML content for viewing the file |
| 19 | +
|
| 20 | + Raises: |
| 21 | + ValueError: If the file extension is not supported |
| 22 | + """ |
| 23 | + file_extension = os.path.splitext(file_path)[1].lower() |
| 24 | + file_name = os.path.basename(file_path) |
| 25 | + |
| 26 | + # Define supported file extensions |
| 27 | + supported_extensions = [ |
| 28 | + '.pdf', |
| 29 | + '.png', |
| 30 | + '.jpg', |
| 31 | + '.jpeg', |
| 32 | + '.gif', |
| 33 | + ] |
| 34 | + |
| 35 | + # Check if the file extension is supported |
| 36 | + if file_extension not in supported_extensions: |
| 37 | + raise ValueError( |
| 38 | + f"Unsupported file extension: {file_extension}. " |
| 39 | + f"Supported extensions are: {', '.join(supported_extensions)}" |
| 40 | + ) |
| 41 | + |
| 42 | + # Check if the file exists |
| 43 | + if not os.path.exists(file_path): |
| 44 | + raise ValueError( |
| 45 | + f'File not found locally: {file_path}. Please download the file to the local machine and try again.' |
| 46 | + ) |
| 47 | + |
| 48 | + # Read file content directly |
| 49 | + file_content = None |
| 50 | + mime_type = mimetypes.guess_type(file_path)[0] or 'application/octet-stream' |
| 51 | + |
| 52 | + # For binary files (images, PDFs), encode as base64 |
| 53 | + if file_extension in ['.pdf', '.png', '.jpg', '.jpeg', '.gif', '.bmp']: |
| 54 | + with open(file_path, 'rb') as file: |
| 55 | + file_content = base64.b64encode(file.read()).decode('utf-8') |
| 56 | + # For text files, read as text |
| 57 | + else: |
| 58 | + with open(file_path, 'r', encoding='utf-8') as file: |
| 59 | + file_content = file.read() |
| 60 | + |
| 61 | + return f"""<!DOCTYPE html> |
| 62 | +<html lang="en"> |
| 63 | +<head> |
| 64 | + <meta charset="UTF-8"> |
| 65 | + <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| 66 | + <title>File Viewer - {file_name}</title> |
| 67 | + <script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.min.js"></script> |
| 68 | + <style> |
| 69 | + body, html {{ margin: 0; padding: 0; height: 100%; overflow: hidden; font-family: Arial, sans-serif; }} |
| 70 | + #viewer-container {{ width: 100%; height: 100vh; overflow: auto; }} |
| 71 | + .page {{ margin: 10px auto; box-shadow: 0 0 10px rgba(0,0,0,0.3); }} |
| 72 | + .text-content {{ margin: 20px; white-space: pre-wrap; font-family: monospace; line-height: 1.5; }} |
| 73 | + .error {{ color: red; margin: 20px; }} |
| 74 | + img {{ max-width: 100%; margin: 20px auto; display: block; }} |
| 75 | + </style> |
| 76 | +</head> |
| 77 | +<body> |
| 78 | + <div id="viewer-container"></div> |
| 79 | + <script> |
| 80 | + const filePath = "{file_path}"; |
| 81 | + const fileExtension = "{file_extension}"; |
| 82 | + const fileContent = `{file_content if file_extension not in ['.pdf', '.png', '.jpg', '.jpeg', '.gif', '.bmp'] else ''}`; |
| 83 | + const fileBase64 = "{file_content if file_extension in ['.pdf', '.png', '.jpg', '.jpeg', '.gif', '.bmp'] else ''}"; |
| 84 | + const mimeType = "{mime_type}"; |
| 85 | + const container = document.getElementById('viewer-container'); |
| 86 | +
|
| 87 | + async function loadContent() {{ |
| 88 | + try {{ |
| 89 | + if (fileExtension === '.pdf') {{ |
| 90 | + pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.worker.min.js'; |
| 91 | + const binaryString = atob(fileBase64); |
| 92 | + const bytes = new Uint8Array(binaryString.length); |
| 93 | + for (let i = 0; i < binaryString.length; i++) {{ |
| 94 | + bytes[i] = binaryString.charCodeAt(i); |
| 95 | + }} |
| 96 | +
|
| 97 | + const loadingTask = pdfjsLib.getDocument({{data: bytes.buffer}}); |
| 98 | + const pdf = await loadingTask.promise; |
| 99 | +
|
| 100 | + // Get total number of pages |
| 101 | + const numPages = pdf.numPages; |
| 102 | +
|
| 103 | + // Render each page |
| 104 | + for (let pageNum = 1; pageNum <= numPages; pageNum++) {{ |
| 105 | + const page = await pdf.getPage(pageNum); |
| 106 | +
|
| 107 | + // Set scale for rendering |
| 108 | + const viewport = page.getViewport({{ scale: 1.5 }}); |
| 109 | +
|
| 110 | + // Create canvas for rendering |
| 111 | + const canvas = document.createElement('canvas'); |
| 112 | + canvas.className = 'page'; |
| 113 | + canvas.width = viewport.width; |
| 114 | + canvas.height = viewport.height; |
| 115 | + container.appendChild(canvas); |
| 116 | +
|
| 117 | + // Render PDF page into canvas context |
| 118 | + const context = canvas.getContext('2d'); |
| 119 | + const renderContext = {{ |
| 120 | + canvasContext: context, |
| 121 | + viewport: viewport |
| 122 | + }}; |
| 123 | +
|
| 124 | + await page.render(renderContext).promise; |
| 125 | + }} |
| 126 | + }} else if (['.png', '.jpg', '.jpeg', '.gif', '.bmp'].includes(fileExtension)) {{ |
| 127 | + const img = document.createElement('img'); |
| 128 | + img.src = `data:${{mimeType}};base64,${{fileBase64}}`; |
| 129 | + img.alt = filePath.split('/').pop(); |
| 130 | + container.appendChild(img); |
| 131 | + }} else {{ |
| 132 | + const pre = document.createElement('pre'); |
| 133 | + pre.className = 'text-content'; |
| 134 | + pre.textContent = fileContent; |
| 135 | + container.appendChild(pre); |
| 136 | + }} |
| 137 | + }} catch (error) {{ |
| 138 | + console.error('Error:', error); |
| 139 | + container.innerHTML = `<div class="error"><h2>Error loading file</h2><p>${{error.message}}</p></div>`; |
| 140 | + }} |
| 141 | + }} |
| 142 | +
|
| 143 | + window.onload = loadContent; |
| 144 | + </script> |
| 145 | +</body> |
| 146 | +</html>""" |
0 commit comments