mirror of
https://github.com/chatmail/core.git
synced 2026-10-01 02:31:02 +03:00
api: create QR codes from any data (#6090)
this PR adds a function that can be used to create any QR code, in a raw form. this can be used to create add-contact as well as add-second-device QR codes (eg. `dc_create_qr_svg(dc_get_securejoin_qr())`) - as well as for other QR codes as proxies. the disadvantage of the rich-formatted QR codes as created by `dc_get_securejoin_qr_svg()` and `dc_backup_provider_get_qr_svg()` were: - they do not look good and cannot interact with UI layout wise (but also tapping eg. an address is not easily possible) - esp. text really looks bad. even with [some](https://github.com/deltachat/deltachat-android/pull/2138/commits/e5dc8fe3d82e8b72631ae5806e9c4c7eec93a37a) [hacks](https://github.com/deltachat/deltachat-android/pull/2215) it [stays buggy](https://github.com/deltachat/deltachat-ios/issues/2200); the bugs mainly come from different SVG implementation, all need their own quirks - accessibility is probably bad as well we thought that time, SVG is a great thing for QR codes, but apart from basic geometrics, it is not. so, we avoid text, this also means to avoid putting an avatar in the middle of the QR code (we can put some generic symbol there, eg. different ones for add-contact and add-second-device). while this looks like a degradation, also other messengers use more raw QR codes. also, we removed many data from the QR code anyway, eg. the email address is no longer there. that time, sharing QR images was more a thing, meanwhile we have invite links, that are much better for that purpose. in theory, we could also leave the SVG path completely and go for PNG - which we did not that time as PNG and text looks bad, as the system font is not easily usable :) but going for PNG would add further challenges as passing binary data around, and also UI-implemtation-wise, that would be a larger step. so, let's stay with SVG in a form we know is compatible. the old QR code functions are deprecated.
This commit is contained in:
@@ -14,6 +14,65 @@ use crate::qr::{self, Qr};
|
||||
use crate::securejoin;
|
||||
use crate::stock_str::{self, backup_transfer_qr};
|
||||
|
||||
/// Create a QR code from any input data.
|
||||
pub fn create_qr_svg(qrcode_content: &str) -> Result<String> {
|
||||
let all_size = 512.0;
|
||||
let qr_code_size = 416.0;
|
||||
|
||||
let qr = QrCode::encode_text(qrcode_content, QrCodeEcc::Medium)?;
|
||||
let mut svg = String::with_capacity(28000);
|
||||
let mut w = tagger::new(&mut svg);
|
||||
|
||||
w.elem("svg", |d| {
|
||||
d.attr("xmlns", "http://www.w3.org/2000/svg")?;
|
||||
d.attr("viewBox", format_args!("0 0 {all_size} {all_size}"))?;
|
||||
d.attr("xmlns:xlink", "http://www.w3.org/1999/xlink")?; // required for enabling xlink:href on browsers
|
||||
Ok(())
|
||||
})?
|
||||
.build(|w| {
|
||||
// background
|
||||
w.single("rect", |d| {
|
||||
d.attr("x", 0)?;
|
||||
d.attr("y", 0)?;
|
||||
d.attr("width", all_size)?;
|
||||
d.attr("height", all_size)?;
|
||||
d.attr("style", "fill:#ffffff")?;
|
||||
Ok(())
|
||||
})?;
|
||||
// QR code
|
||||
w.elem("g", |d| {
|
||||
d.attr(
|
||||
"transform",
|
||||
format!(
|
||||
"translate({},{})",
|
||||
(all_size - qr_code_size) / 2.0,
|
||||
((all_size - qr_code_size) / 2.0)
|
||||
),
|
||||
)
|
||||
})?
|
||||
.build(|w| {
|
||||
w.single("path", |d| {
|
||||
let mut path_data = String::with_capacity(0);
|
||||
let scale = qr_code_size / qr.size() as f32;
|
||||
|
||||
for y in 0..qr.size() {
|
||||
for x in 0..qr.size() {
|
||||
if qr.get_module(x, y) {
|
||||
path_data += &format!("M{x},{y}h1v1h-1z");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
d.attr("style", "fill:#000000")?;
|
||||
d.attr("d", path_data)?;
|
||||
d.attr("transform", format!("scale({scale})"))
|
||||
})
|
||||
})
|
||||
})?;
|
||||
|
||||
Ok(svg)
|
||||
}
|
||||
|
||||
/// Returns SVG of the QR code to join the group or verify contact.
|
||||
///
|
||||
/// If `chat_id` is `None`, returns verification QR code.
|
||||
@@ -304,6 +363,14 @@ mod tests {
|
||||
|
||||
use super::*;
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_create_qr_svg() -> Result<()> {
|
||||
let svg = create_qr_svg("this is a test QR code \" < > &")?;
|
||||
assert!(svg.contains("<svg"));
|
||||
assert!(svg.contains("</svg>"));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_svg_escaping() {
|
||||
let svg = inner_generate_secure_join_qr_code(
|
||||
|
||||
Reference in New Issue
Block a user