librsvg source for verification 2026-05-22
1
rsvg_convert/COPYING.LIB
Symbolic link
@@ -0,0 +1 @@
|
||||
../COPYING.LIB
|
||||
74
rsvg_convert/Cargo.toml
Normal file
@@ -0,0 +1,74 @@
|
||||
[package]
|
||||
name = "rsvg_convert"
|
||||
version.workspace = true
|
||||
authors.workspace = true
|
||||
description.workspace = true
|
||||
license.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
|
||||
# So that we can use an rsvg-convert name instead of the default rsvg_convert
|
||||
autobins = false
|
||||
|
||||
[package.metadata.system-deps]
|
||||
cairo-pdf = { version = "1.18", optional = true }
|
||||
cairo-ps = { version = "1.18", optional = true }
|
||||
cairo-svg = { version = "1.18", optional = true }
|
||||
|
||||
[dependencies]
|
||||
cairo-rs = { workspace = true, features = [
|
||||
"v1_18",
|
||||
"v1_16",
|
||||
"pdf",
|
||||
"ps",
|
||||
"svg",
|
||||
] }
|
||||
cast.workspace = true
|
||||
chrono = { workspace = true, default-features = false, features = [
|
||||
"clock",
|
||||
"std",
|
||||
] }
|
||||
clap = { workspace = true, features = ["cargo", "derive"] } # rsvg-convert
|
||||
clap_complete.workspace = true # rsvg-convert
|
||||
cssparser.workspace = true
|
||||
gio.workspace = true
|
||||
glib.workspace = true
|
||||
libc.workspace = true
|
||||
librsvg.workspace = true
|
||||
librsvg-c.workspace = true
|
||||
pango.workspace = true
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
gio-unix.workspace = true
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
gio-win32.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
assert_cmd.workspace = true
|
||||
predicates.workspace = true
|
||||
tempfile.workspace = true
|
||||
url.workspace = true
|
||||
lopdf.workspace = true
|
||||
png.workspace = true
|
||||
float-cmp.workspace = true
|
||||
librsvg = { workspace = true, features = ["test-utils"] }
|
||||
|
||||
[build-dependencies]
|
||||
system-deps.workspace = true
|
||||
|
||||
[lints.rust]
|
||||
unexpected_cfgs = { level = "warn", check-cfg = [
|
||||
'cfg(system_deps_have_cairo_pdf)',
|
||||
'cfg(system_deps_have_cairo_ps)',
|
||||
'cfg(system_deps_have_cairo_svg)',
|
||||
] }
|
||||
|
||||
[features]
|
||||
avif = ["librsvg/avif"]
|
||||
|
||||
[[bin]]
|
||||
name = "rsvg-convert"
|
||||
path = "src/main.rs"
|
||||
3
rsvg_convert/build.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
fn main() {
|
||||
system_deps::Config::new().probe().unwrap();
|
||||
}
|
||||
54
rsvg_convert/meson.build
Normal file
@@ -0,0 +1,54 @@
|
||||
rsvg_convert_files = files(
|
||||
'Cargo.toml',
|
||||
'src/main.rs',
|
||||
'tests/internal_predicates/file.rs',
|
||||
'tests/internal_predicates/mod.rs',
|
||||
'tests/internal_predicates/pdf.rs',
|
||||
'tests/internal_predicates/png.rs',
|
||||
'tests/internal_predicates/svg.rs',
|
||||
'tests/rsvg_convert.rs',
|
||||
)
|
||||
|
||||
rsvg_convert_output = ['rsvg-convert@0@'.format(ext_exe)]
|
||||
|
||||
if get_option('debug') and is_msvc_style
|
||||
rsvg_convert_output += ['rsvg-convert.pdb']
|
||||
endif
|
||||
|
||||
rsvg_convert = custom_target(
|
||||
'rsvg-convert',
|
||||
build_by_default: true,
|
||||
output: rsvg_convert_output,
|
||||
console: true,
|
||||
install: true,
|
||||
install_dir: get_option('prefix') / get_option('bindir'),
|
||||
depends: librsvg_rust_dep,
|
||||
depend_files: rsvg_convert_files,
|
||||
env: extra_env,
|
||||
command: [
|
||||
cargo_wrapper,
|
||||
cargo_wrapper_args,
|
||||
'--command=build',
|
||||
'--current-build-dir', '@OUTDIR@',
|
||||
'--current-source-dir', meson.current_source_dir(),
|
||||
'--packages', 'rsvg_convert',
|
||||
'--bin', 'rsvg-convert'
|
||||
] + avif_feature_args,
|
||||
)
|
||||
|
||||
if build_tests
|
||||
test(
|
||||
'Rust tests (rsvg_convert)',
|
||||
cargo_wrapper,
|
||||
timeout: -1, # no timeout
|
||||
args: [
|
||||
cargo_wrapper_args,
|
||||
'--current-build-dir', meson.current_build_dir(),
|
||||
'--command=test',
|
||||
'--current-source-dir', meson.current_source_dir(),
|
||||
'--packages', 'rsvg_convert',
|
||||
] + avif_feature_args,
|
||||
env: extra_env,
|
||||
depends: rsvg_convert
|
||||
)
|
||||
endif
|
||||
333
rsvg_convert/src/lib.rs
Normal file
@@ -0,0 +1,333 @@
|
||||
//! This crate exists only to enable testing (by the `ci/check-rsvg-convert-options`
|
||||
//! crate) that rsvg-convert's CLI options are fully and properly documented in its
|
||||
//! man page (`rsvg-convert.rst`).
|
||||
|
||||
use clap::crate_version;
|
||||
use clap_complete::Shell;
|
||||
|
||||
use rsvg::LengthUnit;
|
||||
use rsvg::rsvg_convert_only::{
|
||||
CssLength, Horizontal, Normalize, Parse, Signed, Unsigned, Validate, Vertical,
|
||||
};
|
||||
|
||||
use std::ffi::OsString;
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub fn build_cli() -> clap::Command {
|
||||
let supported_formats = vec![
|
||||
"png",
|
||||
#[cfg(system_deps_have_cairo_pdf)]
|
||||
"pdf",
|
||||
#[cfg(system_deps_have_cairo_pdf)]
|
||||
"pdf1.7",
|
||||
#[cfg(system_deps_have_cairo_pdf)]
|
||||
"pdf1.6",
|
||||
#[cfg(system_deps_have_cairo_pdf)]
|
||||
"pdf1.5",
|
||||
#[cfg(system_deps_have_cairo_pdf)]
|
||||
"pdf1.4",
|
||||
#[cfg(system_deps_have_cairo_ps)]
|
||||
"ps",
|
||||
#[cfg(system_deps_have_cairo_ps)]
|
||||
"eps",
|
||||
#[cfg(system_deps_have_cairo_svg)]
|
||||
"svg",
|
||||
];
|
||||
|
||||
// If any change is made to these options, please update `rsvg-convert.rst`
|
||||
// (in the repository root) accordingly, run:
|
||||
//
|
||||
// $ cargo run -p ci --bin check-rsvg-convert-options
|
||||
//
|
||||
// and make the neccesary corrections, if there are errors.
|
||||
|
||||
clap::Command::new("rsvg-convert")
|
||||
.version(concat!("version ", crate_version!()))
|
||||
.about("Convert SVG files to other image formats")
|
||||
.disable_version_flag(true)
|
||||
.disable_help_flag(true)
|
||||
.arg(
|
||||
clap::Arg::new("help")
|
||||
.short('?')
|
||||
.long("help")
|
||||
.help("Display the help")
|
||||
.action(clap::ArgAction::Help)
|
||||
)
|
||||
.arg(
|
||||
clap::Arg::new("version")
|
||||
.short('v')
|
||||
.long("version")
|
||||
.help("Display the version information")
|
||||
.action(clap::ArgAction::SetTrue),
|
||||
)
|
||||
.arg(
|
||||
clap::Arg::new("res_x")
|
||||
.short('d')
|
||||
.long("dpi-x")
|
||||
.num_args(1)
|
||||
.value_name("number")
|
||||
.default_value("96")
|
||||
.value_parser(parse_resolution)
|
||||
.help("Pixels per inch")
|
||||
.action(clap::ArgAction::Set),
|
||||
)
|
||||
.arg(
|
||||
clap::Arg::new("res_y")
|
||||
.short('p')
|
||||
.long("dpi-y")
|
||||
.num_args(1)
|
||||
.value_name("number")
|
||||
.default_value("96")
|
||||
.value_parser(parse_resolution)
|
||||
.help("Pixels per inch")
|
||||
.action(clap::ArgAction::Set),
|
||||
)
|
||||
.arg(
|
||||
clap::Arg::new("zoom_x")
|
||||
.short('x')
|
||||
.long("x-zoom")
|
||||
.num_args(1)
|
||||
.value_name("number")
|
||||
.conflicts_with("zoom")
|
||||
.value_parser(parse_zoom_factor)
|
||||
.help("Horizontal zoom factor")
|
||||
.action(clap::ArgAction::Set),
|
||||
)
|
||||
.arg(
|
||||
clap::Arg::new("zoom_y")
|
||||
.short('y')
|
||||
.long("y-zoom")
|
||||
.num_args(1)
|
||||
.value_name("number")
|
||||
.conflicts_with("zoom")
|
||||
.value_parser(parse_zoom_factor)
|
||||
.help("Vertical zoom factor")
|
||||
.action(clap::ArgAction::Set),
|
||||
)
|
||||
.arg(
|
||||
clap::Arg::new("zoom")
|
||||
.short('z')
|
||||
.long("zoom")
|
||||
.num_args(1)
|
||||
.value_name("number")
|
||||
.value_parser(parse_zoom_factor)
|
||||
.help("Zoom factor")
|
||||
.action(clap::ArgAction::Set),
|
||||
)
|
||||
.arg(
|
||||
clap::Arg::new("size_x")
|
||||
.short('w')
|
||||
.long("width")
|
||||
.num_args(1)
|
||||
.value_name("length")
|
||||
.value_parser(parse_length::<Horizontal, Unsigned>)
|
||||
.help("Width [defaults to the width of the SVG]")
|
||||
.action(clap::ArgAction::Set),
|
||||
)
|
||||
.arg(
|
||||
clap::Arg::new("size_y")
|
||||
.short('h')
|
||||
.long("height")
|
||||
.num_args(1)
|
||||
.value_name("length")
|
||||
.value_parser(parse_length::<Vertical, Unsigned>)
|
||||
.help("Height [defaults to the height of the SVG]")
|
||||
.action(clap::ArgAction::Set),
|
||||
)
|
||||
.arg(
|
||||
clap::Arg::new("top")
|
||||
.long("top")
|
||||
.num_args(1)
|
||||
.value_name("length")
|
||||
.value_parser(parse_length::<Vertical, Signed>)
|
||||
.help("Distance between top edge of page and the image [defaults to 0]")
|
||||
.action(clap::ArgAction::Set),
|
||||
)
|
||||
.arg(
|
||||
clap::Arg::new("left")
|
||||
.long("left")
|
||||
.num_args(1)
|
||||
.value_name("length")
|
||||
.value_parser(parse_length::<Horizontal, Signed>)
|
||||
.help("Distance between left edge of page and the image [defaults to 0]")
|
||||
.action(clap::ArgAction::Set),
|
||||
)
|
||||
.arg(
|
||||
clap::Arg::new("page_width")
|
||||
.long("page-width")
|
||||
.num_args(1)
|
||||
.value_name("length")
|
||||
.value_parser(parse_length::<Horizontal, Unsigned>)
|
||||
.help("Width of output media [defaults to the width of the SVG]")
|
||||
.action(clap::ArgAction::Set),
|
||||
)
|
||||
.arg(
|
||||
clap::Arg::new("page_height")
|
||||
.long("page-height")
|
||||
.num_args(1)
|
||||
.value_name("length")
|
||||
.value_parser(parse_length::<Vertical, Unsigned>)
|
||||
.help("Height of output media [defaults to the height of the SVG]")
|
||||
.action(clap::ArgAction::Set),
|
||||
)
|
||||
.arg(
|
||||
clap::Arg::new("format")
|
||||
.short('f')
|
||||
.long("format")
|
||||
.num_args(1)
|
||||
.value_parser(clap::builder::PossibleValuesParser::new(supported_formats.as_slice()))
|
||||
.ignore_case(true)
|
||||
.default_value("png")
|
||||
.help("Output format")
|
||||
.action(clap::ArgAction::Set),
|
||||
)
|
||||
.arg(
|
||||
clap::Arg::new("output")
|
||||
.short('o')
|
||||
.long("output")
|
||||
.num_args(1)
|
||||
.value_parser(clap::value_parser!(PathBuf))
|
||||
.value_name("filename")
|
||||
.help("Output filename [defaults to stdout]")
|
||||
.action(clap::ArgAction::Set),
|
||||
)
|
||||
.arg(
|
||||
clap::Arg::new("export_id")
|
||||
.short('i')
|
||||
.long("export-id")
|
||||
.value_parser(clap::builder::NonEmptyStringValueParser::new())
|
||||
.value_name("object-id")
|
||||
.help("SVG id of object to export [default is to export all objects]")
|
||||
.action(clap::ArgAction::Set),
|
||||
)
|
||||
.arg(
|
||||
clap::Arg::new("accept-language")
|
||||
.short('l')
|
||||
.long("accept-language")
|
||||
.value_parser(clap::builder::NonEmptyStringValueParser::new())
|
||||
.value_name("language-tags")
|
||||
.help("Languages to accept, for example \"es-MX,de,en\" [default uses language from the environment]")
|
||||
.action(clap::ArgAction::Set),
|
||||
)
|
||||
.arg(
|
||||
clap::Arg::new("keep_aspect")
|
||||
.short('a')
|
||||
.long("keep-aspect-ratio")
|
||||
.help("Preserve the aspect ratio")
|
||||
.action(clap::ArgAction::SetTrue),
|
||||
)
|
||||
.arg(
|
||||
clap::Arg::new("background")
|
||||
.short('b')
|
||||
.long("background-color")
|
||||
.num_args(1)
|
||||
.value_name("color")
|
||||
.value_parser(clap::builder::NonEmptyStringValueParser::new())
|
||||
.default_value("none")
|
||||
.help("Set the background color using a CSS color spec")
|
||||
.action(clap::ArgAction::Set),
|
||||
)
|
||||
.arg(
|
||||
clap::Arg::new("stylesheet")
|
||||
.short('s')
|
||||
.long("stylesheet")
|
||||
.num_args(1)
|
||||
.value_parser(clap::value_parser!(PathBuf))
|
||||
.value_name("filename.css")
|
||||
.help("Filename of CSS stylesheet to apply")
|
||||
.action(clap::ArgAction::Set),
|
||||
)
|
||||
.arg(
|
||||
clap::Arg::new("unlimited")
|
||||
.short('u')
|
||||
.long("unlimited")
|
||||
.help("Allow huge SVG files")
|
||||
.action(clap::ArgAction::SetTrue),
|
||||
)
|
||||
.arg(
|
||||
clap::Arg::new("keep_image_data")
|
||||
.long("keep-image-data")
|
||||
.help("Keep image data")
|
||||
.conflicts_with("no_keep_image_data")
|
||||
.action(clap::ArgAction::SetTrue),
|
||||
)
|
||||
.arg(
|
||||
clap::Arg::new("no_keep_image_data")
|
||||
.long("no-keep-image-data")
|
||||
.help("Do not keep image data")
|
||||
.conflicts_with("keep_image_data")
|
||||
.action(clap::ArgAction::SetTrue),
|
||||
)
|
||||
.arg(
|
||||
clap::Arg::new("testing")
|
||||
.long("testing")
|
||||
.help("Render images for librsvg's test suite")
|
||||
.hide(true)
|
||||
.action(clap::ArgAction::SetTrue),
|
||||
)
|
||||
.arg(
|
||||
clap::Arg::new("completion")
|
||||
.long("completion")
|
||||
.help("Output shell completion for the given shell")
|
||||
.num_args(1)
|
||||
.action(clap::ArgAction::Set)
|
||||
.value_parser(clap::value_parser!(Shell))
|
||||
.value_name("shell-name"),
|
||||
)
|
||||
.arg(
|
||||
clap::Arg::new("FILE")
|
||||
.value_parser(clap::value_parser!(OsString))
|
||||
.help("The input file(s) to convert, you can use - for stdin")
|
||||
.num_args(1..)
|
||||
.action(clap::ArgAction::Append),
|
||||
)
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Resolution(pub f64);
|
||||
|
||||
fn parse_resolution(v: &str) -> Result<Resolution, String> {
|
||||
match v.parse::<f64>() {
|
||||
Ok(res) if res > 0.0 => Ok(Resolution(res)),
|
||||
Ok(_) => Err(String::from("Invalid resolution")),
|
||||
Err(e) => Err(format!("{e}")),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct ZoomFactor(pub f64);
|
||||
|
||||
fn parse_zoom_factor(v: &str) -> Result<ZoomFactor, String> {
|
||||
match v.parse::<f64>() {
|
||||
Ok(res) if res > 0.0 => Ok(ZoomFactor(res)),
|
||||
Ok(_) => Err(String::from("Invalid zoom factor")),
|
||||
Err(e) => Err(format!("{e}")),
|
||||
}
|
||||
}
|
||||
|
||||
fn is_absolute_unit(u: LengthUnit) -> bool {
|
||||
use LengthUnit::*;
|
||||
|
||||
match u {
|
||||
Percent | Em | Ex | Ch => false,
|
||||
Px | In | Cm | Mm | Pt | Pc => true,
|
||||
|
||||
// coverage: the following is because LengthUnit is marked non_exhaustive, but
|
||||
// the cases above should really test all the units librsvg knows about.
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_length<N: Normalize, V: Validate>(s: &str) -> Result<CssLength<N, V>, String> {
|
||||
<CssLength<N, V> as Parse>::parse_str(s)
|
||||
.map_err(|_| format!("Invalid value: The argument '{s}' can not be parsed as a length"))
|
||||
.and_then(|l| {
|
||||
if is_absolute_unit(l.unit) {
|
||||
Ok(l)
|
||||
} else {
|
||||
Err(format!(
|
||||
"Invalid value '{s}': supported units are px, in, cm, mm, pt, pc"
|
||||
))
|
||||
}
|
||||
})
|
||||
}
|
||||
1342
rsvg_convert/src/main.rs
Normal file
6
rsvg_convert/tests/fixtures/a-link.svg
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="400" height="400" xmlns="http://www.w3.org/2000/svg">
|
||||
<a href="https://example.com">
|
||||
<rect x="100" y="100" width="200" height="200" fill="lime"/>
|
||||
</a>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 217 B |
BIN
rsvg_convert/tests/fixtures/accept-language-de.png
vendored
Normal file
|
After Width: | Height: | Size: 173 B |
BIN
rsvg_convert/tests/fixtures/accept-language-es.png
vendored
Normal file
|
After Width: | Height: | Size: 172 B |
BIN
rsvg_convert/tests/fixtures/accept-language-fallback.png
vendored
Normal file
|
After Width: | Height: | Size: 173 B |
7
rsvg_convert/tests/fixtures/accept-language.svg
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100">
|
||||
<switch allowReorder="yes">
|
||||
<rect systemLanguage="de" fill="red" width="10" height="10" />
|
||||
<rect systemLanguage="es" fill="lime" width="10" height="10" />
|
||||
<rect fill="yellow" id="rect3" width="10" height="10" />
|
||||
</switch>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 311 B |
4
rsvg_convert/tests/fixtures/bug521-with-viewbox.svg
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="200" height="100" viewBox="0 0 2000 1000">
|
||||
<rect id="foo" x="500" y="600" width="700" height="800"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 161 B |
11
rsvg_convert/tests/fixtures/bug591-vbox-overflow.svg
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg ion="1.1" baseProfile="b" id="svg-root"
|
||||
width="100%" height="100%" viewBox="0 0 4822222222222222222220 360"
|
||||
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="httk">
|
||||
<g>
|
||||
<g opacity="0.5">
|
||||
<rect x="60" y="230" width="80" height="40" fill="+0000ff" opacity=".5"/>
|
||||
<rect x="71" y="240" width="80" height="40" fill="#00ff00" opacity=".5"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 410 B |
BIN
rsvg_convert/tests/fixtures/bug601-zero-stroke-width-render-only-foo.png
vendored
Normal file
|
After Width: | Height: | Size: 95 B |
6
rsvg_convert/tests/fixtures/bug601-zero-stroke-width.svg
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100">
|
||||
<g id="foo">
|
||||
<rect x="50" y="50" width="10" height="10" style="stroke-width: 0; stroke: black; fill: blue"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 234 B |
7
rsvg_convert/tests/fixtures/bug677-partial-pixel.svg
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1.2" height="1.8">
|
||||
<!-- Output should be an image 2x2 pixels in size, with partial coverage for the pixels
|
||||
on the right and bottom.
|
||||
-->
|
||||
<rect x="0" y="0" width="1.2" height="1.8" fill="lime"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 300 B |
4
rsvg_convert/tests/fixtures/dimensions-in.svg
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1in" height="1in" viewBox="0 0 1 1">
|
||||
<rect x="0" y="0" width="1" height="1" fill="blue"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 185 B |
5
rsvg_convert/tests/fixtures/dpi.svg
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1in" height="4in" viewBox="0 0 100 400">
|
||||
<rect id="one" x="0" y="0" width="100" height="200" fill="rgb(0,255,0)"/>
|
||||
<rect id="two" x="0" y="200" width="100" height="200" fill="rgb(0,0,255)"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 288 B |
3
rsvg_convert/tests/fixtures/empty-10x10.svg
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="10" height="10">
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 110 B |
0
rsvg_convert/tests/fixtures/empty.css
vendored
Normal file
3
rsvg_convert/tests/fixtures/empty.svg
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 52 B |
5
rsvg_convert/tests/fixtures/example.svg
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="400" viewBox="0 0 100 400">
|
||||
<rect id="one" x="0" y="0" width="100" height="200" fill="rgb(0,255,0)"/>
|
||||
<rect id="two" x="0" y="200" width="100" height="200" fill="rgb(0,0,255)"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 288 B |
6
rsvg_convert/tests/fixtures/geometry-element.svg
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100">
|
||||
<g transform="rotate(45)" stroke-width="10" stroke="#000000">
|
||||
<rect id="foo" x="10" y="20" width="30" height="40" fill="#0000ff"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 256 B |
BIN
rsvg_convert/tests/fixtures/gimp-wilber-ref.png
vendored
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
978
rsvg_convert/tests/fixtures/gimp-wilber.svg
vendored
Normal file
@@ -0,0 +1,978 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://web.resource.org/cc/"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
inkscape:export-ydpi="120"
|
||||
inkscape:export-xdpi="120"
|
||||
inkscape:export-filename="/home/jimmac/src/cvs/gnome/gimp/themes/Default/images/stock-wilber-64.png"
|
||||
width="48px"
|
||||
height="48px"
|
||||
id="svg11300"
|
||||
sodipodi:version="0.32"
|
||||
inkscape:version="0.44+devel"
|
||||
sodipodi:docbase="/home/jimmac/src/cvs/gnome/gimp/themes/Default/images"
|
||||
sodipodi:docname="stock-wilber.svg">
|
||||
<defs
|
||||
id="defs3">
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient8534"
|
||||
id="linearGradient8951"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="26.162951"
|
||||
y1="30.543303"
|
||||
x2="24.328892"
|
||||
y2="30.985245" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient2446"
|
||||
id="linearGradient8949"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.755165,0.395143,-0.395143,0.755165,-25.91245,6.532586)"
|
||||
x1="13.236155"
|
||||
y1="37.752247"
|
||||
x2="7.7521091"
|
||||
y2="42.282146" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient6963"
|
||||
id="radialGradient8947"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.572694,0,0,1.532639,-55.36682,-21.35823)"
|
||||
cx="15.415101"
|
||||
cy="35.356506"
|
||||
fx="15.415101"
|
||||
fy="35.356506"
|
||||
r="7.5791559" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient6939"
|
||||
id="linearGradient8945"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(-46.40695,-1.802856)"
|
||||
x1="19.394735"
|
||||
y1="30.001331"
|
||||
x2="23.109331"
|
||||
y2="33.438831" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient6951"
|
||||
id="linearGradient8943"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(-49.30496,1.877723)"
|
||||
x1="37.017639"
|
||||
y1="19.239889"
|
||||
x2="27.753893"
|
||||
y2="11.182488" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient6468"
|
||||
id="radialGradient8941"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.318488,0,0,1.318488,-22.1264,-6.241691)"
|
||||
cx="69.473244"
|
||||
cy="19.597878"
|
||||
fx="69.473244"
|
||||
fy="19.597878"
|
||||
r="3.5153139" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient6452"
|
||||
id="linearGradient8939"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="6.3051529"
|
||||
y1="23.362427"
|
||||
x2="5.9846287"
|
||||
y2="31.57" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient8542"
|
||||
id="radialGradient8937"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.916159,9.318684e-2,-0.108765,1.069309,3.253668,-3.029272)"
|
||||
cx="6.0242186"
|
||||
cy="25.271027"
|
||||
fx="6.0242186"
|
||||
fy="25.271027"
|
||||
r="4.8310289" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient8524"
|
||||
id="linearGradient8935"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="14.96875"
|
||||
y1="19.110678"
|
||||
x2="39.524544"
|
||||
y2="46.98568" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient6482"
|
||||
id="linearGradient8933"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(-3.836549,0.345971)"
|
||||
x1="32.350136"
|
||||
y1="28.083355"
|
||||
x2="21.213203"
|
||||
y2="30.293064" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient6500"
|
||||
id="radialGradient8931"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.77275,0,0,1.29668,-16.3404,-6.615959)"
|
||||
cx="18.557627"
|
||||
cy="22.300018"
|
||||
fx="18.557627"
|
||||
fy="22.300018"
|
||||
r="19.2292" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient6492"
|
||||
id="radialGradient8929"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1,0,0,0.284247,0,24.29088)"
|
||||
cx="23"
|
||||
cy="33.9375"
|
||||
fx="23"
|
||||
fy="33.9375"
|
||||
r="18.25" />
|
||||
<linearGradient
|
||||
id="linearGradient8542">
|
||||
<stop
|
||||
style="stop-color:#5b676b;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop8544" />
|
||||
<stop
|
||||
style="stop-color:#141718;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop8546" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient8534">
|
||||
<stop
|
||||
style="stop-color:black;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop8536" />
|
||||
<stop
|
||||
style="stop-color:black;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop8538" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient8524">
|
||||
<stop
|
||||
style="stop-color:white;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop8526" />
|
||||
<stop
|
||||
style="stop-color:white;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop8528" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient2446">
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop2448" />
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop2450" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient6963">
|
||||
<stop
|
||||
style="stop-color:#696969;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop6965" />
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop6967" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient6939">
|
||||
<stop
|
||||
style="stop-color:#bdbdbd;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop6941" />
|
||||
<stop
|
||||
id="stop6947"
|
||||
offset="0.33333334"
|
||||
style="stop-color:#e2e2e2;stop-opacity:1;" />
|
||||
<stop
|
||||
style="stop-color:#a3a3a3;stop-opacity:1;"
|
||||
offset="0.66666669"
|
||||
id="stop6949" />
|
||||
<stop
|
||||
style="stop-color:#dddddd;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop6943" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient6951">
|
||||
<stop
|
||||
style="stop-color:#6e3d09;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop6953" />
|
||||
<stop
|
||||
id="stop6959"
|
||||
offset="0.24242425"
|
||||
style="stop-color:#ea8113;stop-opacity:1;" />
|
||||
<stop
|
||||
style="stop-color:#5c3307;stop-opacity:1;"
|
||||
offset="0.62121212"
|
||||
id="stop6961" />
|
||||
<stop
|
||||
style="stop-color:#e07c12;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop6955" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient6500">
|
||||
<stop
|
||||
style="stop-color:#857c63;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop6502" />
|
||||
<stop
|
||||
style="stop-color:#221f19;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop6504" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient6492">
|
||||
<stop
|
||||
style="stop-color:black;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop6494" />
|
||||
<stop
|
||||
style="stop-color:black;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop6496" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient6482">
|
||||
<stop
|
||||
style="stop-color:black;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop6484" />
|
||||
<stop
|
||||
style="stop-color:black;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop6486" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient6468">
|
||||
<stop
|
||||
style="stop-color:white;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop6470" />
|
||||
<stop
|
||||
style="stop-color:#b9b9b0;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop6472" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient6452">
|
||||
<stop
|
||||
style="stop-color:white;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop6454" />
|
||||
<stop
|
||||
style="stop-color:white;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop6456" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient11520">
|
||||
<stop
|
||||
id="stop11522"
|
||||
offset="0.0000000"
|
||||
style="stop-color:#ffffff;stop-opacity:1.0000000;" />
|
||||
<stop
|
||||
id="stop11524"
|
||||
offset="1.0000000"
|
||||
style="stop-color:#dcdcdc;stop-opacity:1.0000000;" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient11508"
|
||||
inkscape:collect="always">
|
||||
<stop
|
||||
id="stop11510"
|
||||
offset="0"
|
||||
style="stop-color:#000000;stop-opacity:1;" />
|
||||
<stop
|
||||
id="stop11512"
|
||||
offset="1"
|
||||
style="stop-color:#000000;stop-opacity:0;" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient11494"
|
||||
inkscape:collect="always">
|
||||
<stop
|
||||
id="stop11496"
|
||||
offset="0"
|
||||
style="stop-color:#ef2929;stop-opacity:1;" />
|
||||
<stop
|
||||
id="stop11498"
|
||||
offset="1"
|
||||
style="stop-color:#ef2929;stop-opacity:0;" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient11415">
|
||||
<stop
|
||||
id="stop11417"
|
||||
offset="0.0000000"
|
||||
style="stop-color:#204a87;stop-opacity:0.0000000;" />
|
||||
<stop
|
||||
style="stop-color:#204a87;stop-opacity:1.0000000;"
|
||||
offset="0.50000000"
|
||||
id="stop11423" />
|
||||
<stop
|
||||
id="stop11419"
|
||||
offset="1"
|
||||
style="stop-color:#204a87;stop-opacity:0;" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient11399"
|
||||
inkscape:collect="always">
|
||||
<stop
|
||||
id="stop11401"
|
||||
offset="0"
|
||||
style="stop-color:#000000;stop-opacity:1;" />
|
||||
<stop
|
||||
id="stop11403"
|
||||
offset="1"
|
||||
style="stop-color:#000000;stop-opacity:0;" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
gradientTransform="translate(-60.28571,-0.285714)"
|
||||
y2="34.462429"
|
||||
x2="43.615788"
|
||||
y1="3.7744560"
|
||||
x1="15.828360"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient11425"
|
||||
xlink:href="#linearGradient11415"
|
||||
inkscape:collect="always" />
|
||||
<linearGradient
|
||||
gradientTransform="translate(-60.57143,0.000000)"
|
||||
y2="39.033859"
|
||||
x2="35.679932"
|
||||
y1="9.3458843"
|
||||
x1="9.6957054"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient11427"
|
||||
xlink:href="#linearGradient11415"
|
||||
inkscape:collect="always" />
|
||||
<linearGradient
|
||||
y2="33.462429"
|
||||
x2="26.758644"
|
||||
y1="19.774456"
|
||||
x1="13.267134"
|
||||
gradientTransform="translate(-60.85714,0.428571)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient11439"
|
||||
xlink:href="#linearGradient11415"
|
||||
inkscape:collect="always" />
|
||||
<radialGradient
|
||||
r="8.5000000"
|
||||
fy="39.142857"
|
||||
fx="12.071428"
|
||||
cy="39.142857"
|
||||
cx="12.071428"
|
||||
gradientTransform="matrix(1.000000,0.000000,0.000000,0.487395,0.000000,20.06483)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient11441"
|
||||
xlink:href="#linearGradient11399"
|
||||
inkscape:collect="always" />
|
||||
<radialGradient
|
||||
gradientTransform="matrix(1.243453,2.106784e-16,-2.106784e-16,1.243453,-6.713754,-3.742847)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
r="3.8335034"
|
||||
fy="15.048258"
|
||||
fx="27.577173"
|
||||
cy="15.048258"
|
||||
cx="27.577173"
|
||||
id="radialGradient11500"
|
||||
xlink:href="#linearGradient11494"
|
||||
inkscape:collect="always" />
|
||||
<radialGradient
|
||||
r="3.8335034"
|
||||
fy="16.049133"
|
||||
fx="27.577173"
|
||||
cy="16.049133"
|
||||
cx="27.577173"
|
||||
gradientTransform="matrix(1.243453,2.106784e-16,-2.106784e-16,1.243453,-6.713754,-3.742847)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient11504"
|
||||
xlink:href="#linearGradient11494"
|
||||
inkscape:collect="always" />
|
||||
<radialGradient
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.000000,0.000000,0.000000,0.338462,2.166583e-14,29.48178)"
|
||||
r="6.5659914"
|
||||
fy="44.565483"
|
||||
fx="30.203562"
|
||||
cy="44.565483"
|
||||
cx="30.203562"
|
||||
id="radialGradient11514"
|
||||
xlink:href="#linearGradient11508"
|
||||
inkscape:collect="always" />
|
||||
<radialGradient
|
||||
gradientTransform="matrix(1.995058,-1.651527e-32,0.000000,1.995058,-24.32488,-35.70087)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
r="20.530962"
|
||||
fy="35.878170"
|
||||
fx="24.445690"
|
||||
cy="35.878170"
|
||||
cx="24.445690"
|
||||
id="radialGradient11526"
|
||||
xlink:href="#linearGradient11520"
|
||||
inkscape:collect="always" />
|
||||
<radialGradient
|
||||
r="6.5659914"
|
||||
fy="44.565483"
|
||||
fx="30.203562"
|
||||
cy="44.565483"
|
||||
cx="30.203562"
|
||||
gradientTransform="matrix(1.000000,0.000000,0.000000,0.338462,3.185827e-15,29.48178)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient11532"
|
||||
xlink:href="#linearGradient11508"
|
||||
inkscape:collect="always" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient11508"
|
||||
id="radialGradient1348"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.000000,0.000000,0.000000,0.338462,-1.353344e-14,29.48178)"
|
||||
cx="30.203562"
|
||||
cy="44.565483"
|
||||
fx="30.203562"
|
||||
fy="44.565483"
|
||||
r="6.5659914" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient11520"
|
||||
id="radialGradient1350"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.995058,-1.651527e-32,0.000000,1.995058,-24.32488,-35.70087)"
|
||||
cx="24.445690"
|
||||
cy="35.878170"
|
||||
fx="24.445690"
|
||||
fy="35.878170"
|
||||
r="20.530962" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient11494"
|
||||
id="radialGradient1352"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.243453,2.106784e-16,-2.106784e-16,1.243453,-6.713754,-3.742847)"
|
||||
cx="27.577173"
|
||||
cy="16.049133"
|
||||
fx="27.577173"
|
||||
fy="16.049133"
|
||||
r="3.8335034" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient11494"
|
||||
id="radialGradient1354"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.243453,2.106784e-16,-2.106784e-16,1.243453,-6.713754,-3.742847)"
|
||||
cx="27.577173"
|
||||
cy="15.048258"
|
||||
fx="27.577173"
|
||||
fy="15.048258"
|
||||
r="3.8335034" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient11508"
|
||||
id="radialGradient1356"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.000000,0.000000,0.000000,0.338462,2.220359e-14,29.48178)"
|
||||
cx="30.203562"
|
||||
cy="44.565483"
|
||||
fx="30.203562"
|
||||
fy="44.565483"
|
||||
r="6.5659914" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient11520"
|
||||
id="radialGradient1366"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(2.049266,-1.696401e-32,0.000000,2.049266,-25.65002,-37.31089)"
|
||||
cx="24.445690"
|
||||
cy="35.878170"
|
||||
fx="24.445690"
|
||||
fy="35.878170"
|
||||
r="20.530962" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient6452"
|
||||
id="linearGradient6458"
|
||||
x1="6.3051529"
|
||||
y1="23.362427"
|
||||
x2="5.9846287"
|
||||
y2="31.57"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient6468"
|
||||
id="radialGradient6474"
|
||||
cx="69.473244"
|
||||
cy="19.597878"
|
||||
fx="69.473244"
|
||||
fy="19.597878"
|
||||
r="3.5153138"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.318488,1.207574e-15,-1.207574e-15,1.318488,-22.1264,-6.241691)" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient6482"
|
||||
id="linearGradient6488"
|
||||
x1="32.350136"
|
||||
y1="28.083355"
|
||||
x2="21.213203"
|
||||
y2="30.293064"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(-3.836549,0.345971)" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient6492"
|
||||
id="radialGradient6498"
|
||||
cx="23"
|
||||
cy="33.9375"
|
||||
fx="23"
|
||||
fy="33.9375"
|
||||
r="18.25"
|
||||
gradientTransform="matrix(1,0,0,0.284247,0,24.29088)"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient6500"
|
||||
id="radialGradient8522"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.77275,-1.847562e-16,1.351402e-16,1.29668,-16.3404,-6.615959)"
|
||||
cx="18.557627"
|
||||
cy="22.300018"
|
||||
fx="18.557627"
|
||||
fy="22.300018"
|
||||
r="19.2292" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient8524"
|
||||
id="linearGradient8530"
|
||||
x1="14.96875"
|
||||
y1="19.110678"
|
||||
x2="39.524544"
|
||||
y2="46.98568"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient8534"
|
||||
id="linearGradient8540"
|
||||
x1="26.162951"
|
||||
y1="30.543303"
|
||||
x2="24.328892"
|
||||
y2="30.985245"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient8542"
|
||||
id="radialGradient8548"
|
||||
cx="6.0242186"
|
||||
cy="25.271027"
|
||||
fx="6.0242186"
|
||||
fy="25.271027"
|
||||
r="4.8310288"
|
||||
gradientTransform="matrix(0.916159,9.318684e-2,-0.108765,1.069309,3.253668,-3.029272)"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient8534"
|
||||
id="linearGradient4467"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="26.162951"
|
||||
y1="30.543303"
|
||||
x2="24.328892"
|
||||
y2="30.985245" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient6492"
|
||||
id="radialGradient2360"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1,0,0,0.284247,0,24.29088)"
|
||||
cx="23"
|
||||
cy="33.9375"
|
||||
fx="23"
|
||||
fy="33.9375"
|
||||
r="18.25" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient6500"
|
||||
id="radialGradient2362"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.77275,0,0,1.29668,-16.3404,-6.615959)"
|
||||
cx="18.557627"
|
||||
cy="22.300018"
|
||||
fx="18.557627"
|
||||
fy="22.300018"
|
||||
r="19.2292" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient6482"
|
||||
id="linearGradient2364"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(-3.83655,0.345971)"
|
||||
x1="32.350136"
|
||||
y1="28.083355"
|
||||
x2="21.213203"
|
||||
y2="30.293064" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient8524"
|
||||
id="linearGradient2366"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="14.96875"
|
||||
y1="19.110678"
|
||||
x2="39.524544"
|
||||
y2="46.98568" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient8542"
|
||||
id="radialGradient2368"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.916159,9.318684e-2,-0.108765,1.069309,3.253668,-3.029272)"
|
||||
cx="6.0242186"
|
||||
cy="25.271027"
|
||||
fx="6.0242186"
|
||||
fy="25.271027"
|
||||
r="4.8310288" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient6452"
|
||||
id="linearGradient2370"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="6.3051529"
|
||||
y1="23.362427"
|
||||
x2="5.9846287"
|
||||
y2="31.57" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient6468"
|
||||
id="radialGradient2372"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.318488,0,0,1.318488,-22.1264,-6.241691)"
|
||||
cx="69.473244"
|
||||
cy="19.597878"
|
||||
fx="69.473244"
|
||||
fy="19.597878"
|
||||
r="3.5153138" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient6951"
|
||||
id="linearGradient2374"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(-0.117687,-0.528197,0.528197,-0.117687,46.2238,49.69112)"
|
||||
x1="37.017639"
|
||||
y1="19.239889"
|
||||
x2="27.753893"
|
||||
y2="11.182488" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient6939"
|
||||
id="linearGradient2376"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(-0.117687,-0.528197,0.528197,-0.117687,40.56367,46.8748)"
|
||||
x1="19.394735"
|
||||
y1="30.001331"
|
||||
x2="23.109331"
|
||||
y2="33.438831" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient6963"
|
||||
id="radialGradient2378"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(-9.389073e-2,-1.406075,1.370264,-9.149987e-2,18.28382,59.78933)"
|
||||
cx="15.415101"
|
||||
cy="35.356506"
|
||||
fx="15.415101"
|
||||
fy="35.356506"
|
||||
r="7.5791561" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient2446"
|
||||
id="linearGradient2380"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.11984,-0.445379,0.445379,0.11984,42.0839,34.30798)"
|
||||
x1="13.236155"
|
||||
y1="37.752247"
|
||||
x2="7.7521091"
|
||||
y2="42.282146" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
stroke="#ef2929"
|
||||
fill="#eeeeec"
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666"
|
||||
borderopacity="0.15294118"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="1"
|
||||
inkscape:cx="-31.831093"
|
||||
inkscape:cy="9.6042458"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:grid-bbox="true"
|
||||
inkscape:document-units="px"
|
||||
inkscape:showpageshadow="false"
|
||||
inkscape:window-width="1191"
|
||||
inkscape:window-height="1078"
|
||||
inkscape:window-x="269"
|
||||
inkscape:window-y="51" />
|
||||
<metadata
|
||||
id="metadata4">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:creator>
|
||||
<cc:Agent>
|
||||
<dc:title>Jakub Steiner</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:creator>
|
||||
<dc:source>http://jimmac.musichall.cz</dc:source>
|
||||
<cc:license
|
||||
rdf:resource="http://creativecommons.org/licenses/by-sa/2.0/" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
<cc:License
|
||||
rdf:about="http://creativecommons.org/licenses/by-sa/2.0/">
|
||||
<cc:permits
|
||||
rdf:resource="http://web.resource.org/cc/Reproduction" />
|
||||
<cc:permits
|
||||
rdf:resource="http://web.resource.org/cc/Distribution" />
|
||||
<cc:requires
|
||||
rdf:resource="http://web.resource.org/cc/Notice" />
|
||||
<cc:requires
|
||||
rdf:resource="http://web.resource.org/cc/Attribution" />
|
||||
<cc:permits
|
||||
rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
|
||||
<cc:requires
|
||||
rdf:resource="http://web.resource.org/cc/ShareAlike" />
|
||||
</cc:License>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
id="layer1"
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer">
|
||||
<g
|
||||
transform="matrix(1.016627,0,0,1.016627,0.434805,-0.792136)"
|
||||
id="g8883">
|
||||
<path
|
||||
sodipodi:type="arc"
|
||||
style="opacity:1;color:black;fill:url(#radialGradient8929);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.9999997;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:10;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
|
||||
id="path8885"
|
||||
sodipodi:cx="23"
|
||||
sodipodi:cy="33.9375"
|
||||
sodipodi:rx="18.25"
|
||||
sodipodi:ry="5.1875"
|
||||
d="M 41.25 33.9375 A 18.25 5.1875 0 1 1 4.75,33.9375 A 18.25 5.1875 0 1 1 41.25 33.9375 z"
|
||||
transform="matrix(1,0,0,1.53012,0.125,-19.99096)" />
|
||||
<path
|
||||
style="opacity:1;color:black;fill:url(#radialGradient8931);fill-opacity:1;fill-rule:evenodd;stroke:#2e3436;stroke-width:0.98364494;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:10;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
|
||||
d="M 10.374369,12.467884 C 10.374369,12.467884 13.248878,18.395518 19.973611,18.228291 C 34.066126,17.874738 36.309226,10.582719 37.832786,8.7660099 C 42.895143,51.417634 6.0135488,33.362123 4.7175144,26.256467 C 11.965359,24.135147 10.197592,20.069282 10.197592,20.069282 L 10.374369,12.467884 z "
|
||||
id="path8887"
|
||||
sodipodi:nodetypes="cscccc" />
|
||||
<path
|
||||
style="opacity:1;color:black;fill:url(#linearGradient8933);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.9999997;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:10;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
|
||||
d="M 15.73779,30.066049 C 22.47669,31.413886 25.908481,30.164142 27.916965,28.613273 C 27.386635,27.928263 26.480655,27.176962 26.480655,27.176962 C 26.480655,27.176962 28.833972,27.830904 29.662635,28.900535 C 30.488925,29.967103 29.969443,30.624242 29.753196,31.988905 C 29.271785,30.790306 28.373215,30.340813 28.251562,29.864573 C 26.445294,32.3615 21.94512,32.257773 15.73779,30.066049 z "
|
||||
id="path8889"
|
||||
sodipodi:nodetypes="cccsccc" />
|
||||
<path
|
||||
sodipodi:type="inkscape:offset"
|
||||
inkscape:radius="-1.073054"
|
||||
inkscape:original="M 37.5 8.75 C 37.304927 8.7198838 37.083027 8.9384197 36.90625 9.46875 C 36.552697 10.529411 34.061264 17.865197 19.96875 18.21875 C 13.244017 18.385977 10.375 12.46875 10.375 12.46875 L 10.1875 20.0625 C 10.1875 20.0625 11.966595 24.128679 4.71875 26.25 C 6.014785 33.355656 42.502444 51.25055 37.90625 9.53125 C 37.843713 9.0411177 37.695073 8.7801162 37.5 8.75 z "
|
||||
xlink:href="#path4323"
|
||||
style="opacity:0.18539327;color:black;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient8935);stroke-width:0.98364494;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:10;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
|
||||
id="path8891"
|
||||
inkscape:href="#path4323"
|
||||
d="M 36.96875,11.84375 C 36.406772,12.770645 35.562258,13.876916 34.28125,14.9375 C 31.649332,17.116542 27.230687,19.099847 20,19.28125 C 15.775627,19.386299 13.047259,17.347101 11.375,15.53125 L 11.25,20 C 11.386107,20.418802 11.665455,21.390498 11.1875,22.71875 C 10.673186,24.148046 9.0329864,25.610113 6.21875,26.71875 C 6.4690804,27.240783 6.7142344,27.76237 7.46875,28.5 C 8.4967004,29.504945 9.9257833,30.588049 11.625,31.5625 C 15.023433,33.511402 19.426583,35.055712 23.53125,35.125 C 27.635917,35.194288 31.388376,33.89045 33.96875,30.125 C 36.347494,26.653782 37.651223,20.777057 36.96875,11.84375 z " />
|
||||
<path
|
||||
sodipodi:type="arc"
|
||||
style="opacity:1;color:black;fill:url(#radialGradient8937);fill-opacity:1;fill-rule:evenodd;stroke:black;stroke-width:0.98364455;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:10;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
|
||||
id="path8893"
|
||||
sodipodi:cx="6.0987959"
|
||||
sodipodi:cy="27.228739"
|
||||
sodipodi:rx="4.3310289"
|
||||
sodipodi:ry="6.0987959"
|
||||
d="M 10.429825 27.228739 A 4.3310289 6.0987959 0 1 1 1.767767,27.228739 A 4.3310289 6.0987959 0 1 1 10.429825 27.228739 z"
|
||||
transform="matrix(0.810984,-0.585069,0.585069,0.810984,-14.77791,6.947121)" />
|
||||
<path
|
||||
sodipodi:type="inkscape:offset"
|
||||
inkscape:radius="-1.044355"
|
||||
inkscape:original="M 6.09375 21.125 C 3.703022 21.125 1.78125 23.852215 1.78125 27.21875 C 1.78125 30.585285 3.703022 33.312501 6.09375 33.3125 C 8.484478 33.3125 10.4375 30.585285 10.4375 27.21875 C 10.4375 23.852215 8.484478 21.124999 6.09375 21.125 z "
|
||||
xlink:href="#path5198"
|
||||
style="opacity:0.28089887;color:black;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient8939);stroke-width:0.98364493;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:10;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
|
||||
id="path8895"
|
||||
inkscape:href="#path5198"
|
||||
d="M 6.09375,22.15625 C 5.2955008,22.15625 4.5406196,22.602421 3.90625,23.5 C 3.2718804,24.397579 2.8125,25.734204 2.8125,27.21875 C 2.8125,28.703296 3.2718804,30.039921 3.90625,30.9375 C 4.5406196,31.835079 5.2955011,32.28125 6.09375,32.28125 C 6.8919992,32.28125 7.6710339,31.804861 8.3125,30.90625 C 8.9539661,30.007639 9.40625,28.700064 9.40625,27.21875 C 9.40625,25.737436 8.9539662,24.429861 8.3125,23.53125 C 7.6710338,22.632639 6.8919989,22.15625 6.09375,22.15625 z "
|
||||
transform="matrix(0.800389,-0.599481,0.599481,0.800389,-15.2744,7.32784)" />
|
||||
<path
|
||||
sodipodi:type="arc"
|
||||
style="opacity:1;color:black;fill:white;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.9999997;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:10;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
|
||||
id="path8897"
|
||||
sodipodi:cx="4.0658641"
|
||||
sodipodi:cy="23.251263"
|
||||
sodipodi:rx="1.767767"
|
||||
sodipodi:ry="2.1213202"
|
||||
d="M 5.833631 23.251263 A 1.767767 2.1213202 0 1 1 2.2980971,23.251263 A 1.767767 2.1213202 0 1 1 5.833631 23.251263 z" />
|
||||
<path
|
||||
sodipodi:type="arc"
|
||||
style="opacity:1;color:black;fill:#eeeeec;fill-opacity:1;fill-rule:evenodd;stroke:#888a85;stroke-width:0.98364494;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:10;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
|
||||
id="path8899"
|
||||
sodipodi:cx="69.473244"
|
||||
sodipodi:cy="21.837049"
|
||||
sodipodi:rx="3.1819806"
|
||||
sodipodi:ry="3.1819806"
|
||||
d="M 72.655224 21.837049 A 3.1819806 3.1819806 0 1 1 66.291263,21.837049 A 3.1819806 3.1819806 0 1 1 72.655224 21.837049 z"
|
||||
transform="translate(-55.86145,0)" />
|
||||
<path
|
||||
transform="matrix(1.5,0,0,1.5,-82.16821,-10.91852)"
|
||||
d="M 72.655224 21.837049 A 3.1819806 3.1819806 0 1 1 66.291263,21.837049 A 3.1819806 3.1819806 0 1 1 72.655224 21.837049 z"
|
||||
sodipodi:ry="3.1819806"
|
||||
sodipodi:rx="3.1819806"
|
||||
sodipodi:cy="21.837049"
|
||||
sodipodi:cx="69.473244"
|
||||
id="path8901"
|
||||
style="opacity:1;color:black;fill:url(#radialGradient8941);fill-opacity:1;fill-rule:evenodd;stroke:#888a85;stroke-width:0.65576329;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:10;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
|
||||
sodipodi:type="arc" />
|
||||
<path
|
||||
sodipodi:type="arc"
|
||||
style="opacity:1;color:black;fill:#2e3436;fill-opacity:1;fill-rule:evenodd;stroke:black;stroke-width:0.9999997;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:10;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
|
||||
id="path8903"
|
||||
sodipodi:cx="21.743534"
|
||||
sodipodi:cy="21.837049"
|
||||
sodipodi:rx="2.2980971"
|
||||
sodipodi:ry="2.2980971"
|
||||
d="M 24.041631 21.837049 A 2.2980971 2.2980971 0 1 1 19.445437,21.837049 A 2.2980971 2.2980971 0 1 1 24.041631 21.837049 z"
|
||||
transform="translate(1.414216,0.707108)" />
|
||||
<path
|
||||
sodipodi:type="arc"
|
||||
style="opacity:1;color:black;fill:white;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.9999997;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:10;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
|
||||
id="path8905"
|
||||
sodipodi:cx="21.38998"
|
||||
sodipodi:cy="20.953165"
|
||||
sodipodi:rx="1.767767"
|
||||
sodipodi:ry="1.767767"
|
||||
d="M 23.157747 20.953165 A 1.767767 1.767767 0 1 1 19.622213,20.953165 A 1.767767 1.767767 0 1 1 23.157747 20.953165 z"
|
||||
transform="matrix(0.9,0,0,0.9,3.022883,2.625648)" />
|
||||
<path
|
||||
d="M 24.041631 21.837049 A 2.2980971 2.2980971 0 1 1 19.445437,21.837049 A 2.2980971 2.2980971 0 1 1 24.041631 21.837049 z"
|
||||
sodipodi:ry="2.2980971"
|
||||
sodipodi:rx="2.2980971"
|
||||
sodipodi:cy="21.837049"
|
||||
sodipodi:cx="21.743534"
|
||||
id="path8907"
|
||||
style="opacity:1;color:black;fill:#2e3436;fill-opacity:1;fill-rule:evenodd;stroke:black;stroke-width:1.62499952;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:10;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
|
||||
sodipodi:type="arc"
|
||||
transform="matrix(0.615385,0,0,0.615385,1.258896,8.840808)" />
|
||||
<path
|
||||
transform="matrix(0.553846,0,0,0.553846,2.248846,10.02145)"
|
||||
d="M 23.157747 20.953165 A 1.767767 1.767767 0 1 1 19.622213,20.953165 A 1.767767 1.767767 0 1 1 23.157747 20.953165 z"
|
||||
sodipodi:ry="1.767767"
|
||||
sodipodi:rx="1.767767"
|
||||
sodipodi:cy="20.953165"
|
||||
sodipodi:cx="21.38998"
|
||||
id="path8909"
|
||||
style="opacity:1;color:black;fill:white;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.9999997;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:10;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
|
||||
sodipodi:type="arc" />
|
||||
<g
|
||||
id="g8911"
|
||||
transform="matrix(-0.114852,-0.389864,0.389864,-0.114852,24.71507,29.68942)">
|
||||
<path
|
||||
sodipodi:nodetypes="ccssc"
|
||||
inkscape:r_cy="true"
|
||||
inkscape:r_cx="true"
|
||||
id="path8913"
|
||||
d="M -23.364209,23.568336 L -19.654111,27.037511 C -11.307128,20.656664 -2.5600338,6.9381374 -2.5600338,6.9381374 C -0.58371893,4.6957251 -2.6769537,3.1876548 -4.7249404,4.5402186 C -4.7249404,4.5402186 -17.695303,14.655085 -23.364209,23.568336 z "
|
||||
style="opacity:1;color:black;fill:url(#linearGradient8943);fill-opacity:1;fill-rule:nonzero;stroke:#673907;stroke-width:2.42021061;stroke-linecap:round;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
|
||||
<path
|
||||
sodipodi:nodetypes="ccccccccc"
|
||||
inkscape:r_cy="true"
|
||||
inkscape:r_cx="true"
|
||||
id="path8915"
|
||||
d="M -30.449715,32.813894 L -28.203395,34.747714 L -19.710629,27.380683 L -19.435568,26.674855 L -18.341163,26.65704 C -18.778663,25.09454 -21.282677,22.273585 -23.157677,22.273585 L -23.075658,23.36366 L -23.745649,23.743687 L -30.449715,32.813894 z "
|
||||
style="opacity:1;color:black;fill:url(#linearGradient8945);fill-opacity:1;fill-rule:nonzero;stroke:#888a85;stroke-width:2.42021061;stroke-linecap:round;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
|
||||
<path
|
||||
inkscape:r_cy="true"
|
||||
inkscape:r_cx="true"
|
||||
id="path8917"
|
||||
d="M -23.801371,28.376767 L -28.166304,33.643238"
|
||||
style="opacity:1;color:black;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:white;stroke-width:2.46045327;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
|
||||
<path
|
||||
sodipodi:nodetypes="cssc"
|
||||
inkscape:r_cy="true"
|
||||
inkscape:r_cx="true"
|
||||
id="path8919"
|
||||
d="M -45.341208,42.769835 C -34.89725,42.769835 -26.971169,44.013565 -25.470603,36.415097 C -24.261354,30.291783 -32.681137,27.357729 -36.853473,32.824236 C -40.87275,38.090207 -45.341208,42.769835 -45.341208,42.769835 z "
|
||||
style="opacity:1;color:black;fill:url(#radialGradient8947);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
|
||||
<path
|
||||
transform="matrix(2.069903,0,0,2.069903,-44.75012,-41.50978)"
|
||||
inkscape:r_cy="true"
|
||||
inkscape:r_cx="true"
|
||||
d="M 8.8749989 37.75 A 1.2499999 1.2499999 0 1 1 6.3749992,37.75 A 1.2499999 1.2499999 0 1 1 8.8749989 37.75 z"
|
||||
sodipodi:ry="1.2499999"
|
||||
sodipodi:rx="1.2499999"
|
||||
sodipodi:cy="37.75"
|
||||
sodipodi:cx="7.624999"
|
||||
id="path8921"
|
||||
style="opacity:0.52777782;color:black;fill:white;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
|
||||
sodipodi:type="arc" />
|
||||
<path
|
||||
sodipodi:type="arc"
|
||||
style="opacity:1;color:black;fill:white;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
|
||||
id="path8923"
|
||||
sodipodi:cx="7.624999"
|
||||
sodipodi:cy="37.75"
|
||||
sodipodi:rx="1.2499999"
|
||||
sodipodi:ry="1.2499999"
|
||||
d="M 8.8749989 37.75 A 1.2499999 1.2499999 0 1 1 6.3749992,37.75 A 1.2499999 1.2499999 0 1 1 8.8749989 37.75 z"
|
||||
inkscape:r_cx="true"
|
||||
inkscape:r_cy="true"
|
||||
transform="matrix(1.396669,0,0,1.396669,-32.05526,-25.87664)" />
|
||||
<path
|
||||
sodipodi:nodetypes="cssc"
|
||||
inkscape:r_cy="true"
|
||||
inkscape:r_cx="true"
|
||||
id="path8925"
|
||||
d="M -38.543723,40.909242 C -38.543723,40.909242 -34.822203,41.003542 -32.427185,39.497247 C -31.579834,38.964324 -30.911411,40.147232 -31.933366,40.584614 C -34.14076,41.529346 -38.543723,40.909242 -38.543723,40.909242 z "
|
||||
style="opacity:0.42777776;color:black;fill:url(#linearGradient8949);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" />
|
||||
</g>
|
||||
<path
|
||||
style="opacity:1;color:black;fill:url(#linearGradient8951);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.9999997;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:10;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
|
||||
d="M 23.003067,31.736544 C 24.500439,31.879636 25.852696,31.464331 26.41496,31.262497 C 26.513185,30.707111 26.951512,29.64124 28.461048,29.571029 L 27.930718,28.642952 C 27.930718,28.642952 25.964077,29.990873 23.864854,30.388621 L 23.003067,31.736544 z "
|
||||
id="path8927"
|
||||
sodipodi:nodetypes="cccccc" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 41 KiB |
11
rsvg_convert/tests/fixtures/hello-world.svg
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="500" height="500">
|
||||
<style>
|
||||
text {
|
||||
font: 50px Sans;
|
||||
fill: black;
|
||||
}
|
||||
</style>
|
||||
|
||||
<text x="50" y="100">Hello world!</text>
|
||||
<text transform="translate(50, 200) rotate(45)">Hello again!</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 267 B |
BIN
rsvg_convert/tests/fixtures/lime-ref.png
vendored
Normal file
|
After Width: | Height: | Size: 94 B |
BIN
rsvg_convert/tests/fixtures/lime-transparent-ref.png
vendored
Normal file
|
After Width: | Height: | Size: 99 B |
BIN
rsvg_convert/tests/fixtures/offset-png.png
vendored
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
13
rsvg_convert/tests/fixtures/sub-rect-no-unit.svg
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://web.resource.org/cc/"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
width="123pt"
|
||||
height="123pt"
|
||||
id="svg1">
|
||||
<rect id="rect-no-unit" width="44" height="45" x="46" y="47"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 455 B |
14
rsvg_convert/tests/fixtures/text-a-link.svg
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="400" height="400" xmlns="http://www.w3.org/2000/svg">
|
||||
<text x="100" y="100">
|
||||
<a href="https://example.com">
|
||||
This is a link to example.com
|
||||
</a>
|
||||
|
||||
<tspan x="100" dy="2em">
|
||||
<a href="https://another.example.com">
|
||||
This is a link to another.example.com
|
||||
</a>
|
||||
</tspan>
|
||||
</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 372 B |
BIN
rsvg_convert/tests/fixtures/zero-offset-png.png
vendored
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
28
rsvg_convert/tests/internal_predicates/file.rs
Normal file
@@ -0,0 +1,28 @@
|
||||
use predicates::prelude::*;
|
||||
use predicates::str::StartsWithPredicate;
|
||||
|
||||
use super::pdf::PdfPredicate;
|
||||
use super::png::PngPredicate;
|
||||
use super::svg::SvgPredicate;
|
||||
|
||||
/// Predicates to check that some output ([u8]) is of a certain file type
|
||||
|
||||
pub fn is_png() -> PngPredicate {
|
||||
PngPredicate {}
|
||||
}
|
||||
|
||||
pub fn is_ps() -> StartsWithPredicate {
|
||||
predicate::str::starts_with("%!PS-Adobe-3.0\n")
|
||||
}
|
||||
|
||||
pub fn is_eps() -> StartsWithPredicate {
|
||||
predicate::str::starts_with("%!PS-Adobe-3.0 EPSF-3.0\n")
|
||||
}
|
||||
|
||||
pub fn is_pdf() -> PdfPredicate {
|
||||
PdfPredicate {}
|
||||
}
|
||||
|
||||
pub fn is_svg() -> SvgPredicate {
|
||||
SvgPredicate {}
|
||||
}
|
||||
4
rsvg_convert/tests/internal_predicates/mod.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
pub mod file;
|
||||
mod pdf;
|
||||
mod png;
|
||||
mod svg;
|
||||
355
rsvg_convert/tests/internal_predicates/pdf.rs
Normal file
@@ -0,0 +1,355 @@
|
||||
use chrono::{DateTime, Local, Utc};
|
||||
use float_cmp::approx_eq;
|
||||
use lopdf::{self, Dictionary, Object};
|
||||
use predicates::prelude::*;
|
||||
use predicates::reflection::{Case, Child, PredicateReflection, Product};
|
||||
use std::cmp;
|
||||
use std::fmt;
|
||||
|
||||
/// Checks that the variable of type [u8] can be parsed as a PDF file.
|
||||
#[derive(Debug)]
|
||||
pub struct PdfPredicate {}
|
||||
|
||||
impl PdfPredicate {
|
||||
pub fn with_page_count(self, num_pages: usize) -> DetailPredicate<Self> {
|
||||
DetailPredicate::<Self> {
|
||||
p: self,
|
||||
d: Detail::PageCount(num_pages),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_page_size(
|
||||
self,
|
||||
idx: usize,
|
||||
width_in_points: f32,
|
||||
height_in_points: f32,
|
||||
) -> DetailPredicate<Self> {
|
||||
DetailPredicate::<Self> {
|
||||
p: self,
|
||||
d: Detail::PageSize(
|
||||
Dimensions {
|
||||
w: width_in_points,
|
||||
h: height_in_points,
|
||||
unit: 1.0,
|
||||
},
|
||||
idx,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_creation_date(self, when: DateTime<Utc>) -> DetailPredicate<Self> {
|
||||
DetailPredicate::<Self> {
|
||||
p: self,
|
||||
d: Detail::CreationDate(when),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_link(self, link: &str) -> DetailPredicate<Self> {
|
||||
DetailPredicate::<Self> {
|
||||
p: self,
|
||||
d: Detail::Link(link.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_text(self, text: &str) -> DetailPredicate<Self> {
|
||||
DetailPredicate::<Self> {
|
||||
p: self,
|
||||
d: Detail::Text(text.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_version(self, version: &str) -> DetailPredicate<Self> {
|
||||
DetailPredicate::<Self> {
|
||||
p: self,
|
||||
d: Detail::Version(version.to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Predicate<[u8]> for PdfPredicate {
|
||||
fn eval(&self, data: &[u8]) -> bool {
|
||||
lopdf::Document::load_mem(data).is_ok()
|
||||
}
|
||||
|
||||
fn find_case<'a>(&'a self, _expected: bool, data: &[u8]) -> Option<Case<'a>> {
|
||||
match lopdf::Document::load_mem(data) {
|
||||
Ok(_) => None,
|
||||
Err(e) => Some(Case::new(Some(self), false).add_product(Product::new("Error", e))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PredicateReflection for PdfPredicate {}
|
||||
|
||||
impl fmt::Display for PdfPredicate {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "is a PDF")
|
||||
}
|
||||
}
|
||||
|
||||
/// Extends a PdfPredicate by a check for page count, page size or creation date.
|
||||
#[derive(Debug)]
|
||||
pub struct DetailPredicate<PdfPredicate> {
|
||||
p: PdfPredicate,
|
||||
d: Detail,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Detail {
|
||||
PageCount(usize),
|
||||
PageSize(Dimensions, usize),
|
||||
CreationDate(DateTime<Utc>),
|
||||
Link(String),
|
||||
Text(String),
|
||||
Version(String),
|
||||
}
|
||||
|
||||
/// A PDF page's dimensions from its `MediaBox`.
|
||||
///
|
||||
/// Note that `w` and `h` given in `UserUnit`, which is by default 1.0 = 1/72 inch.
|
||||
#[derive(Debug)]
|
||||
struct Dimensions {
|
||||
w: f32,
|
||||
h: f32,
|
||||
unit: f32, // UserUnit, in points (1/72 of an inch)
|
||||
}
|
||||
|
||||
impl Dimensions {
|
||||
pub fn from_media_box(obj: &lopdf::Object, unit: Option<f32>) -> lopdf::Result<Dimensions> {
|
||||
let a = obj.as_array()?;
|
||||
Ok(Dimensions {
|
||||
w: a[2].as_float()?,
|
||||
h: a[3].as_float()?,
|
||||
unit: unit.unwrap_or(1.0),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn width_in_pt(&self) -> f32 {
|
||||
self.w * self.unit
|
||||
}
|
||||
|
||||
pub fn height_in_pt(&self) -> f32 {
|
||||
self.h * self.unit
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Dimensions {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{} pt x {} pt", self.width_in_pt(), self.height_in_pt())
|
||||
}
|
||||
}
|
||||
|
||||
impl cmp::PartialEq for Dimensions {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
approx_eq!(
|
||||
f32,
|
||||
self.width_in_pt(),
|
||||
other.width_in_pt(),
|
||||
epsilon = 0.0001
|
||||
) && approx_eq!(
|
||||
f32,
|
||||
self.height_in_pt(),
|
||||
other.height_in_pt(),
|
||||
epsilon = 0.0001
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl cmp::Eq for Dimensions {}
|
||||
|
||||
trait Details {
|
||||
fn get_page_count(&self) -> usize;
|
||||
fn get_page_size(&self, idx: usize) -> Option<Dimensions>;
|
||||
fn get_creation_date(&self) -> Option<DateTime<Utc>>;
|
||||
fn get_from_trailer<'a>(&'a self, key: &[u8]) -> lopdf::Result<&'a lopdf::Object>;
|
||||
fn get_from_page<'a>(&'a self, idx: usize, key: &[u8]) -> lopdf::Result<&'a lopdf::Object>;
|
||||
}
|
||||
|
||||
impl DetailPredicate<PdfPredicate> {
|
||||
fn eval_doc(&self, doc: &lopdf::Document) -> bool {
|
||||
match &self.d {
|
||||
Detail::PageCount(n) => doc.get_page_count() == *n,
|
||||
Detail::PageSize(d, idx) => doc.get_page_size(*idx).map_or(false, |dim| dim == *d),
|
||||
Detail::CreationDate(d) => doc.get_creation_date().map_or(false, |date| date == *d),
|
||||
Detail::Link(link) => document_has_link(doc, link),
|
||||
Detail::Text(text) => document_has_text(doc, text),
|
||||
Detail::Version(version) => document_has_version(doc, version),
|
||||
}
|
||||
}
|
||||
|
||||
fn find_case_for_doc<'a>(&'a self, expected: bool, doc: &lopdf::Document) -> Option<Case<'a>> {
|
||||
if self.eval_doc(doc) == expected {
|
||||
let product = self.product_for_doc(doc);
|
||||
Some(Case::new(Some(self), false).add_product(product))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn product_for_doc(&self, doc: &lopdf::Document) -> Product {
|
||||
match &self.d {
|
||||
Detail::PageCount(_) => Product::new(
|
||||
"actual page count",
|
||||
format!("{} page(s)", doc.get_page_count()),
|
||||
),
|
||||
Detail::PageSize(_, idx) => Product::new(
|
||||
"actual page size",
|
||||
match doc.get_page_size(*idx) {
|
||||
Some(dim) => format!("{}", dim),
|
||||
None => "None".to_string(),
|
||||
},
|
||||
),
|
||||
Detail::CreationDate(_) => Product::new(
|
||||
"actual creation date",
|
||||
format!("{:?}", doc.get_creation_date()),
|
||||
),
|
||||
Detail::Link(_) => Product::new(
|
||||
"actual link contents",
|
||||
"FIXME: who knows, but it's not what we expected".to_string(),
|
||||
),
|
||||
Detail::Text(_) => {
|
||||
Product::new("actual text contents", doc.extract_text(&[1]).unwrap())
|
||||
}
|
||||
Detail::Version(_) => Product::new("actual version contents", doc.version.to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Details for lopdf::Document {
|
||||
fn get_page_count(&self) -> usize {
|
||||
self.get_pages().len()
|
||||
}
|
||||
|
||||
fn get_page_size(&self, idx: usize) -> Option<Dimensions> {
|
||||
match self.get_from_page(idx, b"MediaBox") {
|
||||
Ok(obj) => {
|
||||
let unit = self
|
||||
.get_from_page(idx, b"UserUnit")
|
||||
.and_then(Object::as_float)
|
||||
.ok();
|
||||
Dimensions::from_media_box(obj, unit).ok()
|
||||
}
|
||||
Err(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_creation_date(&self) -> Option<DateTime<Utc>> {
|
||||
match self.get_from_trailer(b"CreationDate") {
|
||||
Ok(obj) => match obj.as_datetime() {
|
||||
Some(d) => {
|
||||
let local_datetime = DateTime::<Local>::try_from(d).ok()?;
|
||||
Some(local_datetime.into())
|
||||
}
|
||||
|
||||
None => None,
|
||||
},
|
||||
|
||||
Err(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_from_trailer<'a>(&'a self, key: &[u8]) -> lopdf::Result<&'a lopdf::Object> {
|
||||
let id = self.trailer.get(b"Info")?.as_reference()?;
|
||||
self.get_object(id)?.as_dict()?.get(key)
|
||||
}
|
||||
|
||||
fn get_from_page<'a>(&'a self, idx: usize, key: &[u8]) -> lopdf::Result<&'a lopdf::Object> {
|
||||
let mut iter = self.page_iter();
|
||||
for _ in 0..idx {
|
||||
let _ = iter.next();
|
||||
}
|
||||
match iter.next() {
|
||||
Some(id) => self.get_object(id)?.as_dict()?.get(key),
|
||||
None => Err(lopdf::Error::PageNumberNotFound(idx as u32)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Predicate<[u8]> for DetailPredicate<PdfPredicate> {
|
||||
fn eval(&self, data: &[u8]) -> bool {
|
||||
match lopdf::Document::load_mem(data) {
|
||||
Ok(doc) => self.eval_doc(&doc),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn find_case<'a>(&'a self, expected: bool, data: &[u8]) -> Option<Case<'a>> {
|
||||
match lopdf::Document::load_mem(data) {
|
||||
Ok(doc) => self.find_case_for_doc(expected, &doc),
|
||||
Err(e) => Some(Case::new(Some(self), false).add_product(Product::new("Error", e))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PredicateReflection for DetailPredicate<PdfPredicate> {
|
||||
fn children<'a>(&'a self) -> Box<dyn Iterator<Item = Child<'a>> + 'a> {
|
||||
let params = vec![Child::new("predicate", &self.p)];
|
||||
Box::new(params.into_iter())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for DetailPredicate<PdfPredicate> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match &self.d {
|
||||
Detail::PageCount(n) => write!(f, "is a PDF with {} page(s)", n),
|
||||
Detail::PageSize(d, _) => write!(f, "is a PDF sized {}", d),
|
||||
Detail::CreationDate(d) => write!(f, "is a PDF created {:?}", d),
|
||||
Detail::Link(l) => write!(f, "is a PDF with a link to {}", l),
|
||||
Detail::Text(t) => write!(f, "is a PDF with \"{}\" in its text content", t),
|
||||
Detail::Version(v) => write!(f, "is a PDF with version {}", v),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This is an extremely trivial test for a string being present in the document's
|
||||
// text objects.
|
||||
fn document_has_text(document: &lopdf::Document, needle: &str) -> bool {
|
||||
if let Ok(haystack) = text_from_first_page(document) {
|
||||
haystack.contains(needle)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn document_has_version(document: &lopdf::Document, version_to_search: &str) -> bool {
|
||||
document.version == version_to_search
|
||||
}
|
||||
|
||||
// We do a super simple test that a PDF actually contains an Annotation object
|
||||
// with a particular link. We don't test that this annotation is actually linked
|
||||
// from a page; that would be nicer.
|
||||
fn document_has_link(document: &lopdf::Document, link_text: &str) -> bool {
|
||||
document
|
||||
.objects
|
||||
.values()
|
||||
.any(|obj| object_is_annotation_with_link(obj, link_text))
|
||||
}
|
||||
|
||||
fn object_is_annotation_with_link(object: &Object, link_text: &str) -> bool {
|
||||
object
|
||||
.as_dict()
|
||||
.map(|dict| dict_is_annotation(dict) && dict_has_a_with_link(dict, link_text))
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
fn dict_is_annotation(dict: &Dictionary) -> bool {
|
||||
dict.get(b"Type")
|
||||
.and_then(|type_val| type_val.as_name())
|
||||
.map(|name| name == b"Annot")
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
fn dict_has_a_with_link(dict: &Dictionary, link_text: &str) -> bool {
|
||||
dict.get(b"A")
|
||||
.and_then(|obj| obj.as_dict())
|
||||
.and_then(|dict| dict.get(b"URI"))
|
||||
.and_then(|obj| obj.as_str())
|
||||
.map(|string| string == link_text.as_bytes())
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
fn text_from_first_page(doc: &lopdf::Document) -> lopdf::Result<String> {
|
||||
// This is extremely simplistic; lopdf just concatenates all the text in the page
|
||||
// into a single string.
|
||||
doc.extract_text(&[1])
|
||||
}
|
||||
191
rsvg_convert/tests/internal_predicates/png.rs
Normal file
@@ -0,0 +1,191 @@
|
||||
use predicates::prelude::*;
|
||||
use predicates::reflection::{Case, Child, PredicateReflection, Product};
|
||||
use std::fmt;
|
||||
use std::io::{BufReader, Cursor};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use rsvg::rsvg_convert_only::{SharedImageSurface, SurfaceType};
|
||||
|
||||
use rsvg::test_utils::compare_surfaces::BufferDiff;
|
||||
use rsvg::test_utils::reference_utils::{Compare, Deviation, Reference, surface_from_png};
|
||||
|
||||
/// Checks that the variable of type [u8] can be parsed as a PNG file.
|
||||
#[derive(Debug)]
|
||||
pub struct PngPredicate {}
|
||||
|
||||
impl PngPredicate {
|
||||
pub fn with_size(self, w: u32, h: u32) -> SizePredicate<Self> {
|
||||
SizePredicate::<Self> { p: self, w, h }
|
||||
}
|
||||
|
||||
pub fn with_contents<P: AsRef<Path>>(self, reference: P) -> ReferencePredicate<Self> {
|
||||
let mut path = PathBuf::new();
|
||||
path.push(reference);
|
||||
ReferencePredicate::<Self> { p: self, path }
|
||||
}
|
||||
}
|
||||
|
||||
impl Predicate<[u8]> for PngPredicate {
|
||||
fn eval(&self, data: &[u8]) -> bool {
|
||||
let decoder = png::Decoder::new(Cursor::new(data));
|
||||
decoder.read_info().is_ok()
|
||||
}
|
||||
|
||||
fn find_case<'a>(&'a self, _expected: bool, data: &[u8]) -> Option<Case<'a>> {
|
||||
let decoder = png::Decoder::new(Cursor::new(data));
|
||||
match decoder.read_info() {
|
||||
Ok(_) => None,
|
||||
Err(e) => Some(Case::new(Some(self), false).add_product(Product::new("Error", e))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PredicateReflection for PngPredicate {}
|
||||
|
||||
impl fmt::Display for PngPredicate {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "is a PNG")
|
||||
}
|
||||
}
|
||||
|
||||
/// Extends a PngPredicate by a check for a given size of the PNG file.
|
||||
#[derive(Debug)]
|
||||
pub struct SizePredicate<PngPredicate> {
|
||||
p: PngPredicate,
|
||||
w: u32,
|
||||
h: u32,
|
||||
}
|
||||
|
||||
impl SizePredicate<PngPredicate> {
|
||||
fn eval_info(&self, info: &png::Info) -> bool {
|
||||
info.width == self.w && info.height == self.h
|
||||
}
|
||||
|
||||
fn find_case_for_info<'a>(&'a self, expected: bool, info: &png::Info) -> Option<Case<'a>> {
|
||||
if self.eval_info(info) == expected {
|
||||
let product = self.product_for_info(info);
|
||||
Some(Case::new(Some(self), false).add_product(product))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn product_for_info(&self, info: &png::Info) -> Product {
|
||||
let actual_size = format!("{} x {}", info.width, info.height);
|
||||
Product::new("actual size", actual_size)
|
||||
}
|
||||
}
|
||||
|
||||
impl Predicate<[u8]> for SizePredicate<PngPredicate> {
|
||||
fn eval(&self, data: &[u8]) -> bool {
|
||||
let decoder = png::Decoder::new(Cursor::new(data));
|
||||
match decoder.read_info() {
|
||||
Ok(reader) => self.eval_info(reader.info()),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn find_case<'a>(&'a self, expected: bool, data: &[u8]) -> Option<Case<'a>> {
|
||||
let decoder = png::Decoder::new(Cursor::new(data));
|
||||
match decoder.read_info() {
|
||||
Ok(reader) => self.find_case_for_info(expected, reader.info()),
|
||||
Err(e) => Some(Case::new(Some(self), false).add_product(Product::new("Error", e))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PredicateReflection for SizePredicate<PngPredicate> {
|
||||
fn children<'a>(&'a self) -> Box<dyn Iterator<Item = Child<'a>> + 'a> {
|
||||
let params = vec![Child::new("predicate", &self.p)];
|
||||
Box::new(params.into_iter())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for SizePredicate<PngPredicate> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "is a PNG with size {} x {}", self.w, self.h)
|
||||
}
|
||||
}
|
||||
|
||||
/// Extends a PngPredicate by a comparison to the contents of a reference file
|
||||
#[derive(Debug)]
|
||||
pub struct ReferencePredicate<PngPredicate> {
|
||||
p: PngPredicate,
|
||||
path: PathBuf,
|
||||
}
|
||||
|
||||
impl ReferencePredicate<PngPredicate> {
|
||||
fn diff_acceptable(diff: &BufferDiff) -> bool {
|
||||
match diff {
|
||||
BufferDiff::DifferentSizes => false,
|
||||
BufferDiff::Diff(diff) => !diff.inacceptable(),
|
||||
}
|
||||
}
|
||||
|
||||
fn diff_surface(&self, surface: &SharedImageSurface) -> Option<BufferDiff> {
|
||||
let reference = Reference::from_png(&self.path);
|
||||
if let Ok(diff) = reference.compare(surface) {
|
||||
if !Self::diff_acceptable(&diff) {
|
||||
return Some(diff);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn find_case_for_surface<'a>(
|
||||
&'a self,
|
||||
expected: bool,
|
||||
surface: &SharedImageSurface,
|
||||
) -> Option<Case<'a>> {
|
||||
let diff = self.diff_surface(surface);
|
||||
if diff.is_some() != expected {
|
||||
let product = self.product_for_diff(&diff.unwrap());
|
||||
Some(Case::new(Some(self), false).add_product(product))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn product_for_diff(&self, diff: &BufferDiff) -> Product {
|
||||
let difference = format!("{}", diff);
|
||||
Product::new("images differ", difference)
|
||||
}
|
||||
}
|
||||
|
||||
impl Predicate<[u8]> for ReferencePredicate<PngPredicate> {
|
||||
fn eval(&self, data: &[u8]) -> bool {
|
||||
if let Ok(surface) = surface_from_png(&mut BufReader::new(data)) {
|
||||
let surface = SharedImageSurface::wrap(surface, SurfaceType::SRgb).unwrap();
|
||||
self.diff_surface(&surface).is_some()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn find_case<'a>(&'a self, expected: bool, data: &[u8]) -> Option<Case<'a>> {
|
||||
match surface_from_png(&mut BufReader::new(data)) {
|
||||
Ok(surface) => {
|
||||
let surface = SharedImageSurface::wrap(surface, SurfaceType::SRgb).unwrap();
|
||||
self.find_case_for_surface(expected, &surface)
|
||||
}
|
||||
Err(e) => Some(Case::new(Some(self), false).add_product(Product::new("Error", e))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PredicateReflection for ReferencePredicate<PngPredicate> {
|
||||
fn children<'a>(&'a self) -> Box<dyn Iterator<Item = Child<'a>> + 'a> {
|
||||
let params = vec![Child::new("predicate", &self.p)];
|
||||
Box::new(params.into_iter())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ReferencePredicate<PngPredicate> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"is a PNG that matches the reference {}",
|
||||
self.path.display()
|
||||
)
|
||||
}
|
||||
}
|
||||
175
rsvg_convert/tests/internal_predicates/svg.rs
Normal file
@@ -0,0 +1,175 @@
|
||||
use float_cmp::approx_eq;
|
||||
use gio::MemoryInputStream;
|
||||
use gio::glib::Bytes;
|
||||
use predicates::prelude::*;
|
||||
use predicates::reflection::{Case, Child, PredicateReflection, Product};
|
||||
use std::cmp;
|
||||
use std::fmt;
|
||||
|
||||
use rsvg::{CairoRenderer, Length, Loader, LoadingError, SvgHandle};
|
||||
|
||||
/// Checks that the variable of type [u8] can be parsed as a SVG file.
|
||||
#[derive(Debug)]
|
||||
pub struct SvgPredicate {}
|
||||
|
||||
impl SvgPredicate {
|
||||
pub fn with_size(self, width: Length, height: Length) -> DetailPredicate<Self> {
|
||||
DetailPredicate::<Self> {
|
||||
p: self,
|
||||
d: Detail::Size(Dimensions {
|
||||
w: width,
|
||||
h: height,
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn svg_from_bytes(data: &[u8]) -> Result<SvgHandle, LoadingError> {
|
||||
let bytes = Bytes::from(data);
|
||||
let stream = MemoryInputStream::from_bytes(&bytes);
|
||||
Loader::new().read_stream(&stream, None::<&gio::File>, None::<&gio::Cancellable>)
|
||||
}
|
||||
|
||||
impl Predicate<[u8]> for SvgPredicate {
|
||||
fn eval(&self, data: &[u8]) -> bool {
|
||||
svg_from_bytes(data).is_ok()
|
||||
}
|
||||
|
||||
fn find_case<'a>(&'a self, _expected: bool, data: &[u8]) -> Option<Case<'a>> {
|
||||
match svg_from_bytes(data) {
|
||||
Ok(_) => None,
|
||||
Err(e) => Some(Case::new(Some(self), false).add_product(Product::new("Error", e))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PredicateReflection for SvgPredicate {}
|
||||
|
||||
impl fmt::Display for SvgPredicate {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "is an SVG")
|
||||
}
|
||||
}
|
||||
|
||||
/// Extends a SVG Predicate by a check for its size
|
||||
#[derive(Debug)]
|
||||
pub struct DetailPredicate<SvgPredicate> {
|
||||
p: SvgPredicate,
|
||||
d: Detail,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Detail {
|
||||
Size(Dimensions),
|
||||
}
|
||||
|
||||
/// SVG's dimensions
|
||||
#[derive(Debug)]
|
||||
struct Dimensions {
|
||||
w: Length,
|
||||
h: Length,
|
||||
}
|
||||
|
||||
impl Dimensions {
|
||||
pub fn width(&self) -> f64 {
|
||||
self.w.length
|
||||
}
|
||||
|
||||
pub fn height(&self) -> f64 {
|
||||
self.h.length
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Dimensions {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}{} x {}{}",
|
||||
self.width(),
|
||||
self.w.unit,
|
||||
self.height(),
|
||||
self.h.unit
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl cmp::PartialEq for Dimensions {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
approx_eq!(f64, self.width(), other.width(), epsilon = 0.000_001)
|
||||
&& approx_eq!(f64, self.height(), other.height(), epsilon = 0.000_001)
|
||||
&& (self.w.unit == self.h.unit)
|
||||
&& (self.h.unit == other.h.unit)
|
||||
&& (other.h.unit == other.w.unit)
|
||||
}
|
||||
}
|
||||
|
||||
impl cmp::Eq for Dimensions {}
|
||||
|
||||
impl DetailPredicate<SvgPredicate> {
|
||||
fn eval_doc(&self, handle: &SvgHandle) -> bool {
|
||||
match &self.d {
|
||||
Detail::Size(d) => {
|
||||
let renderer = CairoRenderer::new(handle);
|
||||
let dimensions = renderer.intrinsic_dimensions();
|
||||
(dimensions.width, dimensions.height) == (d.w, d.h)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn find_case_for_doc<'a>(&'a self, expected: bool, handle: &SvgHandle) -> Option<Case<'a>> {
|
||||
if self.eval_doc(handle) == expected {
|
||||
let product = self.product_for_doc(handle);
|
||||
Some(Case::new(Some(self), false).add_product(product))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn product_for_doc(&self, handle: &SvgHandle) -> Product {
|
||||
match &self.d {
|
||||
Detail::Size(_) => {
|
||||
let renderer = CairoRenderer::new(handle);
|
||||
let dimensions = renderer.intrinsic_dimensions();
|
||||
|
||||
Product::new(
|
||||
"actual size",
|
||||
format!(
|
||||
"width={:?}, height={:?}",
|
||||
dimensions.width, dimensions.height
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Predicate<[u8]> for DetailPredicate<SvgPredicate> {
|
||||
fn eval(&self, data: &[u8]) -> bool {
|
||||
match svg_from_bytes(data) {
|
||||
Ok(handle) => self.eval_doc(&handle),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn find_case<'a>(&'a self, expected: bool, data: &[u8]) -> Option<Case<'a>> {
|
||||
match svg_from_bytes(data) {
|
||||
Ok(handle) => self.find_case_for_doc(expected, &handle),
|
||||
Err(e) => Some(Case::new(Some(self), false).add_product(Product::new("Error", e))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PredicateReflection for DetailPredicate<SvgPredicate> {
|
||||
fn children<'a>(&'a self) -> Box<dyn Iterator<Item = Child<'a>> + 'a> {
|
||||
let params = vec![Child::new("predicate", &self.p)];
|
||||
Box::new(params.into_iter())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for DetailPredicate<SvgPredicate> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match &self.d {
|
||||
Detail::Size(d) => write!(f, "is an SVG sized {}", d),
|
||||
}
|
||||
}
|
||||
}
|
||||