Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 89 additions & 3 deletions datawrapper/charts/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -737,10 +737,17 @@ def export_png(
width: int | None = None,
height: int | None = None,
plain: bool = False,
scale: int = 1,
zoom: int = 2,
transparent: bool = False,
border_width: int = 0,
border_color: str | None = None,
logo: Literal["auto", "on", "off"] = "auto",
logo_id: str | None = None,
dark: bool = False,
ligatures: bool = True,
full_vector: bool = False,
download: bool = False,
access_token: str | None = None,
timeout: int = 30,
) -> bytes:
Expand All @@ -750,10 +757,17 @@ def export_png(
width: Width of visualization in pixels. If not specified, uses chart width.
height: Height of visualization in pixels. If not specified, uses chart height.
plain: If True, exports only the visualization without header/footer.
zoom: Scale multiplier for PNG resolution (e.g., 2 = 2x resolution).
scale: Size multiplier that changes actual dimensions (e.g., 2 = 2x size).
zoom: Resolution multiplier for sharper output at same visual size (e.g., 2 = 2x DPI).
transparent: If True, exports with transparent background.
border_width: Margin around visualization in pixels.
border_color: Color of the border (e.g., "#FFFFFF"). If not specified, uses chart background color.
logo: Logo display mode: "auto", "on", or "off".
logo_id: Custom logo ID (pattern: ^[a-zA-Z0-9-]+$).
dark: If True, exports in dark mode.
ligatures: If True (default), enables typography ligatures.
full_vector: If True, exports as full vector output.
download: If True, includes download headers in response.
access_token: Optional Datawrapper API access token.
timeout: Timeout for the API request in seconds.

Expand All @@ -780,9 +794,15 @@ def export_png(
params = {
"unit": "px",
"plain": str(plain).lower(),
"scale": str(scale),
"zoom": str(zoom),
"transparent": str(transparent).lower(),
"borderWidth": str(border_width),
"logo": logo,
"dark": str(dark).lower(),
"ligatures": str(ligatures).lower(),
"fullVector": str(full_vector).lower(),
"download": str(download).lower(),
}

if width is not None:
Expand All @@ -791,6 +811,8 @@ def export_png(
params["height"] = str(height)
if border_color is not None:
params["borderColor"] = border_color
if logo_id is not None:
params["logoId"] = logo_id

# Make the API request
response = client.get(
Expand All @@ -813,8 +835,16 @@ def export_pdf(
unit: Literal["px", "mm", "inch"] = "px",
mode: Literal["rgb", "cmyk"] = "rgb",
scale: int = 1,
zoom: int = 2,
transparent: bool = False,
border_width: int = 0,
border_color: str | None = None,
logo: Literal["auto", "on", "off"] = "auto",
logo_id: str | None = None,
dark: bool = False,
ligatures: bool = True,
full_vector: bool = False,
download: bool = False,
access_token: str | None = None,
timeout: int = 30,
) -> bytes:
Expand All @@ -826,9 +856,17 @@ def export_pdf(
plain: If True, exports only the visualization without header/footer.
unit: Unit for measurements: "px", "mm", or "inch".
mode: Color mode: "rgb" or "cmyk".
scale: Scale multiplier for PDF resolution.
scale: Size multiplier that changes actual dimensions (e.g., 2 = 2x size).
zoom: Resolution multiplier for sharper output at same visual size (e.g., 2 = 2x DPI).
transparent: If True, exports with transparent background.
border_width: Margin around visualization.
border_color: Color of the border (e.g., "#FFFFFF"). If not specified, uses chart background color.
logo: Logo display mode: "auto", "on", or "off".
logo_id: Custom logo ID (pattern: ^[a-zA-Z0-9-]+$).
dark: If True, exports in dark mode.
ligatures: If True (default), enables typography ligatures.
full_vector: If True, exports as full vector output.
download: If True, includes download headers in response.
access_token: Optional Datawrapper API access token.
timeout: Timeout for the API request in seconds.

Expand Down Expand Up @@ -863,7 +901,14 @@ def export_pdf(
"mode": mode,
"plain": str(plain).lower(),
"scale": str(scale),
"zoom": str(zoom),
"transparent": str(transparent).lower(),
"borderWidth": str(border_width),
"logo": logo,
"dark": str(dark).lower(),
"ligatures": str(ligatures).lower(),
"fullVector": str(full_vector).lower(),
"download": str(download).lower(),
}

if width is not None:
Expand All @@ -872,11 +917,14 @@ def export_pdf(
params["height"] = str(height)
if border_color is not None:
params["borderColor"] = border_color
if logo_id is not None:
params["logoId"] = logo_id

# Make the API request
response = client.get(
f"{client._CHARTS_URL}/{self.chart_id}/export/pdf",
params=params,
timeout=timeout,
)

# Return raw bytes
Expand All @@ -890,6 +938,17 @@ def export_svg(
width: int | None = None,
height: int | None = None,
plain: bool = False,
scale: int = 1,
zoom: int = 2,
transparent: bool = False,
border_width: int = 0,
border_color: str | None = None,
logo: Literal["auto", "on", "off"] = "auto",
logo_id: str | None = None,
dark: bool = False,
ligatures: bool = True,
full_vector: bool = False,
download: bool = False,
access_token: str | None = None,
timeout: int = 30,
) -> bytes:
Expand All @@ -899,6 +958,17 @@ def export_svg(
width: Width of visualization. If not specified, uses chart width.
height: Height of visualization. If not specified, uses chart height.
plain: If True, exports only the visualization without header/footer.
scale: Size multiplier that changes actual dimensions (e.g., 2 = 2x size).
zoom: Resolution multiplier for sharper output at same visual size (e.g., 2 = 2x DPI).
Copy link

Copilot AI Dec 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The description of zoom for SVG export may be misleading. SVG is a vector format that doesn't have DPI (dots per inch) since it's resolution-independent. Consider revising the description to clarify how zoom applies to SVG format, or mention that it primarily affects rasterization when the SVG is converted.

Suggested change
zoom: Resolution multiplier for sharper output at same visual size (e.g., 2 = 2x DPI).
zoom: Scale multiplier for rendering. For SVG (a vector format), this affects the scale at which the SVG is rendered or rasterized (e.g., when converting to PNG). It does not affect intrinsic resolution or DPI.

Copilot uses AI. Check for mistakes.
transparent: If True, exports with transparent background.
border_width: Margin around visualization in pixels.
border_color: Color of the border (e.g., "#FFFFFF"). If not specified, uses chart background color.
logo: Logo display mode: "auto", "on", or "off".
logo_id: Custom logo ID (pattern: ^[a-zA-Z0-9-]+$).
dark: If True, exports in dark mode.
ligatures: If True (default), enables typography ligatures.
full_vector: If True, exports as full vector output.
download: If True, includes download headers in response.
access_token: Optional Datawrapper API access token.
timeout: Timeout for the API request in seconds.

Expand All @@ -922,12 +992,28 @@ def export_svg(
client = self._get_client(access_token)

# Build query parameters
params: dict[str, str] = {}
params = {
"unit": "px",
"plain": str(plain).lower(),
"scale": str(scale),
"zoom": str(zoom),
"transparent": str(transparent).lower(),
"borderWidth": str(border_width),
"logo": logo,
"dark": str(dark).lower(),
"ligatures": str(ligatures).lower(),
"fullVector": str(full_vector).lower(),
"download": str(download).lower(),
}

if width is not None:
params["width"] = str(width)
if height is not None:
params["height"] = str(height)
if border_color is not None:
params["borderColor"] = border_color
if logo_id is not None:
params["logoId"] = logo_id

# Make the API request
response = client.get(
Expand Down
45 changes: 44 additions & 1 deletion tests/integration/test_base_export.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,8 +184,16 @@ def mock_client_factory(access_token=None):
unit="mm",
mode="cmyk",
scale=2,
zoom=3,
transparent=True,
border_width=10,
border_color="#FF0000",
logo="on",
logo_id="my-logo",
dark=True,
ligatures=False,
full_vector=True,
download=True,
)

# Verify
Expand All @@ -200,8 +208,16 @@ def mock_client_factory(access_token=None):
assert params["unit"] == "mm"
assert params["mode"] == "cmyk"
assert params["scale"] == "2"
assert params["zoom"] == "3"
assert params["transparent"] == "true"
assert params["borderWidth"] == "10"
assert params["borderColor"] == "#FF0000"
assert params["logo"] == "on"
assert params["logoId"] == "my-logo"
assert params["dark"] == "true"
assert params["ligatures"] == "false"
assert params["fullVector"] == "true"
assert params["download"] == "true"

def test_export_pdf_no_chart_id(self):
"""Test that export_pdf raises ValueError when no chart_id is set."""
Expand Down Expand Up @@ -287,7 +303,22 @@ def mock_client_factory(access_token=None):
# Create chart and export with all parameters
chart = BarChart(title="Test Chart")
chart.chart_id = "abc123"
result = chart.export_svg(width=800, height=600, plain=True)
result = chart.export_svg(
width=800,
height=600,
plain=True,
scale=2,
zoom=3,
transparent=True,
border_width=10,
border_color="#FF0000",
logo="off",
logo_id="custom-logo",
dark=True,
ligatures=False,
full_vector=True,
download=True,
)

# Verify
assert result == b"<svg>SVG_DATA</svg>"
Expand All @@ -299,6 +330,18 @@ def mock_client_factory(access_token=None):
assert url == "https://api.datawrapper.de/v3/charts/abc123/export/svg"
assert params["width"] == "800"
assert params["height"] == "600"
assert params["plain"] == "true"
assert params["scale"] == "2"
assert params["zoom"] == "3"
assert params["transparent"] == "true"
assert params["borderWidth"] == "10"
assert params["borderColor"] == "#FF0000"
assert params["logo"] == "off"
assert params["logoId"] == "custom-logo"
assert params["dark"] == "true"
assert params["ligatures"] == "false"
assert params["fullVector"] == "true"
assert params["download"] == "true"

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