Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Serve fonts for MapLibre rendering #704

Closed
nyurik opened this issue Jun 5, 2023 · 7 comments
Closed

Serve fonts for MapLibre rendering #704

nyurik opened this issue Jun 5, 2023 · 7 comments

Comments

@nyurik
Copy link
Member

nyurik commented Jun 5, 2023

Martin should serve font assets for MapLibre rendering, given a font file.

API

I think source IDs could be used here as font IDs, and we could also, possibly as a subdirectory like /fonts/...

  • /fonts/<font_source_id> - returns some JSON font metadata. Any standards here?
  • /fonts/<font_source_id>/<unicode_range> - one of the ways fonts are sub-divided is by unicode pages - because it is assumed that you only need some unicode range for a specific region of the map
  • TBD: some other API for loading full font or some other aspects of it?
  • TBD: what is a logical placement of the font sources - the root or the fonts subpath in a url? Having a dedicated font space would allow for a cleaner separation I think.

Configuration

Config file and possibly CLI should have a simple option to serve a font file. Since font files are essentially identical to mbtiles and pmtiles from configuration perspective (a single file produces a single source), we can use identical config structure:

# Publish font files
fonts:
  paths:
    # scan this whole dir, matching all font files
    - /dir-path
    # specific font file will be published as fnt source
    - /path/to/fnt.ttf
  sources:
    # named source matching source name to a single file
    fnt-src1: /path/to/my_font.ttf

The idea of font serving was initially proposed here by @sharkAndshark

See also #705 for sprite serving

@sharkAndshark
Copy link
Collaborator

sharkAndshark commented Jun 6, 2023

So the API would be like:

  • /fonts/<font_space>/<unicode_range>.pbf

I think it's quite intuitive and useful. Hehe

The full font or transformation internally

Maybe we could ignore this part and start from a basic. Kepp watching is it import?

  • User could just drop .ttf fonts into directory in Martin and then retrieve .pbf from Marin by API. It's convient, no need to worry about trival steps looping: adjusting style -> find/download fonts -> transform to pbf -> upload to server -> adjusting style.
  • Other client like ol-mapbox-style cannot use PBF/SDF glyphs for text-font layout property, it relies on web fonts.

@catalinconstant
Copy link

catalinconstant commented Jun 26, 2023

Here is draft implementation I did mbtileserver-rs, :

[dependencies]
pbf_font_tools = "2.2.0"
protobuf = "3.2.0"
urlencoding = "2.1.2"
lazy_static = "1.4"
use hyper::header::{CONTENT_ENCODING, CONTENT_TYPE, HOST};
use hyper::{Body, Request, Response, StatusCode};
use lazy_static::lazy_static;
use pbf_font_tools::get_font_stack;
use protobuf::Message;
use regex::Regex;
use serde_json::json;
use urlencoding::decode;
lazy_static! {
static ref FONT_URL_RE: Regex = Regex::new(r"^/fonts/(?P<font_names>.*)/(?P<start>\d+)-(?P<end>\d+)\.(?P<format>[a-zA-Z]+)").unwrap();
}
    let path = decode(uri.path()).unwrap().into_owned();
 if path.starts_with("/fonts") {
                let matches = FONT_URL_RE.captures(&path).unwrap();
                let font_names: Vec<&str> = matches
                    .name("font_names")
                    .unwrap()
                    .as_str()
                    .split(",")
                    .collect();
                let start = matches
                    .name("start")
                    .unwrap()
                    .as_str()
                    .parse::<u32>()
                    .unwrap();
                let end = matches
                    .name("end")
                    .unwrap()
                    .as_str()
                    .parse::<u32>()
                    .unwrap();
                let fonts_stack =
                    get_font_stack(Path::new("./fonts"), &font_names, start, end)
                        .await
                        .unwrap();

                return Ok(Response::builder()
                    .header(CONTENT_TYPE, "application/x-protobuf")
                    .body(Body::from(fonts_stack.write_to_bytes().unwrap()))
                    .unwrap());
            }
        }
    };

@nyurik
Copy link
Member Author

nyurik commented Jun 26, 2023

Thanks @catalinconstant ! I have been looking at that lib to do exactly what you propose. I recently did the same implementation for sprites in #715 -- you may want to look at these two places

  • martin/src/srv/server.rs - handling HTTP requests and parsing params - you can use the same method to parse the name and the range of the font
  • martin/src/sprites/mod.rs - the actual implementation
    Everything else is just some relevant refactorings that are not relevant.

Let me know if you want to hack on it, or I could look at it later on to adapt your code

@catalinconstant
Copy link

Thanks @catalinconstant ! I have been looking at that lib to do exactly what you propose. I recently did the same implementation for sprites in #715 -- you may want to look at these two places

  • martin/src/srv/server.rs - handling HTTP requests and parsing params - you can use the same method to parse the name and the range of the font
  • martin/src/sprites/mod.rs - the actual implementation
    Everything else is just some relevant refactorings that are not relevant.

Let me know if you want to hack on it, or I could look at it later on to adapt your code

If you could adapt the code will better for me. Thanks

@nyurik
Copy link
Member Author

nyurik commented Jun 26, 2023

sure, i will try to get to it sometime next week - this week is FOSS4G, so i'm travelling

@nyurik nyurik self-assigned this Oct 22, 2023
@nyurik
Copy link
Member Author

nyurik commented Oct 22, 2023

Partial implementation is in #755 - still needs some polishing, but its getting there

@nyurik
Copy link
Member Author

nyurik commented Oct 30, 2023

done in #755, will be released soon

@nyurik nyurik closed this as completed Oct 30, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants