RazrFalcon / resvg

An SVG rendering library.
Mozilla Public License 2.0
2.74k stars 220 forks source link

Render text in wasm32-unknown-unknown #229

Closed dabreegster closed 3 years ago

dabreegster commented 4 years ago

usvg currently works great in wasm environments as long as the optional "text" feature is disabled. I'd love to get text support working too. From my understanding so far, there are at least 3 issues:

The first two are relatively straightforward. As part of #202, design an API to feed a FontDB into usvg that can be instantiated many different ways, one of them just being raw bytes of a font file. (include_bytes! is a low-effort way to get this working in a wasm environment.)

The harfbuzz dependency seems harder. My requirements are limited to simple Latin left-to-right text; harfbuzz is overkill. I found font2svg and think it might be possible to make something much simpler. Before I go down this rabbit hole, I wanted to ask for your advice.

Background

I'm using usvg in A/B Street to turn both normal SVG files and text into paths for lyon, which then give me polygons. This does the usvg->lyon->my simple Polygon abstraction path, based off this example from lyon. I'm using this same pipeline for text as well. (There are many inefficient and silly things that the text pipeline does right now, like constantly re-load fonts.) In a few weeks, I'm hoping to properly split off my drawing, geometry, and GUI libraries as new crates for other people to use. These libraries attempt to give exactly the same experience on native and web -- not using system UI controls/style -- without any changes to user code needed for different platforms.

RazrFalcon commented 4 years ago

The harfbuzz dependency seems harder.

Believe me, I know =) https://github.com/RazrFalcon/rustybuzz

My requirements are limited to simple Latin left-to-right text; harfbuzz is overkill.

The harfbuzz dependency is mandatory. I do not plan reducing text rendering to Latin only, since this is against the SVG spec.

Anyway, Rust version will be way smaller.

like constantly re-load fonts

In your code or in usvg? Because usvg does this too.

Basically, the resvg 0.10 roadmap looks like this:

  1. Port harfbuzz. I hope I will finish in 2-3 month.
  2. Write a font query library (for TrueType fonts only for now). There are a lot of questions there also, like the caching, fontconfig integration and the API in general. Basically, you will be able to create a fonts storage (system, custom path, raw binary data) and then pass it to usvg.
  3. Keep only the raqote backend.
dabreegster commented 4 years ago

In your code or in usvg? Because usvg does this too.

In usvg; I meant that I'm calling usvg::Tree::from_str repeatedly, which loads fonts everytime.

It sounds like resvg 0.10 will exactly make wasm work. Let me know how I can help!

RazrFalcon commented 4 years ago

Yes, resvg/usvg were focused on correctness, spec conformance for now. And since I'm almost finished with the spec, I can start optimizing it bit by bit.

Right now, I'm implementing a zero-allocation variable fonts support in ttf-parser. Not sure if someone could actually help me with this task :smile: Anyway, I will let you know when the first version of "fontdb" became available for testing.

khaledhosny commented 4 years ago

HarfBuzz builds to WASM just fine, I have been using it for months, see https://github.com/harfbuzz/harfbuzzjs

dabreegster commented 4 years ago

Oh? I'm not too familiar with the wasm/Rust interop, but there may be some way to get https://crates.io/crates/harfbuzz-sys to expose the JS bindings.

I'd personally prefer a pure Rust stack, so the work on rustybuzz is awesome.

dabreegster commented 4 years ago

@RazrFalcon, I got text support in web working for the simple Latin LTR case, to unblock getting my project on the web. Before Rustybuzz is complete, do you think it's worth exposing this functionality to all users of usvg? The change is reasonably small: https://github.com/dabreegster/resvg/commit/97ff3956fe7404333a72c2951a844d457510a4f0 and https://github.com/dabreegster/fontdb/commit/04c8ffc9f0c2676202ff9562a2f52415d74b8d68. I can send PRs if you'd like. If it muddies up the API too much, I'm fine just pinning to my fork temporarily.

RazrFalcon commented 4 years ago

No, I thinks that a fork is the best solution for now.

As for rustybuzz, I have no idea when it became pure Rust. Sadly, I don't have time right now to work on it.

RazrFalcon commented 3 years ago

I guess it's fixed now.

dabreegster commented 3 years ago

Congratulations on the rustybuzz release! There are a few small build changes to fontdb and usvg to avoid native dependencies like memmap; I'm preparing PRs now.

RazrFalcon commented 3 years ago

Oh, I've forgot about it. Since I've done with huge rewrites (tiny-skia, rustybuzz), I hope to release resvg/usvg more often.

DerKarlos commented 3 years ago

"usvg currently works great in wasm environments as long as the optional "text" feature is disabled." How could I disable text? Do I have to build the lib myselve? Could some one show me the shell command?

RazrFalcon commented 3 years ago

@DerKarlos Can you clarify what you want to do?

DerKarlos commented 3 years ago

Thank you for your fast replay! This issue starts with this sentence. So I assume, there is a variated cradle of resvg or a way to build one, that does not render text but does compile with wasm-pack. If I build resvg, memmap2 is included which does not build for WASM.

dabreegster commented 3 years ago

The issue is fixed; usvg works just fine in wasm now. You need to disable the system-fonts feature. Something like this should help. If you want to see a quick demo of usvg rendering text on wasm, see here.

DerKarlos commented 3 years ago

That sounds good. But sorry, I am new to Rust and what ever I try, at last memmap2 gets used by wasm-pack and: 363 MmapInner::map_mut(self.get_len(file)?, file, self.offset, self.populate) supplied 4 arguments, expected 3 arguments

The demo looks good but where to find the source? Or any example (with an Cargo.toml) to read SVC and write it to a byte array?

RazrFalcon commented 3 years ago

I assume this is what you want.

cargo build --target wasm32-unknown-unknown --no-default-features --features text
DerKarlos commented 3 years ago

Hm? To build my project, I use wasm-pack build --target web --out-name web --out-dir ./pkg If I use yours: error: Package o2t v0.1.0 (/Users/karlos/Public/o2t) does not have the feature text

dabreegster commented 3 years ago

The demo looks good but where to find the source?

https://github.com/a-b-street/abstreet/tree/master/widgetry_demo, and the widgetry crate has the library implementation. See https://a-b-street.github.io/docs/tech/dev/ui.html for design.

o2t v0.1.0 (/Users/karlos/Public/o2t) does not have the feature text

Is your repository online? No promises, but easier to help looking at your specific issue.

DerKarlos commented 3 years ago

You may see my code, I send you an invite. I am puzzled: widgetry_demo does not use usvg Still looking at your hints ...

dabreegster commented 3 years ago

Thanks for the invitation, I'll take a look when I find time.

The demo crate pulls in the widgetry crate, where the work happens: https://github.com/a-b-street/abstreet/blob/c5671557defbd80ce749b8fa7faf7c166b3d23dd/widgetry/Cargo.toml#L9

DerKarlos commented 3 years ago

Sorry, I still don't see a way. The crux may be the tiny_skia. I used your with resvg/examples/minimal.rs and was happy with the binary build. But not with the build by wasm-pack: As memmap2 is used as soon as I ad usvg to the dependencies. usvg reads the SVG syntax and calls lyon (but your example in lyon does not call ender( ... , pixmap

Your examples are just the "main.rs", no how to build. Bin is no problem but could you please add the files to build to WASM and how to call carto? (The input file my be a path in the Web or a inline string constand. I do not need the output file, just the pixmap.data() )