Skip to content

Commit

Permalink
add
Browse files Browse the repository at this point in the history
  • Loading branch information
baseplate-admin committed May 24, 2024
1 parent 488a3c2 commit 3af66ac
Show file tree
Hide file tree
Showing 9 changed files with 86 additions and 106 deletions.
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ Documentation = "https://resvg-py.readthedocs.io/"

[tool.maturin]
features = ["pyo3/extension-module"]
python-source = "src/python"

[tool.poe.tasks]
test = "pytest . -rP"
Expand Down
72 changes: 56 additions & 16 deletions resvg_py.pyi → src/python/resvg_py/__init__.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
import os
import gzip

from typing import Literal
from .resvg_py import _svg_to_bytes, __version__, __author__

__all__ = [
__version__,
__author__,
"svg_to_bytes",
]

__version__: str
__author__: str

def svg_to_bytes(
svg_string: str | None = None,
svg_path: str | None = None,
svg: str,
background: str | None = None,
skip_system_fonts: bool | None = False,
log_information: bool = False,
Expand All @@ -16,23 +23,21 @@ def svg_to_bytes(
resources_dir: 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_family: str | None = "Times New Roman",
serif_family: str | None = "Times New Roman",
sans_serif_family: str | None = "Arial",
cursive_family: str | None = "Comic Sans MS",
fantasy_family: str | None = "Impact",
monospace_family: str | None = "Courier New",
font_files: list[str] | None = None,
font_dirs: list[str] | None = None,
shape_rendering: Literal[
"optimize_speed", "crisp_edges", "geometric_precision"
] = Literal["geometric_precision"],
] = "geometric_precision",
text_rendering: Literal[
"optimize_speed", "optimize_legibility", "optimize_legibility"
] = Literal["optimize_legibility"],
image_rendering: Literal["optimize_quality", "optimize_speed"] = Literal[
"optimize_quality"
],
] = "optimize_legibility",
image_rendering: Literal["optimize_quality", "optimize_speed"] = "optimize_quality",
) -> list[bytes]:
"""
:param svg_str: A string containing valid svg.
Expand All @@ -58,4 +63,39 @@ def svg_to_bytes(
:param background: A `CSS color <https://developer.mozilla.org/en-US/docs/Web/CSS/color_value>`_ value that describes the canvas size.
"""

...
if os.path.exists(svg):
with open(svg, "rb") as f:
try:
svg_string = f.read().decode()
except UnicodeDecodeError:
# File is probably gzip
file = gzip.open(f, "rt")
svg_string = file.read()
print(svg_string)
else:
svg_string = svg

return _svg_to_bytes(
svg_string,
background,
skip_system_fonts,
log_information,
width,
height,
zoom,
dpi,
resources_dir,
languages,
font_size,
font_family,
serif_family,
sans_serif_family,
cursive_family,
fantasy_family,
monospace_family,
font_files,
font_dirs,
shape_rendering,
text_rendering,
image_rendering,
)
97 changes: 18 additions & 79 deletions src/rust/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ Based on
use pyo3::prelude::*;
use resvg;


#[derive(Clone, Copy, PartialEq, Debug)]
enum FitTo {
/// Keep original size.
Expand Down Expand Up @@ -168,34 +167,9 @@ fn resvg_magic(mut options: Opts, svg_string: String) -> Result<Vec<u8>, String>
}

#[pyfunction]
#[pyo3(signature = (
svg_string= None,
svg_path = None,
background = None,
skip_system_fonts= false,
log_information = false,
width = None,
height= None,
zoom = None,
dpi = 0,
resources_dir = None,
languages = vec![],
font_size = 16,
font_family = "Times New Roman".to_owned(),
serif_family = "Times New Roman".to_owned(),
sans_serif_family = "Arial".to_owned(),
cursive_family = "Comic Sans MS".to_owned(),
fantasy_family = "Impact".to_owned(),
monospace_family = "Courier New".to_owned(),
font_files = None,
font_dirs = None,
shape_rendering = "geometric_precision".to_owned(),
text_rendering = "optimize_legibility".to_owned(),
image_rendering = "optimize_quality".to_owned(),
))]
fn svg_to_bytes(
svg_string: Option<String>,
svg_path: Option<String>,

fn _svg_to_bytes(
svg_string: String,
// Background
background: Option<String>,
// Skip System Fonts
Expand All @@ -215,51 +189,23 @@ fn svg_to_bytes(
font_size: Option<u32>,
font_family: Option<String>,
serif_family: Option<String>,
sans_serif_family:Option<String>,
sans_serif_family: Option<String>,
cursive_family: Option<String>,
fantasy_family: Option<String>,
monospace_family:Option<String>,
monospace_family: Option<String>,
font_files: Option<Vec<String>>,
font_dirs: Option<Vec<String>>,
// Effects based
shape_rendering:Option<String>,
shape_rendering: Option<String>,
text_rendering: Option<String>,
image_rendering: Option<String>,

) -> PyResult<Vec<u8>> {
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 {
_svg_string = svg_string;
}

// Only check for path if provided string is empty
if _svg_string.is_empty() {
if let Some(svg_path) = svg_path {
if std::path::Path::new(&svg_path).exists() {
let mut svg_data =
std::fs::read(&svg_path).expect("failed to open the provided file");
if svg_data.starts_with(&[0x1f, 0x8b]) {
svg_data = resvg::usvg::decompress_svgz(&svg_data)
.expect("can't decompress the svg file");
};
_svg_string = std::str::from_utf8(&svg_data)
.expect("can't convert bytes to utf-8")
.to_owned();
}
}
}

if _svg_string.is_empty() {
panic!("`svg_string` is empty or `svg_path` contains empty invalid svg");
}

let mut fit_to = FitTo::Original;
let mut default_size = resvg::usvg::Size::from_wh(100.0, 100.0).unwrap();

Expand Down Expand Up @@ -331,47 +277,40 @@ fn svg_to_bytes(
image_href_resolver: resvg::usvg::ImageHrefResolver::default(),
};



let options = Opts {
usvg_opt: usvg_options,
background: _background,
skip_system_fonts: skip_system_fonts.unwrap_or(false),
fit_to,
serif_family:serif_family,
sans_serif_family:sans_serif_family,
cursive_family:cursive_family,
fantasy_family:fantasy_family,
monospace_family:monospace_family,
serif_family: serif_family,
sans_serif_family: sans_serif_family,
cursive_family: cursive_family,
fantasy_family: fantasy_family,
monospace_family: monospace_family,
font_files,
font_dirs,
};
let pixmap = resvg_magic(options, _svg_string.trim().to_owned()).unwrap();
let pixmap = resvg_magic(options, svg_string.trim().to_owned()).unwrap();
Ok(pixmap)
}

fn get_version() -> &'static str {
static VERSION : std::sync::OnceLock<String> = std::sync::OnceLock::new();
static VERSION: std::sync::OnceLock<String> = std::sync::OnceLock::new();

VERSION.get_or_init(||{
env!("CARGO_PKG_VERSION").to_owned()
})
VERSION.get_or_init(|| env!("CARGO_PKG_VERSION").to_owned())
}

fn get_author() -> &'static str {
static AUTHOR : std::sync::OnceLock<String> = std::sync::OnceLock::new();
static AUTHOR: std::sync::OnceLock<String> = std::sync::OnceLock::new();

AUTHOR.get_or_init(||{

env!("CARGO_PKG_AUTHORS").to_owned()
})
AUTHOR.get_or_init(|| env!("CARGO_PKG_AUTHORS").to_owned())
}

