table-renderer 0.2.0__tar.gz → 0.3.0__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: table-renderer
3
- Version: 0.2.0
3
+ Version: 0.3.0
4
4
  Summary: A Python library for rendering structured tables as high-quality images or HTML.
5
5
  Keywords: table,renderer,rendering,image,html,weasyprint,pdfium,report
6
6
  Author: pTaunium
@@ -174,6 +174,9 @@ Fine-tune your output image quality and margins.
174
174
  # High-res JPG with custom 20px padding
175
175
  table.to_image("output.jpg", dpi=300, padding=20)
176
176
 
177
+ # PNG with a solid white background (instead of the default transparent)
178
+ table.to_image("output_white.png", background_color="white")
179
+
177
180
  # Lightweight WebP with tight margins
178
181
  table.to_image("output.webp", dpi=72, padding=0)
179
182
  ```
@@ -151,6 +151,9 @@ Fine-tune your output image quality and margins.
151
151
  # High-res JPG with custom 20px padding
152
152
  table.to_image("output.jpg", dpi=300, padding=20)
153
153
 
154
+ # PNG with a solid white background (instead of the default transparent)
155
+ table.to_image("output_white.png", background_color="white")
156
+
154
157
  # Lightweight WebP with tight margins
155
158
  table.to_image("output.webp", dpi=72, padding=0)
156
159
  ```
@@ -4,7 +4,7 @@ build-backend = "uv_build"
4
4
 
5
5
  [project]
6
6
  name = "table-renderer"
7
- version = "0.2.0"
7
+ version = "0.3.0"
8
8
  description = "A Python library for rendering structured tables as high-quality images or HTML."
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -6,6 +6,6 @@ try:
6
6
  __version__ = importlib.metadata.version("table-renderer")
7
7
  except importlib.metadata.PackageNotFoundError:
8
8
  # Package is not installed (e.g. during local development without pip install -e .)
9
- __version__ = "0.2.0"
9
+ __version__ = "0.3.0"
10
10
 
11
11
  __all__ = ["Cell", "Column", "Row", "Style", "StyledObject", "Table", "__version__"]
@@ -25,7 +25,7 @@ class Table(StyledObject):
25
25
  self._cells = [[Cell() for _ in range(cols)] for _ in range(rows)]
26
26
  self._row_objects = [Row(i) for i in range(rows)]
27
27
  self._col_objects = [Column(i) for i in range(cols)]
28
- self.width: int | str = "100%"
28
+ self.width: int | str = "auto"
29
29
  self.font_files: list[str] = []
30
30
 
31
31
  def cell(self, row_index: int, col_index: int) -> Cell:
@@ -112,7 +112,14 @@ class Table(StyledObject):
112
112
  self.font_files.append(font_path)
113
113
  return self
114
114
 
115
- def to_image(self, output_path: str, *, dpi: int = 144, padding: int = 10) -> None:
115
+ def to_image(
116
+ self,
117
+ output_path: str,
118
+ *,
119
+ dpi: int = 144,
120
+ padding: int = 10,
121
+ background_color: str = "transparent",
122
+ ) -> None:
116
123
  """
117
124
  Render the table to an image file.
118
125
 
@@ -120,17 +127,27 @@ class Table(StyledObject):
120
127
  output_path: Path to the output file (e.g., 'table.png', 'table.webp').
121
128
  dpi: Resolution of the output image.
122
129
  padding: Padding around the table in pixels.
130
+ background_color: Background color of the image.
123
131
  """
124
- render_to_image(self, output_path, dpi=dpi, padding=padding)
132
+ render_to_image(
133
+ self,
134
+ output_path,
135
+ dpi=dpi,
136
+ padding=padding,
137
+ background_color=background_color,
138
+ )
125
139
 
126
- def to_html(self, output_path: str) -> None:
140
+ def to_html(
141
+ self, output_path: str, *, background_color: str = "transparent"
142
+ ) -> None:
127
143
  """
128
144
  Export the table to an HTML file.
129
145
 
130
146
  Args:
131
147
  output_path: Path to the output HTML file.
148
+ background_color: Background color of the HTML body.
132
149
  """
133
- save_html(self, output_path)
150
+ save_html(self, output_path, background_color=background_color)
134
151
 
135
152
  def _prepare_render(self) -> None:
136
153
  """Handle merging logic and cascading styles before rendering."""
@@ -19,7 +19,7 @@ HTML_TEMPLATE = """
19
19
  body {
20
20
  margin: 0;
21
21
  padding: 20px;
22
- background-color: transparent;
22
+ background-color: {{ background_color }};
23
23
  }
24
24
  {% for font in font_faces %}
25
25
  @font-face {
@@ -37,7 +37,14 @@ table {
37
37
  td {
38
38
  padding: 8px;
39
39
  box-sizing: border-box;
40
+ {{ table_style }}
41
+ }
42
+
43
+ {% for row in rows %}
44
+ .row-{{ row.index }} {
45
+ {{ row.style }}
40
46
  }
47
+ {% endfor %}
41
48
 
42
49
  {% for col in columns %}
43
50
  .col-{{ col.index }} {
@@ -45,12 +52,6 @@ td {
45
52
  {{ col.style }}
46
53
  }
47
54
  {% endfor %}
48
-
49
- {% for row in rows %}
50
- .row-{{ row.index }} {
51
- {{ row.style }}
52
- }
53
- {% endfor %}
54
55
  </style>
55
56
  </head>
56
57
  <body>
@@ -60,10 +61,10 @@ td {
60
61
  {% for c_idx in range(cells[r_idx]|length) %}
61
62
  {% set cell = cells[r_idx][c_idx] %}
62
63
  {% if not cell.is_merged %}
63
- <td class="col-{{ c_idx }}"
64
+ <td class="row-{{ r_idx }} col-{{ c_idx }}"
64
65
  rowspan="{{ cell.row_span }}"
65
66
  colspan="{{ cell.col_span }}"
66
- style="{{ cell.final_style }}">
67
+ style="{{ cell.style.to_css() }}">
67
68
  {% if cell.image_url %}
68
69
  <img src="{{ cell.image_url }}"
69
70
  style="{% if cell.image_width %}width: {{ cell.image_width }}px;{% endif %}
@@ -82,12 +83,13 @@ td {
82
83
  """
