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.
Files changed (268) hide show
  1. pylitehtml-0.2.2/CLAUDE.md +109 -0
  2. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/PKG-INFO +107 -112
  3. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/README.md +106 -111
  4. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/pyproject.toml +1 -1
  5. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/src/cpp/binding.cpp +32 -21
  6. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/src/cpp/encode.cpp +12 -10
  7. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/src/cpp/encode.h +3 -1
  8. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/src/cpp/font_manager.cpp +0 -1
  9. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/src/cpp/image_cache.cpp +102 -81
  10. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/src/cpp/image_cache.h +18 -5
  11. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/src/cpp/py_container.cpp +0 -4
  12. pylitehtml-0.2.2/src/cpp/vendor/nanosvg.h +3278 -0
  13. pylitehtml-0.2.2/src/cpp/vendor/nanosvgrast.h +1473 -0
  14. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/src/python/pylitehtml/__init__.py +7 -4
  15. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/src/python/pylitehtml/__init__.pyi +0 -1
  16. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/src/python/pylitehtml/_core.pyi +0 -3
  17. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/src/python/pylitehtml/_html2png.py +2 -1
  18. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/src/python/pylitehtml/markdown.py +36 -6
  19. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/CMakeLists.txt +0 -0
  20. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/LICENSE +0 -0
  21. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/fonts/DejaVuSans.ttf +0 -0
  22. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/fonts/NotoSans-Regular.ttf +0 -0
  23. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/fonts/NotoSansSC-Regular.otf +0 -0
  24. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/fonts/fonts.conf +0 -0
  25. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/pyrightconfig.json +0 -0
  26. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/src/cpp/base64.h +0 -0
  27. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/src/cpp/font_manager.h +0 -0
  28. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/src/cpp/http_util.cpp +0 -0
  29. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/src/cpp/http_util.h +0 -0
  30. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/src/cpp/py_container.h +0 -0
  31. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/src/cpp/vendor/httplib.h +0 -0
  32. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/src/python/pylitehtml/_jinja.py +0 -0
  33. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/src/python/pylitehtml/fonts/DejaVuSans.ttf +0 -0
  34. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/src/python/pylitehtml/fonts/NotoSans-Regular.ttf +0 -0
  35. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/src/python/pylitehtml/fonts/NotoSansSC-Regular.otf +0 -0
  36. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/src/python/pylitehtml/fonts/fonts.conf +0 -0
  37. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/src/python/pylitehtml/py.typed +0 -0
  38. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/.clang-format +0 -0
  39. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/CMakeLists.txt +0 -0
  40. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/LICENSE +0 -0
  41. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/README.md +0 -0
  42. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/cmake/litehtmlConfig.cmake +0 -0
  43. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/cairo/cairo_borders.cpp +0 -0
  44. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/cairo/cairo_borders.h +0 -0
  45. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/cairo/cairo_images_cache.h +0 -0
  46. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/cairo/conic_gradient.cpp +0 -0
  47. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/cairo/conic_gradient.h +0 -0
  48. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/cairo/container_cairo.cpp +0 -0
  49. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/cairo/container_cairo.h +0 -0
  50. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/cairo/container_cairo_pango.cpp +0 -0
  51. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/cairo/container_cairo_pango.h +0 -0
  52. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/cairo/render2png.cpp +0 -0
  53. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/cairo/render2png.h +0 -0
  54. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/haiku/container_haiku.cpp +0 -0
  55. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/haiku/container_haiku.h +0 -0
  56. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/test/Bitmap.cpp +0 -0
  57. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/test/Bitmap.h +0 -0
  58. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/test/Font.cpp +0 -0
  59. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/test/Font.h +0 -0
  60. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/test/canvas_ity.hpp +0 -0
  61. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/test/fonts/OFL.txt +0 -0
  62. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/test/fonts/ahem-README +0 -0
  63. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/test/fonts/ahem.ttf +0 -0
  64. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/test/fonts/readme.txt +0 -0
  65. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/test/fonts/terminus-12px.yaff +0 -0
  66. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/test/fonts/terminus-14px.yaff +0 -0
  67. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/test/fonts/terminus-16px-bold.yaff +0 -0
  68. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/test/fonts/terminus-16px.yaff +0 -0
  69. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/test/fonts/terminus-18px.yaff +0 -0
  70. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/test/fonts/terminus-20px.yaff +0 -0
  71. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/test/fonts/terminus-22px.yaff +0 -0
  72. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/test/fonts/terminus-24px.yaff +0 -0
  73. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/test/fonts/terminus-28px.yaff +0 -0
  74. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/test/fonts/terminus-32px.yaff +0 -0
  75. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/test/lodepng.cpp +0 -0
  76. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/test/lodepng.h +0 -0
  77. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/test/test_container.cpp +0 -0
  78. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/test/test_container.h +0 -0
  79. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/windows/cairo/cairo_font.cpp +0 -0
  80. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/windows/cairo/cairo_font.h +0 -0
  81. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/windows/cairo/windows_container.cpp +0 -0
  82. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/windows/cairo/windows_container.h +0 -0
  83. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/windows/gdiplus/gdiplus_container.cpp +0 -0
  84. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/windows/gdiplus/gdiplus_container.h +0 -0
  85. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/windows/win32/win32_container.cpp +0 -0
  86. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/containers/windows/win32/win32_container.h +0 -0
  87. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/doc/document_container.md +0 -0
  88. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/doc/document_createFromString.md +0 -0
  89. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/doc/using.md +0 -0
  90. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/background.h +0 -0
  91. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/borders.h +0 -0
  92. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/codepoint.h +0 -0
  93. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/css_length.h +0 -0
  94. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/css_margins.h +0 -0
  95. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/css_offsets.h +0 -0
  96. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/css_parser.h +0 -0
  97. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/css_position.h +0 -0
  98. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/css_properties.h +0 -0
  99. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/css_selector.h +0 -0
  100. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/css_tokenizer.h +0 -0
  101. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/document.h +0 -0
  102. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/document_container.h +0 -0
  103. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/el_anchor.h +0 -0
  104. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/el_base.h +0 -0
  105. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/el_before_after.h +0 -0
  106. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/el_body.h +0 -0
  107. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/el_break.h +0 -0
  108. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/el_cdata.h +0 -0
  109. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/el_comment.h +0 -0
  110. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/el_div.h +0 -0
  111. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/el_font.h +0 -0
  112. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/el_image.h +0 -0
  113. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/el_link.h +0 -0
  114. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/el_para.h +0 -0
  115. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/el_script.h +0 -0
  116. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/el_space.h +0 -0
  117. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/el_style.h +0 -0
  118. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/el_table.h +0 -0
  119. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/el_td.h +0 -0
  120. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/el_text.h +0 -0
  121. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/el_title.h +0 -0
  122. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/el_tr.h +0 -0
  123. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/element.h +0 -0
  124. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/encodings.h +0 -0
  125. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/flex_item.h +0 -0
  126. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/flex_line.h +0 -0
  127. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/font_description.h +0 -0
  128. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/formatting_context.h +0 -0
  129. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/gradient.h +0 -0
  130. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/html.h +0 -0
  131. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/html_microsyntaxes.h +0 -0
  132. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/html_tag.h +0 -0
  133. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/iterators.h +0 -0
  134. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/line_box.h +0 -0
  135. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/master_css.h +0 -0
  136. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/media_query.h +0 -0
  137. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/num_cvt.h +0 -0
  138. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/os_types.h +0 -0
  139. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/render_block.h +0 -0
  140. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/render_block_context.h +0 -0
  141. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/render_flex.h +0 -0
  142. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/render_image.h +0 -0
  143. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/render_inline.h +0 -0
  144. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/render_inline_context.h +0 -0
  145. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/render_item.h +0 -0
  146. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/render_table.h +0 -0
  147. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/scroll_view.h +0 -0
  148. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/string_id.h +0 -0
  149. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/style.h +0 -0
  150. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/stylesheet.h +0 -0
  151. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/table.h +0 -0
  152. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/types.h +0 -0
  153. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/url.h +0 -0
  154. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/url_path.h +0 -0
  155. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/utf8_strings.h +0 -0
  156. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml/web_color.h +0 -0
  157. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/include/litehtml.h +0 -0
  158. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/litehtml.vcxproj +0 -0
  159. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/litehtml.vcxproj.filters +0 -0
  160. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/background.cpp +0 -0
  161. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/codepoint.cpp +0 -0
  162. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/css_borders.cpp +0 -0
  163. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/css_length.cpp +0 -0
  164. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/css_parser.cpp +0 -0
  165. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/css_properties.cpp +0 -0
  166. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/css_selector.cpp +0 -0
  167. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/css_tokenizer.cpp +0 -0
  168. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/document.cpp +0 -0
  169. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/document_container.cpp +0 -0
  170. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/el_anchor.cpp +0 -0
  171. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/el_base.cpp +0 -0
  172. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/el_before_after.cpp +0 -0
  173. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/el_body.cpp +0 -0
  174. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/el_break.cpp +0 -0
  175. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/el_cdata.cpp +0 -0
  176. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/el_comment.cpp +0 -0
  177. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/el_div.cpp +0 -0
  178. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/el_font.cpp +0 -0
  179. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/el_image.cpp +0 -0
  180. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/el_link.cpp +0 -0
  181. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/el_para.cpp +0 -0
  182. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/el_script.cpp +0 -0
  183. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/el_space.cpp +0 -0
  184. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/el_style.cpp +0 -0
  185. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/el_table.cpp +0 -0
  186. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/el_td.cpp +0 -0
  187. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/el_text.cpp +0 -0
  188. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/el_title.cpp +0 -0
  189. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/el_tr.cpp +0 -0
  190. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/element.cpp +0 -0
  191. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/encodings.cpp +0 -0
  192. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/flex_item.cpp +0 -0
  193. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/flex_line.cpp +0 -0
  194. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/formatting_context.cpp +0 -0
  195. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/gradient.cpp +0 -0
  196. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/gumbo/CMakeLists.txt +0 -0
  197. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/gumbo/LICENSE +0 -0
  198. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/gumbo/attribute.c +0 -0
  199. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/gumbo/char_ref.c +0 -0
  200. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/gumbo/char_ref.rl +0 -0
  201. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/gumbo/error.c +0 -0
  202. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/gumbo/include/gumbo/attribute.h +0 -0
  203. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/gumbo/include/gumbo/char_ref.h +0 -0
  204. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/gumbo/include/gumbo/error.h +0 -0
  205. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/gumbo/include/gumbo/insertion_mode.h +0 -0
  206. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/gumbo/include/gumbo/parser.h +0 -0
  207. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/gumbo/include/gumbo/string_buffer.h +0 -0
  208. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/gumbo/include/gumbo/string_piece.h +0 -0
  209. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/gumbo/include/gumbo/tag_enum.h +0 -0
  210. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/gumbo/include/gumbo/tag_gperf.h +0 -0
  211. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/gumbo/include/gumbo/tag_sizes.h +0 -0
  212. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/gumbo/include/gumbo/tag_strings.h +0 -0
  213. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/gumbo/include/gumbo/token_type.h +0 -0
  214. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/gumbo/include/gumbo/tokenizer.h +0 -0
  215. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/gumbo/include/gumbo/tokenizer_states.h +0 -0
  216. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/gumbo/include/gumbo/utf8.h +0 -0
  217. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/gumbo/include/gumbo/util.h +0 -0
  218. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/gumbo/include/gumbo/vector.h +0 -0
  219. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/gumbo/include/gumbo.h +0 -0
  220. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/gumbo/parser.c +0 -0
  221. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/gumbo/string_buffer.c +0 -0
  222. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/gumbo/string_piece.c +0 -0
  223. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/gumbo/tag.c +0 -0
  224. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/gumbo/tag.in +0 -0
  225. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/gumbo/tokenizer.c +0 -0
  226. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/gumbo/utf8.c +0 -0
  227. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/gumbo/util.c +0 -0
  228. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/gumbo/vector.c +0 -0
  229. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/gumbo/visualc/include/strings.h +0 -0
  230. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/html.cpp +0 -0
  231. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/html_microsyntaxes.cpp +0 -0
  232. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/html_tag.cpp +0 -0
  233. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/internal.h +0 -0
  234. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/iterators.cpp +0 -0
  235. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/line_box.cpp +0 -0
  236. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/media_query.cpp +0 -0
  237. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/num_cvt.cpp +0 -0
  238. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/render_block.cpp +0 -0
  239. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/render_block_context.cpp +0 -0
  240. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/render_flex.cpp +0 -0
  241. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/render_image.cpp +0 -0
  242. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/render_inline_context.cpp +0 -0
  243. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/render_item.cpp +0 -0
  244. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/render_table.cpp +0 -0
  245. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/string_id.cpp +0 -0
  246. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/strtod.cpp +0 -0
  247. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/style.cpp +0 -0
  248. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/stylesheet.cpp +0 -0
  249. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/table.cpp +0 -0
  250. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/url.cpp +0 -0
  251. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/url_path.cpp +0 -0
  252. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/utf8_strings.cpp +0 -0
  253. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/src/web_color.cpp +0 -0
  254. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/support/README.md +0 -0
  255. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/support/draw_buffer/draw_buffer.cpp +0 -0
  256. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/support/draw_buffer/draw_buffer.h +0 -0
  257. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/support/gtkmm4/html_widget.cpp +0 -0
  258. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/support/gtkmm4/html_widget.h +0 -0
  259. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/support/webpage/html_host.h +0 -0
  260. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/support/webpage/http_request.cpp +0 -0
  261. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/support/webpage/http_request.h +0 -0
  262. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/support/webpage/http_requests_pool.cpp +0 -0
  263. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/support/webpage/http_requests_pool.h +0 -0
  264. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/support/webpage/web_history.cpp +0 -0
  265. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/support/webpage/web_history.h +0 -0
  266. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/support/webpage/web_page.cpp +0 -0
  267. {pylitehtml-0.2.1 → pylitehtml-0.2.2}/third_party/litehtml/support/webpage/web_page.h +0 -0
  268. {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.1
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
  ![CI](https://github.com/tyql688/pylitehtml/actions/workflows/test.yml/badge.svg)
54
54
 
55
- HTML+CSS → PNG/JPEG 图像渲染器。轻量级,无需无头浏览器,线程安全。需要 Python ≥ 3.10。
55
+ HTML + CSS → PNG/JPEG 图像渲染器:**轻量、无需无头浏览器、线程安全**。
56
56
 
57
- ## 效果展示
58
-
59
- [`examples/`](https://github.com/tyql688/pylitehtml/blob/main/examples/) 下有若干**独立可运行**的示例,各渲染一张图(无浏览器、纯 pylitehtml,与 `tests/` 分开)。
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 图标 | ![html](https://raw.githubusercontent.com/tyql688/pylitehtml/main/assets/showcase_html.png) |
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` | ![css](https://raw.githubusercontent.com/tyql688/pylitehtml/main/assets/showcase_css.png) |
65
- | [`showcase_markdown.py`](https://github.com/tyql688/pylitehtml/blob/main/examples/showcase_markdown.py) | 内置**零依赖** Markdown 转换器的端到端输出 | ![markdown](https://raw.githubusercontent.com/tyql688/pylitehtml/main/assets/showcase_markdown.png) |
66
- | [`showcase_jinja2.py`](https://github.com/tyql688/pylitehtml/blob/main/examples/showcase_jinja2.py) | Jinja2 数据驱动模板(循环 / 条件 / 过滤器 / **autoescape**,一张订单发票) | ![jinja2](https://raw.githubusercontent.com/tyql688/pylitehtml/main/assets/showcase_jinja2.png) |
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`) | ![gfm](https://raw.githubusercontent.com/tyql688/pylitehtml/main/assets/showcase_markdown_full.png) |
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 / 行号引用代码块) | ![doc](https://raw.githubusercontent.com/tyql688/pylitehtml/main/assets/markdown_doc.png) |
63
+ 需要 Python 3.10。不执行 JavaScript(KaTeX 数学公式 / Mermaid 图需预渲染,或改用浏览器方案)。
69
64
 
70
- > 中英文混排共享同一基线(不会出现中文“飞起来”的错位);文本测量为子像素精度;
71
- > 模板变量经 autoescape 安全转义;抓取失败的远程图片自动跳过,不影响整图渲染。
65
+ ## 安装
72
66
 
73
67
  ```bash
74
- python examples/showcase_html.py # 生成 assets/showcase_html.png(需联网加载真实图片)
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
- ## 性能(对比 Playwright)
71
+ ## 30 秒上手
81
72
 
82
- 同一篇文档各渲染 30 次(macOS arm64,width=720;基准脚本
83
- [`bench/bench_vs_playwright.py`](https://github.com/tyql688/pylitehtml/blob/main/bench/bench_vs_playwright.py)):
73
+ 三个最常用入口,按需取用:
84
74
 
85
- | 指标 | pylitehtml | Playwright(无头 Chromium) |
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
- 要点:稳态吞吐 pylitehtml **约快 1.4×**;真正的差距在**部署体积与冷启动** ——
93
- 无需 ~190MB Chromium、无浏览器进程,特别适合容器 / Serverless / 边缩缩容场景。
94
- 需要执行 JS(数学排版、Mermaid 绘图)时仍应选 Playwright。
77
+ ```python
78
+ from pylitehtml import html_to_png
95
79
 
96
- ```bash
97
- pip install playwright && playwright install chromium # 或: uv pip install --group bench
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
- ```bash
104
- pip install pylitehtml
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 pylitehtml
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
- # Markdown PNG(零依赖内置转换器)
117
- from pylitehtml.markdown import markdown_to_png
118
- png = markdown_to_png("# 标题\n\n- 列表\n- [x] 任务")
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
- # 需要精细控制时用底层接口:render / Renderer(不注入默认样式)
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
- # 本地 HTML 文件(自动解析相对路径的 CSS/图片)+ Jinja2 模板
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 图标 | ![html](https://raw.githubusercontent.com/tyql688/pylitehtml/main/assets/showcase_html.png) |
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` | ![css](https://raw.githubusercontent.com/tyql688/pylitehtml/main/assets/showcase_css.png) |
112
+ | [`showcase_markdown.py`](https://github.com/tyql688/pylitehtml/blob/main/examples/showcase_markdown.py) | 内置**零依赖** Markdown 转换器的端到端输出 | ![markdown](https://raw.githubusercontent.com/tyql688/pylitehtml/main/assets/showcase_markdown.png) |
113
+ | [`showcase_jinja2.py`](https://github.com/tyql688/pylitehtml/blob/main/examples/showcase_jinja2.py) | Jinja2 数据驱动模板(循环 / 条件 / 过滤器 / **autoescape**,一张订单发票) | ![jinja2](https://raw.githubusercontent.com/tyql688/pylitehtml/main/assets/showcase_jinja2.png) |
114
+ | [`markdown_full_converter.py`](https://github.com/tyql688/pylitehtml/blob/main/examples/markdown_full_converter.py) | 用 markdown-it-py 接入**完整 GFM**(脚注 / 定义列表 / Pygments 语法高亮) | ![gfm](https://raw.githubusercontent.com/tyql688/pylitehtml/main/assets/showcase_markdown_full.png) |
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):多表格 / 嵌套列表 / 多语言代码 / 行号引用代码块) | ![doc](https://raw.githubusercontent.com/tyql688/pylitehtml/main/assets/markdown_doc.png) |
135
116
 
136
- ## 高级封装:html_to_png / markdown_to_png
117
+ > 文本测量为子像素精度;模板变量经 autoescape 安全转义;抓取失败的远程图片自动跳过,不影响整图渲染。
137
118
 
138
- `render()` 是最底层接口(HTML 字符串 → 图片)。在它之上提供了两个高层便捷函数。
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
- # 传入“片段”时,自动套用一套干净的 GitHub 风默认样式
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 同时放大画布宽度与(默认样式的)根字号,em 布局等比放大
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") # → RawResult(.data/.width/.height)
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 → HTML → 图片(可选、默认零依赖)
141
+ ### `markdown_to_png` — Markdown → 图片
164
142
 
165
- “用 HTML 渲染 Markdown”:内置一个**纯标准库**的轻量 Markdown→HTML 转换器,再交给 `html_to_png`。
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] 任务\n\n> 引用", width=720) # scale 默认 2
172
- html = markdown_to_html("**bold**") # → '<p><strong>bold</strong></p>'
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
- 内置转换器覆盖:标题、粗/斜/`***粗斜***`/删除线/行内代码、反斜杠转义、链接/图片/裸 URL
176
- 行内 raw HTML、硬换行、围栏代码块(不高亮)、引用、有序(含 `start`)/无序/嵌套/任务列表、
177
- 带对齐的 GFM 表格、分隔线。**不支持**:setext 标题、引用式链接、脚注、定义列表、缩进代码块。
152
+ 内置转换器**覆盖**:标题、粗/斜/`***粗斜***`/删除线/行内代码、反斜杠转义、链接/图片/裸 URL、行内 raw HTML、硬换行、围栏代码块(不高亮)、引用、有序(含 `start`)/无序/嵌套/任务列表、带对齐的 GFM 表格、分隔线。**不支持**:setext 标题、引用式链接、脚注、定义列表、缩进代码块。
178
153
 
179
- 需要完整 CommonMark/GFM(脚注、定义列表、语法高亮)时传入 `converter=` 接入 markdown-it-py
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
- > **不执行 JavaScript**:数学公式(KaTeX)与 Mermaid 无法排版/绘制,需调用前自行预渲染或接入完整引擎。
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/SVG) / 本地 / HTTP·HTTPS、`@import` CSS | ✅ |
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
- ## API
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)