/// A Python module implemented in Rust.
#[pymodule]
fn resvg_py(_py: Python, m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add("__version__",get_version())?;
m.add("__version__", get_version())?;
m.add("__author__", get_author())?;
m.add_function(wrap_pyfunction!(svg_to_bytes, m)?)?;
m.add_function(wrap_pyfunction!(_svg_to_bytes, m)?)?;
Ok(())
}
8 changes: 4 additions & 4 deletions tests/font/test_font_render.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
def test_with_system_font():
path = os.path.join(BASE_DIR, "ink.svg")
base = base64.b64encode(
bytes(resvg_py.svg_to_bytes(svg_path=path)),
bytes(resvg_py.svg_to_bytes(path)),
).decode("utf-8")

assert base == output_dict["svg_font_output"]
Expand All @@ -27,7 +27,7 @@ def test_with_system_font():
def test_without_system_font():
path = os.path.join(BASE_DIR, "ink.svg")
base = base64.b64encode(
bytes(resvg_py.svg_to_bytes(svg_path=path, skip_system_fonts=True))
bytes(resvg_py.svg_to_bytes(path, skip_system_fonts=True))
).decode("utf-8")
assert base == output_dict["svg_without_font_output"]

Expand All @@ -38,7 +38,7 @@ def test_wtih_kokoro_font():
base = base64.b64encode(
bytes(
resvg_py.svg_to_bytes(
svg_path=path, skip_system_fonts=True, font_files=[font]
path, skip_system_fonts=True, font_files=[font]
)
)
).decode("utf-8")
Expand All @@ -52,7 +52,7 @@ def test_font_directory():
base = base64.b64encode(
bytes(
resvg_py.svg_to_bytes(
svg_path=path, skip_system_fonts=True, font_dirs=[font]
path, skip_system_fonts=True, font_dirs=[font]
)
)
).decode("utf-8")
Expand Down
2 changes: 1 addition & 1 deletion tests/layer/test_multiple_layer_svg.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@

def test_multiple_layer_svg():
assert (
base64.b64encode(bytes(resvg_py.svg_to_bytes(svg_string=svg_string))).decode(
base64.b64encode(bytes(resvg_py.svg_to_bytes(svg_string))).decode(
"utf-8"
)
== output_dict["multi_layer"]
Expand Down
4 changes: 2 additions & 2 deletions tests/path/test_path.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@

def test_path():
path = os.path.join(BASE_DIR, "acid.svg")
base = base64.b64encode(bytes(resvg_py.svg_to_bytes(svg_path=path))).decode("utf-8")
base = base64.b64encode(bytes(resvg_py.svg_to_bytes(path))).decode("utf-8")
assert base == svg_output


def test_gzip_path():
path = os.path.join(BASE_DIR, "acid.svg.gz")
base = base64.b64encode(bytes(resvg_py.svg_to_bytes(svg_path=path))).decode("utf-8")
base = base64.b64encode(bytes(resvg_py.svg_to_bytes(path))).decode("utf-8")
assert base == svg_output
4 changes: 2 additions & 2 deletions tests/shape/test_complex_camera.py

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion tests/shape/test_normal_shape.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

def test_rectangle():
_base64 = base64.b64encode(
bytes(resvg_py.svg_to_bytes(svg_string=svg_string))
bytes(resvg_py.svg_to_bytes(svg_string))
).decode("utf-8")
assert (
_base64
Expand Down
2 changes: 1 addition & 1 deletion tests/version/test_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@


def test_version_is_string():
assert isinstance(resvg_py.__version__,str)
assert isinstance(resvg_py.__version__, str)

0 comments on commit 3af66ac

Please sign in to comment.