Yatekii / qrbill

2 stars 3 forks source link

Font does not conform to specification #7

Open jacg opened 3 months ago

jacg commented 3 months ago

Section 3.4 of https://www.six-group.com/dam/download/banking-services/standardization/qr-bill/ig-qr-bill-v2.3-en.pdf states that

Only the sans-serif fonts Arial, Frutiger, Helvetica and Liberation Sans are permitted [...]

... all of which are sans-serif fonts. At least on My Machine (TM) a serif font is used.

jacg commented 3 months ago

This seems to be a problem with the fonts found on my machine, in the PDF version only, not SVG.

monheimx9 commented 2 months ago

This seems to be a problem with the fonts found on my machine, in the PDF version only, not SVG.

I don't know if you are on linux as well, but in my case I don't have Helvetica installed, which fallback on whatever font the system find first I noticed it when I tested my tool to generate QRbills from csv files to png

Only the sans-serif fonts Arial, Frutiger, Helvetica and Liberation Sans are permitted in black. Text may not be in italics nor underlined.

Maybe there is a way to fallback to any other font beforementioned if Helvetica is not on the system?

const FONT: Style = Style {
        font_size: Some(10.0),
        font_family: Some("Helvetica"),
        font_weight: None,
    };

I've read somewhere (I think it was on arch linux forums) that Helvetica is not libre, so correct me if I'm wrong but I don't think we could package it directly into the library, if we can maybe it would be another solution to that issue

What do you think?

monheimx9 commented 2 months ago

Also, found something on the usvg library

// Attempts to load system fonts.
    ///
    /// Supports Windows, Linux and macOS.
    ///
    /// System fonts loading is a surprisingly complicated task,
    /// mostly unsolvable without interacting with system libraries.
    /// And since `fontdb` tries to be small and portable, this method
    /// will simply scan some predefined directories.
    /// Which means that fonts that are not in those directories must
    /// be added manually.
    #[cfg(feature = "fs")]
    pub fn load_system_fonts(&mut self)

Which could indicate that the fonts can't be loaded correctly on our respective systems That could explain why it works on the svg (since your svg viewer might rely on system libs to load the font) and not on the pdf who relies on the usvg/fontdb library

It means that if we want it to be reliable all the time we might need to find a workaround, starting with a check before calling the draw_bill function for example But doing so would mean that this check is performed on each draw, not really optimised We could also rely on a file that store the font value on the first call and read from it on the next ones

Mmmmm... I'll let it rest for tonight, I'll come back if I have a nicer idea

monheimx9 commented 2 months ago

After digging a bit more into it, here's some of my thoughts

-Apparently, out of the 4 fonts, only Liberation Sans is on the public domain, I think it would be better if we default on this one instead of Helvetica -We could implement a new Enum for the 4 fonts and default it to Liberation Sans -Liberation Sans should be available on most of the machines, if that's not the case I don't see any issue of packaging it directly into the library -Instead of calling

    options.fontdb_mut().load_system_fonts();

We could use either of those methods:

    /// Loads font files from the selected directory into the `Database`.
    ///
    /// This method will scan directories recursively.
    ///
    /// Will load `ttf`, `otf`, `ttc` and `otc` fonts.
    ///
    /// Unlike other `load_*` methods, this one doesn't return an error.
    /// It will simply skip malformed fonts and will print a warning into the log for each of them.
    #[cfg(feature = "fs")]
    pub fn load_fonts_dir<P: AsRef<std::path::Path>>(&mut self, dir: P) {
        self.load_fonts_dir_impl(dir.as_ref())
    }

or:

    /// Loads a font file into the `Database`.
    ///
    /// Will load all font faces in case of a font collection.
    #[cfg(all(feature = "fs", feature = "memmap"))]
    pub fn load_font_file<P: AsRef<std::path::Path>>(
        &mut self,
        path: P,
    ) -> Result<(), std::io::Error> {
        self.load_font_file_impl(path.as_ref())
    }

And if the user don't want to use Liberation Sans, we could, change the signature of write_to_pdf() and pass an Enum into it

Here's my 2 cents

jacg commented 2 months ago

(Yes, I'm on Linux too.)

I'd like it to work out of the box, so I'd be in favour of packing the fonts directly into the library. Do you have a scheme in mind for how that would be done? How much space do the fonts occupy?

Maybe add a feature for including the fonts?

monheimx9 commented 2 months ago

About the size: In theory we wouls only need "Liberation Sans Regular" ans "Liberation Sans Bold" if I'm correct

On the technical side of thing: This is something I've never done before, I only know some of the theory Apparently we can't package external assets that can be included at runtime From what I've read we should use something like the macro "include_bytes!()" to be able to include the assets at compilation time

Which introduce another problem that I didn't take into consideration: I'm not sure if we can load fonts from a reader, since in theory we provides only path to the font file or directory So even if we include the font at compile time, there are points that are still unclear for me

At this time, the easiest thing we can do is using Liberation Sans and praying that this font is available on most machines (at least I think on almost every Linux System it should be) On my windows machine at work its also there and I don't recall to ever had to install it We could return an Error or a Warning if the font isn't found on the system

So... I'm a bit split in half on that matter, I'm not so sure what is the direction we should take that make the most sense