83
84
 
84
85
 
85
- def render_to_html(table: "Table") -> str:
86
+ def render_to_html(table: "Table", *, background_color: str = "transparent") -> str:
86
87
  """
87
88
  Generate the HTML representation of the table.
88
89
 
89
90
  Args:
90
91
  table: The Table object to render.
92
+ background_color: The background color of the body.
91
93
 
92
94
  Returns:
93
95
  A string containing the complete HTML document.
@@ -107,12 +109,6 @@ def render_to_html(table: "Table") -> str:
107
109
  row_data = []
108
110
  for col_index in range(len(table._cells[0])):
109
111
  cell = table._cells[row_index][col_index]
110
- row = table.get_row(row_index)
111
- col = table.get_column(col_index)
112
-
113
- # Cascading: Table -> Row/Col -> Cell
114
- base_style = table.style.merge(col.style).merge(row.style)
115
- final_style = base_style.merge(cell.style)
116
112
 
117
113
  # Handle Image URL resolution
118
114
  cell.image_url = ""
@@ -123,8 +119,6 @@ def render_to_html(table: "Table") -> str:
123
119
  abs_img_path = os.path.abspath(cell.image_path)
124
120
  cell.image_url = f"file://{abs_img_path}"
125
121
 
126
- # Attach final style string for template
127
- cell.final_style = final_style.to_css()
128
122
  row_data.append(cell)
129
123
  cells_data.append(row_data)
130
124
 
@@ -151,6 +145,7 @@ def render_to_html(table: "Table") -> str:
151
145
  return template.render(
152
146
  page_width=estimated_width,
153
147
  page_height=estimated_height,
148
+ background_color=background_color,
154
149
  table_width=f"{table.width}px" if isinstance(table.width, int) else table.width,
155
150
  table_style=table.style.to_css(),
156
151
  font_faces=font_faces,
@@ -175,6 +170,7 @@ def render_to_image(
175
170
  *,
176
171
  dpi: int = 144,
177
172
  padding: int = 10,
173
+ background_color: str = "transparent",
178
174
  ) -> None:
179
175
  """
180
176
  Render the table to an image file.
@@ -184,8 +180,9 @@ def render_to_image(
184
180
  output_path: Destination path for the image.
185
181
  dpi: Target resolution.
186
182
  padding: Margin around the table.
183
+ background_color: Background color of the image.
187
184
  """
188
- html_content = render_to_html(table)
185
+ html_content = render_to_html(table, background_color=background_color)
189
186
  # WeasyPrint v53+ removed write_png, so we render to PDF then convert to PNG
190
187
  pdf_bytes = HTML(string=html_content).write_pdf()
191
188
 
@@ -220,17 +217,19 @@ def render_to_image(
220
217
  y_offset += img.height
221
218
 
222
219
  # Visual Auto-Crop: Find the bounding box of non-white pixels
223
- # 1. Convert to RGB to ensure we have a standard background to check
224
- bg = Image.new("RGB", pil_image.size, (255, 255, 255))
220
+ # Use 'L' (grayscale) mode to reduce memory usage (1 byte per pixel vs 3)
225
221
  if pil_image.mode == "RGBA":
226
- bg.paste(pil_image, mask=pil_image.split()[3])
222
+ # Flatten RGBA onto a white grayscale background
223
+ grayscale = Image.new("L", pil_image.size, 255)
224
+ grayscale.paste(pil_image.convert("L"), mask=pil_image.getchannel("A"))
227
225
  else:
228
- bg.paste(pil_image)
226
+ # For RGB or other modes, just convert to grayscale
227
+ grayscale = pil_image.convert("L")
229
228
 
230
- # 2. Invert and find bbox of "ink"
229
+ # Invert and find bbox of "ink" (non-white pixels)
231
230
  import PIL.ImageOps
232
231
 
233
- inverted = PIL.ImageOps.invert(bg)
232
+ inverted = PIL.ImageOps.invert(grayscale)
234
233
  bbox = inverted.getbbox()
235
234
 
236
235
  if bbox:
@@ -252,14 +251,17 @@ def render_to_image(
252
251
  pil_image.save(output_path)
253
252
 
254
253
 
255
- def save_html(table: "Table", output_path: str) -> None:
254
+ def save_html(
255
+ table: "Table", output_path: str, *, background_color: str = "transparent"
256
+ ) -> None:
256
257
  """
257
258
  Save the table's HTML representation to a file.
258
259
 
259
260
  Args:
260
261
  table: The Table object to export.
261
262
  output_path: Path to the destination HTML file.
263
+ background_color: Background color of the HTML body.
262
264
  """
263
- html_content = render_to_html(table)
265
+ html_content = render_to_html(table, background_color=background_color)
264
266
  with open(output_path, "w", encoding="utf-8") as f:
265
267
  f.write(html_content)