text2img/lib.rs
1#![deny(missing_docs)]
2//! Creates a png image from text.
3//! Useful to protect sensitive information from bots and scrapers,
4//! such as email addresses and telephone numbers in web pages,
5//! and even passwords sent via email
6//!
7//! - [x] Png Support 8 bit rgba
8//! - [x] Font Weight implemented as Random pixels
9//! - [ ] Png Support 1 bit monochrome - to reduce image size
10//! - [ ] Poisson Disk rendering
11
12use fontdue::layout::{CoordinateSystem, Layout, TextStyle};
13use raqote::DrawTarget;
14
15const FONT_DATA: &[u8]= include_bytes!("../assets/Roboto-Regular.ttf") as &[u8];
16
17/// Converts a text string into an image
18///
19/// # Arguments
20/// * `text` - a non empty string to render
21/// * `weight` - a percentage of pixels to be painted
22/// * `font_size` - the size of the font. Note this is not the same as the height of the image
23/// # Return
24/// the result. DrawTarget on success, an error message on failure
25///
26/// # Deprecated
27/// This should not leak the implementation of DrawTarget from the raqote crate,
28/// and will be removed in a future release.
29#[must_use]
30#[deprecated]
31pub fn text2img_internal(text:String, weight:u8, font_size:u8) -> Result<DrawTarget,String> {
32
33 if text.is_empty() {
34 return Err("Content can not be empty".to_owned());
35 }
36
37 if weight==0 {
38 return Err("Weight can not be zero".to_owned());
39 }
40
41 let settings = fontdue::FontSettings::default();
42
43 let font=fontdue::Font::from_bytes(FONT_DATA, settings).unwrap();
44
45 let mut layout = Layout::new(CoordinateSystem::PositiveYDown);
46
47 //TODO_maybe this is for centering text in larger image
48 // let x=0;
49 // let y=0;
50 // let width=500;
51 // let height=70;
52 // layout.reset(&LayoutSettings {
53 // x: x as f32,
54 // y: y as f32,
55 // max_width: Option::Some(width as f32),
56 // max_height: Option::Some(height as f32),
57 // horizontal_align: fontdue::layout::HorizontalAlign::Center,
58 // ..LayoutSettings::default()
59 // });
60
61 let fonts = &[font.clone()];
62 layout.append(fonts, &TextStyle::new(&text, font_size as f32, 0));
63 let height=layout.height() as i32;
64 let first=layout.glyphs().first().unwrap();
65 let last=layout.glyphs().last().unwrap();
66
67 //This calculates the x of the last pixel and adds a margin equivalent to the first glyphs pixel
68 let width=(last.x as usize + last.width + first.x as usize) as i32;
69
70 let mut dt = DrawTarget::new(width, height);
71
72 for glyph in layout.glyphs() {
73 let glyph=glyph.to_owned();
74 let index=glyph.key.glyph_index;
75 let px=glyph.key.px;
76 let (metrics, coverage) = font.rasterize_indexed(index, px);
77
78 let mut image_data = Vec::with_capacity(coverage.len());
79
80 //TODO split this off into a trait
81 for cov in coverage.iter() {
82 let pixel = if weight==100 || fastrand::u8(0..=100)<=weight {
83 rgb_to_u32(0, 0, 0, *cov)
84 } else { 0};
85 image_data.push(pixel);
86 }
87 dt.draw_image_at(
88 glyph.x,
89 glyph.y,
90 &raqote::Image {
91 width: metrics.width as i32,
92 height: metrics.height as i32,
93 data: &image_data,
94 },
95 &raqote::DrawOptions {
96 blend_mode: raqote::BlendMode::Darken,
97 alpha: 1.0,
98 antialias: raqote::AntialiasMode::Gray,
99 },
100 );
101 }
102
103 Ok(dt)
104}
105
106/// Converts a rgba u8 to a u32
107/// # Arguments
108/// * `red` - red component byte
109/// * `green` - green component byte
110/// * `blue` - blue component byte
111/// * `alpha` - aƱpha component byte, 255 is opaque, 0 is transparent
112///
113/// # Return
114/// argb as a u32
115pub fn rgb_to_u32(red: u8, green: u8, blue: u8, alpha: u8) -> u32 {
116 (((alpha as u32) << 24) | ((red as u32) << 16) | ((green as u32) << 8) | (blue as u32)) as u32
117}