chearon / dropflow Goto Github PK
View Code? Open in Web Editor NEWA CSS layout engine
Home Page: https://chearon.github.io/dropflow/
License: MIT License
A CSS layout engine
Home Page: https://chearon.github.io/dropflow/
License: MIT License
layout
needs to know the viewport size so that it can shift lineboxes and extend block boxes as needed, I think. Then somehow painting needs to be able to paint fragments as well.
Needed for good PDF support, and needed by CellEngine.
Full disclosure: I'm a maintainer of Taffy
I just saw your project on HN, and couldn't help but feel like a library I work on: Taffy https://github.com/DioxusLabs/taffy would be a perfect complement to it. We support Flexbox and CSS Grid layout (and blocks-that-contains-blocks, but I suspect you already implement that) but not any of the rest of the CSS2-style layout (we're also missing some features necessary for full web compatibility: position: static
, direction: rtl
, and box-sizing: content-box
being the main ones off the top of my head).
Taffy is implemented in Rust, and the WASM bindings (DioxusLabs/taffy#394) are currently incomplete (Flexbox is usable with the current state of that PR - CSS Grid isn't yet), but I could look into getting those into a releasable state if you are interested.
You may also wish to consider Yoga https://github.com/facebook/yoga (which I am not affiliated with) which is implemented in C++ and has more established WASM bindings but only supports Flexbox.
I'm almost done with this, but currently, containing blocks are assigned in the layout function (like layoutBlockBox
) which are called as the tree is descended for layout. Because inline layout (createLineboxes
) is called before descendants are laid out, an absolutely positioned inline that doesn't have a left
/right
will need to be positioned in createLineboxes
according to what its position would be if it was not absolute. To do that, it needs to be laid out to get its size, but that's not possible if it has no containing block. So the current blocker is to assign containing blocks during box tree creation rather than as a part of the layout step.
I saw that font-variant
support is planned in the README—I just thought I’d create an issue for myself and others to follow for when it’s released.
I really appreciate you open-sourcing the library and I enjoyed reading about the Harfbuzz and Shout-outs sections in the README. I look forward to trying it out later today!
Very cool! Would it be easy to have a kind of lib using Dropflow which would generate a PNG from a vueJS component and props? This could be useful when we want to generate views as PNG (for instance to send them on Discord)
(and would this also allow to render SVG elements in the component?)
I probably need this to be able to showcase a huge range of scripts on the demo site (see #9).
A good way to do this would be by supporting composite fonts via unicode-range
. I can think of some drawbacks:
Short-term I could ship with a predefined database of font URLs each known to work on certain character ranges, and just tailor that to the correct order over time. This would be an opt-in API since it's doing network stuff, and would not need async layout:
const htmlElement = flow.dom(flow.h(...));
await flow.preloadFonts(htmlElement);
flow.renderToCanvas(htmlElement, canvas);
This is a really cool project - thanks for your work on it.
I'm running into an issue where font-style: italic get's a bold style applied to it when there is a bold tag before it.
Here's a code example:
var textflow = <span style="font-size:30px;font-family:'Noto Sans';font-weight:normal;"> <span>normal</span> <br /> <span style="font-weight:bold">bold</span> <br /> <span style="font-style:italic">italic</span> </span>
;
var canvas = document.createElement('canvas');
canvas.style.width = 500;
canvas.style.height = 500;
canvas.style.position = 'absolute';
canvas.style.top = '100px';
canvas.style.left = '100px';
document.body.appendChild(canvas);
await flow.loadNotoFonts(flow.parse(textflow));
var rootElement = flow.parse(textflow);
var tree = flow.generate(rootElement);
flow.layout(tree, 500, 500);
flow.paintToCanvas(tree, canvas.getContext("2d"));
The result of the paintToCanvas is a canvas that renders like this:
The word 'italic' should not be in bold. Any ideas on what could be causing this or if there is something I'm doing wrong?
Thanks,
I tested it on the demo site, and it doesn't seem to support hyphenation at the moment? Is there any planned support for the future?
How to turn on automatic hyphenation?
<html lang="en">
hyphens: auto;
And other CSS properties to control hyphenation. like:
hyphenate-limit-chars: 6 3 2;
hyphenate-limit-lines: 2;
hyphenate-limit-last: always;
hyphenate-limit-zone: 8%
These scripts don't separate words with spaces, and require a dictionary. In line breaking, iterate scripts as well as everything else. If the script is one of these, switch to a specialized break iterator.
This library can break all 4, but need to look into efficiency. Should probably use a trie.
Wikipedia says the first 3 are related through the Khmer writing system:
But Burmese is not:
Sort of related: Vietnam borders Cambodia and Laos, Vietnamese people today use a variant of Latin. I may need to tweak font selection at some point in order to properly select a Vietnamese font for the entire run instead of having a mix of a Latin and Vietnamese font, since I don't think Latin fonts necessarily support all of Vietnamese.
Thank you for creating dropflow! It looks really cool. 🙂
I was wondering if you are also planning to add letter-spacing
at some point? It’s the one essential typographic feature I’m currently missing.
I've been battling some line height issues (or at least what I believe are issues related to line height on block style elements). The image below is just really simple HTML on the left and that same HTML rendered to the canvas on the right. My expectation was that both words would have the same vertical position.
Code for the left 'Test':
<p style="font-size:120px;font-family:'Noto Sans'">Test</p>
Code for the right 'Test':
var textflow = `<p style="font-size:120px;font-family:'Noto Sans'">Test</p>`;
var rootElement = flow.parse(textflow);
var tree = flow.generate(rootElement);
flow.paintToCanvas(tree, context)
Wondering if there is something obvious that I'm missing here? I've tried every combination of line-height and margins that I can think of. Any ideas what I might be overlooking (or if there is an issue)?
This project is really amazing and challenging! I'm amazed that you've pulled it off!
I encountered an issue with my language while testing the demo. My guess is that the engine doesn't understand how to break Khmer text.
រស់ សេរីសុទ្ធា កេីតនៅថ្ងៃទី០៦ ខែ វិច្ឆិកា ឆ្នាំ ១៩៤៧ នាងមានកំណើតនៅក្នុងគ្រួសារកសិករក្រីក្រមួយ ក្នុង ខេត្តបាត់ដំបង ។ សុធា មានឪពុកឈ្មោះ រស់ ប៊ុន និង ម្ដាយឈ្មោះ ណាត់ សាមៀន និង មានបងប្អូនចំនួន ៥ នាក់ ប្រុស១នាក់ ស្រី៤នាក់ (បច្ចុប្បន្នស្លាប់ទាំងអស់) បងស្រីទី១ ឈ្មោះ រស់ សាបឿន, បងស្រីទី២ ឈ្មោះ រស់សាបឿត, បងប្រុសទី៣ ឈ្មោះ រស់ សុគន្ធ, ទី៤សុធាផ្ទាល់ ឈ្មោះ រស់ សេរីសុទ្ធា និង ទី៥ប្អូនស្រី ឈ្មោះ រស់ សេរីសោភ័ណ, សុធា នាងបានចូលសិក្សា ថ្នាក់អនុវិទ្យាល័យនេតយ៉ង់ ក្នុងខេត្តបាត់ដំបង ។ ទេពកោសល្យ របស់សុធា បានរកឃើញនៅអាយុ ១៣ ឆ្នាំ ពេលដែលនាងបានច្រៀងចម្រៀង ប្រចាំថ្នាក់អនុវិទ្យាល័យ ក្នុងឆ្នាំ ១៩៦០ សិស្សរួមថ្នាក់ និងលោកគ្រូ អ្នកគ្រូ តែងតែសរសើរទឹកដមសម្លេងដ៏ច្បាស់ ស្រទន់ និង ខ្លាំងរបស់ សុធា ដែលប្រៀបបាននិង សម្លេងរណ្ដំរបស់សត្វរៃ ហើយសុធា ត្រូវបានគេដាក់ឈ្មោះហៅក្រៅពេលនោះថា (អារៃ) ដោយឪពុករបស់សុធា មើលឃើញពីទេពកោសល្យផ្នែកចម្រៀងដូចនេះ ក៏បាននាំសុធា ដើរច្រៀងតាមពិធីមង្គលការ និង ភោជនីយដ្ឋានផ្សេងៗ ក្នុងខេត្តបាត់ដំបង ។
This should not be too hard. Add a new paint backend that implements:
text
(produce SVG <text>
elements)edge
(produce a <rect>
)rect
(produce a <rect>
)And concatenates a string. Add a public paintToSvg
method that returns the SVG string from the backend.
Harder:
SVGElement
s or VNodes in case people are rendering with React or Vue?Hi, this project is very useful for me. But it seems can't display chinese character.
I download a chinese font from google font here. Copy your example code, just replace the font to the downloaded font, and replace the 'hello world' to ramdom chinese character. The result is two square. It works if i change back to english.
Thanks for your great work!
This project looks great, but it seems to be missing a license. Would you be willing to add an explicit licence so that people can know when and where they can use it? My preferred licence would be dual MIT/Apache 2.0 but obviously you can and should choose whichever license you see fit.
I'm not able to get dropflow running with the basic example from the README.
Reproduction Link: https://github.com/Bobakanoosh/dropflow-not-working
Reproduction Steps:
pnpm i
pnpx tsx main.ts
Yields:
Error: ENOENT: no such file or directory, open 'C:\Users\jackb\Documents\Development\testing\dropflow\node_modules\.pnpm\[email protected]\node_modules\dropflow\dist\overflow.wasm'
Until there's support for table/grid/flexbox, there should be an example of how you can construct higher-level layout manually
Right now the method is to scale the canvas by a certain amount before painting and round paint coordinates for rects to the nearest integer. But that only produces sharp edges when the zoom is an integer.
It's not hard to support any arbitrary zoom level (like 1.3333
). Don't scale the canvas at all and instead, the painting code receives the zoom level as an input, multiplies the layout coordinates by that, and then rounds. This is particularly useful for browser zoom, which makes the window.devicePixelRatio
non-even. Things should line up correctly.
Browsers use a much more complicated method of snapping the layouts to pixels. I don't totally know why that is; maybe to produce more desirable results at the extremes.
Nice project! There are a few typographic properties that Chrome and Firefox haven’t prioritized (after a decade!), so maybe adding them to dropflow would finally let us render near print-quality text on the web. They seem straightforward to me, but would it be a lot of work?
feature | CSS property | Safari | Chrome | Firefox |
---|---|---|---|---|
Drop capitals | initial-letter | ✔️ | ✔️ | ❌ |
Hanging indent | text-indent with hanging and each-line |
✔️ | ❌ | ✔️ |
Hanging punctuation | hanging-punctuation | ✔️ | ❌ | ❌ |
Orphans | orphans | ✔️ | ✔️ | ❌ |
Drop capitals:
Hanging indent:
Hanging punctuation:
Is the <img/>
tag going to be supported? I currently use Satori to convert HTML+CSS --> PNG for OG Image purposes, but it's a bit slow. Was moving to this and then realized it doesn't support images.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.