Coverage for gws-app/gws/lib/htmlx/__init__.py: 97%
30 statements
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-05 13:35 +0200
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-05 13:35 +0200
1"""HTML utilities.
3Escapes strings for HTML and renders HTML documents to PDF or PNG files with the
4external ``wkhtmltopdf`` and ``wkhtmltoimage`` tools. The HTML is first written to
5``<out_path>.html``, which is left in place, and the tool converts it to ``out_path``.
6JavaScript is disabled and local files can be loaded.
8Example::
10 html = f'<h1>{gws.lib.htmlx.escape(title)}</h1>'
11 gws.lib.htmlx.render_to_pdf(html, '/tmp/out.pdf', page_size=(210, 297, gws.Uom.mm))
12"""
14import html
16import gws
17import gws.lib.osx
18import gws.lib.uom
21def escape(s: str, quote=True) -> str:
22 """Escape a string for use in HTML.
24 Args:
25 s: A string.
26 quote: If ``True``, also escape double and single quotes.
28 Returns:
29 The escaped string.
30 """
31 return html.escape(s, quote=quote)
34def render_to_pdf(html: str, out_path: str, page_size: gws.UomSize = None, page_margin: gws.UomExtent = None) -> str:
35 """Render an HTML string to a PDF file with ``wkhtmltopdf``.
37 Args:
38 html: HTML content.
39 out_path: Path of the PDF file to create.
40 page_size: Page size, converted to mm. A4 portrait by default.
41 page_margin: Page margins (top, right, bottom, left). The values are passed to
42 ``wkhtmltopdf`` as millimetres, the unit is not converted. No margins by default.
44 Returns:
45 The output path.
47 Raises:
48 ``gws.lib.osx.Error``: If the command fails.
49 """
50 mar = page_margin or (0, 0, 0, 0, gws.Uom.mm)
52 # Page sizes need to be in mm.
53 psz = (210, 297, gws.Uom.mm)
54 if page_size:
55 psz = gws.lib.uom.size_to_mm(page_size, gws.lib.uom.PDF_DPI)
57 gws.u.write_file(out_path + '.html', html)
59 cmd = [
60 'wkhtmltopdf',
61 '--disable-javascript',
62 '--disable-smart-shrinking',
63 '--load-error-handling',
64 'ignore',
65 '--enable-local-file-access',
66 '--dpi',
67 _int_str(gws.lib.uom.PDF_DPI),
68 '--margin-top',
69 _int_str(mar[0]),
70 '--margin-right',
71 _int_str(mar[1]),
72 '--margin-bottom',
73 _int_str(mar[2]),
74 '--margin-left',
75 _int_str(mar[3]),
76 '--page-width',
77 _int_str(psz[0]),
78 '--page-height',
79 _int_str(psz[1]),
80 'page',
81 out_path + '.html',
82 out_path,
83 ]
85 gws.lib.osx.run(cmd)
86 return out_path
89def render_to_png(html: str, out_path: str, page_size: gws.UomSize = None, page_margin: list[int] = None) -> str:
90 """Render an HTML string to a PNG image with ``wkhtmltoimage``.
92 The image has a transparent background.
94 Args:
95 html: HTML content.
96 out_path: Path of the PNG file to create.
97 page_size: Image size, converted to pixels at ``gws.lib.uom.PDF_DPI``. If set, the image is cropped to this size.
98 page_margin: Margins in pixels (top, right, bottom, left), applied as a body style.
100 Returns:
101 The output path.
103 Raises:
104 ``gws.lib.osx.Error``: If the command fails.
105 """
106 if page_margin:
107 mar = page_margin
108 html = f"""
109 <body style="margin:{mar[0]}px {mar[1]}px {mar[2]}px {mar[3]}px">
110 {html}
111 </body>
112 """
114 gws.u.write_file(out_path + '.html', html)
116 cmd = ['wkhtmltoimage']
118 if page_size:
119 # Page sizes need to be in pixels.
120 psz = gws.lib.uom.size_to_px(page_size, gws.lib.uom.PDF_DPI)
121 w, h, _ = psz
122 cmd.extend(
123 [
124 '--width',
125 _int_str(w),
126 '--height',
127 _int_str(h),
128 '--crop-w',
129 _int_str(w),
130 '--crop-h',
131 _int_str(h),
132 ]
133 )
135 cmd.extend(
136 [
137 '--disable-javascript',
138 '--disable-smart-width',
139 '--transparent',
140 '--enable-local-file-access',
141 out_path + '.html',
142 out_path,
143 ]
144 )
146 gws.lib.osx.run(cmd)
147 return out_path
150def _int_str(x) -> str:
151 return str(int(x))