Skip to content

Commit bceee3e

Browse files
authored
Merge pull request #78 from sorairolake/feature/eps
feat: Add EPS support
2 parents c578416 + 4bc2323 commit bceee3e

File tree

12 files changed

+515
-14
lines changed

12 files changed

+515
-14
lines changed

.github/workflows/rust.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ jobs:
1616
strategy:
1717
fail-fast: true
1818
matrix:
19-
rustc: [1.67.1, stable] # MSVR and current stable rustc
19+
rustc: [1.70.0, stable] # MSVR and current stable rustc
2020
steps:
2121
- uses: actions/checkout@v2
2222
- uses: actions-rs/toolchain@v1

Cargo.toml

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ description = "QR code encoder in Rust"
44
license = "MIT OR Apache-2.0"
55
version = "0.14.1"
66
edition = "2021"
7-
rust-version = "1.67.1"
7+
rust-version = "1.70.0"
88
authors = ["kennytm <kennytm@gmail.com>"]
99
keywords = ["qrcode"]
1010
categories = ["encoding", "multimedia::images"]
@@ -25,12 +25,13 @@ image = { version = "0.25", default-features = false, optional = true }
2525
image = "0.25"
2626

2727
[features]
28-
default = ["std", "image", "svg", "pic"]
28+
default = ["std", "image", "svg", "pic", "eps"]
2929
image = ["dep:image", "std"]
3030
std = []
3131
bench = []
3232
svg = []
3333
pic = []
34+
eps = []
3435

3536
[[bin]]
3637
name = "qrencode"
@@ -49,3 +50,7 @@ required-features = ["svg"]
4950
[[example]]
5051
name = "encode_pic"
5152
required-features = ["pic"]
53+
54+
[[example]]
55+
name = "encode_eps"
56+
required-features = ["eps"]

README.md

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ fn main() {
5959
.quiet_zone(false)
6060
.module_dimensions(2, 1)
6161
.build();
62-
println!("{}", string);
62+
println!("{string}");
6363
}
6464
```
6565

@@ -102,7 +102,7 @@ fn main() {
102102
.dark_color(svg::Color("#800000"))
103103
.light_color(svg::Color("#ffff80"))
104104
.build();
105-
println!("{}", image);
105+
println!("{image}");
106106
}
107107
```
108108

@@ -122,7 +122,7 @@ fn main() {
122122
.dark_color(unicode::Dense1x2::Light)
123123
.light_color(unicode::Dense1x2::Dark)
124124
.build();
125-
println!("{}", image);
125+
println!("{image}");
126126
}
127127
```
128128

@@ -158,7 +158,7 @@ fn main() {
158158
.render::<pic::Color>()
159159
.min_dimensions(1, 1)
160160
.build();
161-
println!("{}", image);
161+
println!("{image}");
162162
}
163163
```
164164

@@ -178,3 +178,44 @@ p(9,4,1,1)
178178
179179
```
180180
See [`test_annex_i_micro_qr_as_pic.pic`](src/test_annex_i_micro_qr_as_pic.pic) for a full example.
181+
182+
## EPS generation
183+
184+
```rust
185+
use qrcode::render::eps;
186+
use qrcode::{EcLevel, QrCode, Version};
187+
188+
fn main() {
189+
let code = QrCode::with_version(b"01234567", Version::Micro(2), EcLevel::L).unwrap();
190+
let image = code
191+
.render()
192+
.min_dimensions(200, 200)
193+
.dark_color(eps::Color([0.5, 0.0, 0.0]))
194+
.light_color(eps::Color([1.0, 1.0, 0.5]))
195+
.build();
196+
println!("{image}");
197+
}
198+
```
199+
200+
Generates [EPS](https://en.wikipedia.org/wiki/Encapsulated_PostScript)
201+
output that renders as follows:
202+
203+
```postscript
204+
%!PS-Adobe-3.0 EPSF-3.0
205+
%%BoundingBox: 0 0 204 204
206+
%%Pages: 1
207+
%%EndComments
208+
gsave
209+
1 1 0.5 setrgbcolor
210+
0 0 204 204 rectfill
211+
grestore
212+
0.5 0 0 setrgbcolor
213+
24 180 12 12 rectfill
214+
36 180 12 12 rectfill
215+
48 180 12 12 rectfill
216+
60 180 12 12 rectfill
217+
72 180 12 12 rectfill
218+
84 180 12 12 rectfill
219+
220+
```
221+
See [`test_annex_i_micro_qr_as_eps.eps`](src/test_annex_i_micro_qr_as_eps.eps) for a full example.

examples/encode_eps.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
use qrcode::render::eps;
2+
use qrcode::{EcLevel, QrCode, Version};
3+
4+
fn main() {
5+
let code = QrCode::with_version(b"01234567", Version::Micro(2), EcLevel::L).unwrap();
6+
let image = code
7+
.render()
8+
.min_dimensions(200, 200)
9+
.dark_color(eps::Color([0.5, 0.0, 0.0]))
10+
.light_color(eps::Color([1.0, 1.0, 0.5]))
11+
.build();
12+
println!("{image}");
13+
}

src/lib.rs

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
//!
2222
//! // You can also render it into a string.
2323
//! let string = code.render().light_color(' ').dark_color('#').build();
24-
//! println!("{}", string);
24+
//! println!("{string}");
2525
//! # }
2626
//! ```
2727
@@ -414,3 +414,30 @@ mod pic_tests {
414414
assert_eq!(&image, expected);
415415
}
416416
}
417+
418+
#[cfg(all(test, feature = "eps"))]
419+
mod eps_tests {
420+
use crate::render::eps::Color as EpsColor;
421+
use crate::{EcLevel, QrCode, Version};
422+
423+
#[test]
424+
fn test_annex_i_qr_as_eps() {
425+
let code = QrCode::new(b"01234567").unwrap();
426+
let image = code.render::<EpsColor>().build();
427+
let expected = include_str!("test_annex_i_qr_as_eps.eps");
428+
assert_eq!(&image, expected);
429+
}
430+
431+
#[test]
432+
fn test_annex_i_micro_qr_as_eps() {
433+
let code = QrCode::with_version(b"01234567", Version::Micro(2), EcLevel::L).unwrap();
434+
let image = code
435+
.render()
436+
.min_dimensions(200, 200)
437+
.dark_color(EpsColor([0.5, 0.0, 0.0]))
438+
.light_color(EpsColor([1.0, 1.0, 0.5]))
439+
.build();
440+
let expected = include_str!("test_annex_i_micro_qr_as_eps.eps");
441+
assert_eq!(&image, expected);
442+
}
443+
}

