Skip to content

Commit 8d52d1c

Browse files
authored
Merge pull request #518 from palewire/main
feat(charts): add comprehensive export options for PNG, PDF, and SVG
2 parents ee7bc43 + e83df3a commit 8d52d1c

2 files changed

Lines changed: 133 additions & 4 deletions

File tree

datawrapper/charts/base.py

Lines changed: 89 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -737,10 +737,17 @@ def export_png(
737737
width: int | None = None,
738738
height: int | None = None,
739739
plain: bool = False,
740+
scale: int = 1,
740741
zoom: int = 2,
741742
transparent: bool = False,
742743
border_width: int = 0,
743744
border_color: str | None = None,
745+
logo: Literal["auto", "on", "off"] = "auto",
746+
logo_id: str | None = None,
747+
dark: bool = False,
748+
ligatures: bool = True,
749+
full_vector: bool = False,
750+
download: bool = False,
744751
access_token: str | None = None,
745752
timeout: int = 30,
746753
) -> bytes:
@@ -750,10 +757,17 @@ def export_png(
750757
width: Width of visualization in pixels. If not specified, uses chart width.
751758
height: Height of visualization in pixels. If not specified, uses chart height.
752759
plain: If True, exports only the visualization without header/footer.
753-
zoom: Scale multiplier for PNG resolution (e.g., 2 = 2x resolution).
760+
scale: Size multiplier that changes actual dimensions (e.g., 2 = 2x size).
761+
zoom: Resolution multiplier for sharper output at same visual size (e.g., 2 = 2x DPI).
754762
transparent: If True, exports with transparent background.
755763
border_width: Margin around visualization in pixels.
756764
border_color: Color of the border (e.g., "#FFFFFF"). If not specified, uses chart background color.
765+
logo: Logo display mode: "auto", "on", or "off".
766+
logo_id: Custom logo ID (pattern: ^[a-zA-Z0-9-]+$).
767+
dark: If True, exports in dark mode.
768+
ligatures: If True (default), enables typography ligatures.
769+
full_vector: If True, exports as full vector output.
770+
download: If True, includes download headers in response.
757771
access_token: Optional Datawrapper API access token.
758772
timeout: Timeout for the API request in seconds.
759773
@@ -780,9 +794,15 @@ def export_png(
780794
params = {
781795
"unit": "px",
782796
"plain": str(plain).lower(),
797+
"scale": str(scale),
783798
"zoom": str(zoom),
784799
"transparent": str(transparent).lower(),
785800
"borderWidth": str(border_width),
801+
"logo": logo,
802+
"dark": str(dark).lower(),
803+
"ligatures": str(ligatures).lower(),
804+
"fullVector": str(full_vector).lower(),
805+
"download": str(download).lower(),
786806
}
787807

788808
if width is not None:
@@ -791,6 +811,8 @@ def export_png(
791811
params["height"] = str(height)
792812
if border_color is not None:
793813
params["borderColor"] = border_color
814+
if logo_id is not None:
815+
params["logoId"] = logo_id
794816

795817
# Make the API request
796818
response = client.get(
@@ -813,8 +835,16 @@ def export_pdf(
813835
unit: Literal["px", "mm", "inch"] = "px",
814836
mode: Literal["rgb", "cmyk"] = "rgb",
815837
scale: int = 1,
838+
zoom: int = 2,
839+
transparent: bool = False,
816840
border_width: int = 0,
817841
border_color: str | None = None,
842+
logo: Literal["auto", "on", "off"] = "auto",
843+
logo_id: str | None = None,
844+
dark: bool = False,
845+
ligatures: bool = True,
846+
full_vector: bool = False,
847+
download: bool = False,
818848
access_token: str | None = None,
819849
timeout: int = 30,
820850
) -> bytes:
@@ -826,9 +856,17 @@ def export_pdf(
826856
plain: If True, exports only the visualization without header/footer.
827857
unit: Unit for measurements: "px", "mm", or "inch".
828858
mode: Color mode: "rgb" or "cmyk".
829-
scale: Scale multiplier for PDF resolution.
859+
scale: Size multiplier that changes actual dimensions (e.g., 2 = 2x size).
860+
zoom: Resolution multiplier for sharper output at same visual size (e.g., 2 = 2x DPI).
861+
transparent: If True, exports with transparent background.
830862
border_width: Margin around visualization.
831863
border_color: Color of the border (e.g., "#FFFFFF"). If not specified, uses chart background color.
864+
logo: Logo display mode: "auto", "on", or "off".
865+
logo_id: Custom logo ID (pattern: ^[a-zA-Z0-9-]+$).
866+
dark: If True, exports in dark mode.
867+
ligatures: If True (default), enables typography ligatures.
868+
full_vector: If True, exports as full vector output.
869+
download: If True, includes download headers in response.
832870
access_token: Optional Datawrapper API access token.
833871
timeout: Timeout for the API request in seconds.
834872
@@ -863,7 +901,14 @@ def export_pdf(
863901
"mode": mode,
864902
"plain": str(plain).lower(),
865903
"scale": str(scale),
904+
"zoom": str(zoom),
905+
"transparent": str(transparent).lower(),
866906
"borderWidth": str(border_width),
907+
"logo": logo,
908+
"dark": str(dark).lower(),
909+
"ligatures": str(ligatures).lower(),
910+
"fullVector": str(full_vector).lower(),
911+
"download": str(download).lower(),
867912
}
868913

869914
if width is not None:
@@ -872,11 +917,14 @@ def export_pdf(
872917
params["height"] = str(height)
873918
if border_color is not None:
874919
params["borderColor"] = border_color
920+
if logo_id is not None:
921+
params["logoId"] = logo_id
875922

876923
# Make the API request
877924
response = client.get(
878925
f"{client._CHARTS_URL}/{self.chart_id}/export/pdf",
879926
params=params,
927+
timeout=timeout,
880928
)
881929

882930
# Return raw bytes
@@ -890,6 +938,17 @@ def export_svg(
890938
width: int | None = None,
891939
height: int | None = None,
892940
plain: bool = False,
941+
scale: int = 1,
942+
zoom: int = 2,
943+
transparent: bool = False,
944+
border_width: int = 0,
945+
border_color: str | None = None,
946+
logo: Literal["auto", "on", "off"] = "auto",
947+
logo_id: str | None = None,
948+
dark: bool = False,
949+
ligatures: bool = True,
950+
full_vector: bool = False,
951+
download: bool = False,
893952
access_token: str | None = None,
894953
timeout: int = 30,
895954
) -> bytes:
@@ -899,6 +958,17 @@ def export_svg(
899958
width: Width of visualization. If not specified, uses chart width.
900959
height: Height of visualization. If not specified, uses chart height.
901960
plain: If True, exports only the visualization without header/footer.
961+
scale: Size multiplier that changes actual dimensions (e.g., 2 = 2x size).
962+
zoom: Resolution multiplier for sharper output at same visual size (e.g., 2 = 2x DPI).
963+
transparent: If True, exports with transparent background.
964+
border_width: Margin around visualization in pixels.
965+
border_color: Color of the border (e.g., "#FFFFFF"). If not specified, uses chart background color.
966+
logo: Logo display mode: "auto", "on", or "off".
967+
logo_id: Custom logo ID (pattern: ^[a-zA-Z0-9-]+$).
968+
dark: If True, exports in dark mode.
969+
ligatures: If True (default), enables typography ligatures.
970+
full_vector: If True, exports as full vector output.
971+
download: If True, includes download headers in response.
902972
access_token: Optional Datawrapper API access token.
903973
timeout: Timeout for the API request in seconds.
904974
@@ -922,12 +992,28 @@ def export_svg(
922992
client = self._get_client(access_token)
923993

924994
# Build query parameters
925-
params: dict[str, str] = {}
995+
params = {
996+
"unit": "px",
997+
"plain": str(plain).lower(),
998+
"scale": str(scale),
999+
"zoom": str(zoom),
1000+
"transparent": str(transparent).lower(),
1001+
"borderWidth": str(border_width),
1002+
"logo": logo,
1003+
"dark": str(dark).lower(),
1004+
"ligatures": str(ligatures).lower(),
1005+
"fullVector": str(full_vector).lower(),
1006+
"download": str(download).lower(),
1007+
}
9261008

9271009
if width is not None:
9281010
params["width"] = str(width)
9291011
if height is not None:
9301012
params["height"] = str(height)
1013+
if border_color is not None:
1014+
params["borderColor"] = border_color
1015+
if logo_id is not None:
1016+
params["logoId"] = logo_id
9311017

9321018
# Make the API request
9331019
response = client.get(

tests/integration/test_base_export.py

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,8 +184,16 @@ def mock_client_factory(access_token=None):
184184
unit="mm",
185185
mode="cmyk",
186186
scale=2,
187+
zoom=3,
188+
transparent=True,
187189
border_width=10,
188190
border_color="#FF0000",
191+
logo="on",
192+
logo_id="my-logo",
193+
dark=True,
194+
ligatures=False,
195+
full_vector=True,
196+
download=True,
189197
)
190198

191199
# Verify
@@ -200,8 +208,16 @@ def mock_client_factory(access_token=None):
200208
assert params["unit"] == "mm"
201209
assert params["mode"] == "cmyk"
202210
assert params["scale"] == "2"
211+
assert params["zoom"] == "3"
212+
assert params["transparent"] == "true"
203213
assert params["borderWidth"] == "10"
204214
assert params["borderColor"] == "#FF0000"
215+
assert params["logo"] == "on"
216+
assert params["logoId"] == "my-logo"
217+
assert params["dark"] == "true"
218+
assert params["ligatures"] == "false"
219+
assert params["fullVector"] == "true"
220+
assert params["download"] == "true"
205221

206222
def test_export_pdf_no_chart_id(self):
207223
"""Test that export_pdf raises ValueError when no chart_id is set."""
@@ -287,7 +303,22 @@ def mock_client_factory(access_token=None):
287303
# Create chart and export with all parameters
288304
chart = BarChart(title="Test Chart")
289305
chart.chart_id = "abc123"
290-
result = chart.export_svg(width=800, height=600, plain=True)
306+
result = chart.export_svg(
307+
width=800,
308+
height=600,
309+
plain=True,
310+
scale=2,
311+
zoom=3,
312+
transparent=True,
313+
border_width=10,
314+
border_color="#FF0000",
315+
logo="off",
316+
logo_id="custom-logo",
317+
dark=True,
318+
ligatures=False,
319+
full_vector=True,
320+
download=True,
321+
)
291322

292323
# Verify
293324
assert result == b"<svg>SVG_DATA</svg>"
@@ -299,6 +330,18 @@ def mock_client_factory(access_token=None):
299330
assert url == "https://api.datawrapper.de/v3/charts/abc123/export/svg"
300331
assert params["width"] == "800"
301332
assert params["height"] == "600"
333+
assert params["plain"] == "true"
334+
assert params["scale"] == "2"
335+
assert params["zoom"] == "3"
336+
assert params["transparent"] == "true"
337+
assert params["borderWidth"] == "10"
338+
assert params["borderColor"] == "#FF0000"
339+
assert params["logo"] == "off"
340+
assert params["logoId"] == "custom-logo"
341+
assert params["dark"] == "true"
342+
assert params["ligatures"] == "false"
343+
assert params["fullVector"] == "true"
344+
assert params["download"] == "true"
302345

303346
def test_export_svg_no_chart_id(self):
304347
"""Test SVG export raises error when no chart_id is set."""

0 commit comments

Comments
 (0)