render.py 5.97 KB
Newer Older
wanglch's avatar
wanglch committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
#!/usr/bin/env python3
import os
import asyncio
from pathlib import Path
from playwright.async_api import async_playwright

# Simple configuration
CONFIG = {
    "input_file": os.path.join(os.path.dirname(__file__), "templates", "listpage.js"),  # React component file
    "output_pdf": "book-page.pdf",     # Output PDF filename
    "temp_html": "temp-render.html",   # Temporary HTML file
    "wait_time": 1500,                 # Time to wait for rendering (ms)
    "device_scale": 2,                 # Resolution multiplier
    "debug": True                      # Keep temp files for debugging
}

async def create_html_file():
    """Create a temporary HTML file that loads the React component from a file."""
    try:
        # Check if input file exists
        input_path = Path(CONFIG["input_file"])
        if not input_path.exists():
            print(f"Error: Input file '{input_path}' not found")
            return False
        
        # Read the component file
        with open(input_path, 'r', encoding='utf-8') as f:
            component_code = f.read()
        
        # Create HTML that will load our component
        html_content = """
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Book Page Template</title>
  <script src="https://unpkg.com/react@17/umd/react.development.js"></script>
  <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
  <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
  <style>
    * {
      box-sizing: border-box;
    }
    
    html, body {
      margin: 0;
      padding: 0;
      width: 8.5in;
      height: 11in;
      overflow: hidden;
    }
    
    #root {
      width: 100%;
      height: 100%;
      padding: 0.25in;
      overflow: hidden;
    }
    
    @media print {
      body {
        -webkit-print-color-adjust: exact;
        print-color-adjust: exact;
      }
    }
  </style>
</head>
<body>
  <div id="root"></div>

  <script type="text/babel">
    // The React component code loaded from external file
    """ + component_code + """
    
    // Render only the book page part, not the controls
    ReactDOM.render(
      <BookPageTemplate />,
      document.getElementById('root')
    );
  </script>
</body>
</html>
        """
        
        with open(CONFIG["temp_html"], 'w', encoding='utf-8') as f:
            f.write(html_content)
            
        print(f"Created HTML file: {CONFIG['temp_html']}")
        print(f"Using React component from: {CONFIG['input_file']}")
        return True
    except Exception as e:
        print(f"Error creating HTML file: {e}")
        print(f"Exception details: {str(e)}")
        import traceback
        traceback.print_exc()
        return False

async def render_to_pdf():
    """Render the React component to PDF using Playwright."""
    try:
        # Create the HTML file first
        html_created = await create_html_file()
        if not html_created:
            print("Failed to create HTML file")
            return
        
        print("Launching browser...")
        async with async_playwright() as p:
            # Launch the browser with more debugging options
            browser = await p.chromium.launch(
                headless=True,  # True for production, False for debugging
            )
            
            # Create a new page for letter size paper
            page = await browser.new_page(
                viewport={"width": 816, "height": 1056},  # 8.5in x 11in at 96dpi
                device_scale_factor=CONFIG["device_scale"]
            )
            
            # Get absolute path to HTML file
            html_path = Path(CONFIG["temp_html"]).absolute()
            html_uri = f"file://{html_path}"
            
            print(f"Navigating to: {html_uri}")
            
            # Add event listeners for console messages and errors
            page.on("console", lambda msg: print(f"Browser console: {msg.text}"))
            page.on("pageerror", lambda err: print(f"Browser page error: {err}"))
            
            # Navigate with longer timeout and wait for network idle
            await page.goto(html_uri, wait_until="networkidle", timeout=30000)
            
            # Wait for React to render
            await page.wait_for_timeout(CONFIG["wait_time"])
            
            # Add a check to ensure the component rendered
            element_count = await page.evaluate("""() => {
                const root = document.getElementById('root');
                return root.childElementCount;
            }""")
            
            if element_count == 0:
                print("Warning: No elements found in root. Component may not have rendered.")
            else:
                print(f"Found {element_count} elements in root. Component rendered successfully.")
            
            # Save debug screenshot
            if CONFIG["debug"]:
                await page.screenshot(path="debug-screenshot.png")
                print("Debug screenshot saved")
            
            # Generate PDF
            print("Generating PDF...")
            await page.pdf(
                path=CONFIG["output_pdf"],
                format="Letter",
                print_background=True,
                margin={"top": "0", "right": "0", "bottom": "0", "left": "0"}
            )
            
            print(f"PDF generated successfully: {CONFIG['output_pdf']}")
            
            # Close the browser
            await browser.close()
        
        # Cleanup temp files if not in debug mode
        if not CONFIG["debug"] and Path(CONFIG["temp_html"]).exists():
            Path(CONFIG["temp_html"]).unlink()
            print("Temporary HTML file removed")
            
    except Exception as e:
        print(f"Error generating PDF: {e}")

if __name__ == "__main__":
    # Run the async function
    try:
        asyncio.run(render_to_pdf())
    except Exception as e:
        print(f"Fatal error: {e}")
        import traceback
        traceback.print_exc()