Skip to content

Commit

Permalink
Support saving to PDF with VlConvert 1.0 (#3244)
Browse files Browse the repository at this point in the history
  • Loading branch information
jonmmease committed Nov 3, 2023
1 parent 18aa513 commit e5fb1f0
Show file tree
Hide file tree
Showing 6 changed files with 37 additions and 27 deletions.
8 changes: 4 additions & 4 deletions altair/utils/_importers.py
Expand Up @@ -8,7 +8,7 @@ def import_vegafusion() -> ModuleType:
try:
version = importlib_version("vegafusion")
if Version(version) < Version(min_version):
raise ImportError(
raise RuntimeError(
f"The vegafusion package must be version {min_version} or greater. "
f"Found version {version}"
)
Expand All @@ -29,11 +29,11 @@ def import_vegafusion() -> ModuleType:


def import_vl_convert() -> ModuleType:
min_version = "0.14.0"
min_version = "1.0.0"
try:
version = importlib_version("vl-convert-python")
if Version(version) < Version(min_version):
raise ImportError(
raise RuntimeError(
f"The vl-convert-python package must be version {min_version} or greater. "
f"Found version {version}"
)
Expand All @@ -58,7 +58,7 @@ def import_pyarrow_interchange() -> ModuleType:
version = importlib_version("pyarrow")

if Version(version) < Version(min_version):
raise ImportError(
raise RuntimeError(
f"The pyarrow package must be version {min_version} or greater. "
f"Found version {version}"
)
Expand Down
2 changes: 1 addition & 1 deletion altair/utils/_vegafusion_data.py
Expand Up @@ -182,7 +182,7 @@ def compile_with_vegafusion(vegalite_spec: dict) -> dict:


def using_vegafusion() -> bool:
"""Check whether the vegafusion data transfomer is enabled"""
"""Check whether the vegafusion data transformer is enabled"""
# Local import to avoid circular ImportError
from altair import data_transformers

Expand Down
29 changes: 23 additions & 6 deletions altair/utils/mimebundle.py
@@ -1,6 +1,8 @@
from .deprecation import AltairDeprecationWarning
from .html import spec_to_html
from ._importers import import_vl_convert
import struct
import warnings


def spec_to_mimebundle(
Expand Down Expand Up @@ -145,11 +147,31 @@ def _spec_to_mimebundle_with_engine(spec, format, mode, **kwargs):
return {"image/png": png}, {
"image/png": {"width": w / factor, "height": h / factor}
}
elif format == "pdf":
scale = kwargs.get("scale_factor", 1)
if mode == "vega":
pdf = vlc.vega_to_pdf(
spec,
scale=scale,
)
else:
pdf = vlc.vegalite_to_pdf(
spec,
vl_version=vl_version,
scale=scale,
)
return {"application/pdf": pdf}
else:
# This should be validated above
# but raise exception for the sake of future development
raise ValueError("Unexpected format {fmt!r}".format(fmt=format))
elif normalized_engine == "altairsaver":
warnings.warn(
"The altair_saver export engine is deprecated and will be removed in a future version.\n"
"Please migrate to the vl-convert engine",
AltairDeprecationWarning,
stacklevel=1,
)
import altair_saver

return altair_saver.render(spec, format, mode=mode, **kwargs)
Expand Down Expand Up @@ -192,18 +214,13 @@ def _validate_normalize_engine(engine, format):
raise ValueError(
"The 'vl-convert' conversion engine requires the vl-convert-python package"
)
if format == "pdf":
raise ValueError(
"The 'vl-convert' conversion engine does not support the {fmt!r} format.\n"
"Use the 'altair_saver' engine instead".format(fmt=format)
)
elif normalized_engine == "altairsaver":
if altair_saver is None:
raise ValueError(
"The 'altair_saver' conversion engine requires the altair_saver package"
)
elif normalized_engine is None:
if vlc is not None and format != "pdf":
if vlc is not None:
normalized_engine = "vlconvert"
elif altair_saver is not None:
normalized_engine = "altairsaver"
Expand Down
5 changes: 2 additions & 3 deletions doc/user_guide/saving_charts.rst
Expand Up @@ -172,9 +172,8 @@ or::
pip install vl-convert-python

Unlike altair_saver_, vl-convert_ does not require any external dependencies.
However, it only supports saving charts to PNG and SVG formats. To save directly to
PDF, altair_saver_ is still required. See the vl-convert documentation for information
on other `limitations <https://github.com/vega/vl-convert#limitations>`_.
See the vl-convert documentation for information and for known
`limitations <https://github.com/vega/vl-convert#limitations>`_.

altair_saver
^^^^^^^^^^^^
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Expand Up @@ -64,7 +64,7 @@ dev = [
"pytest-cov",
"m2r",
"vega_datasets",
"vl-convert-python>=0.14.0",
"vl-convert-python>=1.0.0",
"mypy",
"pandas-stubs",
"types-jsonschema",
Expand Down
18 changes: 6 additions & 12 deletions tests/vegalite/v5/test_api.py
Expand Up @@ -322,23 +322,15 @@ def test_save(format, engine, basic_chart):
return

elif engine == "vl-convert":
if vlc is None and format != "bogus":
with pytest.raises(ValueError) as err:
basic_chart.save(out, format=format, engine=engine)
assert "vl-convert-python" in str(err.value)
return
elif format == "pdf":
if format == "bogus":
with pytest.raises(ValueError) as err:
basic_chart.save(out, format=format, engine=engine)
assert (
f"The 'vl-convert' conversion engine does not support the '{format}' format"
in str(err.value)
)
assert f"Unsupported format: '{format}'" in str(err.value)
return
elif format not in ("png", "svg"):
elif vlc is None:
with pytest.raises(ValueError) as err:
basic_chart.save(out, format=format, engine=engine)
assert f"Unsupported format: '{format}'" in str(err.value)
assert "vl-convert-python" in str(err.value)
return

basic_chart.save(out, format=format, engine=engine)
Expand All @@ -353,6 +345,8 @@ def test_save(format, engine, basic_chart):
assert content.startswith("<svg")
elif format == "png":
assert content.startswith(b"\x89PNG")
elif format == "pdf":
assert content.startswith(b"%PDF-")

fid, filename = tempfile.mkstemp(suffix="." + format)
os.close(fid)
Expand Down

0 comments on commit e5fb1f0

Please sign in to comment.