pylitehtml 0.2.1__tar.gz → 0.2.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pylitehtml-0.2.2/CLAUDE.md +109 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/PKG-INFO +107 -112
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/README.md +106 -111
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/pyproject.toml +1 -1
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/src/cpp/binding.cpp +32 -21
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/src/cpp/encode.cpp +12 -10
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/src/cpp/encode.h +3 -1
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/src/cpp/font_manager.cpp +0 -1
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/src/cpp/image_cache.cpp +102 -81
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/src/cpp/image_cache.h +18 -5
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/src/cpp/py_container.cpp +0 -4
- pylitehtml-0.2.2/src/cpp/vendor/nanosvg.h +3278 -0
- pylitehtml-0.2.2/src/cpp/vendor/nanosvgrast.h +1473 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/src/python/pylitehtml/__init__.py +7 -4
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/src/python/pylitehtml/__init__.pyi +0 -1
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/src/python/pylitehtml/_core.pyi +0 -3
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/src/python/pylitehtml/_html2png.py +2 -1
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/src/python/pylitehtml/markdown.py +36 -6
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/CMakeLists.txt +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/LICENSE +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/fonts/DejaVuSans.ttf +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/fonts/NotoSans-Regular.ttf +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/fonts/NotoSansSC-Regular.otf +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/fonts/fonts.conf +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/pyrightconfig.json +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/src/cpp/base64.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/src/cpp/font_manager.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/src/cpp/http_util.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/src/cpp/http_util.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/src/cpp/py_container.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/src/cpp/vendor/httplib.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/src/python/pylitehtml/_jinja.py +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/src/python/pylitehtml/fonts/DejaVuSans.ttf +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/src/python/pylitehtml/fonts/NotoSans-Regular.ttf +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/src/python/pylitehtml/fonts/NotoSansSC-Regular.otf +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/src/python/pylitehtml/fonts/fonts.conf +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/src/python/pylitehtml/py.typed +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/.clang-format +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/CMakeLists.txt +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/LICENSE +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/README.md +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/cmake/litehtmlConfig.cmake +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/cairo/cairo_borders.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/cairo/cairo_borders.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/cairo/cairo_images_cache.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/cairo/conic_gradient.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/cairo/conic_gradient.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/cairo/container_cairo.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/cairo/container_cairo.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/cairo/container_cairo_pango.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/cairo/container_cairo_pango.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/cairo/render2png.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/cairo/render2png.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/haiku/container_haiku.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/haiku/container_haiku.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/test/Bitmap.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/test/Bitmap.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/test/Font.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/test/Font.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/test/canvas_ity.hpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/test/fonts/OFL.txt +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/test/fonts/ahem-README +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/test/fonts/ahem.ttf +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/test/fonts/readme.txt +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/test/fonts/terminus-12px.yaff +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/test/fonts/terminus-14px.yaff +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/test/fonts/terminus-16px-bold.yaff +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/test/fonts/terminus-16px.yaff +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/test/fonts/terminus-18px.yaff +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/test/fonts/terminus-20px.yaff +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/test/fonts/terminus-22px.yaff +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/test/fonts/terminus-24px.yaff +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/test/fonts/terminus-28px.yaff +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/test/fonts/terminus-32px.yaff +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/test/lodepng.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/test/lodepng.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/test/test_container.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/test/test_container.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/windows/cairo/cairo_font.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/windows/cairo/cairo_font.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/windows/cairo/windows_container.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/windows/cairo/windows_container.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/windows/gdiplus/gdiplus_container.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/windows/gdiplus/gdiplus_container.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/windows/win32/win32_container.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/windows/win32/win32_container.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/doc/document_container.md +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/doc/document_createFromString.md +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/doc/using.md +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/background.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/borders.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/codepoint.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/css_length.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/css_margins.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/css_offsets.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/css_parser.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/css_position.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/css_properties.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/css_selector.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/css_tokenizer.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/document.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/document_container.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/el_anchor.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/el_base.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/el_before_after.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/el_body.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/el_break.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/el_cdata.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/el_comment.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/el_div.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/el_font.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/el_image.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/el_link.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/el_para.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/el_script.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/el_space.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/el_style.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/el_table.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/el_td.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/el_text.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/el_title.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/el_tr.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/element.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/encodings.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/flex_item.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/flex_line.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/font_description.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/formatting_context.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/gradient.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/html.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/html_microsyntaxes.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/html_tag.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/iterators.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/line_box.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/master_css.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/media_query.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/num_cvt.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/os_types.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/render_block.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/render_block_context.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/render_flex.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/render_image.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/render_inline.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/render_inline_context.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/render_item.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/render_table.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/scroll_view.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/string_id.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/style.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/stylesheet.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/table.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/types.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/url.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/url_path.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/utf8_strings.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/web_color.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/litehtml.vcxproj +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/litehtml.vcxproj.filters +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/background.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/codepoint.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/css_borders.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/css_length.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/css_parser.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/css_properties.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/css_selector.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/css_tokenizer.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/document.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/document_container.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/el_anchor.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/el_base.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/el_before_after.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/el_body.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/el_break.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/el_cdata.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/el_comment.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/el_div.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/el_font.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/el_image.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/el_link.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/el_para.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/el_script.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/el_space.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/el_style.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/el_table.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/el_td.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/el_text.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/el_title.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/el_tr.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/element.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/encodings.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/flex_item.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/flex_line.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/formatting_context.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/gradient.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/gumbo/CMakeLists.txt +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/gumbo/LICENSE +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/gumbo/attribute.c +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/gumbo/char_ref.c +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/gumbo/char_ref.rl +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/gumbo/error.c +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/gumbo/include/gumbo/attribute.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/gumbo/include/gumbo/char_ref.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/gumbo/include/gumbo/error.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/gumbo/include/gumbo/insertion_mode.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/gumbo/include/gumbo/parser.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/gumbo/include/gumbo/string_buffer.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/gumbo/include/gumbo/string_piece.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/gumbo/include/gumbo/tag_enum.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/gumbo/include/gumbo/tag_gperf.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/gumbo/include/gumbo/tag_sizes.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/gumbo/include/gumbo/tag_strings.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/gumbo/include/gumbo/token_type.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/gumbo/include/gumbo/tokenizer.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/gumbo/include/gumbo/tokenizer_states.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/gumbo/include/gumbo/utf8.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/gumbo/include/gumbo/util.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/gumbo/include/gumbo/vector.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/gumbo/include/gumbo.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/gumbo/parser.c +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/gumbo/string_buffer.c +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/gumbo/string_piece.c +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/gumbo/tag.c +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/gumbo/tag.in +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/gumbo/tokenizer.c +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/gumbo/utf8.c +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/gumbo/util.c +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/gumbo/vector.c +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/gumbo/visualc/include/strings.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/html.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/html_microsyntaxes.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/html_tag.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/internal.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/iterators.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/line_box.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/media_query.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/num_cvt.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/render_block.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/render_block_context.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/render_flex.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/render_image.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/render_inline_context.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/render_item.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/render_table.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/string_id.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/strtod.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/style.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/stylesheet.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/table.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/url.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/url_path.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/utf8_strings.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/web_color.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/support/README.md +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/support/draw_buffer/draw_buffer.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/support/draw_buffer/draw_buffer.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/support/gtkmm4/html_widget.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/support/gtkmm4/html_widget.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/support/webpage/html_host.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/support/webpage/http_request.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/support/webpage/http_request.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/support/webpage/http_requests_pool.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/support/webpage/http_requests_pool.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/support/webpage/web_history.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/support/webpage/web_history.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/support/webpage/web_page.cpp +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/support/webpage/web_page.h +0 -0
- {pylitehtml-0.2.1 → pylitehtml-0.2.2}/vcpkg.json +0 -0
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## What this is
|
|
6
|
+
|
|
7
|
+
HTML+CSS → PNG/JPEG/RAW image renderer: a pybind11 C++ extension (`_core`) wrapping
|
|
8
|
+
[litehtml](third_party/litehtml) (submodule) + Cairo/Pango/FontConfig, with a thin Python layer
|
|
9
|
+
on top. No headless browser, no JavaScript execution. README is in Chinese and is the primary
|
|
10
|
+
user-facing doc.
|
|
11
|
+
|
|
12
|
+
## Commands
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
git clone --recursive … # third_party/litehtml is a submodule; --recursive is required
|
|
16
|
+
|
|
17
|
+
# Build deps (macOS): brew install cairo pango fontconfig webp jpeg-turbo openssl@3 cmake ninja
|
|
18
|
+
# On macOS force Apple clang:
|
|
19
|
+
CC=/usr/bin/clang CXX=/usr/bin/clang++ uv pip install -e ".[dev]" --no-build-isolation --python .venv/bin/python
|
|
20
|
+
# (requires scikit-build-core + pybind11 installed first; --no-build-isolation is required)
|
|
21
|
+
|
|
22
|
+
.venv/bin/python -m pytest tests/ -q # full suite
|
|
23
|
+
.venv/bin/python -m pytest tests/test_images.py -q -k svg # single file / pattern
|
|
24
|
+
|
|
25
|
+
# Lint/format — CI pins ruff (see .github/workflows/test.yml, version: "X.Y.Z").
|
|
26
|
+
# Run the SAME version locally or local-pass/CI-fail mismatches occur:
|
|
27
|
+
uvx ruff@0.15.16 check . && uvx ruff@0.15.16 format --check .
|
|
28
|
+
|
|
29
|
+
# Examples need extra deps (PEP 735 group): uv pip install --group examples
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Any change to `src/cpp/` requires re-running the editable install (it does not auto-rebuild).
|
|
33
|
+
Pyright runs in `basic` mode against `.venv` (`pyrightconfig.json`; `tests/` is on `extraPaths`).
|
|
34
|
+
Async tests need no decorators — `asyncio_mode = "auto"` in pyproject.
|
|
35
|
+
|
|
36
|
+
## Build-time feature gates (silent at runtime)
|
|
37
|
+
|
|
38
|
+
- **HTTPS** is compiled in only when OpenSSL ≥ 3.0 is found (`CPPHTTPLIB_OPENSSL_SUPPORT` in
|
|
39
|
+
CMakeLists). Without it, `https://` resources are silently skipped — never an error. This is
|
|
40
|
+
why Linux wheels use manylinux_2_34 (OpenSSL 3) instead of 2_28 (1.1.1).
|
|
41
|
+
- **SVG** is rasterized by vendored nanosvg (`src/cpp/vendor/nanosvg*.h`, stdlib-only,
|
|
42
|
+
always compiled in — no CMake/CI/vcpkg wiring needed). Coverage is the graphics subset:
|
|
43
|
+
paths/shapes/strokes/`userSpaceOnUse` gradients; NO `<text>`, filters, or
|
|
44
|
+
`objectBoundingBox` gradient coords (renders flat). Inline `<svg>` markup can never work —
|
|
45
|
+
litehtml has no SVG layout logic; it delegates all image decoding to this container.
|
|
46
|
+
SVG tests assert real pixels (`tests/test_images.py`) — keep it that way; the pre-nanosvg
|
|
47
|
+
"renders without crash" assertions hid a fully dead SVG path for months.
|
|
48
|
+
|
|
49
|
+
## Architecture
|
|
50
|
+
|
|
51
|
+
Render pipeline: `pylitehtml.Renderer.render()` (Python, `__init__.py`) → `_core.Renderer.render`
|
|
52
|
+
(`binding.cpp`, releases the GIL around C++ work) → `PyContainer::render` (`py_container.cpp`,
|
|
53
|
+
implements `litehtml::document_container`; litehtml calls its virtual overrides back during
|
|
54
|
+
layout/draw — they are NOT dead code even if nothing in `src/cpp` calls them) → `encode.cpp`
|
|
55
|
+
(PNG via cairo, JPEG via libjpeg with setjmp-safe error handler, RAW swizzled directly into a
|
|
56
|
+
preallocated `PyBytes`).
|
|
57
|
+
|
|
58
|
+
Invariants that span files:
|
|
59
|
+
|
|
60
|
+
- **The render surface is always opaque** — `PyContainer::create_surface` paints a white
|
|
61
|
+
background first. `encode.cpp`'s JPEG flatten (`c_premult + (255 - a)`) and the RAW swizzle
|
|
62
|
+
rely on cairo's premultiplied ARGB32; don't "fix" them for straight alpha.
|
|
63
|
+
- **Threading contract**: construct `Renderer` on one thread, then `render()` may be called from
|
|
64
|
+
many threads concurrently (GIL released; litehtml document is per-render).
|
|
65
|
+
`FontManager` holds process-wide fontconfig state behind `once_flag` + mutex —
|
|
66
|
+
constructing a Renderer with `extra` fonts mutates process state.
|
|
67
|
+
- **ImageCache** (`image_cache.cpp`) is the security boundary for resources:
|
|
68
|
+
`resolve()` blocks `file://` and root-relative paths unless the document base is `file://`
|
|
69
|
+
(i.e. came from `render_file`). It is a thread-safe LRU (atomic `last_used` stamps bumped
|
|
70
|
+
under a shared lock) with in-flight load dedup (`loading_` set + `condition_variable_any`).
|
|
71
|
+
Every decoder (PNG/JPEG/WebP/SVG) must reject error surfaces — handing an error surface to
|
|
72
|
+
cairo poisons the page's whole context.
|
|
73
|
+
- **`render_file`** injects `<base href="file://…">` (HTML-entity-escaped; litehtml's parser
|
|
74
|
+
decodes it back) so the C++ side resolves relative CSS/images. Keyword args switch it into
|
|
75
|
+
Jinja2 mode (`_jinja.py`: autoescape on, env `lru_cache`d per resolved directory).
|
|
76
|
+
- **markdown.py** is a deliberately small stdlib-only converter (stash-placeholder inline pass +
|
|
77
|
+
line-oriented block parser). URL schemes go through `_safe_url` (cmark `--safe` semantics).
|
|
78
|
+
Don't add third-party deps here — full GFM is the user's job via `converter=`.
|
|
79
|
+
- **`markdown_to_*` defaults differ from `html_to_*` on purpose** (`scale=2`,
|
|
80
|
+
`locale="zh-CN"` for Han glyph selection) — documented in their docstrings; not a bug.
|
|
81
|
+
- **Type stubs**: `__init__.pyi` and `_core.pyi` are hand-written. Any public API change must
|
|
82
|
+
update them in the same commit.
|
|
83
|
+
|
|
84
|
+
## CI / packaging gotchas
|
|
85
|
+
|
|
86
|
+
- Release flow: bump `version` in pyproject.toml, push tag `v*` → `wheels.yml` builds
|
|
87
|
+
cp310–cp314 wheels (cibuildwheel) + sdist → publishes to PyPI via Trusted Publishing (OIDC,
|
|
88
|
+
`environment: pypi` — no token) → creates the GitHub release.
|
|
89
|
+
- `wheels.yml` runs `CIBW_TEST_COMMAND` against specific test files (currently
|
|
90
|
+
`tests/test_render.py tests/test_async.py`) — renaming test files breaks release builds.
|
|
91
|
+
- Those cibuildwheel smoke tests must not import numpy (only `[dev]`-extra deps installed in
|
|
92
|
+
the test.yml jobs; cibw installs a smaller set).
|
|
93
|
+
- Windows builds via vcpkg use the shared composite action
|
|
94
|
+
`.github/actions/setup-vcpkg-win` (used by both test.yml and wheels.yml — edit once).
|
|
95
|
+
- The wheel ships only the `pylitehtml/` package; litehtml's own install rules are excluded in
|
|
96
|
+
`pyproject.toml` (`wheel.exclude`). `src/python/pylitehtml/fonts` is a symlink to repo-root
|
|
97
|
+
`fonts/`; CMake installs the real files into the wheel.
|
|
98
|
+
- `assets/*.png` are README screenshots regenerated by `examples/*.py`. Pixel output is
|
|
99
|
+
sensitive to Homebrew pango/harfbuzz versions, so byte-diffs vs committed images do not by
|
|
100
|
+
themselves indicate a rendering regression — verify visually / against a same-environment
|
|
101
|
+
HEAD build before concluding code changed rendering.
|
|
102
|
+
|
|
103
|
+
## Conventions
|
|
104
|
+
|
|
105
|
+
- Examples in `examples/` are intentionally self-contained (copy-paste runnable, public API
|
|
106
|
+
only) — don't extract shared helpers from them.
|
|
107
|
+
- ruff: E501 ignored repo-wide (long inline CSS/HTML); `__init__.py` ignores E402 (imports must
|
|
108
|
+
follow the Windows DLL-path setup).
|
|
109
|
+
- Commit style: conventional commits (`feat:`/`fix:`/`refactor:`/`ci:`/`docs:`), English.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: pylitehtml
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: HTML+CSS to image renderer based on litehtml
|
|
5
5
|
Keywords: html,css,render,image,png,litehtml,screenshot,markdown
|
|
6
6
|
Author: tyql688
|
|
@@ -52,132 +52,106 @@ Description-Content-Type: text/markdown
|
|
|
52
52
|
|
|
53
53
|