src/optimize.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -354,10 +354,8 @@ mod optimize_tests {
354354
}
355355
assert!(
356356
opt_segs == expected,
357-
"Optimization gave something better: {} < {} ({:?})",
358-
new_len,
359-
total_encoded_len(expected, version),
360-
opt_segs
357+
"Optimization gave something better: {new_len} < {} ({opt_segs:?})",
358+
total_encoded_len(expected, version)
361359
);
362360
}
363361

src/render/eps.rs

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
//! EPS rendering support.
2+
//!
3+
//! # Example
4+
//!
5+
//! ```
6+
//! use qrcode::QrCode;
7+
//! use qrcode::render::eps;
8+
//!
9+
//! let code = QrCode::new(b"Hello").unwrap();
10+
//! let eps = code.render::<eps::Color>().build();
11+
//! println!("{eps}");
12+
13+
#![cfg(feature = "eps")]
14+
15+
use alloc::format;
16+
use alloc::string::String;
17+
use core::fmt::Write;
18+
19+
use crate::render::{Canvas as RenderCanvas, Pixel};
20+
use crate::types::Color as ModuleColor;
21+
22+
/// An EPS color (`[R, G, B]`).
23+
///
24+
/// Each value must be in the range of 0.0 to 1.0.
25+
#[derive(Copy, Clone, Default, PartialEq, PartialOrd)]
26+
pub struct Color(pub [f64; 3]);
27+
28+
impl Pixel for Color {
29+
type Canvas = Canvas;
30+
type Image = String;
31+
32+
fn default_color(color: ModuleColor) -> Self {
33+
Color(color.select(Default::default(), [1.0; 3]))
34+
}
35+
}
36+
37+
#[doc(hidden)]
38+
pub struct Canvas {
39+
eps: String,
40+
height: u32,
41+
}
42+
43+
impl RenderCanvas for Canvas {
44+
type Pixel = Color;
45+
type Image = String;
46+
47+
fn new(width: u32, height: u32, dark_pixel: Color, light_pixel: Color) -> Self {
48+
Canvas {
49+
eps: format!(
50+
concat!(
51+
"%!PS-Adobe-3.0 EPSF-3.0\n",
52+
"%%BoundingBox: 0 0 {w} {h}\n",
53+
"%%Pages: 1\n",
54+
"%%EndComments\n",
55+
"gsave\n",
56+
"{bgr} {bgg} {bgb} setrgbcolor\n",
57+
"0 0 {w} {h} rectfill\n",
58+
"grestore\n",
59+
"{fgr} {fgg} {fgb} setrgbcolor\n"
60+
),
61+
w = width,
62+
h = height,
63+
fgr = dark_pixel.0[0],
64+
fgg = dark_pixel.0[1],
65+
fgb = dark_pixel.0[2],
66+
bgr = light_pixel.0[0],
67+
bgg = light_pixel.0[1],
68+
bgb = light_pixel.0[2],
69+
),
70+
height,
71+
}
72+
}
73+
74+
fn draw_dark_pixel(&mut self, x: u32, y: u32) {
75+
self.draw_dark_rect(x, y, 1, 1);
76+
}
77+
78+
fn draw_dark_rect(&mut self, left: u32, top: u32, width: u32, height: u32) {
79+
let bottom = self.height - top;
80+
writeln!(self.eps, "{left} {bottom} {width} {height} rectfill").unwrap();
81+
}
82+
83+
fn into_image(mut self) -> String {
84+
self.eps.push_str("%%EOF");
85+
self.eps
86+
}
87+
}

src/render/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use crate::cast::As;
44
use crate::types::Color;
55
use core::cmp::max;
66

7+
pub mod eps;
78
pub mod image;
89
pub mod pic;
910
pub mod string;

src/render/pic.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
//!
99
//! let code = QrCode::new(b"Hello").unwrap();
1010
//! let pic = code.render::<pic::Color>().build();
11-
//! println!("{}", pic);
11+
//! println!("{pic}");
1212
1313
#![cfg(feature = "pic")]
1414

src/render/svg.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
//!
99
//! let code = QrCode::new(b"Hello").unwrap();
1010
//! let svg_xml = code.render::<svg::Color>().build();
11-
//! println!("{}", svg_xml);
11+
//! println!("{svg_xml}");
1212
1313
#![cfg(feature = "svg")]
1414

0 commit comments

Comments
 (0)