HN Tanıtım: Node veya <canvas> için CSS düzen motoru Dropflow
(github.com/chearon)- Dropflow, temel CSS standartları olan inline, block, float, positioning ve gelecekte table’ları incelemek için geliştirilmiş bir CSS düzen motorudur; flexbox ve grid kapsam dışıdır
- Node ve node-canvas ile arka uçta PDF/görsel üretimi için kullanılabilir; tarayıcıda ise canvas üzerinde satır sonlandırmalı zengin metin render edebilir
- Metin düzeni çift yönlü ve RTL metin, grafem bazında font fallback, renkli aksan işaretleri, OpenType/TrueType font kaydı ve HarfBuzz tabanlı shaping destekler
- CSS destek kapsamı 30’dan fazla özelliği içerir;
float,clear,display: inline-block,position: relative,overflow,z-index,zoomgibi özellikler çalışır;table,absolute,fixed,transformgibi özellikler plan aşamasındadır - En yüksek performans, HTML/CSS ayrıştırmayı atlayan hyperscript API ve yeniden kullanılabilir style nesneleriyle elde edilir; 2019 MacBook Pro’da 500’den fazla paragraflık The Little Prince’i HTML’den görsele 160 ms’nin altında dönüştürür
Dropflow’un ele aldığı CSS düzen kapsamı
- Dropflow, foundational CSS standards kapsamını incelemek için geliştirilmiş bir CSS düzen motorudur
- Hedeflenenler inline, block, float, positioning ve gelecekte tables’tır
- flexbox ve grid hedeflenmez
- Yüksek kaliteli metin düzeni uygulamasına sahiptir ve dünyanın birçok dilini gösterebilir
- İki kullanım alanı sunulur
- Node ve node-canvas kullanarak arka uçta PDF veya görsel üretimi
- Tarayıcıda canvas üzerinde satır sonlandırmalı zengin metin render etme
Başlıca özellikler
- 30’dan fazla CSS özelliğini destekler;
floatgibi karmaşık özellikler de buna dahildir - Metinle ilgili özellikleri geniştir
- Çift yönlü metin ve RTL metin
- Grafem bazında font fallback
- Renkli aksan işaretleri
- Tercih edilen satır sonu işleme; örneğin başlangıç padding’ini sonraki satıra taşıma
- Optimize edilmiş shaping
- Girdi yöntemi olarak HTML/CSS’nin yanı sıra, stilleri nesne olarak ileten hyperscript
h()API de desteklenir - OpenType/TrueType buffer’ları kaydedilebilir ve kayıt zorunludur
<img>JPEG, BMP, PNG ve GIF’i destekler; ancak destek arka uca göre değişebilir- Kalıtılan ve cascade edilmiş stiller iki kez hesaplanmaz
- Tipleri eksiksizdir, çok sayıda testi vardır ve hızlı çalışmayı hedefler
CSS destek durumu
- Inline formatting’de çalışan öğeler arasında
color,direction,font-family,font-size,font-stretch,font-style,font-weight,line-height,text-align,vertical-align,white-space,word-break,overflow-wrap,word-wrap,word-spacingvb. bulunur - Inline formatting’de
font-variant,letter-spacing,tab-size,text-decoration,unicode-bidiplanlandı durumundadır - Block formatting’de
clearvefloatçalışırwriting-modeiçinhorizontal-tb,vertical-lr,vertical-rlkısmen uygulanmış durumdadır- BFC’de uygulanmıştır, ancak IFC’de henüz uygulanmamıştır
- Boxes and positioning’de birçok kutu ve yerleşim özelliği çalışır
background-clip,background-color,border-color,border-style,border-widthtop,right,bottom,leftbox-sizingdisplay: block,inline,inline-block,flow-root,noneheight,margin,padding,overflow,width,z-index,zoomposition: relative
- Boxes and positioning’de
display: table,min/max-height,min/max-width,position: absolute,position: fixed,transformplanlandı durumundadır
Temel kullanım akışı
- Dropflow, tarayıcıyla aynı şekilde kalıtılmış ve hesaplanmış stillere sahip bir DOM temelinde çalışır
- Tipik akış şöyledir
- Düzen öncesinde
FontFaceile font kaydı flow.style()ile style nesnesi oluşturmaflow.h()ile DOM oluşturmaflow.dom()ile render edilecek ağacı hazırlamarenderToCanvas()ile canvas’ın tamamında layout ve paint işlemi yapma
- Düzen öncesinde
- HTML API de sağlanır; ancak yalnızca performans önemli değilse veya öğrenme amacıyla önerilir
- Ayrıştırma ek süre yaratır ve bundle boyutunu ciddi ölçüde artırır
- HTML ayrıştırmanın @fb55 sayesinde hızlı olduğu belirtilir
- Şu anda yalnızca
styleHTML özelliği desteklenir;classhenüz çalışmaz
Performans özellikleri
- Performans en üst düzey hedeftir ve doğruluktan sonra gelir
- Örneklere dayalı performans rakamları sunulur
- Birden fazla inline span ve farklı fontlar içeren 8 paragrafı HTML’den görsele dönüştürme: 2019 MacBook Pro’da 9 ms, 2012 MacBook Pro’da 13 ms
- 500’den fazla paragraflık The Little Prince’i HTML’den görsele dönüştürme: 2019 MacBook Pro’da 160 ms’nin altında, 2012 MacBook Pro’da 250 ms’nin altında
- 10 karakterlik kelime oluşturup yalnızca layout yapma: 2019 MacBook Pro’da 25 µs’nin altında, 2012 MacBook Pro’da 50 µs’nin altında
- En hızlı performans, DOM’u hyperscript API ile doğrudan oluşturup normal HTML/CSS ayrıştırma adımlarını atladığınızda elde edilir
- style nesnelerini yeniden kullanmak daha fazla avantaj sağlar
- Farklı genişliklerde reflow yapmak, layout tree’yi yeniden oluşturmaktan daha hızlıdır
API yapısı
- Temel adımlar ikidir
- Font kaydı
- Hyperscript API veya Parse API ile DOM oluşturma
- Basit kullanımda DOM doğrudan canvas’a render edilebilir
renderToCanvas(el, canvas), tüm layout’u render etmek için canvas’ın width ve height değerlerini viewport boyutu olarak kullanır
- Daha düşük seviyeli API kullanıldığında layout korunabilir
- Bağımlı kaynakları yükleme
- DOM’un layout’unu oluşturma
- layout reflow
- HTML5 canvas gibi bir hedefe paint etme
- Bu yöntem farklı boyutlara reflow yapmak, görünmeyen layout’u paint etmemek veya intrinsics elde etmek için kullanılabilir
Font işleme
- Bir Dropflow programının ilk adımı, CSS font özellikleriyle seçilecek fontları kaydetmektir
- Dropflow sistem fontlarını aramaz, bu nedenle en az bir kez
FontFaceoluşturup eklemek gerekir - Font kayıt API’si CSS Font Loading API’nin bir alt kümesini uygular ve standart dışı
loadSyncmetodunu ekler - Sunucu tarafında
file:///URL’lerireadFileSyncile senkron yüklenebilir ArrayBuffer, tarayıcıdaki gibi constructor’da hemen yüklenirregisterNotoFonts, tüm Noto Sans font ailelerini kaydeder- Fontlar FontSource tarafından dağıtılır ve jsDelivr tarafından barındırılır
- 200’den fazla Noto Sans fontu bulunduğu ve CJK fontlarının
unicodeRangedizeleri büyük olduğu için büyük bir import’tur - Tarayıcıda production kullanımında tek tek fontları kaydetmenin daha iyi olduğu belirtilir
- Latin için italic fontlar kaydedilir; tüm script’ler için normal 400 ve bold 700 kaydedilir
- Çince, Korece ve Japonca ortak Unicode code point’leri paylaşır; ancak karakterler farklı render edilebildiğinden, mümkünse belirli dile ait font kullanmak daha iyidir
Layout, reflow, paint
layout(el), box tree, fragmentation tree ve glyph’leri içeren bir layout oluşturur- box tree genel olarak DOM tree ile eşleşir; ancak anonim metin içeriği nedeniyle daha fazla box oluşabilir veya
display: nonenedeniyle daha az olabilir
- box tree genel olarak DOM tree ile eşleşir; ancak anonim metin içeriği nedeniyle daha fazla box oluşabilir veya
reflow(layout, width = 640, height = 480), box’ları yerleştirir, metni satırlara böler ve paint edilebilir hale getirir- block box’larda margin collapsing
- Metni HarfBuzz’a aktarma
- Font fallback yinelemesi
- Satır sonu ve break point’lere göre reshaping
- float yerleşimi ve
clearişleme directionve metin yönüne göre shaped text span ve background yerleşimifloat,inline-block,absoluteiçeriklerinin intrinsics hesaplaması- normal flow sonrasında
positionişleme
- Paint hedefleri şu anda Canvas ve SVG destekler
paintToCanvas, tarayıcı canvas’ına, node-canvas’a ve benzer standart uyumlu context’lere paint ederpaintToSvg, SVG string’i oluşturur veFontFace’e verilen URL’lere referans veren@font-facekurallarını içerirpaintToSvgElements, mevcut bir SVG içine çizim yapmak için<svg>ve@font-facekurallarını eklemezpaintToHtml, mutlak konumlandırılmış öğelerden flat list oluşturur; kullanımı önerilmez ama geliştirme sırasında yararlı olabilir
DOM API ve ortam hook’ları
- Hyperscript ve Parse API’den alınan kök
HTMLElement, tarayıcınınquerySelectorailesine benzer şekilde tag name,id,classile öğe bulma metotları sağlarquery(selector)tek birHTMLElementveyanulldöndürürqueryAll(selector)HTMLElement[]döndürür
HTMLElementbağlı bir render box’a sahip olabilir- Genellikle birdir; ancak inline ve block içerikler karıştığında birden fazla olabilir
BlockContainer, absolute positioned element, floated element, inline-block ve block-level element için oluşturulurReplacedBoxgörseller için oluşturulur
- Dropflow, çeşitli ortamlara uyarlanabilecek şekilde tasarlanmıştır
- Tarayıcıda font ve görseller
fetchile yüklenir; font buffer’larıdocument.fontsiçine kaydedilir - Nodejs’te fontlar
fs.readFileSyncile senkron yüklenebilir - canvas arka ucu ve
node-canvasvarsanode-canvasınregisterFontmetodu çağrılır node-canvasfont buffer desteklemediğindenfile://URL kullanılmalıdır
- Tarayıcıda font ve görseller
@napi-rs/canvasveyaskia-canvaskullanmak içinflow.environment.registerFont’u ilgili font kayıt API’sine bağlayan birkaç satır kod gerekir- Ortamda 6 hook bulunur
wasmLocatorregisterFontresolveUrlresolveUrlSynccreateDecodedImagedestroyDecodedImage
HarfBuzz tabanlı metin shaping
- Glyph layout, WebAssembly’ye derlenmiş HarfBuzz tarafından yapılır
measureTextAPI ile metin span konumlandırma yaklaşımıyla elde edilmesi zor olan doğruluğu hedefler- Örnek olarak Google Sheets’te
"AV"içindeki yalnızca"V"farklı renkle boyanırsa kerning kaybolur ve harfler normalden daha uzak görünür- Çünkü iki
measureTextvefillTextçağrısı karakter bazında gerçekleşir; bu da contextual glyph advance’i ortadan kaldırır
- Çünkü iki
- Dropflow, rengin değiştiği noktada değil, daha kaba shaping boundary’lerinde HarfBuzz kullanarak fontları daha doğru destekler
- WebAssembly’ye derlenmiş HarfBuzz,
CanvasRenderingContext2D’ninmeasureTextmetoduna benzer performans göstergeleri sunabilirmeasureTextkadar hızlı değildir, ama belirgin ölçüde yavaş da olmadığı belirtilir- İkisinin de metin düzeni yığınındaki baskın darboğaz olmadığı belirtilir
measureTexttabanlı metin düzeninin hızlı çalışması için word cache gerekir; GSuite uygulamalarının bu yöntemi kullandığı belirtilir- word cache, boşlukları aşan etkileri olan fontları destekleyemez
- Bu tür fontları desteklemek için paragrafın break index’lerinde ikili arama gerekir ve bunun tüm paragrafı HarfBuzz’a vermekten çok daha yavaş olduğu belirtilir
- Renkli aksan işaretleri
measureTextile mümkün değildir
Bağımlı projeler
- Dropflow’un
package.jsonbağımlılığı yoktur; ancak birçok projenin çalışmalarından yararlanır - JavaScript bağımlılıkları projeye check-in edilmiştir; odağı korumak ve dependency-of-dependency sorunlarından kaçınmak için çeşitli düzeylerde değiştirilmiştir
- Başlıca projeler şunlardır
- harfbuzz: font shaping ve gerekli font API’leri sağlar
- Tehreer/SheenBidi: bidi boundary hesaplama
- foliojs/linebreak: Unicode break index sağlama
- peggyjs/peggy: CSS parser oluşturma
- fb55/htmlparser2: HTML parsing
- google/emoji-segmenter: emoji segmenting
- foliojs/grapheme-breaker: Unicode grapheme boundary sağlama; Unicode 15 için kapsamlı biçimde değiştirildi
- foliojs/unicode-trie: hızlı Unicode data için kullanılır; kullanılmayan bölümlerin kaldırılması için kapsamlı biçimde değiştirildi
3 yorum
Orijinal başlık meğerse "Show HN: Dropflow, a CSS layout engine for node or <canvas>" imiş. Şu anda ise "GN⁺: HN Tanıtımı: Node veya <canvas> için CSS yerleşim motoru, Dropflow</canvas>" olarak girilmiş görünüyor.
Başlıkta etiket olduğu için detay sayfasındaki başlık kısmı bozuluyor sanırım.. haha
escapelazım..Hacker News yorumları
Günümüzde backend'de güzel görünümlü PDF üretmenin temel yolu bir headless browser başlatıp tarayıcı API'siyle HTML/CSS'yi PDF'ye dönüştürmek; ancak sunucuda tarayıcı örnekleri çalıştırmanın ve büyük iş yüklerine göre ölçeklemenin maliyeti epey yüksek.
Bu, oyunu değiştiren bir araç; artık tarayıcı ek yükü olmadan HTML/CSS ile PDF tasarlayıp üretebilirsiniz.
Yalnız benim ürettiğim PDF'ler kesinlikle pek güzel değil; fark bu olabilir.
Hepsinin ADA uyumlu olması gerekiyordu, bu yüzden Canvas renderer'a geçmek zor. Çünkü böyle olunca erişilebilirlik kayboluyor.
Gerçekten iyi görünüyor. Önceki işimde sürükleyici bir çevrimiçi öğrenme platformu geliştirmiştik; Oculus Quest 2 ile Savunma Bakanlığı personeline yabancı dil eğitimi veriyor, WebXR, Three.js vb. kullanıyorduk.
Unity3D bir kez yeterliydi; app store incelemesinden geçmek de istemiyorduk. Kendi cihaz filomuz olduğu için sorun değildi.
En büyük zorluklardan biri ders materyali içerik iş akışını oluşturmaktı ve bunu neredeyse tek başıma yapmam gerekti. Gerçek dil eğitmenleri PowerPoint ile PDF hazırlıyor, benim yaptığım özel editörle PDF'leri içerik veritabanına yüklüyor, eğitim ortamına yerleştiriyor, ardından PDFJS ile Canvas öğesine render edip 3D bir dikdörtgenin dokusu olarak kullanıyordum.
Böyle bir araç olsaydı insanlar materyal hazırlamak için PowerPoint etrafından dolanmak zorunda kalmazdı. PowerPoint yöntemi, önceki denemede insanlara Photoshop ile görsel hazırlatmaktan çok daha hızlı bir iş akışı sağlamıştı; ama uygulamanın içine bir tabela editörü koyabilseydik “ortamda nasıl görüneceğini tahmin et → PDF olarak dışa aktar → DB'ye yükle → gerçek görünümü kontrol et” döngüsünü ortadan kaldıracağı için daha iyi olurdu.
Gerçi ürün değil yalnızca hizmet satmayı bilen bir iş geliştirme ya da pazarlama ekibimiz olmadığı için bunun da pek anlamı olmazdı.
Kendi projem https://htwins.net/scale2 ve SVG ya da Canvas kullanan diğer çalışmalar için böyle bir şey arıyordum.
Flexbox ile uğraşıyorsanız, responsive layout hazırlarken birden fazla özellikle ilgilenmemeniz için süreci sadeleştiren bir araç kullanabilirsiniz: https://flexboxcss.com
Harika. Bu tür araçlar, tarayıcı rendering engine denen sihirli kutuyu anlaşılır hale getirdiği için çok önemli.
HTML ve CSS rendering hakkında tamamen makine tarafından okunabilir bir spesifikasyon oluşturulabilirse renderer üretmek mümkün olurdu. Tarayıcıya özgü tuhaf davranışlar bunun üzerinde extension olarak yer alabilir. https://github.com/tawesoft/html5spec gibi bir şeyin gerçek engine'lerde kullanılacak hale gelmesi güzel olurdu.
Son zamanlarda merak ettiğim şeye oldukça yakın. CSS ve SVG'nin, grafik ve UI kütüphanelerinin üzerinde bir soyutlama katmanı olarak kullanılıp kullanılamayacağını düşünüyordum.
node-canvas'ı ilk kez duydum; bunun çizim tarafını tamamlıyor gibi görünüyor. Bu araç ise bir UI kütüphanesinden ihtiyaç duyduğum her şey olan layout kısmını üstlenebilir gibi.
CSS implementasyonunun ne kadar zor olduğunu da merak ediyorum. Epey karmaşık olduğunu duymuştum.
CSS implementasyonu zordu, ama en büyük engel bilginin iyi görünür olmamasıydı.
En zor kısım metin yerleşimi. Glyph'lerle uğraşmak ve RTL için geriye doğru dolaşmak baş ağrıtıcı; satır kırma da gerçekten karmaşık hale geliyor. Gerekli bilginin tek bir yerde toplanmamış olması işi daha da zorlaştırıyor.
Başlarda block layout'u bitirdikten sonra birkaç yıl boyunca haftada yalnızca birkaç saat çalışarak text shaping ve itemization ayrıntılarını, yapılacakları ve kaçınılacakları öğrenmem gerekti. Birçok şeyi Pango [1] kaynak kodunu okuyarak öğrendim, kalanını da Google aramalarıyla bir araya getirdim.
Bunun dışında W3C spesifikasyonları neredeyse her şeyi kapsıyor. CSS2 standardı [2], şimdiye kadar okuduğum en güzel belgelerden biriydi. İçsel olarak tutarlı ve öz; yıllar süren düşünme ve deneme-yanılmanın sonucu. CSS3 de harika ama CSS2 her şeyin temeli.
[1] https://gitlab.gnome.org/GNOME/pango/
[2] https://www.w3.org/TR/CSS22/
Dünyaya büyük bir katkı. Herkesin “birinin $X yapması lazım” diye düşündüğü ama kimsenin yapmadığı klasik örneklerden biri gibi görünüyor.
Layout için CSS kullanmayı seven biri olarak bugünlerde çoğunlukla Flexbox ve Grid'e dayanıyorum. Henüz desteklememeniz tamamen anlaşılır; ama bir gün destekleme planınız olup olmadığını merak ediyorum. Varsa başkaları nasıl yardımcı olabilir?
Gerçekten müthiş. HTML'yi programatik olarak PNG'ye dönüştürmenin ne kadar zor olduğunu çoğu kişi muhtemelen bilmiyor.
Node ile tarayıcı arasındaki farklar ya da HTML ile Canvas arasındaki farklar yüzünden sayısız küçük sorunla karşılaşıyorsunuz.
Yararlı görünüyor. Önce CSS'yi anlayıp onun üzerine bir layout engine inşa etmenin ne kadar çok iş gerektirdiğini hayal etmek zor.