|
|
54
54
|
|
|
55
|
-
HTML+CSS → PNG/JPEG
|
|
55
|
+
HTML + CSS → PNG/JPEG 图像渲染器:**轻量、无需无头浏览器、线程安全**。
|
|
56
56
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
57
|
+
- **快** — 稳态单次渲染约 15 ms(比无头 Chromium 快约 1.4×),冷启动约 0.2 s
|
|
58
|
+
- **轻** — 不需要下载 ~190 MB 的 Chromium,官方 wheel 开箱即用(Linux / macOS / Windows)
|
|
59
|
+
- **中文友好** — 内置 Noto Sans + SC 字体,中英文混排共享基线(不会出现中文"飞起来")
|
|
60
|
+
- **线程安全** — 渲染期间释放 GIL,可多线程并发,`asyncio` 友好
|
|
61
|
+
- **内置电池** — 零依赖 Markdown 转换器、Jinja2 模板、HTTPS 图片加载
|
|
60
62
|
|
|
61
|
-
|
|
62
|
-
| --- | --- | --- |
|
|
63
|
-
| [`showcase_html.py`](https://github.com/tyql688/pylitehtml/blob/main/examples/showcase_html.py) | HTML/CSS 能力参考:排版、颜色/渐变、表格、代码、列表、进度条 + **真实 HTTPS 图片** + `data:` URI 图标 |  |
|
|
64
|
-
| [`showcase_css.py`](https://github.com/tyql688/pylitehtml/blob/main/examples/showcase_css.py) | More CSS:conic/radial 渐变、边框样式、`position`、`::before`、`text-overflow:ellipsis`、`white-space:pre`、`vertical-align` |  |
|
|
65
|
-
| [`showcase_markdown.py`](https://github.com/tyql688/pylitehtml/blob/main/examples/showcase_markdown.py) | 内置**零依赖** Markdown 转换器的端到端输出 |  |
|
|
66
|
-
| [`showcase_jinja2.py`](https://github.com/tyql688/pylitehtml/blob/main/examples/showcase_jinja2.py) | Jinja2 数据驱动模板(循环 / 条件 / 过滤器 / **autoescape**,一张订单发票) |  |
|
|
67
|
-
| [`markdown_full_converter.py`](https://github.com/tyql688/pylitehtml/blob/main/examples/markdown_full_converter.py) | 用 markdown-it-py 接入**完整 GFM**(脚注 / 定义列表 / Pygments 语法高亮;需 `pip install markdown-it-py mdit-py-plugins pygments`) |  |
|
|
68
|
-
| [`render_markdown_doc.py`](https://github.com/tyql688/pylitehtml/blob/main/examples/render_markdown_doc.py) | 渲染一篇**真实复杂文档**([`markdown.md`](https://github.com/tyql688/pylitehtml/blob/main/examples/markdown.md):多表格 / 嵌套列表 / 多语言代码 / Mermaid 源码 / 数学 TeX / 行号引用代码块) |  |
|
|
63
|
+
需要 Python ≥ 3.10。不执行 JavaScript(KaTeX 数学公式 / Mermaid 图需预渲染,或改用浏览器方案)。
|
|
69
64
|
|
|
70
|
-
|
|
71
|
-
> 模板变量经 autoescape 安全转义;抓取失败的远程图片自动跳过,不影响整图渲染。
|
|
65
|
+
## 安装
|
|
72
66
|
|
|
73
67
|
```bash
|
|
74
|
-
|
|
75
|
-
python examples/showcase_css.py
|
|
76
|
-
python examples/showcase_markdown.py
|
|
77
|
-
python examples/showcase_jinja2.py
|
|
68
|
+
pip install pylitehtml
|
|
78
69
|
```
|
|
79
70
|
|
|
80
|
-
##
|
|
71
|
+
## 30 秒上手
|
|
81
72
|
|
|
82
|
-
|
|
83
|
-
[`bench/bench_vs_playwright.py`](https://github.com/tyql688/pylitehtml/blob/main/bench/bench_vs_playwright.py)):
|
|
73
|
+
三个最常用入口,按需取用:
|
|
84
74
|
|
|
85
|
-
|
|
86
|
-
| --- | --- | --- |
|
|
87
|
-
| 单次渲染(warm,均值) | **≈ 15.5 ms** | ≈ 21 ms |
|
|
88
|
-
| 首屏 / 冷启动 | ≈ 0.2 s | ≈ 0.25 s(OS 已缓存)/ **首次可达 ~3.4 s** |
|
|
89
|
-
| 额外依赖 | 无(仅系统 Cairo/Pango 等) | **需下载 Chromium ~190 MB+**,每次渲染有浏览器进程内存开销 |
|
|
90
|
-
| JavaScript | ❌ 不执行 | ✅ 可执行(KaTeX/Mermaid 等) |
|
|
75
|
+
**① HTML → 图片**(推荐入口,自动套用一套干净的 GitHub 风默认样式)
|
|
91
76
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
需要执行 JS(数学排版、Mermaid 绘图)时仍应选 Playwright。
|
|
77
|
+
```python
|
|
78
|
+
from pylitehtml import html_to_png
|
|
95
79
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
python bench/bench_vs_playwright.py 30
|
|
80
|
+
png = html_to_png("<h1>Hello</h1><p>世界</p>", width=720)
|
|
81
|
+
open("out.png", "wb").write(png)
|
|
99
82
|
```
|
|
100
83
|
|
|
101
|
-
|
|
84
|
+
**② Markdown → 图片**(内置转换器,零第三方依赖)
|
|
102
85
|
|
|
103
|
-
```
|
|
104
|
-
|
|
86
|
+
```python
|
|
87
|
+
from pylitehtml.markdown import markdown_to_png
|
|
88
|
+
|
|
89
|
+
png = markdown_to_png("# 标题\n\n- 列表\n- [x] 任务\n\n> 引用")
|
|
105
90
|
```
|
|
106
91
|
|
|
107
|
-
|
|
92
|
+
**③ 模板文件 → 图片**(本地 HTML 自动解析相对路径的 CSS/图片;带关键字参数即按 Jinja2 模板渲染)
|
|
108
93
|
|
|
109
94
|
```python
|
|
110
|
-
import
|
|
111
|
-
|
|
112
|
-
# 最简单:HTML 片段 → PNG(自动套用一套干净的默认样式)
|
|
113
|
-
png = pylitehtml.html_to_png("<h1>Hello</h1><p>世界</p>")
|
|
114
|
-
open("out.png", "wb").write(png)
|
|
95
|
+
from pylitehtml import render_file
|
|
115
96
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
97
|
+
png = render_file("project/index.html", width=800) # 纯文件
|
|
98
|
+
png = render_file("order.html", width=800,
|
|
99
|
+
title="订单", items=[{"name": "苹果", "price": 5}]) # Jinja2 模板
|
|
100
|
+
```
|
|
119
101
|
|
|
120
|
-
|
|
121
|
-
png = pylitehtml.render("<h1>Hello</h1>", width=800)
|
|
122
|
-
r = pylitehtml.Renderer(width=800) # 复用,字体只加载一次
|
|
123
|
-
jpg = r.render("<h1>Hello</h1>", fmt="jpeg", quality=90)
|
|
102
|
+
异步场景:`png = await asyncio.to_thread(html_to_png, html)`。
|
|
124
103
|
|
|
125
|
-
|
|
126
|
-
png = r.render_file("project/index.html")
|
|
127
|
-
png = r.render_file("project/order.html", title="订单", items=[...])
|
|
104
|
+
## 效果展示
|
|
128
105
|
|
|
129
|
-
|
|
130
|
-
import asyncio
|
|
131
|
-
png = await asyncio.to_thread(r.render, "<h1>Hello</h1>")
|
|
132
|
-
```
|
|
106
|
+
[`examples/`](https://github.com/tyql688/pylitehtml/blob/main/examples/) 下的示例均**独立可运行**,各渲染一张图(详见 [examples/README](https://github.com/tyql688/pylitehtml/blob/main/examples/README.md)):
|
|
133
107
|
|
|
134
|
-
|
|
108
|
+
| 示例 | 说明 | 渲染结果 |
|
|
109
|
+
| --- | --- | --- |
|
|
110
|
+
| [`showcase_html.py`](https://github.com/tyql688/pylitehtml/blob/main/examples/showcase_html.py) | HTML/CSS 能力参考:排版、颜色/渐变、表格、代码、列表、进度条 + **真实 HTTPS 图片** + `data:` URI 图标 |  |
|
|
111
|
+
| [`showcase_css.py`](https://github.com/tyql688/pylitehtml/blob/main/examples/showcase_css.py) | More CSS:conic/radial 渐变、边框样式、`position`、`::before`、`text-overflow:ellipsis`、`white-space:pre`、`vertical-align` |  |
|
|
112
|
+
| [`showcase_markdown.py`](https://github.com/tyql688/pylitehtml/blob/main/examples/showcase_markdown.py) | 内置**零依赖** Markdown 转换器的端到端输出 |  |
|
|
113
|
+
| [`showcase_jinja2.py`](https://github.com/tyql688/pylitehtml/blob/main/examples/showcase_jinja2.py) | Jinja2 数据驱动模板(循环 / 条件 / 过滤器 / **autoescape**,一张订单发票) |  |
|
|
114
|
+
| [`markdown_full_converter.py`](https://github.com/tyql688/pylitehtml/blob/main/examples/markdown_full_converter.py) | 用 markdown-it-py 接入**完整 GFM**(脚注 / 定义列表 / Pygments 语法高亮) |  |
|
|
115
|
+
| [`render_markdown_doc.py`](https://github.com/tyql688/pylitehtml/blob/main/examples/render_markdown_doc.py) | 渲染一篇**真实复杂文档**([`markdown.md`](https://github.com/tyql688/pylitehtml/blob/main/examples/markdown.md):多表格 / 嵌套列表 / 多语言代码 / 行号引用代码块) |  |
|
|
135
116
|
|
|
136
|
-
|
|
117
|
+
> 文本测量为子像素精度;模板变量经 autoescape 安全转义;抓取失败的远程图片自动跳过,不影响整图渲染。
|
|
137
118
|
|
|
138
|
-
|
|
119
|
+
## 使用指南
|
|
139
120
|
|
|
140
|
-
### `html_to_png` — 带默认样式的 HTML
|
|
121
|
+
### `html_to_png` — 带默认样式的 HTML 渲染
|
|
141
122
|
|
|
142
123
|
```python
|
|
143
124
|
from pylitehtml import html_to_png, html_to_image, wrap_html, DEFAULT_CSS
|
|
144
125
|
|
|
145
|
-
#
|
|
126
|
+
# 传入"片段"时,自动套用默认样式;传入完整文档(含 <html>/<body>)时按原样渲染
|
|
146
127
|
png = html_to_png("<h1>标题</h1><p>正文 <code>code</code></p>", width=720)
|
|
147
|
-
|
|
148
|
-
# 传入完整文档(含 <html>/<body>)时,按原样渲染,不再注入默认样式
|
|
149
128
|
png = html_to_png("<html><body style='background:#000'>…</body></html>")
|
|
150
129
|
|
|
151
|
-
# HiDPI:scale
|
|
130
|
+
# HiDPI:scale 同时放大画布宽度与根字号,em 布局等比放大
|
|
152
131
|
png = html_to_png(fragment, width=720, scale=2)
|
|
153
132
|
|
|
154
133
|
# 追加/覆盖样式;或拿原始 RGBA 像素
|
|
155
134
|
png = html_to_png(fragment, extra_css="body{background:#0d1117;color:#e6edf3}")
|
|
156
|
-
raw = html_to_image(fragment, fmt="raw")
|
|
135
|
+
raw = html_to_image(fragment, fmt="raw") # → RawResult(.data/.width/.height)
|
|
157
136
|
doc = wrap_html("<p>x</p>", css=DEFAULT_CSS) # 仅生成完整 HTML 文档字符串
|
|
158
137
|
```
|
|
159
138
|
|
|
160
|
-
主要参数:`width`、`scale`(HiDPI 倍数,默认 1)、`wrap`(`None` 自动判断片段/整文档;`True/False`
|
|
161
|
-
`css` / `extra_css`、`fmt`(`"png"`/`"jpeg"`)、`quality`、`height`、`shrink_to_fit`、`locale`、`fonts`、`images`。
|
|
139
|
+
主要参数:`width`、`scale`(HiDPI 倍数,默认 1)、`wrap`(`None` 自动判断片段/整文档;`True/False` 强制)、`css` / `extra_css`、`fmt`(`"png"`/`"jpeg"`)、`quality`、`height`、`shrink_to_fit`、`locale`、`fonts`、`images`。
|
|
162
140
|
|
|
163
|
-
### `markdown_to_png` — Markdown →
|
|
141
|
+
### `markdown_to_png` — Markdown → 图片
|
|
164
142
|
|
|
165
|
-
|
|
166
|
-
**不引入** `markdown-it-py`、`pygments` 等任何第三方依赖。
|
|
143
|
+
内置一个**纯标准库**的轻量 Markdown→HTML 转换器,再交给 `html_to_png`,不引入 `markdown-it-py`、`pygments` 等任何第三方依赖:
|
|
167
144
|
|
|
168
145
|
```python
|
|
169
146
|
from pylitehtml.markdown import markdown_to_png, markdown_to_html
|
|
170
147
|
|
|
171
|
-
png = markdown_to_png("# 标题\n\n- 列表\n- [x]
|
|
172
|
-
html = markdown_to_html("**bold**")
|
|
148
|
+
png = markdown_to_png("# 标题\n\n- 列表\n- [x] 任务", width=720) # scale 默认 2
|
|
149
|
+
html = markdown_to_html("**bold**") # → '<p><strong>bold</strong></p>'
|
|
173
150
|
```
|
|
174
151
|
|
|
175
|
-
|
|
176
|
-
行内 raw HTML、硬换行、围栏代码块(不高亮)、引用、有序(含 `start`)/无序/嵌套/任务列表、
|
|
177
|
-
带对齐的 GFM 表格、分隔线。**不支持**:setext 标题、引用式链接、脚注、定义列表、缩进代码块。
|
|
152
|
+
内置转换器**覆盖**:标题、粗/斜/`***粗斜***`/删除线/行内代码、反斜杠转义、链接/图片/裸 URL、行内 raw HTML、硬换行、围栏代码块(不高亮)、引用、有序(含 `start`)/无序/嵌套/任务列表、带对齐的 GFM 表格、分隔线。**不支持**:setext 标题、引用式链接、脚注、定义列表、缩进代码块。
|
|
178
153
|
|
|
179
|
-
需要完整 CommonMark/GFM
|
|
180
|
-
见 [`examples/markdown_full_converter.py`](https://github.com/tyql688/pylitehtml/blob/main/examples/markdown_full_converter.py):
|
|
154
|
+
需要完整 CommonMark/GFM 时,传入 `converter=`(任意 `Callable[[str], str]`)接入 markdown-it-py:
|
|
181
155
|
|
|
182
156
|
```python
|
|
183
157
|
from markdown_it import MarkdownIt
|
|
@@ -189,11 +163,40 @@ md = MarkdownIt("gfm-like").use(footnote_plugin).use(deflist_plugin)
|
|
|
189
163
|
png = markdown_to_png(text, converter=md.render)
|
|
190
164
|
```
|
|
191
165
|
|
|
192
|
-
|
|
166
|
+
### `Renderer` / `render` — 底层接口(不注入默认样式)
|
|
193
167
|
|
|
194
|
-
|
|
168
|
+
需要复用实例(字体只加载一次)或精细控制输出时使用:
|
|
195
169
|
|
|
196
|
-
|
|
170
|
+
```python
|
|
171
|
+
import pylitehtml
|
|
172
|
+
|
|
173
|
+
png = pylitehtml.render("<h1>Hello</h1>", width=800) # 一次性便捷函数
|
|
174
|
+
|
|
175
|
+
r = pylitehtml.Renderer(width=800) # 可复用、可并发
|
|
176
|
+
jpg = r.render("<h1>Hello</h1>", fmt="jpeg", quality=90)
|
|
177
|
+
raw = r.render("<h1>Hello</h1>", fmt="raw") # RGBA 原始像素
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
`fmt="raw"` 返回的 `RawResult` 可直接转 PIL / NumPy:`Image.frombytes("RGBA", (raw.width, raw.height), raw.data)` 或 `np.frombuffer(raw.data, np.uint8).reshape(raw.height, raw.width, 4)`。
|
|
181
|
+
|
|
182
|
+
### `render_file` 与 Jinja2 模板
|
|
183
|
+
|
|
184
|
+
按标准网页组织目录,自动解析相对路径的 CSS/图片;传入关键字参数即作为 [Jinja2](https://jinja.palletsprojects.com/) 模板渲染(变量 / 循环 / 条件 / 过滤器 / `include` / `extends`):
|
|
185
|
+
|
|
186
|
+
```python
|
|
187
|
+
r = pylitehtml.Renderer(width=800)
|
|
188
|
+
png = r.render_file("project/index.html") # 纯文件
|
|
189
|
+
png = r.render_file("order.html", title="订单", items=[{"name": "苹果", "price": 5}]) # 模板
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
> **autoescape 默认开启**:`.html` 模板里的 `{{ 变量 }}` 会转义 `<`/`&`/`"`,不可信数据也不会注入。
|
|
193
|
+
> **本地文件安全**:`file://` 与根路径资源仅在 `render_file()` 下加载;直接 `render()` 字符串时忽略。
|
|
194
|
+
|
|
195
|
+
### 多线程与异步
|
|
196
|
+
|
|
197
|
+
先在单线程构造 `Renderer`,之后可多线程并发 `render()`(渲染期间释放 GIL);异步用 `await asyncio.to_thread(r.render, html)`。带 `extra` 字体的构造会改写进程级字体配置,勿在渲染进行中于其它线程构造新 `Renderer`。
|
|
198
|
+
|
|
199
|
+
## 能力边界
|
|
197
200
|
|
|
198
201
|
litehtml = CSS 2.1 + 部分 CSS3。下表为**实测**结论:
|
|
199
202
|
|
|
@@ -206,7 +209,8 @@ litehtml = CSS 2.1 + 部分 CSS3。下表为**实测**结论:
|
|
|
206
209
|
| `text-decoration`、`<mark>`/`<sub>`/`<sup>`/`<kbd>`、`white-space:pre/nowrap` | ✅ |
|
|
207
210
|
| 列表(嵌套 / `list-style-type` / 任务列表)、定义列表、表格(对齐 / `:nth-child`) | ✅ |
|
|
208
211
|
| `float`、`inline-block`、`position:relative/absolute`、`text-overflow:ellipsis`、`::before/::after` | ✅ |
|
|
209
|
-
| 图片 `<img>`:`data:`(PNG/JPEG/WebP
|
|
212
|
+
| 图片 `<img>`:`data:`(PNG/JPEG/WebP) / 本地 / HTTP·HTTPS、`@import` CSS | ✅ |
|
|
213
|
+
| SVG 图片(内置 nanosvg:path/形状/描边/`userSpaceOnUse` 渐变;不含 `<text>`/滤镜/内联 `<svg>` 标记) | ⚠️ 图形子集 |
|
|
210
214
|
| 中文 / 多语言(内置 Noto Sans + SC,中英共享基线、子像素测量) | ✅ |
|
|
211
215
|
| 多线程并发、`asyncio.to_thread` | ✅ |
|
|
212
216
|
| Flexbox(简单行 / 列) | ⚠️ 部分 |
|
|
@@ -215,9 +219,25 @@ litehtml = CSS 2.1 + 部分 CSS3。下表为**实测**结论:
|
|
|
215
219
|
|
|
216
220
|
> 图片抓取失败会自动跳过(不报错);官方 wheel(Linux manylinux_2_34 / macOS / Windows)均含 OpenSSL ≥ 3.0,支持 https。
|
|
217
221
|
|
|
218
|
-
|
|
222
|
+
## 性能:对比 Playwright
|
|
223
|
+
|
|
224
|
+
同一篇文档各渲染 30 次(macOS arm64,width=720;基准脚本 [`bench/bench_vs_playwright.py`](https://github.com/tyql688/pylitehtml/blob/main/bench/bench_vs_playwright.py)):
|
|
225
|
+
|
|
226
|
+
| 指标 | pylitehtml | Playwright(无头 Chromium) |
|
|
227
|
+
| --- | --- | --- |
|
|
228
|
+
| 单次渲染(warm,均值) | **≈ 15.5 ms** | ≈ 21 ms |
|
|
229
|
+
| 首屏 / 冷启动 | ≈ 0.2 s | ≈ 0.25 s(OS 已缓存)/ **首次可达 ~3.4 s** |
|
|
230
|
+
| 额外依赖 | 无(仅系统 Cairo/Pango 等) | **需下载 Chromium ~190 MB+**,每次渲染有浏览器进程内存开销 |
|
|
231
|
+
| JavaScript | ❌ 不执行 | ✅ 可执行(KaTeX/Mermaid 等) |
|
|
219
232
|
|
|
220
|
-
|
|
233
|
+
要点:稳态吞吐约快 1.4×;真正的差距在**部署体积与冷启动**——无 Chromium、无浏览器进程,适合容器 / Serverless / 弹性扩缩容场景。需要执行 JS(数学排版、Mermaid 绘图)时仍应选 Playwright。
|
|
234
|
+
|
|
235
|
+
```bash
|
|
236
|
+
pip install playwright && playwright install chromium # 或: uv pip install --group bench
|
|
237
|
+
python bench/bench_vs_playwright.py 30
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
## API 速查
|
|
221
241
|
|
|
222
242
|
```python
|
|
223
243
|
# 高层(推荐):自动默认样式、片段/整文档自动判别、HiDPI scale
|
|
@@ -226,6 +246,9 @@ html_to_png(html, *, width=720, scale=1, wrap=None, css=DEFAULT_CSS, extra_css="
|
|
|
226
246
|
locale="en-US", fonts=None, images=None) -> bytes
|
|
227
247
|
html_to_image(...) # 同参数,fmt 可取 "raw" → RawResult(.data/.width/.height,RGBA 行主序)
|
|
228
248
|
|
|
249
|
+
markdown_to_png(md, *, converter=None, ...) # converter: Callable[[str], str],默认内置转换器
|
|
250
|
+
markdown_to_html(md) -> str
|
|
251
|
+
|
|
229
252
|
# 底层:不注入默认样式
|
|
230
253
|
Renderer(width, *, locale="en-US", dpi=96.0, device_height=600, fonts=None, images=None)
|
|
231
254
|
Renderer.render(html, *, fmt="png", quality=85, height=0, shrink_to_fit=True)
|
|
@@ -236,35 +259,9 @@ FontConfig(default="Noto Sans", size=16, extra=[]) # 已内置 Noto San
|
|
|
236
259
|
ImageConfig(cache_mb=64.0, timeout_ms=5000, max_mb=10.0, allow_http=True)
|
|
237
260
|
```
|
|
238
261
|
|
|
239
|
-
- `fmt="raw"` 返回 RGBA:`Image.frombytes("RGBA",(raw.width,raw.height),raw.data)` 或 `np.frombuffer(...)`。
|
|
240
262
|
- `scale` 等比放大画布与(默认样式的)根字号;`shrink_to_fit` 把画布收窄到内容宽度。
|
|
241
263
|
- 内置字体使 `sans-serif`/`serif`/`monospace` 与中文在无系统字体时也能渲染;`dpi` 影响 `pt` 换算。
|
|
242
264
|
|
|
243
|
-
---
|
|
244
|
-
|
|
245
|
-
## render_file 与 Jinja2
|
|
246
|
-
|
|
247
|
-
按标准网页组织目录,自动解析相对路径的 CSS/图片;传入关键字参数即作为
|
|
248
|
-
[Jinja2](https://jinja.palletsprojects.com/) 模板渲染(变量 / 循环 / 条件 / 过滤器 / `include` / `extends`):
|
|
249
|
-
|
|
250
|
-
```python
|
|
251
|
-
png = r.render_file("project/index.html") # 纯文件
|
|
252
|
-
png = r.render_file("order.html", title="订单", items=[{"name": "苹果", "price": 5}]) # 模板
|
|
253
|
-
```
|
|
254
|
-
|
|
255
|
-
> **autoescape 默认开启**:`.html` 模板里的 `{{ 变量 }}` 会转义 `<`/`&`/`"`,不可信数据也不会注入。
|
|
256
|
-
> **本地文件安全**:`file://` 与根路径资源仅在 `render_file()` 下加载;直接 `render()` 字符串时忽略。
|
|
257
|
-
|
|
258
|
-
---
|
|
259
|
-
|
|
260
|
-
## 线程 / 异步
|
|
261
|
-
|
|
262
|
-
先在单线程构造 `Renderer`,之后可多线程并发 `render()`(渲染期间释放 GIL);
|
|
263
|
-
异步用 `await asyncio.to_thread(r.render, html)`。带 `extra_fonts` 的构造会改写进程级字体配置,
|
|
264
|
-
勿在渲染进行中于其它线程构造新 `Renderer`。
|
|
265
|
-
|
|
266
|
-
---
|
|
267
|
-
|
|
268
265
|
## 从源码构建
|
|
269
266
|
|
|
270
267
|
**Ubuntu / Debian**
|
|
@@ -290,8 +287,6 @@ ruff check . # lint
|
|
|
290
287
|
ruff format . # 格式化(CI 用 `ruff format --check` 校验)
|
|
291
288
|
```
|
|
292
289
|
|
|
293
|
-
---
|
|
294
|
-
|
|
295
290
|
## 基于
|
|
296
291
|
|
|
297
292
|
[litehtml](https://github.com/litehtml/litehtml) · [Cairo](https://www.cairographics.org/) · [Pango](https://pango.gnome.org/) · [FontConfig](https://www.freedesktop.org/wiki/Software/fontconfig/) · [pybind11](https://github.com/pybind/pybind11)
|