---
# System prepended metadata

title: GUI Page Auth
tags: [GUI]

---

---
tags: GUI
---
GUI Page Auth
===

[TOC]


### Table


- table view of default permission
   - [table link](https://docs.google.com/spreadsheets/d/1D-8EKqBc1jix9qmZB1Rk2i1BhfnAkCmwBwGgGwf2sgs/edit?usp=sharing)





### Authentication detail in auth manager

:::spoiler {state="open"} authentication detail
```
DEFAULT_USER_PERMISSION = {
    'vendor': {
        'Leader': {
            'page_10': 2,
            'page_20': 0,
            'page_110': 2,
            'page_130': 0,
            'page_140': 0,
            'page_150': 0,
            'page_210': 0,
            'page_220': 0,
            'page_230': 0,
            'page_240': 0,
            'page_250': 0,
            'page_260': 0,
            'page_270': 0,
            'page_280': 0,
            'page_310': 0,
            'page_320': 0,
            'page_330': 0,
            'page_340': 1,
            'page_350': 0,
            'page_410': 0,
            'page_430': 0,
            'page_440': 0,
            'page_450': 0,
            'page_460': 0,
            'page_470': 0,
            'page_510': 0,
            'page_520': 0,
            'page_530': 0,
            'page_540': 0,
            'page_610': 0,
            'page_630': 0,
            'page_640': 0,
            'page_660': 0,
            'page_670': 0,
            'page_710': 0,
            'page_720': 2,
            'page_730': 0,
            'page_740': 0,
            'page_750': 0,
            'page_810': 2,
            'page_1010': 0,
            'page_1020': 0,
            'page_1030': 0,
            'page_999': 0,
        },
        'User': {
            'page_10': 1,
            'page_20': 0,
            'page_110': 1,
            'page_130': 0,
            'page_140': 0,
            'page_150': 0,
            'page_210': 0,
            'page_220': 0,
            'page_230': 0,
            'page_240': 0,
            'page_250': 0,
            'page_260': 0,
            'page_270': 0,
            'page_280': 0,
            'page_310': 0,
            'page_320': 0,
            'page_330': 0,
            'page_340': 1,
            'page_350': 0,
            'page_410': 0,
            'page_430': 0,
            'page_440': 0,
            'page_450': 0,
            'page_460': 0,
            'page_470': 0,
            'page_510': 0,
            'page_520': 0,
            'page_530': 0,
            'page_540': 0,
            'page_610': 0,
            'page_630': 0,
            'page_640': 0,
            'page_660': 0,
            'page_670': 0,
            'page_710': 0,
            'page_720': 0,
            'page_730': 0,
            'page_740': 0,
            'page_750': 0,
            'page_810': 2,
            'page_1010': 0,
            'page_1020': 0,
            'page_1030': 0,
            'page_999': 0
        }
    },
    'customer': {
        'Leader': {
            'page_10': 2,
            'page_20': 2,
            'page_110': 0,
            'page_130': 2,
            'page_140': 2,
            'page_150': 2,
            'page_210': 2,
            'page_220': 2,
            'page_230': 2,
            'page_240': 2,
            'page_250': 2,
            'page_260': 2,
            'page_270': 2,
            'page_280': 2,
            'page_310': 2,
            'page_320': 2,
            'page_330': 2,
            'page_340': 2,
            'page_350': 2,
            'page_410': 2,
            'page_430': 2,
            'page_440': 2,
            'page_450': 2,
            'page_460': 2,
            'page_470': 2,
            'page_510': 2,
            'page_520': 2,
            'page_530': 2,
            'page_540': 2,
            'page_610': 2,
            'page_630': 2,
            'page_640': 2,
            'page_660': 2,
            'page_670': 2,
            'page_710': 0,
            'page_720': 2,
            'page_730': 0,
            'page_740': 0,
            'page_750': 2,
            'page_810': 2,
            'page_1010': 2,
            'page_1020': 2,
            'page_1030': 2,
            'page_999': 0
        },
        'User': {
            'page_10': 1,
            'page_20': 1,
            'page_110': 0,
            'page_130': 1,
            'page_140': 1,
            'page_150': 1,
            'page_210': 1,
            'page_220': 1,
            'page_230': 1,
            'page_240': 1,
            'page_250': 1,
            'page_260': 1,
            'page_270': 1,
            'page_280': 1,
            'page_310': 1,
            'page_320': 1,
            'page_330': 1,
            'page_340': 1,
            'page_350': 1,
            'page_410': 1,
            'page_430': 1,
            'page_440': 1,
            'page_450': 1,
            'page_460': 1,
            'page_470': 1,
            'page_510': 1,
            'page_520': 1,
            'page_530': 1,
            'page_540': 1,
            'page_610': 1,
            'page_630': 1,
            'page_640': 1,
            'page_660': 1,
            'page_670': 1,
            'page_710': 0,
            'page_720': 0,
            'page_730': 0,
            'page_740': 0,
            'page_750': 1,
            'page_810': 2,
            'page_1010': 1,
            'page_1020': 1,
            'page_1030': 1,
            'page_999': 0
        }
    },
    'reseller': {
        'Leader': {
            'page_10': 2,
            'page_20': 2,
            'page_110': 0,
            'page_130': 2,
            'page_140': 2,
            'page_150': 2,
            'page_210': 2,
            'page_220': 2,
            'page_230': 2,
            'page_240': 2,
            'page_250': 2,
            'page_260': 2,
            'page_270': 2,
            'page_280': 2,
            'page_310': 2,
            'page_320': 2,
            'page_330': 2,
            'page_340': 2,
            'page_350': 2,
            'page_410': 2,
            'page_430': 2,
            'page_440': 2,
            'page_450': 2,
            'page_460': 2,
            'page_470': 2,
            'page_510': 2,
            'page_520': 2,
            'page_530': 2,
            'page_540': 2,
            'page_610': 2,
            'page_630': 2,
            'page_640': 2,
            'page_660': 2,
            'page_670': 2,
            'page_710': 2,
            'page_720': 2,
            'page_730': 2,
            'page_740': 2,
            'page_750': 2,
            'page_810': 2,
            'page_1010': 2,
            'page_1020': 2,
            'page_1030': 2,
            'page_999': 0
        },
        'User': {
            'page_10': 1,
            'page_20': 1,
            'page_110': 0,
            'page_130': 1,
            'page_140': 1,
            'page_150': 1,
            'page_210': 1,
            'page_220': 1,
            'page_230': 1,
            'page_240': 1,
            'page_250': 1,
            'page_260': 1,
            'page_270': 1,
            'page_280': 1,
            'page_310': 1,
            'page_320': 1,
            'page_330': 1,
            'page_340': 1,
            'page_350': 1,
            'page_410': 1,
            'page_430': 1,
            'page_440': 1,
            'page_450': 1,
            'page_460': 1,
            'page_470': 1,
            'page_510': 1,
            'page_520': 1,
            'page_530': 1,
            'page_540': 1,
            'page_610': 1,
            'page_630': 1,
            'page_640': 1,
            'page_660': 1,
            'page_670': 1,
            'page_710': 1,
            'page_720': 1,
            'page_730': 1,
            'page_740': 1,
            'page_750': 1,
            'page_810': 2,
            'page_1010': 1,
            'page_1020': 1,
            'page_1030': 1,
            'page_999': 0
        }
    },
    'admin': {
        'Leader': {
            'page_10': 2,
            'page_20': 2,
            'page_110': 2,
            'page_130': 2,
            'page_140': 2,
            'page_150': 2,
            'page_210': 2,
            'page_220': 2,
            'page_230': 2,
            'page_240': 2,
            'page_250': 2,
            'page_260': 2,
            'page_270': 2,
            'page_280': 2,
            'page_310': 2,
            'page_320': 2,
            'page_330': 2,
            'page_340': 2,
            'page_350': 2,
            'page_410': 2,
            'page_430': 2,
            'page_440': 2,
            'page_450': 2,
            'page_460': 2,
            'page_470': 2,
            'page_510': 2,
            'page_520': 2,
            'page_530': 2,
            'page_540': 2,
            'page_610': 2,
            'page_630': 2,
            'page_640': 2,
            'page_660': 2,
            'page_670': 2,
            'page_710': 2,
            'page_720': 2,
            'page_730': 2,
            'page_740': 2,
            'page_750': 2,
            'page_810': 2,
            'page_1010': 2,
            'page_1020': 2,
            'page_1030': 2,
            'page_999': 2
        },
        'User': {
            'page_10': 1,
            'page_20': 1,
            'page_110': 1,
            'page_130': 1,
            'page_140': 1,
            'page_150': 1,
            'page_210': 1,
            'page_220': 1,
            'page_230': 1,
            'page_240': 1,
            'page_250': 1,
            'page_260': 1,
            'page_270': 1,
            'page_280': 1,
            'page_310': 1,
            'page_320': 1,
            'page_330': 1,
            'page_340': 1,
            'page_350': 1,
            'page_410': 1,
            'page_430': 1,
            'page_440': 1,
            'page_450': 1,
            'page_460': 1,
            'page_470': 1,
            'page_510': 1,
            'page_520': 1,
            'page_530': 1,
            'page_540': 1,
            'page_610': 1,
            'page_630': 1,
            'page_640': 1,
            'page_660': 1,
            'page_670': 1,
            'page_710': 1,
            'page_720': 1,
            'page_730': 1,
            'page_740': 1,
            'page_750': 1,
            'page_810': 2,
            'page_1010': 1,
            'page_1020': 1,
            'page_1030': 1,
            'page_999': 1
        }
    }
}

```
:::



### TO rain

```
# main.py
from playwright.async_api import async_playwright
import asyncio
import matplotlib.pyplot as plt
import numpy as np
from io import BytesIO
import base64

# Function to generate a matplotlib plot as base64
def generate_matplotlib_plot(title, color='blue'):
    # Create sample data
    x = np.linspace(0, 10, 100)
    y = np.random.rand(100) * 10  # Random data for simulation
    
    # Create plot
    plt.figure(figsize=(6, 4))  # Adjust size as needed
    plt.plot(x, y, label=title, color=color)
    plt.title(title)
    plt.xlabel("Time")
    plt.ylabel("Value")
    plt.grid(True)
    plt.legend()
    
    # Save plot to memory and convert to base64
    buffer = BytesIO()
    plt.savefig(buffer, format='png', bbox_inches='tight', dpi=100)
    plt.close()
    buffer.seek(0)
    image_png = buffer.getvalue()
    image_base64 = base64.b64encode(image_png).decode('utf-8')
    return f"data:image/png;base64,{image_base64}"

async def generate_pdf():
    # Define your fixed HTML template here
    html_template = """
    \n    <!DOCTYPE html>\n    <html>\n      <head>\n        <meta charset="utf-8" />\n        <link rel="stylesheet" href="https://unpkg.com/grapesjs/dist/css/grapes.min.css">\n        <style>\n  @page { size: A4 portrait; margin: 0; }\n  body { margin: 0; padding: 0; display: block !important; }\n\n  .a4-page {\n    top: 0;\n    left: 0;\n    right: 0;\n    bottom: 0;\n    width: 210mm;\n    height: 297mm;\n    box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);\n    padding: 20mm;\n    box-sizing: border-box;\n    overflow: hidden;\n    page-break-after: always;\n  }\n\n  .a4-page:last-child {\n    page-break-after: auto;\n  }\n\n  .a4-page * {\n    break-inside: avoid;\n    page-break-inside: avoid;\n  }\n</style>\n        <style>* { box-sizing: border-box; } body {margin: 0;}body{display:flex;margin-top:0px;margin-left:0px;margin-right:0px;margin-bottom:0px;background-clip:initial;background-size:initial;justify-content:center;background-color:rgb(240, 240, 240);background-image:initial;background-origin:initial;background-repeat:initial;background-attachment:initial;background-position-x:initial;background-position-y:initial;}.a4-page{top:0px;left:0px;right:0px;width:210mm;bottom:0px;height:297mm;box-shadow:rgba(0, 0, 0, 0.1) 0px 0px 5px;box-sizing:border-box;overflow-x:hidden;overflow-y:hidden;break-after:page;padding-top:20mm;padding-left:20mm;padding-right:20mm;padding-bottom:20mm;}.a4-page:last-child{break-after:auto;}.a4-page.page-id-1{flex-direction:column;}.gjs-row{width:100%;display:table;padding-top:10px;padding-left:10px;padding-right:10px;padding-bottom:10px;}.gjs-cell{width:8%;height:75px;display:table-cell;}#i2u2l{height:10%;display:flex;align-items:flex-end;flex-direction:column;justify-content:center;}#izv0i{width:auto;height:auto;}#ihe7p{width:auto;height:auto;}#iaxq3{height:80%;display:flex;align-items:center;flex-direction:column;justify-content:center;}#i4i9j{width:auto;height:auto;display:flex;justify-content:center;}#i0bm2{padding:10px;font-size:36px;font-weight:700;}#i4i9j-2{width:auto;height:auto;margin:10px 0 0 0;display:flex;justify-content:center;}#i3orm{font-size:30px;font-weight:600;}#izv0i-2{width:auto;height:auto;}#ihe7p-2{width:auto;height:auto;}#i2u2l-2{height:10%;display:flex;align-items:flex-end;flex-direction:column;justify-content:center;}#ieorg{height:85%;display:flex;align-items:center;flex-direction:column;justify-content:center;}#i7cz1{width:auto;height:auto;}#i80qi{width:auto;height:auto;}#i0bm2-2{padding:10px;font-size:30px;font-weight:400;}#i9xfk{width:100%;height:auto;}#iwd5h{width:100%;height:auto;padding:0 0 0 20px;}#ivbeh{width:100%;height:auto;}#i6uue{color:#336987;height:60px;display:flex;padding:10px 10px 10px 20px;align-items:center;border-radius:8px 8px 8px 8px;justify-content:flex-start;background-color:#d1e8f5;}#ibqal{width:auto;padding:10px 10px 10px 20px;}#ie26i{font-size:20px;font-weight:700;}#i4i9j-2-2{width:auto;height:auto;margin:10px 0 0 0;display:flex;justify-content:center;}#izv0i-2-2{width:auto;height:auto;}#ihe7p-2-2{width:auto;height:auto;}#i2u2l-2-2{height:10%;display:flex;align-items:flex-end;flex-direction:column;justify-content:center;}#i0bm2-2-2{padding:10px;font-size:30px;font-weight:400;}#i7cz1-2{width:auto;height:auto;}#i80qi-2{width:auto;height:auto;}#i6uue-2{color:#336987;height:60px;display:flex;padding:10px 10px 10px 20px;align-items:center;border-radius:8px 8px 8px 8px;justify-content:flex-start;background-color:#d1e8f5;}#i9xfk-2{width:100%;height:auto;}#ie26i-2{font-size:20px;font-weight:700;}#ibqal-2{width:auto;padding:10px 10px 10px 20px;}#ivbeh-2{width:100%;height:auto;}#iwd5h-2{width:100%;height:auto;padding:0 0 0 20px;}#ieorg-2{height:85%;display:flex;align-items:center;flex-direction:column;justify-content:center;}#izv0i-2-2-2{width:auto;height:auto;}#ihe7p-2-2-2{width:auto;height:auto;}#i2u2l-2-2-2{height:10%;display:flex;align-items:flex-end;flex-direction:column;justify-content:center;}#i0bm2-2-2-2{padding:10px;font-size:30px;font-weight:400;}#i7cz1-2-2{width:auto;height:auto;}#i80qi-2-2{width:auto;height:auto;}#i6uue-2-2{color:#336987;height:60px;display:flex;padding:10px 10px 10px 20px;align-items:center;border-radius:8px 8px 8px 8px;justify-content:flex-start;background-color:#d1e8f5;}#i9xfk-2-2{width:100%;height:auto;}#ie26i-2-2{font-size:20px;font-weight:700;}#ibqal-2-2{width:auto;padding:10px 10px 10px 20px;}#ivbeh-2-2{width:100%;height:auto;}#iwd5h-2-2{width:100%;height:auto;padding:0 0 0 20px;}#ieorg-2-2{height:85%;display:flex;align-items:center;flex-direction:column;justify-content:center;}.toc{width:100%;height:auto;line-height:1.5;}.toc-title{font-size:28px;text-align:center;font-weight:700;margin-bottom:8px;}.toc-list{display:grid;row-gap:16px;column-gap:16px;margin-top:0px;margin-left:0px;padding-top:0px;margin-right:0px;padding-left:0px;margin-bottom:0px;padding-right:0px;padding-bottom:0px;list-style-type:none;list-style-image:initial;list-style-position:initial;}.toc-item{display:flex;row-gap:8px;column-gap:8px;align-items:baseline;}.toc-text{font-size:18px;overflow-wrap:anywhere;}.toc-dots{flex-grow:1;transform:translateY(-0.2em);flex-basis:auto;flex-shrink:1;border-bottom-color:rgb(153, 153, 153);border-bottom-style:dotted;border-bottom-width:1px;}.toc-page{width:3ch;text-align:right;font-variant-numeric:tabular-nums;}#icqzez{height:5%;}#ivcbrl{width:auto;height:auto;display:flex;align-items:center;justify-content:center;}#i2n4o1{padding:10px;}#i2p8tc{padding:10px;}#i74029{width:auto;height:auto;display:flex;align-items:center;justify-content:center;}#ibtthp{height:5%;}#ibsy4o{padding:10px;}#it62uu{width:auto;height:auto;display:flex;align-items:center;justify-content:center;}#igaewu{height:5%;}#i9e98e{color:black;width:300px;}#iqviej{width:{{jitter_url_image_width}}px;height:{{jitter_url_image_height}}px;objectFit:cover;}#ipp2lg{width:{{packet_loss_url_image_width}}px;height:{{packet_loss_url_image_height}}px;objectFit:cover;}#igc3zv{width:{{latency_url_image_width}}px;height:{{latency_url_image_height}}px;objectFit:cover;}@media (max-width: 768px){.gjs-cell{width:100%;display:block;}}</style>\n      </head>\n      <body><div class="a4-page page-id-1"><div id="i2u2l" class="gjs-row"><div id="izv0i" class="gjs-cell"><span id="io74f" data-variable="true">Start Date: {{start_date}}</span></div><div id="ihe7p" class="gjs-cell"><span id="i4ftj" data-variable="true">End Date: {{end_date}}</span></div></div><div id="iaxq3" class="gjs-row"><div id="i4i9j" class="gjs-cell"><div id="i0bm2">Device Report</div></div><div id="i4i9j-2" class="gjs-cell"><span id="i3orm" data-variable="true">({{device_name}})</span></div><div id="i4i9j-2-2" class="gjs-cell"><img id="i9e98e" src="https://amerdocker.ddns.net/hermesimage/image/4afdd72a4ae34ac881d24444b41391c0.png"/></div></div></div><div class="a4-page page-id-2"><div class="toc"><div class="toc-title">TABLE OF CONTENTS</div><ul class="toc-list"><li class="toc-item"><span class="toc-text">Jitter</span><span aria-hidden="true" class="toc-dots"></span><span class="toc-page">1</span></li><li class="toc-item"><span class="toc-text">Packet Loss</span><span aria-hidden="true" class="toc-dots"></span><span class="toc-page">2</span></li><li class="toc-item"><span class="toc-text">Latency</span><span aria-hidden="true" class="toc-dots"></span><span class="toc-page">3</span></li></ul></div></div><div class="a4-page page-id-3"><div id="i2u2l-2" class="gjs-row"><div id="izv0i-2" class="gjs-cell"><span id="io74f-2" data-variable="true">Start Date: {{start_date}}</span></div><div id="ihe7p-2" class="gjs-cell"><span id="ibpvp" data-variable="true">End Date: {{end_date}}</span></div></div><div id="ieorg" class="gjs-row"><div id="i7cz1" class="gjs-cell"><div id="i0bm2-2">Jitter</div></div><div id="i80qi" class="gjs-cell"><img data-variable-image="true" data-image-type="fixed" id="iqviej" src="{{jitter_url}}"/></div><div id="i9xfk" class="gjs-cell"><div id="i6uue">TIPS: VOIP / Video Streaming recommand jitter should lower than 30ms</div></div><div id="ivbeh" class="gjs-cell"><div id="ibqal"><u><i id="ie26i">Highlight Summary:</i></u></div></div><div id="iwd5h" class="gjs-cell"><span data-variable="true">{{jitter_hight_summary}}</span></div></div><div id="icqzez" class="gjs-row"><div id="ivcbrl" class="gjs-cell"><div id="i2n4o1">1<br/></div></div></div></div><div class="a4-page page-id-4"><div id="i2u2l-2-2" class="gjs-row"><div id="izv0i-2-2" class="gjs-cell"><span id="io74f-2-2" data-variable="true">Start Date: {{start_date}}</span></div><div id="ihe7p-2-2" class="gjs-cell"><span id="ibpvp-2" data-variable="true">End Date: {{end_date}}</span></div></div><div id="ieorg-2" class="gjs-row"><div id="i7cz1-2" class="gjs-cell"><div id="i0bm2-2-2">Packet Loss</div></div><div id="i80qi-2" class="gjs-cell"><img data-variable-image="true" data-image-type="fixed" id="ipp2lg" src="{{packet_loss_url}}"/></div><div id="i9xfk-2" class="gjs-cell"><div id="i6uue-2">TIPS: VOIP / Video Streaming recommend packet loss should lower than 5%</div></div><div id="ivbeh-2" class="gjs-cell"><div id="ibqal-2"><u><i id="ie26i-2">Highlight Summary:</i></u></div></div><div id="iwd5h-2" class="gjs-cell"><span id="i93t7" data-variable="true">{{packet_loss_highlight_summary}}</span></div></div><div id="ibtthp" class="gjs-row"><div id="i74029" class="gjs-cell"><div id="i2p8tc">2</div></div></div></div><div class="a4-page page-id-5"><div id="i2u2l-2-2-2" class="gjs-row"><div id="izv0i-2-2-2" class="gjs-cell"><span id="io74f-2-2-2" data-variable="true">Start Date: {{start_date}}</span></div><div id="ihe7p-2-2-2" class="gjs-cell"><span id="ibpvp-2-2" data-variable="true">End Date: {{end_date}}</span></div></div><div id="ieorg-2-2" class="gjs-row"><div id="i7cz1-2-2" class="gjs-cell"><div id="i0bm2-2-2-2">Latency</div></div><div id="i80qi-2-2" class="gjs-cell"><img data-variable-image="true" data-image-type="fixed" id="igc3zv" src="{{latency_url}}"/></div><div id="i9xfk-2-2" class="gjs-cell"><div id="i6uue-2-2">TIPS: VOIP / Video Streaming recommend latency should lower than 150ms</div></div><div id="ivbeh-2-2" class="gjs-cell"><div id="ibqal-2-2"><u><i id="ie26i-2-2">Highlight Summary:</i></u></div></div><div id="iwd5h-2-2" class="gjs-cell"><span data-variable="true">{{latency_highlight_summary}}</span></div></div><div id="igaewu" class="gjs-row"><div id="it62uu" class="gjs-cell"><div id="ibsy4o">3<br/></div></div></div></div></body>\n    </html>\n    
    """

    try:
        # Generate matplotlib plots for each section
        jitter_image = generate_matplotlib_plot("Jitter Chart", color="blue")
        packetloss_image = generate_matplotlib_plot("Packet Loss Chart", color="red")
        latency_image = generate_matplotlib_plot("Latency Chart", color="green")

        # Sample data for variables
        start_date = "2025-08-01"
        end_date = "2025-08-31"
        device_name = "Sample Device"
        jitter_hight_summary = "Jitter values remained below 30ms for 95% of the time."
        packet_loss_highlight_summary = "Packet loss was below 5% throughout the period."
        latency_highlight_summary = "Latency averaged 120ms, within acceptable range."

        # Render the HTML template with variables
        html_with_images = html_template.replace("{{start_date}}", start_date)\
                                       .replace("{{end_date}}", end_date)\
                                       .replace("{{device_name}}", device_name)\
                                       .replace("{{jitter_hight_summary}}", jitter_hight_summary)\
                                       .replace("{{packet_loss_highlight_summary}}", packet_loss_highlight_summary)\
                                       .replace("{{latency_highlight_summary}}", latency_highlight_summary)\
                                       .replace("{{jitter_url}}", jitter_image)\
                                       .replace("{{packet_loss_url}}", packetloss_image)\
                                       .replace("{{latency_url}}", latency_image)\
                                       .replace("{{jitter_url_image_width}}", "400")\
                                       .replace("{{jitter_url_image_height}}", "320")\
                                       .replace("{{packet_loss_url_image_width}}", "400")\
                                       .replace("{{packet_loss_url_image_height}}", "320")\
                                       .replace("{{latency_url_image_width}}", "400")\
                                       .replace("{{latency_url_image_height}}", "320")

        async with async_playwright() as p:
            browser = await p.chromium.launch(
                args=["--no-sandbox", "--disable-setuid-sandbox"]
            )
            page = await browser.new_page()
            await page.set_content(html_with_images, wait_until="load")
            await page.emulate_media(media="screen")

            pdf_bytes = await page.pdf(
                format="A4",
                print_background=True,
                margin={"top": "0", "right": "0", "bottom": "0", "left": "0"},
            )
            await browser.close()

        # Save the PDF to a file
        with open("output.pdf", "wb") as f:
            f.write(pdf_bytes)

        print("PDF generated successfully: output.pdf")
    except Exception as e:
        print(f"Error: {str(e)}")

if __name__ == "__main__":
    asyncio.run(generate_pdf())
```