diff --git a/.github/workflows/CI.yaml b/.github/workflows/CI.yaml index c7df273..19a48ac 100644 --- a/.github/workflows/CI.yaml +++ b/.github/workflows/CI.yaml @@ -1,15 +1,14 @@ # This file is autogenerated by maturin v1.5.1 # To update, run # -# maturin generate-ci github +# maturin generate-ci github # name: CI on: push: branches: - - main - - master + - '*' tags: - '*' pull_request: @@ -45,7 +44,7 @@ jobs: uses: PyO3/maturin-action@v1 with: target: ${{ matrix.platform.target }} - args: --release --out dist --find-interpreter + args: --release --out dist --find-interpreter sccache: 'true' manylinux: auto - name: Upload wheels diff --git a/Cargo.lock b/Cargo.lock index 4af2fb9..a4ec3e2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -384,6 +384,7 @@ dependencies = [ name = "resvg_py" version = "0.1.4" dependencies = [ + "log", "pyo3", "resvg", "svgtypes", diff --git a/Cargo.toml b/Cargo.toml index f49a81c..e30e6cb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,14 +10,20 @@ path = "src/rust/lib.rs" crate-type = ["cdylib"] [dependencies] +log = "0.4.21" pyo3 = "0.21.2" resvg = { version = "0.41.0", features = ["raster-images","text"] } svgtypes = "0.15.0" [profile.release.package."*"] -opt-level = 3 +codegen-units = 1 +opt-level ='z' +strip = true [profile.release] +panic = "abort" +codegen-units = 1 lto = "fat" -opt-level = 3 +opt-level ='z' +strip = true \ No newline at end of file diff --git a/docs/debugging.rst b/docs/debugging.rst new file mode 100644 index 0000000..cbdb6b2 --- /dev/null +++ b/docs/debugging.rst @@ -0,0 +1,37 @@ +Debugging +========= + +While `resvg-py`_ is a very thin wrapper around the `resvg`_ project there might be bugs in *resvg-py* (or *resvg*). + +In order to debug the issue you have to enable logging in `resvg-py`_ + +How to log in `resvg-py`_? + +When you call `resvg-py`_'s function in your code you can pass `log_information=True` to print debug information to the stdout + +For example: + +.. code-block:: python + + import resvg_py + import base64 + + svg_string = """ + +   + + """ + + # a large list of bytes + png_bytes: list[bytes] = resvg_py.svg_to_bytes( + svg_string=svg_string, + log_information = True ## <----------- CHECK THIS LINE + ) + base64_utf8_str = base64.b64encode(bytes(png_bytes)).decode("utf-8") + print(f"data:image/png;base64,{base64_utf8_str}") + + + + +.. _resvg-py: https://github.com/baseplate-admin/resvg-py +.. _resvg: https://docs.rs/resvg/latest/resvg/ \ No newline at end of file diff --git a/docs/index.rst b/docs/index.rst index 197d7bb..d743e6e 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -15,6 +15,7 @@ Safe bindings for `resvg `_ installation usage resvg + debugging contributing Indices and tables diff --git a/docs/resvg.rst b/docs/resvg.rst index 1967ba9..dee3569 100644 --- a/docs/resvg.rst +++ b/docs/resvg.rst @@ -5,4 +5,4 @@ Resvg Module .. autofunction:: svg_to_bytes -.. autofuncton:: version \ No newline at end of file +.. autofunction:: version \ No newline at end of file diff --git a/poetry.lock b/poetry.lock index 6cfba74..7904402 100644 --- a/poetry.lock +++ b/poetry.lock @@ -340,6 +340,17 @@ files = [ {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, ] +[[package]] +name = "pastel" +version = "0.2.1" +description = "Bring colors to your terminal." +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "pastel-0.2.1-py2.py3-none-any.whl", hash = "sha256:4349225fcdf6c2bb34d483e523475de5bb04a5c10ef711263452cb37d7dd4364"}, + {file = "pastel-0.2.1.tar.gz", hash = "sha256:e6581ac04e973cac858828c6202c1e1e81fee1dc7de7683f3e1ffe0bfd8a573d"}, +] + [[package]] name = "pluggy" version = "1.5.0" @@ -355,6 +366,24 @@ files = [ dev = ["pre-commit", "tox"] testing = ["pytest", "pytest-benchmark"] +[[package]] +name = "poethepoet" +version = "0.26.1" +description = "A task runner that works well with poetry." +optional = false +python-versions = ">=3.8" +files = [ + {file = "poethepoet-0.26.1-py3-none-any.whl", hash = "sha256:aa43b443fec5d17d7e76771cccd484e5285805301721a74f059c483ad3276edd"}, + {file = "poethepoet-0.26.1.tar.gz", hash = "sha256:aaad8541f6072617a60bcff2562d00779b58b353bd0f1847b06d8d0f2b6dc192"}, +] + +[package.dependencies] +pastel = ">=0.2.1,<0.3.0" +tomli = ">=1.2.2" + +[package.extras] +poetry-plugin = ["poetry (>=1.0,<2.0)"] + [[package]] name = "pygments" version = "2.18.0" @@ -602,6 +631,17 @@ lint = ["docutils-stubs", "flake8", "mypy"] standalone = ["Sphinx (>=5)"] test = ["pytest"] +[[package]] +name = "tomli" +version = "2.0.1" +description = "A lil' TOML parser" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, + {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, +] + [[package]] name = "tornado" version = "6.4" @@ -639,7 +679,24 @@ h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] +[[package]] +name = "ziglang" +version = "0.12.0" +description = "Zig is a general-purpose programming language and toolchain for maintaining robust, optimal, and reusable software." +optional = false +python-versions = "~=3.5" +files = [ + {file = "ziglang-0.12.0-py3-none-macosx_10_9_x86_64.whl", hash = "sha256:c6d1053f0e1d486cdf374f33b7ed00d99040d03f45eed5122a7522b0b7a75946"}, + {file = "ziglang-0.12.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:1f9291fd98c4e1ece04285ddf019b9999b2e81fb4f785b550b6d91b1e1699bc9"}, + {file = "ziglang-0.12.0-py3-none-manylinux_2_12_i686.manylinux2010_i686.musllinux_1_1_i686.whl", hash = "sha256:1cb9e6938ec8cddba124ebf2dcaf950e19b5b807eaa18cb976c569e6edef5ae3"}, + {file = "ziglang-0.12.0-py3-none-manylinux_2_12_x86_64.manylinux2010_x86_64.musllinux_1_1_x86_64.whl", hash = "sha256:0c777f1d5d9be32d0edffc79956d03ed35f868a8db7b0552da0984182aa10ff0"}, + {file = "ziglang-0.12.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:8da631bfb5e49390f0551a4412111c8a14671fd7f151e1adcc420317b02efcf6"}, + {file = "ziglang-0.12.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.musllinux_1_1_armv7l.whl", hash = "sha256:ccec405a443016647c47edcd48faf15be9d3add21c2c13bc77217b8fe87a1508"}, + {file = "ziglang-0.12.0-py3-none-win32.whl", hash = "sha256:9373c68cf654d69f46411eff2fd48cb8495b8d9bcd6e88b65783ef8ab4bfe4ae"}, + {file = "ziglang-0.12.0-py3-none-win_amd64.whl", hash = "sha256:044136e276e80dfdc7cfaada80efe8679d0ba9f8abf911f8c68afa44ca259148"}, +] + [metadata] lock-version = "2.0" python-versions = "^3.12" -content-hash = "6e6f724ac7d4c9d3fbf916a57381bdaffc5dca5fd727b55a0e208e22541809ad" +content-hash = "c3a7e31d5d8cc1c622df22d9c5906ea6a55ddb1fda172759e2fd9f07f7b686b4" diff --git a/pyproject.toml b/pyproject.toml index 8d54141..c352425 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,8 +9,10 @@ package-mode = false [tool.poetry.dependencies] python = "^3.12" + [tool.poetry.group.dev.dependencies] pytest = "^8.1.1" +poethepoet = "^0.26.1" [tool.poetry.group.docs.dependencies] sphinx = "^7.2.6" @@ -38,3 +40,8 @@ Documentation = "https://resvg-py.readthedocs.io/" [tool.maturin] features = ["pyo3/extension-module"] + +[tool.poe.tasks] +test = "pytest . -rP" +"build:release" = "maturin build --release" +"dev" = "maturin build" \ No newline at end of file diff --git a/resvg_py.pyi b/resvg_py.pyi index 75a2506..3722250 100644 --- a/resvg_py.pyi +++ b/resvg_py.pyi @@ -3,17 +3,22 @@ from typing import Literal def svg_to_bytes( svg_string: str | None = None, svg_path: str | None = None, + background: str | None = None, + skip_system_fonts: bool | None = False, + log_information: bool = False, width: int | None = None, height: int | None = None, + zoom: int | None = None, + dpi: int | None = 0, resources_dir: str | None = None, - languages: list[str] | None = None, - font_size: int | None = None, - font_family: str | None = None, - serif_family: str | None = None, - sans_serif_family: str | None = None, - cursive_family: str | None = None, - fantasy_family: str | None = None, - monospace_family: str | None = None, + languages: list[str] | None = [], + font_size: int | None = 16, + font_family: str | None = Literal["Times New Roman"], + serif_family: str | None = Literal["Times New Roman"], + sans_serif_family: str | None = Literal["Arial"], + cursive_family: str | None = Literal["Comic Sans MS"], + fantasy_family: str | None = ["Impact"], + monospace_family: str | None = Literal["Courier New"], font_files: list[str] | None = None, font_dirs: list[str] | None = None, shape_rendering: Literal[ @@ -25,8 +30,6 @@ def svg_to_bytes( image_rendering: Literal["optimize_quality", "optimize_speed"] = Literal[ "optimize_quality" ], - background: str | None = None, - skip_system_fonts: bool | None = None, ) -> list[bytes]: """ :param svg_str: A string containing valid svg. diff --git a/src/rust/lib.rs b/src/rust/lib.rs index c5ad513..12f4cdf 100644 --- a/src/rust/lib.rs +++ b/src/rust/lib.rs @@ -65,7 +65,7 @@ fn load_fonts(options: &mut Opts, fontdb: &mut resvg::usvg::fontdb::Database) { if let Some(font_files) = &options.font_files { for path in font_files { if let Err(e) = fontdb.load_font_file(path) { - println!("Failed to load '{}' cause {}.", path.to_string(), e); + log::warn!("Failed to load '{}' cause {}.", path.to_string(), e); } } } @@ -144,6 +144,8 @@ fn svg_to_bytes( height: Option, zoom: Option, dpi: Option, + // Log informations + log_information: Option, // Resource Directory resources_dir: Option, // Fonts @@ -166,6 +168,12 @@ fn svg_to_bytes( // Skip System Fonts skip_system_fonts: Option, ) -> PyResult> { + if log_information.unwrap_or(false) { + if let Ok(()) = log::set_logger(&LOGGER) { + log::set_max_level(log::LevelFilter::Warn); + } + } + let mut _svg_string = String::new(); if let Some(svg_string) = svg_string { @@ -254,7 +262,7 @@ fn svg_to_bytes( let usvg_options = resvg::usvg::Options { resources_dir: _resources_dir, dpi: dpi.unwrap_or(0) as f32, - font_family: font_family.unwrap_or_else(|| "Times New Roman".to_string()), + font_family: font_family.unwrap_or("Times New Roman".to_owned()), font_size: font_size.unwrap_or(16) as f32, languages: languages.unwrap_or(vec![]), shape_rendering: _shape_rendering, @@ -293,3 +301,35 @@ fn resvg_py(_py: Python, m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_function(wrap_pyfunction!(version, m)?)?; Ok(()) } + +/// A simple stderr logger. +static LOGGER: SimpleLogger = SimpleLogger; +struct SimpleLogger; +impl log::Log for SimpleLogger { + fn enabled(&self, metadata: &log::Metadata) -> bool { + metadata.level() <= log::LevelFilter::Warn + } + + fn log(&self, record: &log::Record) { + if self.enabled(record.metadata()) { + let target = if !record.target().is_empty() { + record.target() + } else { + record.module_path().unwrap_or_default() + }; + + let line = record.line().unwrap_or(0); + let args = record.args(); + + match record.level() { + log::Level::Error => println!("Error (in {}:{}): {}", target, line, args), + log::Level::Warn => println!("Warning (in {}:{}): {}", target, line, args), + log::Level::Info => println!("Info (in {}:{}): {}", target, line, args), + log::Level::Debug => println!("Debug (in {}:{}): {}", target, line, args), + log::Level::Trace => println!("Trace (in {}:{}): {}", target, line, args), + } + } + } + + fn flush(&self) {} +}