242 lines
6.2 KiB
JavaScript

import { UUID } from "./uuid.js";
import { fromStyle } from "./style.js";
import { FontStyle } from "./font.js";
import { Fill } from "./fill.js";
export const TextAlign = {
LEFT: 0,
CENTER: 1,
RIGHT: 2,
JUSTIFY: 3,
fromStyle,
};
export const TextDirection = {
LTR: 0,
RTL: 1,
fromStyle,
};
export const TextDecoration = {
NONE: 0,
UNDERLINE: 1,
LINE_THROUGH: 2,
OVERLINE: 3,
fromStyle,
};
export const TextTransform = {
NONE: 0,
UPPERCASE: 1,
LOWERCASE: 2,
CAPITALIZE: 3,
fromStyle,
};
export class TextSpan {
static BYTE_LENGTH = 1340;
static fromDOM(spanElement, fontManager) {
const elementStyle = spanElement.style; //window.getComputedStyle(leafElement);
const fontSize = parseFloat(elementStyle.getPropertyValue("font-size"));
const fontStyle =
FontStyle.fromStyle(elementStyle.getPropertyValue("font-style")) ??
FontStyle.NORMAL;
const fontWeight = parseInt(elementStyle.getPropertyValue("font-weight"));
const letterSpacing = parseFloat(
elementStyle.getPropertyValue("letter-spacing"),
);
const fontFamily = elementStyle.getPropertyValue("font-family");
console.log("fontFamily", fontFamily);
const fontStyles = fontManager.fonts.get(fontFamily);
const textDecoration = TextDecoration.fromStyle(
elementStyle.getPropertyValue("text-decoration"),
);
const textTransform = TextTransform.fromStyle(
elementStyle.getPropertyValue("text-transform"),
);
const textDirection = TextDirection.fromStyle(
elementStyle.getPropertyValue("text-direction"),
);
console.log(fontWeight, fontStyle);
const font = fontStyles.find(
(currentFontStyle) =>
currentFontStyle.weightAsNumber === fontWeight &&
currentFontStyle.styleAsNumber === fontStyle,
);
if (!font) {
throw new Error(`Invalid font "${fontFamily}"`);
}
return new TextSpan({
fontId: font.id, // leafElement.style.getPropertyValue("--font-id"),
fontFamilyHash: 0,
fontVariantId: UUID.ZERO, // leafElement.style.getPropertyValue("--font-variant-id"),
fontStyle,
fontSize,
fontWeight,
letterSpacing,
textDecoration,
textTransform,
textDirection,
text: spanElement.textContent,
});
}
fontId = UUID.ZERO;
fontFamilyHash = 0;
fontVariantId = UUID.ZERO;
fontStyle = 0;
fontSize = 16;
fontWeight = 400;
letterSpacing = 0.0;
textDecoration = 0;
textTransform = 0;
textDirection = 0;
#text = "";
fills = [new Fill()];
constructor(init) {
this.fontId = init?.fontId ?? UUID.ZERO;
this.fontStyle = init?.fontStyle ?? 0;
this.fontSize = init?.fontSize ?? 16;
this.fontWeight = init?.fontWeight ?? 400;
this.letterSpacing = init?.letterSpacing ?? 0;
this.textDecoration = init?.textDecoration ?? 0;
this.textTransform = init?.textTransform ?? 0;
this.textDirection = init?.textDirection ?? 0;
this.#text = init?.text ?? "";
this.fills = init?.fills ?? [new Fill()];
}
get text() {
return this.#text;
}
set text(newText) {
this.#text = newText;
}
get textByteLength() {
const text = this.text;
const textEncoder = new TextEncoder();
const textBuffer = textEncoder.encode(text);
return textBuffer.byteLength;
}
get leafByteLength() {
return TextLeaf.BYTE_LENGTH;
}
}
export class TextParagraph {
static BYTE_LENGTH = 48;
static fromDOM(paragraphElement, fontManager) {
return new TextParagraph({
textAlign: TextAlign.fromStyle(
paragraphElement.style.getPropertyValue("text-align"),
),
textDecoration: TextDecoration.fromStyle(
paragraphElement.style.getPropertyValue("text-decoration"),
),
textTransform: TextTransform.fromStyle(
paragraphElement.style.getPropertyValue("text-transform"),
),
textDirection: TextDirection.fromStyle(
paragraphElement.style.getPropertyValue("text-direction"),
),
lineHeight: parseFloat(
paragraphElement.style.getPropertyValue("line-height"),
),
letterSpacing: parseFloat(
paragraphElement.style.getPropertyValue("letter-spacing"),
),
leaves: Array.from(paragraphElement.children, (leafElement) =>
TextSpan.fromDOM(leafElement, fontManager),
),
});
}
#leaves = [];
textAlign = 0;
textDecoration = 0;
textTransform = 0;
textDirection = 0;
lineHeight = 1.2;
letterSpacing = 0;
constructor(init) {
this.textAlign = init?.textAlign ?? TextAlign.LEFT;
this.textDecoration = init?.textDecoration ?? TextDecoration.NONE;
this.textTransform = init?.textTransform ?? TextTransform.NONE;
this.textDirection = init?.textDirection ?? TextDirection.LTR;
this.lineHeight = init?.lineHeight ?? 1.2;
this.letterSpacing = init?.letterSpacing ?? 0.0;
this.#leaves = init?.leaves ?? [];
if (
!Array.isArray(this.#leaves) ||
!this.#leaves.every((leaf) => leaf instanceof TextSpan)
) {
throw new TypeError("Invalid text leaves");
}
}
get leaves() {
return this.#leaves;
}
get text() {
return this.#leaves.reduce((acc, leaf) => acc + leaf.text, "");
}
get textBuffer() {
const textEncoder = new TextEncoder();
const textBuffer = textEncoder.encode(this.text);
return textBuffer;
}
get byteLength() {
return (
this.#leaves.reduce((acc, leaf) => acc + leaf.leafByteLength, 0) +
this.textBuffer.byteLength +
TextParagraph.BYTE_LENGTH
);
}
}
export class TextContent {
static fromDOM(rootElement, fontManager) {
const paragraphs = Array.from(rootElement.children, (paragraph) =>
TextParagraph.fromDOM(paragraph, fontManager),
);
return new TextContent({ paragraphs });
}
#paragraphs = [];
constructor(init) {
this.#paragraphs = init?.paragraphs ?? [];
if (
!Array.isArray(this.#paragraphs) ||
!this.#paragraphs.every((paragraph) => paragraph instanceof TextParagraph)
) {
throw new TypeError("Invalid text paragraphs");
}
}
get paragraphs() {
return this.#paragraphs;
}
updateFromDOM(rootElement, fontManager) {
this.#paragraphs = Array.from(rootElement.children, (paragraph) =>
TextParagraph.fromDOM(paragraph, fontManager),
);
}
}