is_svg/lib.rs
1// SPDX-FileCopyrightText: 2024 Shun Sakai
2//
3// SPDX-License-Identifier: Apache-2.0 OR MIT
4
5//! The `is-svg` crate is a library for testing whether a given data is a [SVG]
6//! image.
7//!
8//! This crate assumes the given data to be a valid SVG image if
9//! [`usvg::Tree::from_data`] returns [`Ok`], and an invalid SVG image if it
10//! returns [`Err`]. It supports both a SVG string and a [gzip-compressed] SVG
11//! data.
12//!
13//! # Examples
14//!
15//! ```
16//! assert_eq!(
17//! is_svg::is_svg(include_str!("../tests/data/w3/svg-logo-v.svg")),
18//! true
19//! );
20//! assert_eq!(
21//! is_svg::is_svg(include_bytes!("../tests/data/w3/svg-logo-v.png")),
22//! false
23//! );
24//!
25//! // `.svgz` is also supported.
26//! assert_eq!(
27//! is_svg::is_svg(include_bytes!("../tests/data/w3/svg-logo-v.svgz")),
28//! true
29//! );
30//! ```
31//!
32//! [SVG]: https://www.w3.org/Graphics/SVG/
33//! [gzip-compressed]: https://datatracker.ietf.org/doc/html/rfc1952
34
35#![doc(html_root_url = "https://docs.rs/is-svg/0.2.1/")]
36// Lint levels of rustc.
37#![deny(missing_docs)]
38
39use usvg::{Options, Tree};
40
41/// Magic number of gzip defined in [RFC 1952].
42///
43/// [RFC 1952]: https://datatracker.ietf.org/doc/html/rfc1952
44const GZIP_MAGIC_NUMBER: [u8; 2] = [0x1f, 0x8b];
45
46/// Returns [`true`] if `data` is a valid [SVG] data, and [`false`] otherwise.
47///
48/// This function also supports the [gzip-compressed] SVG image (`.svgz`).
49///
50/// # Examples
51///
52/// ```
53/// assert_eq!(
54/// is_svg::is_svg(include_str!("../tests/data/w3/svg-logo-v.svg")),
55/// true
56/// );
57/// assert_eq!(
58/// is_svg::is_svg(include_bytes!("../tests/data/w3/svg-logo-v.png")),
59/// false
60/// );
61///
62/// assert_eq!(
63/// is_svg::is_svg(include_bytes!("../tests/data/w3/svg-logo-v.svgz")),
64/// true
65/// );
66/// ```
67///
68/// [SVG]: https://www.w3.org/Graphics/SVG/
69/// [gzip-compressed]: https://datatracker.ietf.org/doc/html/rfc1952
70#[inline]
71pub fn is_svg(data: impl AsRef<[u8]>) -> bool {
72 let inner = |data: &[u8]| -> bool {
73 let opt = Options::default();
74 Tree::from_data(data, &opt).is_ok()
75 };
76 inner(data.as_ref())
77}
78
79/// Returns [`true`] if `data` is a valid non [gzip-compressed] [SVG] data
80/// (`.svg`), and [`false`] otherwise.
81///
82/// This function returns [`false`] if `data` is a valid SVG data, but
83/// gzip-compressed (`.svgz`).
84///
85/// # Examples
86///
87/// ```
88/// assert_eq!(
89/// is_svg::is_svg_string(include_str!("../tests/data/w3/svg-logo-v.svg")),
90/// true
91/// );
92/// assert_eq!(
93/// is_svg::is_svg_string(include_bytes!("../tests/data/w3/svg-logo-v.png")),
94/// false
95/// );
96///
97/// assert_eq!(
98/// is_svg::is_svg_string(include_bytes!("../tests/data/w3/svg-logo-v.svgz")),
99/// false
100/// );
101/// ```
102///
103/// [gzip-compressed]: https://datatracker.ietf.org/doc/html/rfc1952
104/// [SVG]: https://www.w3.org/Graphics/SVG/
105#[inline]
106pub fn is_svg_string(data: impl AsRef<[u8]>) -> bool {
107 let inner = |data: &[u8]| -> bool { is_svg(data) && !data.starts_with(&GZIP_MAGIC_NUMBER) };
108 inner(data.as_ref())
109}
110
111/// Returns [`true`] if `data` is a valid [gzip-compressed] [SVG] data
112/// (`.svgz`), and [`false`] otherwise.
113///
114/// This function returns [`false`] if `data` is a valid SVG data, but non
115/// gzip-compressed (`.svg`).
116///
117/// # Examples
118///
119/// ```
120/// assert_eq!(
121/// is_svg::is_svgz(include_bytes!("../tests/data/w3/svg-logo-v.svgz")),
122/// true
123/// );
124/// assert_eq!(
125/// is_svg::is_svgz(include_bytes!("../tests/data/w3/svg-logo-v.png")),
126/// false
127/// );
128///
129/// assert_eq!(
130/// is_svg::is_svgz(include_str!("../tests/data/w3/svg-logo-v.svg")),
131/// false
132/// );
133/// ```
134///
135/// [gzip-compressed]: https://datatracker.ietf.org/doc/html/rfc1952
136/// [SVG]: https://www.w3.org/Graphics/SVG/
137#[inline]
138pub fn is_svgz(data: impl AsRef<[u8]>) -> bool {
139 let inner = |data: &[u8]| -> bool { is_svg(data) && data.starts_with(&GZIP_MAGIC_NUMBER) };
140 inner(data.as_ref())
141}