mirror of
https://github.com/penpot/penpot.git
synced 2025-12-11 22:14:05 +01:00
🎉 Add missing styles on text leaves
This commit is contained in:
committed by
Elena Torro
parent
15eee0d8d8
commit
596193d34d
@@ -16,7 +16,7 @@
|
||||
[app.render-wasm.wasm :as wasm]))
|
||||
|
||||
(def ^:const PARAGRAPH-ATTR-U8-SIZE 44)
|
||||
(def ^:const LEAF-ATTR-U8-SIZE 56)
|
||||
(def ^:const LEAF-ATTR-U8-SIZE 60)
|
||||
|
||||
(defn- encode-text
|
||||
"Into an UTF8 buffer. Returns an ArrayBuffer instance"
|
||||
@@ -79,6 +79,7 @@
|
||||
(reduce (fn [offset leaf]
|
||||
(let [font-style (sr/translate-font-style (get leaf :font-style))
|
||||
font-size (get leaf :font-size)
|
||||
letter-spacing (get leaf :letter-spacing)
|
||||
font-weight (get leaf :font-weight)
|
||||
font-id (f/normalize-font-id (get leaf :font-id))
|
||||
font-family (hash (get leaf :font-family))
|
||||
@@ -104,15 +105,21 @@
|
||||
text-transform
|
||||
(or (sr/translate-text-transform (:text-transform leaf))
|
||||
(sr/translate-text-transform (:text-transform paragraph))
|
||||
(sr/translate-text-transform "none"))]
|
||||
(sr/translate-text-transform "none"))
|
||||
|
||||
text-direction
|
||||
(or (sr/translate-text-direction (:text-direction leaf))
|
||||
(sr/translate-text-direction (:text-direction paragraph))
|
||||
(sr/translate-text-direction "ltr"))]
|
||||
|
||||
(-> offset
|
||||
(mem/write-u8 dview font-style)
|
||||
(mem/write-u8 dview text-decoration)
|
||||
(mem/write-u8 dview text-transform)
|
||||
(+ 1) ;;padding
|
||||
(mem/write-u8 dview text-direction)
|
||||
|
||||
(mem/write-f32 dview font-size)
|
||||
(mem/write-f32 dview letter-spacing)
|
||||
(mem/write-u32 dview font-weight)
|
||||
|
||||
(mem/write-uuid dview font-id)
|
||||
|
||||
@@ -310,7 +310,8 @@
|
||||
[text-direction]
|
||||
(case text-direction
|
||||
"ltr" 0
|
||||
"rtl" 1))
|
||||
"rtl" 1
|
||||
0))
|
||||
|
||||
(defn translate-font-style
|
||||
[font-style]
|
||||
|
||||
@@ -3,12 +3,20 @@
|
||||
color: #eee;
|
||||
}
|
||||
|
||||
html, body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
canvas {
|
||||
width: 100cqw;
|
||||
}
|
||||
|
||||
.text-editor-container {
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
#output {
|
||||
font-family: monospace;
|
||||
padding: 1rem;
|
||||
border: 1px solid #333;
|
||||
.playground {
|
||||
display: grid;
|
||||
max-width: 1280px;
|
||||
}
|
||||
|
||||
@@ -8,220 +8,216 @@
|
||||
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,300..800;1,300..800&family=Playwrite+ES:wght@100..400&family=Playwrite+NZ:wght@100..400&family=Playwrite+US+Trad:wght@100..400&family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap" rel="stylesheet">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Penpot - Text Editor Playground</title>
|
||||
<style>
|
||||
#output {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<form>
|
||||
<fieldset>
|
||||
<legend>Styles</legend>
|
||||
<!-- Font -->
|
||||
<div class="form-group">
|
||||
<label for="font-family">Font family</label>
|
||||
<select id="font-family">
|
||||
<option value="Open+Sans">Open Sans</option>
|
||||
<option value="sourcesanspro">Source Sans Pro</option>
|
||||
<option value="whatever">Whatever</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="font-size">Font size</label>
|
||||
<input id="font-size" type="number" value="14" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="font-weight">Font weight</label>
|
||||
<select id="font-weight">
|
||||
<option value="100">100</option>
|
||||
<option value="200">200</option>
|
||||
<option value="300">300</option>
|
||||
<option value="400">400 (normal)</option>
|
||||
<option value="500">500</option>
|
||||
<option value="600">600</option>
|
||||
<option value="700">700 (bold)</option>
|
||||
<option value="800">800</option>
|
||||
<option value="900">900</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="font-style">Font style</label>
|
||||
<select id="font-style">
|
||||
<option value="normal">normal</option>
|
||||
<option value="italic">italic</option>
|
||||
<option value="oblique">oblique</option>
|
||||
</select>
|
||||
</div>
|
||||
<!-- Text attributes -->
|
||||
<div class="form-group">
|
||||
<label for="line-height">Line height</label>
|
||||
<input id="line-height" type="number" value="1.0" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="letter-spacing">Letter spacing</label>
|
||||
<input id="letter-spacing" type="number" value="0.0" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="direction-ltr">LTR</label>
|
||||
<input id="direction-ltr" type="radio" name="direction" value="ltr" checked />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="direction-rtl">RTL</label>
|
||||
<input id="direction-rtl" type="radio" name="direction" value="rtl" />
|
||||
</div>
|
||||
<!-- Text Align -->
|
||||
<div class="form-group">
|
||||
<label for="text-align-left">Align left</label>
|
||||
<input id="text-align-left" type="radio" name="text-align" value="left" checked />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="text-align-center">Align center</label>
|
||||
<input id="text-align-center" type="radio" name="text-align" value="center" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="text-align-right">Align right</label>
|
||||
<input id="text-align-right" type="radio" name="text-align" value="right" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="text-align-justify">Align justify</label>
|
||||
<input id="text-align-justify" type="radio" name="text-align" value="justify" />
|
||||
</div>
|
||||
<!-- Text Transform -->
|
||||
<div class="form-group">
|
||||
<label for="text-transform-none">None</label>
|
||||
<input id="text-transform-none" type="radio" name="text-transform" value="none" checked />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="text-transform-uppercase">Uppercase</label>
|
||||
<input id="text-transform-uppercase" type="radio" name="text-transform" value="uppercase" checked />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="text-transform-capitalize">Capitalize</label>
|
||||
<input id="text-transform-capitalize" type="radio" name="text-transform" value="capitalize" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="text-transform-lowercase">Lowercase</label>
|
||||
<input id="text-transform-lowercase" type="radio" name="text-transform" value="lowercase" />
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>Debug</legend>
|
||||
<div class="form-group">
|
||||
<label for="direction">Direction</label>
|
||||
<input id="direction" readonly type="text" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="focus-node">Focus Node</label>
|
||||
<input id="focus-node" readonly type="text" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="focus-offset">Focus offset</label>
|
||||
<input id="focus-offset" readonly type="number">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="focus-inline">Focus Inline</label>
|
||||
<input id="focus-inline" readonly type="text" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="focus-paragraph">Focus Paragraph</label>
|
||||
<input id="focus-paragraph" readonly type="text" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="anchor-node">Anchor Node</label>
|
||||
<input id="anchor-node" readonly type="text" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="anchor-offset">Anchor offset</label>
|
||||
<input id="anchor-offset" readonly type="number">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="anchor-inline">Anchor Inline</label>
|
||||
<input id="anchor-inline" readonly type="text" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="anchor-paragraph">Anchor Paragraph</label>
|
||||
<input id="anchor-paragraph" readonly type="text" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="start-container">Start container</label>
|
||||
<input id="start-container" readonly type="text" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="start-offset">Start offset</label>
|
||||
<input id="start-offset" readonly type="text" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="end-container">End container</label>
|
||||
<input id="end-container" readonly type="text" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="end-offset">End offset</label>
|
||||
<input id="end-offset" readonly type="text" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="multi">Multi?</label>
|
||||
<input id="multi" readonly type="checkbox" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="multi-inline">Multi inline?</label>
|
||||
<input id="multi-inline" readonly type="checkbox" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="multi-paragraph">Multi paragraph?</label>
|
||||
<input id="multi-paragraph" readonly type="checkbox" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="is-text-focus">Is text focus?</label>
|
||||
<input id="is-text-focus" readonly type="checkbox" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="is-text-anchor">Is text anchor?</label>
|
||||
<input id="is-text-anchor" readonly type="checkbox" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="is-paragraph-start">Is paragraph start?</label>
|
||||
<input id="is-paragraph-start" readonly type="checkbox" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="is-paragraph-end">Is paragraph end?</label>
|
||||
<input id="is-paragraph-end" readonly type="checkbox" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="is-inline-start">Is inline start?</label>
|
||||
<input id="is-inline-start" readonly type="checkbox" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="is-inline-end">Is inline end?</label>
|
||||
<input id="is-inline-end" readonly type="checkbox">
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
<!--
|
||||
<div class="playground">
|
||||
<form>
|
||||
<fieldset>
|
||||
<legend>Styles</legend>
|
||||
<!-- Font -->
|
||||
<div class="form-group">
|
||||
<label for="font-family">Font family</label>
|
||||
<select id="font-family">
|
||||
<option value="Open+Sans">Open Sans</option>
|
||||
<option value="sourcesanspro">Source Sans Pro</option>
|
||||
<option value="whatever">Whatever</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="font-size">Font size</label>
|
||||
<input id="font-size" type="number" value="14" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="font-weight">Font weight</label>
|
||||
<select id="font-weight">
|
||||
<option value="100">100</option>
|
||||
<option value="200">200</option>
|
||||
<option value="300">300</option>
|
||||
<option value="400">400 (normal)</option>
|
||||
<option value="500">500</option>
|
||||
<option value="600">600</option>
|
||||
<option value="700">700 (bold)</option>
|
||||
<option value="800">800</option>
|
||||
<option value="900">900</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="font-style">Font style</label>
|
||||
<select id="font-style">
|
||||
<option value="normal">normal</option>
|
||||
<option value="italic">italic</option>
|
||||
<option value="oblique">oblique</option>
|
||||
</select>
|
||||
</div>
|
||||
<!-- Text attributes -->
|
||||
<div class="form-group">
|
||||
<label for="line-height">Line height</label>
|
||||
<input id="line-height" type="number" value="1.0" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="letter-spacing">Letter spacing</label>
|
||||
<input id="letter-spacing" type="number" value="0.0" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="direction-ltr">LTR</label>
|
||||
<input id="direction-ltr" type="radio" name="direction" value="ltr" checked />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="direction-rtl">RTL</label>
|
||||
<input id="direction-rtl" type="radio" name="direction" value="rtl" />
|
||||
</div>
|
||||
<!-- Text Align -->
|
||||
<div class="form-group">
|
||||
<label for="text-align-left">Align left</label>
|
||||
<input id="text-align-left" type="radio" name="text-align" value="left" checked />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="text-align-center">Align center</label>
|
||||
<input id="text-align-center" type="radio" name="text-align" value="center" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="text-align-right">Align right</label>
|
||||
<input id="text-align-right" type="radio" name="text-align" value="right" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="text-align-justify">Align justify</label>
|
||||
<input id="text-align-justify" type="radio" name="text-align" value="justify" />
|
||||
</div>
|
||||
<!-- Text Transform -->
|
||||
<div class="form-group">
|
||||
<label for="text-transform-none">None</label>
|
||||
<input id="text-transform-none" type="radio" name="text-transform" value="none" checked />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="text-transform-uppercase">Uppercase</label>
|
||||
<input id="text-transform-uppercase" type="radio" name="text-transform" value="uppercase" checked />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="text-transform-capitalize">Capitalize</label>
|
||||
<input id="text-transform-capitalize" type="radio" name="text-transform" value="capitalize" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="text-transform-lowercase">Lowercase</label>
|
||||
<input id="text-transform-lowercase" type="radio" name="text-transform" value="lowercase" />
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>Debug</legend>
|
||||
<div class="form-group">
|
||||
<label for="direction">Direction</label>
|
||||
<input id="direction" readonly type="text" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="focus-node">Focus Node</label>
|
||||
<input id="focus-node" readonly type="text" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="focus-offset">Focus offset</label>
|
||||
<input id="focus-offset" readonly type="number">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="focus-inline">Focus Inline</label>
|
||||
<input id="focus-inline" readonly type="text" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="focus-paragraph">Focus Paragraph</label>
|
||||
<input id="focus-paragraph" readonly type="text" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="anchor-node">Anchor Node</label>
|
||||
<input id="anchor-node" readonly type="text" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="anchor-offset">Anchor offset</label>
|
||||
<input id="anchor-offset" readonly type="number">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="anchor-inline">Anchor Inline</label>
|
||||
<input id="anchor-inline" readonly type="text" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="anchor-paragraph">Anchor Paragraph</label>
|
||||
<input id="anchor-paragraph" readonly type="text" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="start-container">Start container</label>
|
||||
<input id="start-container" readonly type="text" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="start-offset">Start offset</label>
|
||||
<input id="start-offset" readonly type="text" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="end-container">End container</label>
|
||||
<input id="end-container" readonly type="text" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="end-offset">End offset</label>
|
||||
<input id="end-offset" readonly type="text" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="multi">Multi?</label>
|
||||
<input id="multi" readonly type="checkbox" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="multi-inline">Multi inline?</label>
|
||||
<input id="multi-inline" readonly type="checkbox" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="multi-paragraph">Multi paragraph?</label>
|
||||
<input id="multi-paragraph" readonly type="checkbox" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="is-text-focus">Is text focus?</label>
|
||||
<input id="is-text-focus" readonly type="checkbox" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="is-text-anchor">Is text anchor?</label>
|
||||
<input id="is-text-anchor" readonly type="checkbox" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="is-paragraph-start">Is paragraph start?</label>
|
||||
<input id="is-paragraph-start" readonly type="checkbox" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="is-paragraph-end">Is paragraph end?</label>
|
||||
<input id="is-paragraph-end" readonly type="checkbox" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="is-inline-start">Is inline start?</label>
|
||||
<input id="is-inline-start" readonly type="checkbox" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="is-inline-end">Is inline end?</label>
|
||||
<input id="is-inline-end" readonly type="checkbox">
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
<!--
|
||||
|
||||
Editor
|
||||
Editor
|
||||
|
||||
-->
|
||||
<div class="text-editor-container align-top">
|
||||
<div
|
||||
id="text-editor-selection-imposter"
|
||||
class="text-editor-selection-imposter"></div>
|
||||
<div
|
||||
class="text-editor-content"
|
||||
contenteditable="true"
|
||||
role="textbox"
|
||||
aria-multiline="true"
|
||||
aria-autocomplete="none"
|
||||
spellcheck="false"
|
||||
autocapitalize="false"></div>
|
||||
-->
|
||||
<div class="text-editor-container align-top">
|
||||
<div
|
||||
id="text-editor-selection-imposter"
|
||||
class="text-editor-selection-imposter"></div>
|
||||
<div
|
||||
class="text-editor-content"
|
||||
contenteditable="true"
|
||||
role="textbox"
|
||||
aria-multiline="true"
|
||||
aria-autocomplete="none"
|
||||
spellcheck="false"
|
||||
autocapitalize="false"></div>
|
||||
</div>
|
||||
<!--
|
||||
|
||||
Text output
|
||||
|
||||
-->
|
||||
<canvas id="canvas"></canvas>
|
||||
</div>
|
||||
<!--
|
||||
|
||||
Text output
|
||||
|
||||
-->
|
||||
<div id="output"></div>
|
||||
<canvas id="canvas"></canvas>
|
||||
<script type="module">
|
||||
import "./style.css";
|
||||
import "./editor/TextEditor.css";
|
||||
@@ -403,7 +399,7 @@
|
||||
});
|
||||
}
|
||||
|
||||
const fontSize = 120;
|
||||
const fontSize = 14;
|
||||
const children = [];
|
||||
const uuid = crypto.randomUUID();
|
||||
children.push(uuid);
|
||||
@@ -416,11 +412,8 @@
|
||||
});
|
||||
|
||||
textEditor.addEventListener("needslayout", (e) => {
|
||||
console.log("needs layout");
|
||||
useShape(uuid);
|
||||
console.log("useShape", uuid);
|
||||
updateTextShape(fontSize, textEditor.root);
|
||||
console.log("updateTextShape", fontSize);
|
||||
render();
|
||||
})
|
||||
|
||||
@@ -473,24 +466,11 @@
|
||||
|
||||
const x1 = 0;
|
||||
const y1 = 0;
|
||||
const width = 1000;
|
||||
const height = 500;
|
||||
const width = canvas.width;
|
||||
const height = canvas.height;
|
||||
Module._set_shape_selrect(x1, y1, x1 + width, y1 + height);
|
||||
|
||||
/*
|
||||
if (Math.random() < 0.3) {
|
||||
const numStrokes = getRandomInt(1, 3);
|
||||
for (let j = 0; j < numStrokes; j++) {
|
||||
const strokeWidth = getRandomInt(1, 10);
|
||||
Module._add_shape_center_stroke(strokeWidth, 0, 0, 0);
|
||||
const color = getRandomColor();
|
||||
const argb2 = hexToU32ARGB(color, getRandomFloat(0.1, 1.0));
|
||||
addShapeSolidStrokeFill(argb2);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
addTextShape(fontSize, "Hello, World!");
|
||||
addTextShape(fontSize, "");
|
||||
|
||||
useShape("00000000-0000-0000-0000-000000000000");
|
||||
setShapeChildren(children);
|
||||
|
||||
@@ -290,11 +290,61 @@ function getFontStyle(fontStyle) {
|
||||
}
|
||||
}
|
||||
|
||||
export function updateTextShape(fontSize, root) {
|
||||
const paragraphAttrSize = 48;
|
||||
const leafAttrSize = 56;
|
||||
const fillSize = 160;
|
||||
const PARAGRAPH_ATTR_SIZE = 48;
|
||||
const LEAF_ATTR_SIZE = 60;
|
||||
const FILL_SIZE = 160;
|
||||
|
||||
function setParagraphData(dview, { numLeaves, textAlign, textDirection, textDecoration, textTransform, lineHeight, letterSpacing }) {
|
||||
// Set number of leaves
|
||||
dview.setUint32(0, numLeaves, true);
|
||||
|
||||
// Serialize paragraph attributes
|
||||
dview.setUint8(4, textAlign, true); // text-align: left
|
||||
dview.setUint8(5, textDirection, true); // text-direction: LTR
|
||||
dview.setUint8(6, textDecoration, true); // text-decoration: none
|
||||
dview.setUint8(7, textTransform, true); // text-transform: none
|
||||
dview.setFloat32(8, lineHeight, true); // line-height
|
||||
dview.setFloat32(12, letterSpacing, true); // letter-spacing
|
||||
dview.setUint32(16, 0, true); // typography-ref-file (UUID part 1)
|
||||
dview.setUint32(20, 0, true); // typography-ref-file (UUID part 2)
|
||||
dview.setUint32(24, 0, true); // typography-ref-file (UUID part 3)
|
||||
dview.setUint32(28, 0, true); // typography-ref-file (UUID part 4)
|
||||
dview.setUint32(32, 0, true); // typography-ref-id (UUID part 1)
|
||||
dview.setUint32(36, 0, true); // typography-ref-id (UUID part 2)
|
||||
dview.setUint32(40, 0, true); // typography-ref-id (UUID part 3)
|
||||
dview.setUint32(44, 0, true); // typography-ref-id (UUID part 4)
|
||||
}
|
||||
|
||||
function setLeafData(dview, leafOffset, {
|
||||
fontStyle,
|
||||
fontSize,
|
||||
fontWeight,
|
||||
letterSpacing,
|
||||
textSize,
|
||||
totalFills
|
||||
}) {
|
||||
// Serialize leaf attributes
|
||||
dview.setUint8(leafOffset + 0, fontStyle, true); // font-style: normal
|
||||
dview.setUint8(leafOffset + 1, 0, true); // text-decoration: none
|
||||
dview.setUint8(leafOffset + 2, 0, true); // text-transform: none
|
||||
dview.setUint8(leafOffset + 3, 0, true); // text-direction: ltr
|
||||
dview.setFloat32(leafOffset + 4, fontSize, true); // font-size
|
||||
dview.setFloat32(leafOffset + 8, letterSpacing, true); // letter-spacing
|
||||
dview.setInt32(leafOffset + 12, fontWeight, true); // font-weight: normal
|
||||
dview.setUint32(leafOffset + 16, 0, true); // font-id (UUID part 1)
|
||||
dview.setUint32(leafOffset + 20, 0, true); // font-id (UUID part 2)
|
||||
dview.setUint32(leafOffset + 24, 0, true); // font-id (UUID part 3)
|
||||
dview.setUint32(leafOffset + 28, 0, true); // font-id (UUID part 4)
|
||||
dview.setUint32(leafOffset + 32, 0, true); // font-family hash
|
||||
dview.setUint32(leafOffset + 36, 0, true); // font-variant-id (UUID part 1)
|
||||
dview.setUint32(leafOffset + 40, 0, true); // font-variant-id (UUID part 2)
|
||||
dview.setUint32(leafOffset + 44, 0, true); // font-variant-id (UUID part 3)
|
||||
dview.setUint32(leafOffset + 48, 0, true); // font-variant-id (UUID part 4)
|
||||
dview.setUint32(leafOffset + 52, textSize, true); // text-length
|
||||
dview.setUint32(leafOffset + 56, totalFills, true); // total fills count
|
||||
}
|
||||
|
||||
export function updateTextShape(fontSize, root) {
|
||||
// Calculate fills
|
||||
const fills = [
|
||||
{
|
||||
@@ -305,14 +355,14 @@ export function updateTextShape(fontSize, root) {
|
||||
];
|
||||
|
||||
const totalFills = fills.length;
|
||||
const totalFillsSize = totalFills * fillSize;
|
||||
const totalFillsSize = totalFills * FILL_SIZE;
|
||||
|
||||
const paragraphs = root.children;
|
||||
console.log("paragraphs", paragraphs.length);
|
||||
|
||||
Module._clear_shape_text();
|
||||
for (const paragraph of paragraphs) {
|
||||
let totalSize = paragraphAttrSize;
|
||||
let totalSize = PARAGRAPH_ATTR_SIZE;
|
||||
|
||||
const leaves = paragraph.children;
|
||||
const numLeaves = leaves.length;
|
||||
@@ -323,7 +373,7 @@ export function updateTextShape(fontSize, root) {
|
||||
const textBuffer = new TextEncoder().encode(text);
|
||||
const textSize = textBuffer.byteLength;
|
||||
console.log("text", text, textSize);
|
||||
totalSize += leafAttrSize + totalFillsSize;
|
||||
totalSize += LEAF_ATTR_SIZE + totalFillsSize;
|
||||
}
|
||||
|
||||
totalSize += paragraph.textContent.length;
|
||||
@@ -371,39 +421,30 @@ export function updateTextShape(fontSize, root) {
|
||||
typography_ref_id: [u32; 4],
|
||||
*/
|
||||
|
||||
// Set number of leaves
|
||||
dview.setUint32(0, numLeaves, true);
|
||||
|
||||
// Serialize paragraph attributes
|
||||
dview.setUint8(4, textAlign, true); // text-align: left
|
||||
dview.setUint8(5, textDirection, true); // text-direction: LTR
|
||||
dview.setUint8(6, textDecoration, true); // text-decoration: none
|
||||
dview.setUint8(7, textTransform, true); // text-transform: none
|
||||
dview.setFloat32(8, lineHeight, true); // line-height
|
||||
dview.setFloat32(12, letterSpacing, true); // letter-spacing
|
||||
dview.setUint32(16, 0, true); // typography-ref-file (UUID part 1)
|
||||
dview.setUint32(20, 0, true); // typography-ref-file (UUID part 2)
|
||||
dview.setUint32(24, 0, true); // typography-ref-file (UUID part 3)
|
||||
dview.setUint32(28, 0, true); // typography-ref-file (UUID part 4)
|
||||
dview.setUint32(32, 0, true); // typography-ref-id (UUID part 1)
|
||||
dview.setUint32(36, 0, true); // typography-ref-id (UUID part 2)
|
||||
dview.setUint32(40, 0, true); // typography-ref-id (UUID part 3)
|
||||
dview.setUint32(44, 0, true); // typography-ref-id (UUID part 4)
|
||||
|
||||
let leafOffset = paragraphAttrSize;
|
||||
setParagraphData(dview, {
|
||||
numLeaves,
|
||||
textAlign,
|
||||
textDecoration,
|
||||
textTransform,
|
||||
textDirection,
|
||||
lineHeight,
|
||||
letterSpacing
|
||||
})
|
||||
let leafOffset = PARAGRAPH_ATTR_SIZE;
|
||||
for (const leaf of leaves) {
|
||||
console.log(
|
||||
"leafOffset",
|
||||
leafOffset,
|
||||
paragraphAttrSize,
|
||||
leafAttrSize,
|
||||
fillSize,
|
||||
PARAGRAPH_ATTR_SIZE,
|
||||
LEAF_ATTR_SIZE,
|
||||
FILL_SIZE,
|
||||
totalFills,
|
||||
totalFillsSize,
|
||||
);
|
||||
const fontStyle = getFontStyle(leaf.style.getPropertyValue("font-style"));
|
||||
const fontSize = parseFloat(leaf.style.getPropertyValue("font-size"));
|
||||
console.log("font-size", fontSize);
|
||||
const letterSpacing = parseFloat(leaf.style.getPropertyValue("letter-spacing"))
|
||||
console.log("font-size", fontSize, "letter-spacing", letterSpacing);
|
||||
const fontWeight = parseInt(
|
||||
leaf.style.getPropertyValue("font-weight"),
|
||||
10,
|
||||
@@ -414,35 +455,29 @@ export function updateTextShape(fontSize, root) {
|
||||
const textBuffer = new TextEncoder().encode(text);
|
||||
const textSize = textBuffer.byteLength;
|
||||
|
||||
// Serialize leaf attributes
|
||||
dview.setUint8(leafOffset + 0, fontStyle, true); // font-style: normal
|
||||
dview.setUint8(leafOffset + 1, 0, true); // text-decoration: none
|
||||
dview.setUint8(leafOffset + 2, 0, true); // text-transform: none
|
||||
dview.setFloat32(leafOffset + 4, fontSize, true); // font-size
|
||||
dview.setInt32(leafOffset + 8, fontWeight, true); // font-weight: normal
|
||||
dview.setUint32(leafOffset + 12, 0, true); // font-id (UUID part 1)
|
||||
dview.setUint32(leafOffset + 16, 0, true); // font-id (UUID part 2)
|
||||
dview.setUint32(leafOffset + 20, 0, true); // font-id (UUID part 3)
|
||||
dview.setUint32(leafOffset + 24, 0, true); // font-id (UUID part 4)
|
||||
dview.setUint32(leafOffset + 28, 0, true); // font-family hash
|
||||
dview.setUint32(leafOffset + 32, 0, true); // font-variant-id (UUID part 1)
|
||||
dview.setUint32(leafOffset + 36, 0, true); // font-variant-id (UUID part 2)
|
||||
dview.setUint32(leafOffset + 40, 0, true); // font-variant-id (UUID part 3)
|
||||
dview.setUint32(leafOffset + 44, 0, true); // font-variant-id (UUID part 4)
|
||||
dview.setUint32(leafOffset + 48, textSize, true); // text-length
|
||||
dview.setUint32(leafOffset + 52, totalFills, true); // total fills count
|
||||
setLeafData(dview, leafOffset, {
|
||||
fontStyle,
|
||||
textDecoration: 0,
|
||||
textTransform: 0,
|
||||
textDirection: 0,
|
||||
fontSize,
|
||||
fontWeight,
|
||||
letterSpacing,
|
||||
textSize,
|
||||
totalFills
|
||||
})
|
||||
|
||||
// Serialize fills
|
||||
let fillOffset = leafOffset + leafAttrSize;
|
||||
let fillOffset = leafOffset + LEAF_ATTR_SIZE;
|
||||
fills.forEach((fill) => {
|
||||
if (fill.type === "solid") {
|
||||
const argb = hexToU32ARGB(fill.color, fill.opacity);
|
||||
dview.setUint8(fillOffset + 0, 0x00, true); // Fill type: solid
|
||||
dview.setUint32(fillOffset + 4, argb, true);
|
||||
fillOffset += fillSize; // Move to the next fill
|
||||
fillOffset += FILL_SIZE; // Move to the next fill
|
||||
}
|
||||
});
|
||||
leafOffset += leafAttrSize + totalFillsSize;
|
||||
leafOffset += LEAF_ATTR_SIZE + totalFillsSize;
|
||||
}
|
||||
|
||||
const text = paragraph.textContent;
|
||||
@@ -459,9 +494,6 @@ export function updateTextShape(fontSize, root) {
|
||||
|
||||
export function addTextShape(fontSize, text) {
|
||||
const numLeaves = 1; // Single text leaf for simplicity
|
||||
const paragraphAttrSize = 48;
|
||||
const leafAttrSize = 56;
|
||||
const fillSize = 160;
|
||||
const textBuffer = new TextEncoder().encode(text);
|
||||
const textSize = textBuffer.byteLength;
|
||||
|
||||
@@ -474,10 +506,10 @@ export function addTextShape(fontSize, text) {
|
||||
},
|
||||
];
|
||||
const totalFills = fills.length;
|
||||
const totalFillsSize = totalFills * fillSize;
|
||||
const totalFillsSize = totalFills * FILL_SIZE;
|
||||
|
||||
// Calculate metadata and total buffer size
|
||||
const metadataSize = paragraphAttrSize + leafAttrSize + totalFillsSize;
|
||||
const metadataSize = PARAGRAPH_ATTR_SIZE + LEAF_ATTR_SIZE + totalFillsSize;
|
||||
const totalSize = metadataSize + textSize;
|
||||
|
||||
// Allocate buffer
|
||||
@@ -485,50 +517,37 @@ export function addTextShape(fontSize, text) {
|
||||
const heap = new Uint8Array(Module.HEAPU8.buffer, bufferPtr, totalSize);
|
||||
const dview = new DataView(heap.buffer, bufferPtr, totalSize);
|
||||
|
||||
// Set number of leaves
|
||||
dview.setUint32(0, numLeaves, true);
|
||||
|
||||
// Serialize paragraph attributes
|
||||
dview.setUint8(4, 1); // text-align: left
|
||||
dview.setUint8(5, 0); // text-direction: LTR
|
||||
dview.setUint8(6, 0); // text-decoration: none
|
||||
dview.setUint8(7, 0); // text-transform: none
|
||||
dview.setFloat32(8, 1.2, true); // line-height
|
||||
dview.setFloat32(12, 0, true); // letter-spacing
|
||||
dview.setUint32(16, 0, true); // typography-ref-file (UUID part 1)
|
||||
dview.setUint32(20, 0, true); // typography-ref-file (UUID part 2)
|
||||
dview.setUint32(24, 0, true); // typography-ref-file (UUID part 3)
|
||||
dview.setInt32(28, 0, true); // typography-ref-file (UUID part 4)
|
||||
dview.setUint32(32, 0, true); // typography-ref-id (UUID part 1)
|
||||
dview.setUint32(36, 0, true); // typography-ref-id (UUID part 2)
|
||||
dview.setUint32(40, 0, true); // typography-ref-id (UUID part 3)
|
||||
dview.setInt32(44, 0, true); // typography-ref-id (UUID part 4)
|
||||
setParagraphData(dview, {
|
||||
numLeaves,
|
||||
textAlign: 0,
|
||||
textDecoration: 0,
|
||||
textTransform: 0,
|
||||
textDirection: 0,
|
||||
lineHeight: 1.2,
|
||||
letterSpacing: 0,
|
||||
});
|
||||
|
||||
// Serialize leaf attributes
|
||||
const leafOffset = paragraphAttrSize;
|
||||
dview.setUint8(leafOffset, 0); // font-style: normal
|
||||
dview.setFloat32(leafOffset + 4, fontSize, true); // font-size
|
||||
dview.setUint32(leafOffset + 8, 400, true); // font-weight: normal
|
||||
dview.setUint32(leafOffset + 12, 0, true); // font-id (UUID part 1)
|
||||
dview.setUint32(leafOffset + 16, 0, true); // font-id (UUID part 2)
|
||||
dview.setUint32(leafOffset + 20, 0, true); // font-id (UUID part 3)
|
||||
dview.setInt32(leafOffset + 24, 0, true); // font-id (UUID part 4)
|
||||
dview.setInt32(leafOffset + 28, 0, true); // font-family hash
|
||||
dview.setUint32(leafOffset + 32, 0, true); // font-variant-id (UUID part 1)
|
||||
dview.setUint32(leafOffset + 36, 0, true); // font-variant-id (UUID part 2)
|
||||
dview.setUint32(leafOffset + 40, 0, true); // font-variant-id (UUID part 3)
|
||||
dview.setInt32(leafOffset + 44, 0, true); // font-variant-id (UUID part 4)
|
||||
dview.setInt32(leafOffset + 48, textSize, true); // text-length
|
||||
dview.setInt32(leafOffset + 52, totalFills, true); // total fills count
|
||||
const leafOffset = PARAGRAPH_ATTR_SIZE;
|
||||
setLeafData(dview, leafOffset, {
|
||||
fontSize,
|
||||
fontWeight: 400,
|
||||
textDecoration: 0,
|
||||
textDirection: 0,
|
||||
textTransform: 0,
|
||||
letterSpacing: 0,
|
||||
textSize,
|
||||
totalFills,
|
||||
});
|
||||
|
||||
// Serialize fills
|
||||
let fillOffset = leafOffset + leafAttrSize;
|
||||
let fillOffset = leafOffset + LEAF_ATTR_SIZE;
|
||||
fills.forEach((fill) => {
|
||||
if (fill.type === "solid") {
|
||||
const argb = hexToU32ARGB(fill.color, fill.opacity);
|
||||
dview.setUint8(fillOffset, 0x00, true); // Fill type: solid
|
||||
dview.setUint32(fillOffset + 4, argb, true);
|
||||
fillOffset += fillSize; // Move to the next fill
|
||||
fillOffset += FILL_SIZE; // Move to the next fill
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -362,11 +362,13 @@ pub struct TextLeaf {
|
||||
text: String,
|
||||
font_family: FontFamily,
|
||||
font_size: f32,
|
||||
letter_spacing: f32,
|
||||
font_style: u8,
|
||||
font_weight: i32,
|
||||
font_variant_id: Uuid,
|
||||
text_decoration: u8,
|
||||
text_transform: u8,
|
||||
text_direction: u8,
|
||||
fills: Vec<shapes::Fill>,
|
||||
}
|
||||
|
||||
@@ -376,9 +378,11 @@ impl TextLeaf {
|
||||
text: String,
|
||||
font_family: FontFamily,
|
||||
font_size: f32,
|
||||
letter_spacing: f32,
|
||||
font_style: u8,
|
||||
text_decoration: u8,
|
||||
text_transform: u8,
|
||||
text_direction: u8,
|
||||
font_weight: i32,
|
||||
font_variant_id: Uuid,
|
||||
fills: Vec<shapes::Fill>,
|
||||
@@ -387,9 +391,11 @@ impl TextLeaf {
|
||||
text,
|
||||
font_family,
|
||||
font_size,
|
||||
letter_spacing,
|
||||
font_style,
|
||||
text_decoration,
|
||||
text_transform,
|
||||
text_direction,
|
||||
font_weight,
|
||||
font_variant_id,
|
||||
fills,
|
||||
@@ -413,7 +419,7 @@ impl TextLeaf {
|
||||
|
||||
style.set_foreground_paint(&paint);
|
||||
style.set_font_size(self.font_size);
|
||||
style.set_letter_spacing(paragraph.letter_spacing);
|
||||
style.set_letter_spacing(self.letter_spacing);
|
||||
style.set_height(paragraph.line_height);
|
||||
style.set_height_override(true);
|
||||
style.set_half_leading(false);
|
||||
@@ -452,7 +458,7 @@ impl TextLeaf {
|
||||
let mut style = self.to_style(paragraph, &Rect::default(), fallback_fonts, blur, blur_mask);
|
||||
style.set_foreground_paint(stroke_paint);
|
||||
style.set_font_size(self.font_size);
|
||||
style.set_letter_spacing(paragraph.letter_spacing);
|
||||
style.set_letter_spacing(self.letter_spacing);
|
||||
style.set_decoration_type(match self.text_decoration {
|
||||
0 => skia::textlayout::TextDecoration::NO_DECORATION,
|
||||
1 => skia::textlayout::TextDecoration::UNDERLINE,
|
||||
@@ -503,6 +509,7 @@ pub struct RawTextLeaf {
|
||||
text_decoration: u8,
|
||||
text_transform: u8,
|
||||
font_size: f32,
|
||||
letter_spacing: f32,
|
||||
font_weight: i32,
|
||||
font_id: [u32; 4],
|
||||
font_family: [u8; 4],
|
||||
@@ -535,8 +542,9 @@ pub struct RawTextLeafData {
|
||||
font_style: u8,
|
||||
text_decoration: u8,
|
||||
text_transform: u8,
|
||||
byte_padding: u8,
|
||||
text_direction: u8,
|
||||
font_size: f32,
|
||||
letter_spacing: f32,
|
||||
font_weight: i32,
|
||||
font_id: [u32; 4],
|
||||
font_family: [u8; 4],
|
||||
@@ -565,8 +573,9 @@ impl From<&[u8]> for RawTextLeafData {
|
||||
font_style: text_leaf.font_style,
|
||||
text_decoration: text_leaf.text_decoration,
|
||||
text_transform: text_leaf.text_transform,
|
||||
byte_padding: 0,
|
||||
text_direction: 0, // TODO: Añadirlo
|
||||
font_size: text_leaf.font_size,
|
||||
letter_spacing: text_leaf.letter_spacing,
|
||||
font_weight: text_leaf.font_weight,
|
||||
font_id: text_leaf.font_id,
|
||||
font_family: text_leaf.font_family,
|
||||
@@ -669,9 +678,11 @@ impl From<&Vec<u8>> for RawTextData {
|
||||
text,
|
||||
font_family,
|
||||
text_leaf.font_size,
|
||||
text_leaf.letter_spacing,
|
||||
text_leaf.font_style,
|
||||
text_leaf.text_decoration,
|
||||
text_leaf.text_transform,
|
||||
text_leaf.text_direction,
|
||||
text_leaf.font_weight,
|
||||
font_variant_id,
|
||||
text_leaf.fills.clone(),
|
||||
|
||||
Reference in New Issue
Block a user