@mlightcad/mtext-parser
Version:
AutoCAD MText parser written in TypeScript
1,062 lines (1,061 loc) • 31.6 kB
JavaScript
var S = /* @__PURE__ */ ((s) => (s[s.NONE = 0] = "NONE", s[s.WORD = 1] = "WORD", s[s.STACK = 2] = "STACK", s[s.SPACE = 3] = "SPACE", s[s.NBSP = 4] = "NBSP", s[s.TABULATOR = 5] = "TABULATOR", s[s.NEW_PARAGRAPH = 6] = "NEW_PARAGRAPH", s[s.NEW_COLUMN = 7] = "NEW_COLUMN", s[s.WRAP_AT_DIMLINE = 8] = "WRAP_AT_DIMLINE", s[s.PROPERTIES_CHANGED = 9] = "PROPERTIES_CHANGED", s))(S || {}), F = /* @__PURE__ */ ((s) => (s[s.BOTTOM = 0] = "BOTTOM", s[s.MIDDLE = 1] = "MIDDLE", s[s.TOP = 2] = "TOP", s))(F || {}), E = /* @__PURE__ */ ((s) => (s[s.DEFAULT = 0] = "DEFAULT", s[s.LEFT = 1] = "LEFT", s[s.RIGHT = 2] = "RIGHT", s[s.CENTER = 3] = "CENTER", s[s.JUSTIFIED = 4] = "JUSTIFIED", s[s.DISTRIBUTED = 5] = "DISTRIBUTED", s))(E || {}), R = /* @__PURE__ */ ((s) => (s[s.NONE = 0] = "NONE", s[s.UNDERLINE = 1] = "UNDERLINE", s[s.OVERLINE = 2] = "OVERLINE", s[s.STRIKE_THROUGH = 4] = "STRIKE_THROUGH", s))(R || {});
const v = {
c: "Ø",
d: "°",
p: "±"
}, y = {
l: 1,
r: 2,
c: 3,
j: 4,
d: 5
/* DISTRIBUTED */
};
function N(s) {
const [t, e, r] = s;
return r << 16 | e << 8 | t;
}
function I(s) {
const t = s & 255, e = s >> 8 & 255, r = s >> 16 & 255;
return [t, e, r];
}
function w(s) {
return s.replace(/\r\n|\r|\n/g, "\\P");
}
function x(s) {
return s.replace(/\\P/g, "").replace(/\\~/g, "").includes("\\");
}
function P(s, t = !1) {
const e = /* @__PURE__ */ new Set(), r = /\\[fF](.*?)[;|]/g;
return [...s.matchAll(r)].forEach((a) => {
let l = a[1].toLowerCase();
t && (l = l.replace(/\.(ttf|otf|woff|shx)$/, "")), e.add(l);
}), e;
}
class O {
/**
* Creates a new ContextStack with an initial context.
* @param initial The initial MTextContext to use as the base of the stack.
*/
constructor(t) {
this.stack = [], this.stack.push(t);
}
/**
* Pushes a copy of the given context onto the stack.
* @param ctx The MTextContext to push (copied).
*/
push(t) {
this.stack.push(t);
}
/**
* Pops the top context from the stack and merges its paragraph properties into the new top context.
* If only one context remains, nothing is popped.
* @returns The popped MTextContext, or undefined if the stack has only one context.
*/
pop() {
if (this.stack.length <= 1) return;
const t = this.stack.pop(), e = this.stack[this.stack.length - 1];
return JSON.stringify(e.paragraph) !== JSON.stringify(t.paragraph) && (e.paragraph = { ...t.paragraph }), t;
}
/**
* Returns the current (top) context on the stack.
*/
get current() {
return this.stack[this.stack.length - 1];
}
/**
* Returns the current stack depth (number of nested blocks), not counting the root context.
*/
get depth() {
return this.stack.length - 1;
}
/**
* Returns the root (bottom) context, which represents the global formatting state.
* Used for paragraph property application.
*/
get root() {
return this.stack[0];
}
/**
* Replaces the current (top) context with the given context.
* @param ctx The new context to set as the current context.
*/
setCurrent(t) {
this.stack[this.stack.length - 1] = t;
}
}
class D {
/**
* Creates a new MTextParser instance
* @param content - The MText content to parse
* @param ctx - Optional initial MText context
* @param options - Parser options
*/
constructor(t, e, r = {}) {
this.continueStroke = !1, this.inStackContext = !1, this.scanner = new d(t);
const a = e ?? new m();
this.ctxStack = new O(a), this.yieldPropertyCommands = r.yieldPropertyCommands ?? !1, this.resetParagraphParameters = r.resetParagraphParameters ?? !1;
}
/**
* Decode multi-byte character from hex code
* @param hex - Hex code string (e.g. "C4E3")
* @returns Decoded character or empty square if invalid
*/
decodeMultiByteChar(t) {
try {
const e = new Uint8Array([
parseInt(t.substr(0, 2), 16),
parseInt(t.substr(2, 2), 16)
]), a = new TextDecoder("gbk").decode(e);
if (a !== "▯")
return a;
const i = new TextDecoder("big5").decode(e);
return i !== "▯" ? i : "▯";
} catch {
return "▯";
}
}
/**
* Push current context onto the stack
*/
pushCtx() {
this.ctxStack.push(this.ctxStack.current);
}
/**
* Pop context from the stack
*/
popCtx() {
this.ctxStack.pop();
}
/**
* Parse stacking expression (numerator/denominator)
* @returns Tuple of [TokenType.STACK, [numerator, denominator, type]]
*/
parseStacking() {
const t = new d(this.extractExpression(!0));
let e = "", r = "", a = "";
const l = () => {
let c = t.peek(), n = !1;
return c.charCodeAt(0) < 32 && (c = " "), c === "\\" && (n = !0, t.consume(1), c = t.peek()), t.consume(1), [c, n];
}, i = () => {
let c = "";
for (; t.hasData; ) {
const [n, p] = l();
if (!p && (n === "/" || n === "#" || n === "^"))
return [c, n];
c += n;
}
return [c, ""];
}, u = (c) => {
let n = "", p = c;
for (; t.hasData; ) {
const [h, o] = l();
if (!(p && h === " ")) {
if (p = !1, !o && h === ";")
break;
n += h;
}
}
return n;
};
return [e, a] = i(), a && (r = u(a === "^")), e === "" && r.includes("I/") ? [2, [" ", " ", "/"]] : a === "^" ? [2, [e, r, "^"]] : [2, [e, r, a]];
}
/**
* Parse MText properties
* @param cmd - The property command to parse
* @returns Property changes if yieldPropertyCommands is true and changes occurred
*/
parseProperties(t) {
const e = this.ctxStack.current.copy(), r = this.ctxStack.current.copy();
switch (t) {
case "L":
r.underline = !0, this.continueStroke = !0;
break;
case "l":
r.underline = !1, r.hasAnyStroke || (this.continueStroke = !1);
break;
case "O":
r.overline = !0, this.continueStroke = !0;
break;
case "o":
r.overline = !1, r.hasAnyStroke || (this.continueStroke = !1);
break;
case "K":
r.strikeThrough = !0, this.continueStroke = !0;
break;
case "k":
r.strikeThrough = !1, r.hasAnyStroke || (this.continueStroke = !1);
break;
case "A":
this.parseAlign(r);
break;
case "C":
this.parseAciColor(r);
break;
case "c":
this.parseRgbColor(r);
break;
case "H":
this.parseHeight(r);
break;
case "W":
this.parseWidth(r);
break;
case "Q":
this.parseOblique(r);
break;
case "T":
this.parseCharTracking(r);
break;
case "p":
this.parseParagraphProperties(r);
break;
case "f":
case "F":
this.parseFontProperties(r);
break;
default:
throw new Error(`Unknown command: ${t}`);
}
if (this.continueStroke = r.hasAnyStroke, r.continueStroke = this.continueStroke, this.ctxStack.setCurrent(r), this.yieldPropertyCommands) {
const a = this.getPropertyChanges(e, r);
if (Object.keys(a).length > 0)
return {
command: t,
changes: a,
depth: this.ctxStack.depth
};
}
}
/**
* Get property changes between two contexts
* @param oldCtx - The old context
* @param newCtx - The new context
* @returns Object containing changed properties
*/
getPropertyChanges(t, e) {
const r = {};
if (t.underline !== e.underline && (r.underline = e.underline), t.overline !== e.overline && (r.overline = e.overline), t.strikeThrough !== e.strikeThrough && (r.strikeThrough = e.strikeThrough), t.color.aci !== e.color.aci && (r.aci = e.color.aci), t.color.rgbValue !== e.color.rgbValue && (r.rgb = e.color.rgb), t.align !== e.align && (r.align = e.align), JSON.stringify(t.fontFace) !== JSON.stringify(e.fontFace) && (r.fontFace = e.fontFace), (t.capHeight.value !== e.capHeight.value || t.capHeight.isRelative !== e.capHeight.isRelative) && (r.capHeight = e.capHeight), (t.widthFactor.value !== e.widthFactor.value || t.widthFactor.isRelative !== e.widthFactor.isRelative) && (r.widthFactor = e.widthFactor), (t.charTrackingFactor.value !== e.charTrackingFactor.value || t.charTrackingFactor.isRelative !== e.charTrackingFactor.isRelative) && (r.charTrackingFactor = e.charTrackingFactor), t.oblique !== e.oblique && (r.oblique = e.oblique), JSON.stringify(t.paragraph) !== JSON.stringify(e.paragraph)) {
const a = {};
t.paragraph.indent !== e.paragraph.indent && (a.indent = e.paragraph.indent), t.paragraph.align !== e.paragraph.align && (a.align = e.paragraph.align), t.paragraph.left !== e.paragraph.left && (a.left = e.paragraph.left), t.paragraph.right !== e.paragraph.right && (a.right = e.paragraph.right), JSON.stringify(t.paragraph.tabs) !== JSON.stringify(e.paragraph.tabs) && (a.tabs = e.paragraph.tabs), Object.keys(a).length > 0 && (r.paragraph = a);
}
return r;
}
/**
* Parse alignment property
* @param ctx - The context to update
*/
parseAlign(t) {
const e = this.scanner.get();
"012".includes(e) ? t.align = parseInt(e) : t.align = 0, this.consumeOptionalTerminator();
}
/**
* Parse height property
* @param ctx - The context to update
*/
parseHeight(t) {
const e = this.extractFloatExpression(!0);
if (e)
try {
e.endsWith("x") ? t.capHeight = {
value: parseFloat(e.slice(0, -1)),
isRelative: !0
} : t.capHeight = {
value: parseFloat(e),
isRelative: !1
};
} catch {
this.scanner.consume(-e.length);
return;
}
this.consumeOptionalTerminator();
}
/**
* Parse width property
* @param ctx - The context to update
*/
parseWidth(t) {
const e = this.extractFloatExpression(!0);
if (e)
try {
e.endsWith("x") ? t.widthFactor = {
value: parseFloat(e.slice(0, -1)),
isRelative: !0
} : t.widthFactor = {
value: parseFloat(e),
isRelative: !1
};
} catch {
this.scanner.consume(-e.length);
return;
}
this.consumeOptionalTerminator();
}
/**
* Parse character tracking property
* @param ctx - The context to update
*/
parseCharTracking(t) {
const e = this.extractFloatExpression(!0);
if (e)
try {
e.endsWith("x") ? t.charTrackingFactor = {
value: Math.abs(parseFloat(e.slice(0, -1))),
isRelative: !0
} : t.charTrackingFactor = {
value: Math.abs(parseFloat(e)),
isRelative: !1
};
} catch {
this.scanner.consume(-e.length);
return;
}
this.consumeOptionalTerminator();
}
/**
* Parse float value or factor
* @param value - Current value to apply factor to
* @returns New value
*/
parseFloatValueOrFactor(t) {
const e = this.extractFloatExpression(!0);
if (e)
if (e.endsWith("x")) {
const r = parseFloat(e.slice(0, -1));
t *= r;
} else
t = parseFloat(e);
return t;
}
/**
* Parse oblique angle property
* @param ctx - The context to update
*/
parseOblique(t) {
const e = this.extractFloatExpression(!1);
e && (t.oblique = parseFloat(e)), this.consumeOptionalTerminator();
}
/**
* Parse ACI color property
* @param ctx - The context to update
*/
parseAciColor(t) {
const e = this.extractIntExpression();
if (e) {
const r = parseInt(e);
r < 257 && (t.color.aci = r);
}
this.consumeOptionalTerminator();
}
/**
* Parse RGB color property
* @param ctx - The context to update
*/
parseRgbColor(t) {
const e = this.extractIntExpression();
if (e) {
const r = parseInt(e) & 16777215;
t.color.rgbValue = r;
}
this.consumeOptionalTerminator();
}
/**
* Extract float expression from scanner
* @param relative - Whether to allow relative values (ending in 'x')
* @returns Extracted expression
*/
extractFloatExpression(t = !1) {
const e = t ? /^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?x?/ : /^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?/, r = this.scanner.tail.match(e);
if (r) {
const a = r[0];
return this.scanner.consume(a.length), a;
}
return "";
}
/**
* Extract integer expression from scanner
* @returns Extracted expression
*/
extractIntExpression() {
const t = this.scanner.tail.match(/^\d+/);
if (t) {
const e = t[0];
return this.scanner.consume(e.length), e;
}
return "";
}
/**
* Extract expression until semicolon or end
* @param escape - Whether to handle escaped semicolons
* @returns Extracted expression
*/
extractExpression(t = !1) {
const e = this.scanner.find(";", t);
if (e < 0) {
const i = this.scanner.tail;
return this.scanner.consume(i.length), i;
}
const a = this.scanner.peek(e - this.scanner.currentIndex - 1) === "\\", l = this.scanner.tail.slice(0, e - this.scanner.currentIndex + (a ? 1 : 0));
return this.scanner.consume(l.length + 1), l;
}
/**
* Parse font properties
* @param ctx - The context to update
*/
parseFontProperties(t) {
const e = this.extractExpression().split("|");
if (e.length > 0 && e[0]) {
const r = e[0];
let a = "Regular", l = 400;
for (const i of e.slice(1))
i.startsWith("b1") ? l = 700 : i === "i" || i.startsWith("i1") ? a = "Italic" : (i === "i0" || i.startsWith("i0")) && (a = "Regular");
t.fontFace = {
family: r,
style: a,
weight: l
};
}
}
/**
* Parse paragraph properties from the MText content
* Handles properties like indentation, alignment, and tab stops
* @param ctx - The context to update
*/
parseParagraphProperties(t) {
const e = new d(this.extractExpression());
let r = t.paragraph.indent, a = t.paragraph.left, l = t.paragraph.right, i = t.paragraph.align, u = [];
const c = () => {
const n = e.tail.match(/^[+-]?\d+(?:\.\d*)?(?:[eE][+-]?\d+)?/);
if (n) {
const p = parseFloat(n[0]);
for (e.consume(n[0].length); e.peek() === ","; )
e.consume(1);
return p;
}
return 0;
};
for (; e.hasData; )
switch (e.get()) {
case "i":
r = c();
break;
case "l":
a = c();
break;
case "r":
l = c();
break;
case "x":
break;
case "q": {
const p = e.get();
for (i = y[p] || 0; e.peek() === ","; )
e.consume(1);
break;
}
case "t":
for (u = []; e.hasData; ) {
const p = e.peek();
if (p === "r" || p === "c") {
e.consume(1);
const h = c();
u.push(p + h.toString());
} else {
const h = c();
isNaN(h) ? e.consume(1) : u.push(h);
}
}
break;
}
t.paragraph = {
indent: r,
left: a,
right: l,
align: i,
tabs: u
};
}
/**
* Consume optional terminator (semicolon)
*/
consumeOptionalTerminator() {
this.scanner.peek() === ";" && this.scanner.consume(1);
}
/**
* Parse MText content into tokens
* @yields MTextToken objects
*/
*parse() {
let r = null;
function a(i) {
const u = { ...i.paragraph };
i.paragraph = {
indent: 0,
left: 0,
right: 0,
align: 0,
tabs: []
};
const c = {};
return u.indent !== 0 && (c.indent = 0), u.left !== 0 && (c.left = 0), u.right !== 0 && (c.right = 0), u.align !== 0 && (c.align = 0), JSON.stringify(u.tabs) !== JSON.stringify([]) && (c.tabs = []), c;
}
const l = () => {
var u;
let i = "";
for (; this.scanner.hasData; ) {
let c = !1, n = this.scanner.peek();
const p = this.scanner.currentIndex;
if (n.charCodeAt(0) < 32) {
if (this.scanner.consume(1), n === " ")
return [5, null];
if (n === `
`)
return [6, null];
n = " ";
}
if (n === "\\")
if ("\\{}".includes(this.scanner.peek(1)))
c = !0, this.scanner.consume(1), n = this.scanner.peek();
else {
if (i)
return [1, i];
this.scanner.consume(1);
const h = this.scanner.get();
switch (h) {
case "~":
return [4, null];
case "P":
return [6, null];
case "N":
return [7, null];
case "X":
return [8, null];
case "S": {
this.inStackContext = !0;
const o = this.parseStacking();
return this.inStackContext = !1, o;
}
case "m":
case "M":
if (this.scanner.peek() === "+") {
this.scanner.consume(1);
const o = (u = this.scanner.tail.match(/^[0-9A-Fa-f]{4}/)) == null ? void 0 : u[0];
if (o) {
this.scanner.consume(4);
const g = this.decodeMultiByteChar(o);
return i ? [1, i] : [1, g];
}
this.scanner.consume(-1);
}
i += "\\M";
continue;
case "U":
if (this.scanner.peek() === "+") {
this.scanner.consume(1);
const o = this.scanner.tail.match(/^[0-9A-Fa-f]{4,8}/);
if (o) {
const g = o[0];
this.scanner.consume(g.length);
const _ = parseInt(g, 16);
let f = "";
try {
f = String.fromCodePoint(_);
} catch {
f = "▯";
}
return i ? [1, i] : [1, f];
}
this.scanner.consume(-1);
}
i += "\\U";
continue;
default:
if (h)
try {
const o = this.parseProperties(h);
if (this.yieldPropertyCommands && o)
return [9, o];
continue;
} catch {
const o = this.scanner.tail.slice(
p,
this.scanner.currentIndex
);
i += o;
}
}
continue;
}
if (n === "%" && this.scanner.peek(1) === "%") {
const h = this.scanner.peek(2).toLowerCase(), o = v[h];
if (o) {
this.scanner.consume(3), i += o;
continue;
} else {
this.scanner.consume(3);
continue;
}
}
if (n === " ")
return i ? (this.scanner.consume(1), r = 3, [1, i]) : (this.scanner.consume(1), [3, null]);
if (!c) {
if (n === "{") {
if (i)
return [1, i];
this.scanner.consume(1), this.pushCtx();
continue;
} else if (n === "}") {
if (i)
return [1, i];
if (this.scanner.consume(1), this.yieldPropertyCommands) {
const h = this.ctxStack.current;
this.popCtx();
const o = this.getPropertyChanges(h, this.ctxStack.current);
if (Object.keys(o).length > 0)
return [
9,
{ command: void 0, changes: o, depth: this.ctxStack.depth }
];
} else
this.popCtx();
continue;
}
}
if (!this.inStackContext && n === "^") {
const h = this.scanner.peek(1);
if (h) {
const o = h.charCodeAt(0);
if (this.scanner.consume(2), o === 32)
i += "^";
else {
if (o === 73)
return i ? [1, i] : [5, null];
if (o === 74)
return i ? [1, i] : [6, null];
if (o === 77)
continue;
i += "▯";
}
continue;
}
}
this.scanner.consume(1), n.charCodeAt(0) >= 32 && (i += n);
}
return i ? [1, i] : [0, null];
};
for (; ; ) {
const [i, u] = l.call(this);
if (i) {
if (yield new k(i, this.ctxStack.current.copy(), u), i === 6 && this.resetParagraphParameters) {
const c = this.ctxStack.current, n = a(c);
this.yieldPropertyCommands && Object.keys(n).length > 0 && (yield new k(9, c.copy(), {
command: void 0,
changes: { paragraph: n },
depth: this.ctxStack.depth
}));
}
r && (yield new k(r, this.ctxStack.current.copy(), null), r = null);
} else
break;
}
}
}
class d {
/**
* Create a new text scanner
* @param text - The text to scan
*/
constructor(t) {
this.text = t, this.textLen = t.length, this._index = 0;
}
/**
* Get the current index in the text
*/
get currentIndex() {
return this._index;
}
/**
* Check if the scanner has reached the end of the text
*/
get isEmpty() {
return this._index >= this.textLen;
}
/**
* Check if there is more text to scan
*/
get hasData() {
return this._index < this.textLen;
}
/**
* Get the next character and advance the index
* @returns The next character, or empty string if at end
*/
get() {
if (this.isEmpty)
return "";
const t = this.text[this._index];
return this._index++, t;
}
/**
* Advance the index by the specified count
* @param count - Number of characters to advance
*/
consume(t = 1) {
this._index = Math.max(0, Math.min(this._index + t, this.textLen));
}
/**
* Look at a character without advancing the index
* @param offset - Offset from current position
* @returns The character at the offset position, or empty string if out of bounds
*/
peek(t = 0) {
const e = this._index + t;
return e >= this.textLen || e < 0 ? "" : this.text[e];
}
/**
* Find the next occurrence of a character
* @param char - The character to find
* @param escape - Whether to handle escaped characters
* @returns Index of the character, or -1 if not found
*/
find(t, e = !1) {
let r = this._index;
for (; r < this.textLen; ) {
if (e && this.text[r] === "\\") {
if (r + 1 < this.textLen) {
if (this.text[r + 1] === t)
return r + 1;
r += 2;
continue;
}
r++;
continue;
}
if (this.text[r] === t)
return r;
r++;
}
return -1;
}
/**
* Get the remaining text from the current position
*/
get tail() {
return this.text.slice(this._index);
}
/**
* Check if the next character is a space
*/
isNextSpace() {
return this.peek() === " ";
}
/**
* Consume spaces until a non-space character is found
* @returns Number of spaces consumed
*/
consumeSpaces() {
let t = 0;
for (; this.isNextSpace(); )
this.consume(), t++;
return t;
}
}
class b {
// Store as 0xRRGGBB or null
/**
* Create a new MTextColor instance.
* @param color The initial color: number for ACI, [r,g,b] for RGB, or null/undefined for default (ACI=256).
*/
constructor(t) {
this._aci = 256, this._rgbValue = null, Array.isArray(t) ? this.rgb = t : typeof t == "number" ? this.aci = t : this.aci = 256;
}
/**
* Get the current ACI color value.
* @returns The ACI color (0-256), or null if using RGB.
*/
get aci() {
return this._aci;
}
/**
* Set the ACI color value. Setting this disables any RGB color.
* @param value The ACI color (0-256), or null to unset.
* @throws Error if value is out of range.
*/
set aci(t) {
if (t === null)
this._aci = null;
else if (t >= 0 && t <= 256)
this._aci = t, this._rgbValue = null;
else
throw new Error("ACI not in range [0, 256]");
}
/**
* Get the current RGB color as a tuple [r, g, b], or null if not set.
* @returns The RGB color tuple, or null if using ACI.
*/
get rgb() {
if (this._rgbValue === null) return null;
const t = this._rgbValue >> 16 & 255, e = this._rgbValue >> 8 & 255, r = this._rgbValue & 255;
return [t, e, r];
}
/**
* Set the RGB color. Setting this disables ACI color.
* @param value The RGB color tuple [r, g, b], or null to use ACI.
*/
set rgb(t) {
if (t) {
const [e, r, a] = t;
this._rgbValue = (e & 255) << 16 | (r & 255) << 8 | a & 255, this._aci = null;
} else
this._rgbValue = null;
}
/**
* Returns true if the color is set by RGB, false if by ACI.
*/
get isRgb() {
return this._rgbValue !== null;
}
/**
* Returns true if the color is set by ACI, false if by RGB.
*/
get isAci() {
return this._rgbValue === null && this._aci !== null;
}
/**
* Get or set the internal RGB value as a number (0xRRGGBB), or null if not set.
* Setting this will switch to RGB mode and set ACI to null.
*/
get rgbValue() {
return this._rgbValue;
}
set rgbValue(t) {
t === null ? this._rgbValue = null : (this._rgbValue = t & 16777215, this._aci = null);
}
/**
* Returns a deep copy of this color.
* @returns A new MTextColor instance with the same color state.
*/
copy() {
const t = new b();
return t._aci = this._aci, t._rgbValue = this._rgbValue, t;
}
/**
* Returns a plain object for serialization.
* @returns An object with aci, rgb (tuple), and rgbValue (number or null).
*/
toObject() {
return { aci: this._aci, rgb: this.rgb, rgbValue: this._rgbValue };
}
/**
* Equality check for color.
* @param other The other MTextColor to compare.
* @returns True if both ACI and RGB values are equal.
*/
equals(t) {
return this._aci === t._aci && this._rgbValue === t._rgbValue;
}
}
class m {
constructor() {
this._stroke = 0, this.continueStroke = !1, this.color = new b(), this.align = 0, this.fontFace = { family: "", style: "Regular", weight: 400 }, this._capHeight = { value: 1, isRelative: !1 }, this._widthFactor = { value: 1, isRelative: !1 }, this._charTrackingFactor = { value: 1, isRelative: !1 }, this.oblique = 0, this.paragraph = {
indent: 0,
left: 0,
right: 0,
align: 0,
tabs: []
};
}
/**
* Get the capital letter height
*/
get capHeight() {
return this._capHeight;
}
/**
* Set the capital letter height
* @param value - Height value
*/
set capHeight(t) {
this._capHeight = {
value: Math.abs(t.value),
isRelative: t.isRelative
};
}
/**
* Get the character width factor
*/
get widthFactor() {
return this._widthFactor;
}
/**
* Set the character width factor
* @param value - Width factor value
*/
set widthFactor(t) {
this._widthFactor = {
value: Math.abs(t.value),
isRelative: t.isRelative
};
}
/**
* Get the character tracking factor
*/
get charTrackingFactor() {
return this._charTrackingFactor;
}
/**
* Set the character tracking factor
* @param value - Tracking factor value
*/
set charTrackingFactor(t) {
this._charTrackingFactor = {
value: Math.abs(t.value),
isRelative: t.isRelative
};
}
/**
* Get the ACI color value
*/
get aci() {
return this.color.aci;
}
/**
* Set the ACI color value
* @param value - ACI color value (0-256)
* @throws Error if value is out of range
*/
set aci(t) {
this.color.aci = t;
}
/**
* Get the RGB color value
*/
get rgb() {
return this.color.rgb;
}
/**
* Set the RGB color value
*/
set rgb(t) {
this.color.rgb = t;
}
/**
* Gets whether the current text should be rendered in italic style.
* @returns {boolean} True if the font style is 'Italic', otherwise false.
*/
get italic() {
return this.fontFace.style === "Italic";
}
/**
* Sets whether the current text should be rendered in italic style.
* @param value - If true, sets the font style to 'Italic'; if false, sets it to 'Regular'.
*/
set italic(t) {
this.fontFace.style = t ? "Italic" : "Regular";
}
/**
* Gets whether the current text should be rendered in bold style.
* This is primarily used for mesh fonts and affects font selection.
* @returns {boolean} True if the font weight is 700 or higher, otherwise false.
*/
get bold() {
return (this.fontFace.weight || 400) >= 700;
}
/**
* Sets whether the current text should be rendered in bold style.
* This is primarily used for mesh fonts and affects font selection.
* @param value - If true, sets the font weight to 700; if false, sets it to 400.
*/
set bold(t) {
this.fontFace.weight = t ? 700 : 400;
}
/**
* Get whether text is underlined
*/
get underline() {
return !!(this._stroke & 1);
}
/**
* Set whether text is underlined
* @param value - Whether to underline
*/
set underline(t) {
this._setStrokeState(1, t);
}
/**
* Get whether text has strike-through
*/
get strikeThrough() {
return !!(this._stroke & 4);
}
/**
* Set whether text has strike-through
* @param value - Whether to strike through
*/
set strikeThrough(t) {
this._setStrokeState(4, t);
}
/**
* Get whether text has overline
*/
get overline() {
return !!(this._stroke & 2);
}
/**
* Set whether text has overline
* @param value - Whether to overline
*/
set overline(t) {
this._setStrokeState(2, t);
}
/**
* Check if any stroke formatting is active
*/
get hasAnyStroke() {
return !!this._stroke;
}
/**
* Set the state of a stroke type
* @param stroke - The stroke type to set
* @param state - Whether to enable or disable the stroke
*/
_setStrokeState(t, e = !0) {
e ? this._stroke |= t : this._stroke &= ~t;
}
/**
* Create a copy of this context
* @returns A new context with the same properties
*/
copy() {
const t = new m();
return t._stroke = this._stroke, t.continueStroke = this.continueStroke, t.color = this.color.copy(), t.align = this.align, t.fontFace = { ...this.fontFace }, t._capHeight = { ...this._capHeight }, t._widthFactor = { ...this._widthFactor }, t._charTrackingFactor = { ...this._charTrackingFactor }, t.oblique = this.oblique, t.paragraph = { ...this.paragraph }, t;
}
}
class k {
/**
* Create a new MText token
* @param type - The token type
* @param ctx - The text context at this token
* @param data - Optional token data
*/
constructor(t, e, r) {
this.type = t, this.ctx = e, this.data = r;
}
}
export {
b as MTextColor,
m as MTextContext,
F as MTextLineAlignment,
E as MTextParagraphAlignment,
D as MTextParser,
R as MTextStroke,
k as MTextToken,
d as TextScanner,
S as TokenType,
w as escapeDxfLineEndings,
P as getFonts,
x as hasInlineFormattingCodes,
I as int2rgb,
N as rgb2int
};
//# sourceMappingURL=parser.es.js.map