@mlightcad/mtext-parser 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.es.js","sources":["../src/parser.ts"],"sourcesContent":["/**\n * Token types used in MText parsing\n */\nexport enum TokenType {\n /** No token */\n NONE = 0,\n /** Word token with string data */\n WORD = 1,\n /** Stack token with [numerator, denominator, type] data */\n STACK = 2,\n /** Space token with no data */\n SPACE = 3,\n /** Non-breaking space token with no data */\n NBSP = 4,\n /** Tab token with no data */\n TABULATOR = 5,\n /** New paragraph token with no data */\n NEW_PARAGRAPH = 6,\n /** New column token with no data */\n NEW_COLUMN = 7,\n /** Wrap at dimension line token with no data */\n WRAP_AT_DIMLINE = 8,\n /** Properties changed token with string data (full command) */\n PROPERTIES_CHANGED = 9,\n}\n\n/**\n * Type for token data based on token type\n */\nexport type TokenData = {\n [TokenType.NONE]: null;\n [TokenType.WORD]: string;\n [TokenType.STACK]: [string, string, string];\n [TokenType.SPACE]: null;\n [TokenType.NBSP]: null;\n [TokenType.TABULATOR]: null;\n [TokenType.NEW_PARAGRAPH]: null;\n [TokenType.NEW_COLUMN]: null;\n [TokenType.WRAP_AT_DIMLINE]: null;\n [TokenType.PROPERTIES_CHANGED]: string;\n};\n\n/**\n * Line alignment options for MText\n */\nexport enum MTextLineAlignment {\n /** Align text to bottom */\n BOTTOM = 0,\n /** Align text to middle */\n MIDDLE = 1,\n /** Align text to top */\n TOP = 2,\n}\n\n/**\n * Paragraph alignment options for MText\n */\nexport enum MTextParagraphAlignment {\n /** Default alignment */\n DEFAULT = 0,\n /** Left alignment */\n LEFT = 1,\n /** Right alignment */\n RIGHT = 2,\n /** Center alignment */\n CENTER = 3,\n /** Justified alignment */\n JUSTIFIED = 4,\n /** Distributed alignment */\n DISTRIBUTED = 5,\n}\n\n/**\n * Text stroke options for MText\n */\nexport enum MTextStroke {\n /** No stroke */\n NONE = 0,\n /** Underline stroke */\n UNDERLINE = 1,\n /** Overline stroke */\n OVERLINE = 2,\n /** Strike-through stroke */\n STRIKE_THROUGH = 4,\n}\n\n/**\n * RGB color tuple\n */\nexport type RGB = [number, number, number];\n\n/**\n * Font face properties\n */\nexport interface FontFace {\n /** Font family name */\n family: string;\n /** Font style (e.g., 'Regular', 'Italic') */\n style: string;\n /** Font weight (e.g., 400 for normal, 700 for bold) */\n weight: number;\n}\n\n/**\n * Font measurement properties\n */\nexport interface FontMeasurements {\n /** Height of capital letters */\n cap_height: number;\n /** Baseline position */\n baseline: number;\n /** Bottom position */\n bottom: number;\n /** Top position */\n top: number;\n /** Total height of the font */\n total_height: number;\n /**\n * Scale measurements by a factor\n * @param factor - The scaling factor\n * @returns New scaled measurements\n */\n scale(factor: number): FontMeasurements;\n}\n\n/**\n * Abstract font interface\n */\nexport interface AbstractFont {\n /**\n * Calculate text width\n * @param text - The text to measure\n * @returns Width of the text\n */\n text_width(text: string): number;\n /**\n * Get width of a space character\n * @returns Width of a space\n */\n space_width(): number;\n /** Font measurements */\n measurements: FontMeasurements;\n}\n\n/**\n * Paragraph properties\n */\nexport interface ParagraphProperties {\n /** Indentation value */\n indent: number;\n /** Left margin value */\n left: number;\n /** Right margin value */\n right: number;\n /** Paragraph alignment */\n align: MTextParagraphAlignment;\n /** Tab stop positions and types */\n tab_stops: (number | string)[];\n}\n\n/**\n * Special character encoding mapping\n */\nconst SPECIAL_CHAR_ENCODING: Record<string, string> = {\n c: 'Ø',\n d: '°',\n p: '±',\n};\n\n/**\n * Character to paragraph alignment mapping\n */\nconst CHAR_TO_ALIGN: Record<string, MTextParagraphAlignment> = {\n l: MTextParagraphAlignment.LEFT,\n r: MTextParagraphAlignment.RIGHT,\n c: MTextParagraphAlignment.CENTER,\n j: MTextParagraphAlignment.JUSTIFIED,\n d: MTextParagraphAlignment.DISTRIBUTED,\n};\n\n/**\n * Convert RGB tuple to integer color value\n * @param rgb - RGB color tuple\n * @returns Integer color value\n */\nexport function rgb2int(rgb: RGB): number {\n const [r, g, b] = rgb;\n return (b << 16) | (g << 8) | r;\n}\n\n/**\n * Convert integer color value to RGB tuple\n * @param value - Integer color value\n * @returns RGB color tuple\n */\nexport function int2rgb(value: number): RGB {\n const r = value & 0xff;\n const g = (value >> 8) & 0xff;\n const b = (value >> 16) & 0xff;\n return [r, g, b];\n}\n\n/**\n * DXF stores some special characters using caret notation. This function\n * decodes this notation to normalize the representation of special characters\n * in the string.\n * see: https://en.wikipedia.org/wiki/Caret_notation\n * @param text - Text to decode\n * @returns Decoded text\n */\nexport function caretDecode(text: string): string {\n return text.replace(/\\^(.)/g, (_, c) => {\n const code = c.charCodeAt(0);\n\n // Handle space after caret\n if (code === 32) {\n // Space\n return '^';\n }\n\n // Handle control characters\n if (code === 73) {\n // Tab (^I)\n return '\\t';\n }\n if (code === 74) {\n // Line Feed (^J)\n return '\\n';\n }\n if (code === 77) {\n // Carriage Return (^M)\n return '';\n }\n\n // Handle all other characters\n return '▯';\n });\n}\n\n/**\n * Escape DXF line endings\n * @param text - Text to escape\n * @returns Escaped text\n */\nexport function escapeDxfLineEndings(text: string): string {\n return text.replace(/\\r\\n|\\r|\\n/g, '\\\\P');\n}\n\n/**\n * Check if text contains inline formatting codes\n * @param text - Text to check\n * @returns True if text contains formatting codes\n */\nexport function hasInlineFormattingCodes(text: string): boolean {\n return text.replace(/\\\\P/g, '').replace(/\\\\~/g, '').includes('\\\\');\n}\n\n/**\n * Main parser class for MText content\n */\nexport class MTextParser {\n private scanner: TextScanner;\n private ctx: MTextContext;\n private ctxStack: MTextContext[] = [];\n private continueStroke: boolean = false;\n private yieldPropertyCommands: boolean;\n private decoder: TextDecoder;\n\n /**\n * Creates a new MTextParser instance\n * @param content - The MText content to parse\n * @param ctx - Optional initial MText context\n * @param yieldPropertyCommands - Whether to yield property change commands\n */\n constructor(content: string, ctx?: MTextContext, yieldPropertyCommands: boolean = false) {\n this.scanner = new TextScanner(caretDecode(content));\n this.ctx = ctx ?? new MTextContext();\n this.yieldPropertyCommands = yieldPropertyCommands;\n this.decoder = new TextDecoder('gbk');\n }\n\n /**\n * Decode multi-byte character from hex code\n * @param hex - Hex code string (e.g. \"C4E3\")\n * @returns Decoded character or empty square if invalid\n */\n private decodeMultiByteChar(hex: string): string {\n try {\n const bytes = new Uint8Array([\n parseInt(hex.substr(0, 2), 16),\n parseInt(hex.substr(2, 2), 16),\n ]);\n // TODO: handle BIG5 encoding too\n return this.decoder.decode(bytes);\n } catch {\n return '▯';\n }\n }\n\n /**\n * Push current context onto the stack\n */\n private pushCtx(): void {\n this.ctxStack.push(this.ctx);\n }\n\n /**\n * Pop context from the stack\n */\n private popCtx(): void {\n if (this.ctxStack.length > 0) {\n this.ctx = this.ctxStack.pop()!;\n }\n }\n\n /**\n * Parse stacking expression (numerator/denominator)\n * @returns Tuple of [TokenType.STACK, [numerator, denominator, type]]\n */\n private parseStacking(): [TokenType, [string, string, string]] {\n const scanner = new TextScanner(this.extractExpression(true));\n let numerator = '';\n let denominator = '';\n let stackingType = '';\n\n const getNextChar = (): [string, boolean] => {\n let c = scanner.peek();\n let escape = false;\n if (c.charCodeAt(0) < 32) {\n c = ' ';\n }\n if (c === '\\\\') {\n escape = true;\n scanner.consume(1);\n c = scanner.peek();\n }\n scanner.consume(1);\n return [c, escape];\n };\n\n const parseNumerator = (): [string, string] => {\n let word = '';\n while (scanner.hasData) {\n const [c, escape] = getNextChar();\n if (!escape && '^/#'.includes(c)) {\n return [word, c];\n }\n word += c;\n }\n return [word, ''];\n };\n\n const parseDenominator = (): string => {\n let word = '';\n while (scanner.hasData) {\n const [c, escape] = getNextChar();\n if (escape && c === ';') {\n word += ';';\n } else {\n word += c;\n }\n }\n return word;\n };\n\n [numerator, stackingType] = parseNumerator();\n if (stackingType) {\n denominator = parseDenominator();\n }\n\n return [TokenType.STACK, [numerator, denominator, stackingType]];\n }\n\n /**\n * Parse MText properties\n * @param cmd - The property command to parse\n */\n private parseProperties(cmd: string): void {\n const newCtx = this.ctx.copy();\n\n switch (cmd) {\n case 'L':\n newCtx.underline = true;\n this.continueStroke = true;\n break;\n case 'l':\n newCtx.underline = false;\n if (!newCtx.hasAnyStroke) {\n this.continueStroke = false;\n }\n break;\n case 'O':\n newCtx.overline = true;\n this.continueStroke = true;\n break;\n case 'o':\n newCtx.overline = false;\n if (!newCtx.hasAnyStroke) {\n this.continueStroke = false;\n }\n break;\n case 'K':\n newCtx.strikeThrough = true;\n this.continueStroke = true;\n break;\n case 'k':\n newCtx.strikeThrough = false;\n if (!newCtx.hasAnyStroke) {\n this.continueStroke = false;\n }\n break;\n case 'A':\n this.parseAlign(newCtx);\n break;\n case 'C':\n this.parseAciColor(newCtx);\n break;\n case 'c':\n this.parseRgbColor(newCtx);\n break;\n case 'H':\n this.parseHeight(newCtx);\n break;\n case 'W':\n this.parseWidth(newCtx);\n break;\n case 'Q':\n this.parseOblique(newCtx);\n break;\n case 'T':\n this.parseCharTracking(newCtx);\n break;\n case 'p':\n this.parseParagraphProperties(newCtx);\n break;\n case 'f':\n case 'F':\n this.parseFontProperties(newCtx);\n break;\n default:\n throw new Error(`Unknown command: ${cmd}`);\n }\n\n newCtx.continueStroke = this.continueStroke;\n this.ctx = newCtx;\n }\n\n /**\n * Parse alignment property\n * @param ctx - The context to update\n */\n private parseAlign(ctx: MTextContext): void {\n const char = this.scanner.get();\n if ('012'.includes(char)) {\n ctx.align = parseInt(char) as MTextLineAlignment;\n } else {\n ctx.align = MTextLineAlignment.BOTTOM;\n }\n this.consumeOptionalTerminator();\n }\n\n /**\n * Parse height property\n * @param ctx - The context to update\n */\n private parseHeight(ctx: MTextContext): void {\n const expr = this.extractFloatExpression(true);\n if (expr) {\n try {\n if (expr.endsWith('x')) {\n // For height command, treat x suffix as absolute value\n ctx.capHeight = Math.abs(parseFloat(expr.slice(0, -1)));\n } else {\n ctx.capHeight = Math.abs(parseFloat(expr));\n }\n } catch {\n // If parsing fails, treat the entire command as literal text\n this.scanner.consume(-expr.length); // Rewind to before the expression\n return;\n }\n }\n this.consumeOptionalTerminator();\n }\n\n /**\n * Parse width property\n * @param ctx - The context to update\n */\n private parseWidth(ctx: MTextContext): void {\n const expr = this.extractFloatExpression(true);\n if (expr) {\n try {\n if (expr.endsWith('x')) {\n // For width command, treat x suffix as absolute value\n ctx.widthFactor = Math.abs(parseFloat(expr.slice(0, -1)));\n } else {\n ctx.widthFactor = Math.abs(parseFloat(expr));\n }\n } catch {\n // If parsing fails, treat the entire command as literal text\n this.scanner.consume(-expr.length); // Rewind to before the expression\n return;\n }\n }\n this.consumeOptionalTerminator();\n }\n\n /**\n * Parse character tracking property\n * @param ctx - The context to update\n */\n private parseCharTracking(ctx: MTextContext): void {\n const expr = this.extractFloatExpression(true);\n if (expr) {\n try {\n if (expr.endsWith('x')) {\n // For tracking command, treat x suffix as absolute value\n ctx.charTrackingFactor = Math.abs(parseFloat(expr.slice(0, -1)));\n } else {\n ctx.charTrackingFactor = Math.abs(parseFloat(expr));\n }\n } catch {\n // If parsing fails, treat the entire command as literal text\n this.scanner.consume(-expr.length); // Rewind to before the expression\n return;\n }\n }\n this.consumeOptionalTerminator();\n }\n\n /**\n * Parse float value or factor\n * @param value - Current value to apply factor to\n * @returns New value\n */\n private parseFloatValueOrFactor(value: number): number {\n const expr = this.extractFloatExpression(true);\n if (expr) {\n if (expr.endsWith('x')) {\n const factor = parseFloat(expr.slice(0, -1));\n value *= Math.abs(factor);\n } else {\n value = Math.abs(parseFloat(expr));\n }\n }\n return value;\n }\n\n /**\n * Parse oblique angle property\n * @param ctx - The context to update\n */\n private parseOblique(ctx: MTextContext): void {\n const obliqueExpr = this.extractFloatExpression(false);\n if (obliqueExpr) {\n ctx.oblique = parseFloat(obliqueExpr);\n }\n this.consumeOptionalTerminator();\n }\n\n /**\n * Parse ACI color property\n * @param ctx - The context to update\n */\n private parseAciColor(ctx: MTextContext): void {\n const aciExpr = this.extractIntExpression();\n if (aciExpr) {\n const aci = parseInt(aciExpr);\n if (aci < 257) {\n ctx.aci = aci;\n ctx.rgb = null;\n }\n }\n this.consumeOptionalTerminator();\n }\n\n /**\n * Parse RGB color property\n * @param ctx - The context to update\n */\n private parseRgbColor(ctx: MTextContext): void {\n const rgbExpr = this.extractIntExpression();\n if (rgbExpr) {\n const value = parseInt(rgbExpr) & 0xffffff;\n const [b, g, r] = int2rgb(value);\n ctx.rgb = [r, g, b];\n }\n this.consumeOptionalTerminator();\n }\n\n /**\n * Extract float expression from scanner\n * @param relative - Whether to allow relative values (ending in 'x')\n * @returns Extracted expression\n */\n private extractFloatExpression(relative: boolean = false): string {\n const pattern = relative\n ? /^[+-]?(?:\\d+(?:\\.\\d*)?|\\.\\d+)(?:[eE][+-]?\\d+)?x?/\n : /^[+-]?(?:\\d+(?:\\.\\d*)?|\\.\\d+)(?:[eE][+-]?\\d+)?/;\n const match = this.scanner.tail.match(pattern);\n if (match) {\n const result = match[0];\n this.scanner.consume(result.length);\n return result;\n }\n return '';\n }\n\n /**\n * Extract integer expression from scanner\n * @returns Extracted expression\n */\n private extractIntExpression(): string {\n const match = this.scanner.tail.match(/^\\d+/);\n if (match) {\n const result = match[0];\n this.scanner.consume(result.length);\n return result;\n }\n return '';\n }\n\n /**\n * Extract expression until semicolon or end\n * @param escape - Whether to handle escaped semicolons\n * @returns Extracted expression\n */\n private extractExpression(escape: boolean = false): string {\n const stop = this.scanner.find(';', escape);\n if (stop < 0) {\n const expr = this.scanner.tail;\n this.scanner.consume(expr.length);\n return expr;\n }\n // Check if the semicolon is escaped by looking at the previous character\n const prevChar = this.scanner.peek(stop - this.scanner.currentIndex - 1);\n const isEscaped = prevChar === '\\\\';\n const expr = this.scanner.tail.slice(0, stop - this.scanner.currentIndex + (isEscaped ? 1 : 0));\n this.scanner.consume(expr.length + 1);\n return expr;\n }\n\n /**\n * Parse font properties\n * @param ctx - The context to update\n */\n private parseFontProperties(ctx: MTextContext): void {\n const parts = this.extractExpression().split('|');\n if (parts.length > 0 && parts[0]) {\n const name = parts[0];\n let style = 'Regular';\n let weight = 400;\n\n for (const part of parts.slice(1)) {\n if (part.startsWith('b1')) {\n weight = 700;\n } else if (part.startsWith('i1')) {\n style = 'Italic';\n }\n }\n\n ctx.fontFace = {\n family: name,\n style,\n weight,\n };\n }\n }\n\n /**\n * Parse paragraph properties from the MText content\n * Handles properties like indentation, alignment, and tab stops\n * @param ctx - The context to update\n */\n private parseParagraphProperties(ctx: MTextContext): void {\n const scanner = new TextScanner(this.extractExpression());\n /** Current indentation value */\n let indent = ctx.paragraph.indent;\n /** Left margin value */\n let left = ctx.paragraph.left;\n /** Right margin value */\n let right = ctx.paragraph.right;\n /** Current paragraph alignment */\n let align = ctx.paragraph.align;\n /** Array of tab stop positions and types */\n let tabStops: (number | string)[] = [];\n\n /**\n * Parse a floating point number from the scanner's current position\n * Handles optional sign, decimal point, and scientific notation\n * @returns The parsed float value, or 0 if no valid number is found\n */\n const parseFloatValue = (): number => {\n const match = scanner.tail.match(/^[+-]?\\d+(?:\\.\\d*)?(?:[eE][+-]?\\d+)?/);\n if (match) {\n const value = parseFloat(match[0]);\n scanner.consume(match[0].length);\n while (scanner.peek() === ',') {\n scanner.consume(1);\n }\n return value;\n }\n return 0;\n };\n\n while (scanner.hasData) {\n const cmd = scanner.get();\n switch (cmd) {\n case 'i': // Indentation\n indent = parseFloatValue();\n break;\n case 'l': // Left margin\n left = parseFloatValue();\n break;\n case 'r': // Right margin\n right = parseFloatValue();\n break;\n case 'x': // Skip\n break;\n case 'q': {\n // Alignment\n const adjustment = scanner.get();\n align = CHAR_TO_ALIGN[adjustment] || MTextParagraphAlignment.DEFAULT;\n while (scanner.peek() === ',') {\n scanner.consume(1);\n }\n break;\n }\n case 't': // Tab stops\n tabStops = [];\n while (scanner.hasData) {\n const type = scanner.peek();\n if (type === 'r' || type === 'c') {\n scanner.consume(1);\n const value = parseFloatValue();\n tabStops.push(type + value.toString());\n } else {\n const value = parseFloatValue();\n if (!isNaN(value)) {\n tabStops.push(value);\n } else {\n scanner.consume(1);\n }\n }\n }\n break;\n }\n }\n\n ctx.paragraph = {\n indent,\n left,\n right,\n align,\n tab_stops: tabStops,\n };\n }\n\n /**\n * Consume optional terminator (semicolon)\n */\n private consumeOptionalTerminator(): void {\n if (this.scanner.peek() === ';') {\n this.scanner.consume(1);\n }\n }\n\n /**\n * Parse MText content into tokens\n * @yields MTextToken objects\n */\n *parse(): Generator<MTextToken> {\n const wordToken = TokenType.WORD;\n const spaceToken = TokenType.SPACE;\n let followupToken: TokenType | null = null;\n\n const nextToken = (): [TokenType, TokenData[TokenType]] => {\n let word = '';\n while (this.scanner.hasData) {\n let escape = false;\n let letter = this.scanner.peek();\n const cmdStartIndex = this.scanner.currentIndex;\n\n // Handle control characters first\n if (letter.charCodeAt(0) < 32) {\n this.scanner.consume(1); // Always consume the control character\n if (letter === '\\t') {\n return [TokenType.TABULATOR, null];\n }\n if (letter === '\\n') {\n return [TokenType.NEW_PARAGRAPH, null];\n }\n letter = ' ';\n }\n\n if (letter === '\\\\') {\n if ('\\\\{}'.includes(this.scanner.peek(1))) {\n escape = true;\n this.scanner.consume(1);\n letter = this.scanner.peek();\n } else {\n if (word) {\n return [wordToken, word];\n }\n this.scanner.consume(1);\n const cmd = this.scanner.get();\n switch (cmd) {\n case '~':\n return [TokenType.NBSP, null];\n case 'P':\n return [TokenType.NEW_PARAGRAPH, null];\n case 'N':\n return [TokenType.NEW_COLUMN, null];\n case 'X':\n return [TokenType.WRAP_AT_DIMLINE, null];\n case 'S':\n return this.parseStacking();\n case 'm':\n case 'M':\n // Handle multi-byte character encoding\n if (this.scanner.peek() === '+') {\n this.scanner.consume(1); // Consume the '+'\n const hexCode = this.scanner.tail.match(/^[0-9A-Fa-f]{4}/)?.[0];\n if (hexCode) {\n this.scanner.consume(4);\n const decodedChar = this.decodeMultiByteChar(hexCode);\n if (word) {\n return [wordToken, word];\n }\n return [wordToken, decodedChar];\n }\n // If no valid hex code found, rewind the '+' character\n this.scanner.consume(-1);\n }\n // If not a valid multi-byte code, treat as literal text\n word += '\\\\M';\n continue;\n default:\n if (cmd) {\n try {\n this.parseProperties(cmd);\n if (this.yieldPropertyCommands) {\n return [\n TokenType.PROPERTIES_CHANGED,\n this.scanner.tail.slice(cmdStartIndex, this.scanner.currentIndex),\n ];\n }\n // After processing a property command, continue with normal parsing\n continue;\n } catch {\n const commandText = this.scanner.tail.slice(\n cmdStartIndex,\n this.scanner.currentIndex\n );\n word += commandText;\n }\n }\n }\n continue;\n }\n }\n\n if (letter === '%' && this.scanner.peek(1) === '%') {\n const code = this.scanner.peek(2).toLowerCase();\n const specialChar = SPECIAL_CHAR_ENCODING[code];\n if (specialChar) {\n this.scanner.consume(3);\n word += specialChar;\n continue;\n } else {\n // Skip invalid special character codes\n this.scanner.consume(3);\n continue;\n }\n }\n\n if (letter === ' ') {\n if (word) {\n this.scanner.consume(1);\n followupToken = spaceToken;\n return [wordToken, word];\n }\n this.scanner.consume(1);\n return [spaceToken, null];\n }\n\n if (!escape) {\n if (letter === '{') {\n if (word) {\n return [wordToken, word];\n }\n this.scanner.consume(1);\n this.pushCtx();\n continue;\n } else if (letter === '}') {\n if (word) {\n return [wordToken, word];\n }\n this.scanner.consume(1);\n this.popCtx();\n continue;\n }\n }\n\n this.scanner.consume(1);\n if (letter.charCodeAt(0) >= 32) {\n word += letter;\n }\n }\n\n if (word) {\n return [wordToken, word];\n }\n return [TokenType.NONE, null];\n };\n\n while (true) {\n const [type, data] = nextToken();\n if (type) {\n yield new MTextToken(type, this.ctx, data);\n if (followupToken) {\n yield new MTextToken(followupToken, this.ctx, null);\n followupToken = null;\n }\n } else {\n break;\n }\n }\n }\n}\n\n/**\n * Text scanner for parsing MText content\n */\nexport class TextScanner {\n private text: string;\n private textLen: number;\n private _index: number;\n\n /**\n * Create a new text scanner\n * @param text - The text to scan\n */\n constructor(text: string) {\n this.text = text;\n this.textLen = text.length;\n this._index = 0;\n }\n\n /**\n * Get the current index in the text\n */\n get currentIndex(): number {\n return this._index;\n }\n\n /**\n * Check if the scanner has reached the end of the text\n */\n get isEmpty(): boolean {\n return this._index >= this.textLen;\n }\n\n /**\n * Check if there is more text to scan\n */\n get hasData(): boolean {\n return this._index < this.textLen;\n }\n\n /**\n * Get the next character and advance the index\n * @returns The next character, or empty string if at end\n */\n get(): string {\n if (this.isEmpty) {\n return '';\n }\n const char = this.text[this._index];\n this._index++;\n return char;\n }\n\n /**\n * Advance the index by the specified count\n * @param count - Number of characters to advance\n */\n consume(count: number = 1): void {\n this._index = Math.max(0, Math.min(this._index + count, this.textLen));\n }\n\n /**\n * Look at a character without advancing the index\n * @param offset - Offset from current position\n * @returns The character at the offset position, or empty string if out of bounds\n */\n peek(offset: number = 0): string {\n const index = this._index + offset;\n if (index >= this.textLen || index < 0) {\n return '';\n }\n return this.text[index];\n }\n\n /**\n * Find the next occurrence of a character\n * @param char - The character to find\n * @param escape - Whether to handle escaped characters\n * @returns Index of the character, or -1 if not found\n */\n find(char: string, escape: boolean = false): number {\n let index = this._index;\n while (index < this.textLen) {\n if (escape && this.text[index] === '\\\\') {\n if (index + 1 < this.textLen) {\n if (this.text[index + 1] === char) {\n return index + 1;\n }\n index += 2;\n continue;\n }\n index++;\n continue;\n }\n if (this.text[index] === char) {\n return index;\n }\n index++;\n }\n return -1;\n }\n\n /**\n * Get the remaining text from the current position\n */\n get tail(): string {\n return this.text.slice(this._index);\n }\n\n /**\n * Check if the next character is a space\n */\n isNextSpace(): boolean {\n return this.peek() === ' ';\n }\n\n /**\n * Consume spaces until a non-space character is found\n * @returns Number of spaces consumed\n */\n consumeSpaces(): number {\n let count = 0;\n while (this.isNextSpace()) {\n this.consume();\n count++;\n }\n return count;\n }\n}\n\n/**\n * MText context class for managing text formatting state\n */\nexport class MTextContext {\n private _stroke: number = 0;\n /** Whether to continue stroke formatting */\n continueStroke: boolean = false;\n private _aci: number = 7;\n /** RGB color value, or null if using ACI */\n rgb: RGB | null = null;\n /** Line alignment */\n align: MTextLineAlignment = MTextLineAlignment.BOTTOM;\n /** Font face properties */\n fontFace: FontFace = { family: '', style: 'Regular', weight: 400 };\n /** Capital letter height */\n capHeight: number = 1.0;\n /** Character width factor */\n widthFactor: number = 1.0;\n /** Character tracking factor */\n charTrackingFactor: number = 1.0;\n /** Oblique angle */\n oblique: number = 0.0;\n /** Paragraph properties */\n paragraph: ParagraphProperties = {\n indent: 0,\n left: 0,\n right: 0,\n align: MTextParagraphAlignment.DEFAULT,\n tab_stops: [],\n };\n\n /**\n * Get the ACI color value\n */\n get aci(): number {\n return this._aci;\n }\n\n /**\n * Set the ACI color value\n * @param value - ACI color value (0-256)\n * @throws Error if value is out of range\n */\n set aci(value: number) {\n if (value >= 0 && value <= 256) {\n this._aci = value;\n this.rgb = null;\n } else {\n throw new Error('ACI not in range [0, 256]');\n }\n }\n\n /**\n * Get whether text is underlined\n */\n get underline(): boolean {\n return Boolean(this._stroke & MTextStroke.UNDERLINE);\n }\n\n /**\n * Set whether text is underlined\n * @param value - Whether to underline\n */\n set underline(value: boolean) {\n this._setStrokeState(MTextStroke.UNDERLINE, value);\n }\n\n /**\n * Get whether text has strike-through\n */\n get strikeThrough(): boolean {\n return Boolean(this._stroke & MTextStroke.STRIKE_THROUGH);\n }\n\n /**\n * Set whether text has strike-through\n * @param value - Whether to strike through\n */\n set strikeThrough(value: boolean) {\n this._setStrokeState(MTextStroke.STRIKE_THROUGH, value);\n }\n\n /**\n * Get whether text has overline\n */\n get overline(): boolean {\n return Boolean(this._stroke & MTextStroke.OVERLINE);\n }\n\n /**\n * Set whether text has overline\n * @param value - Whether to overline\n */\n set overline(value: boolean) {\n this._setStrokeState(MTextStroke.OVERLINE, value);\n }\n\n /**\n * Check if any stroke formatting is active\n */\n get hasAnyStroke(): boolean {\n return Boolean(this._stroke);\n }\n\n /**\n * Set the state of a stroke type\n * @param stroke - The stroke type to set\n * @param state - Whether to enable or disable the stroke\n */\n private _setStrokeState(stroke: MTextStroke, state: boolean = true): void {\n if (state) {\n this._stroke |= stroke;\n } else {\n this._stroke &= ~stroke;\n }\n }\n\n /**\n * Create a copy of this context\n * @returns A new context with the same properties\n */\n copy(): MTextContext {\n const ctx = new MTextContext();\n ctx._stroke = this._stroke;\n ctx.continueStroke = this.continueStroke;\n ctx._aci = this._aci;\n ctx.rgb = this.rgb;\n ctx.align = this.align;\n ctx.fontFace = { ...this.fontFace };\n ctx.capHeight = this.capHeight;\n ctx.widthFactor = this.widthFactor;\n ctx.charTrackingFactor = this.charTrackingFactor;\n ctx.oblique = this.oblique;\n ctx.paragraph = { ...this.paragraph };\n return ctx;\n }\n}\n\n/**\n * Token class for MText parsing\n */\nexport class MTextToken {\n /**\n * Create a new MText token\n * @param type - The token type\n * @param ctx - The text context at this token\n * @param data - Optional token data\n */\n constructor(\n public type: TokenType,\n public ctx: MTextContext,\n public data: TokenData[TokenType]\n ) {}\n}\n"],"names":["TokenType","MTextLineAlignment","MTextParagraphAlignment","MTextStroke","SPECIAL_CHAR_ENCODING","CHAR_TO_ALIGN","rgb2int","rgb","r","g","b","int2rgb","value","caretDecode","text","_","c","code","escapeDxfLineEndings","hasInlineFormattingCodes","MTextParser","content","ctx","yieldPropertyCommands","TextScanner","MTextContext","hex","bytes","scanner","numerator","denominator","stackingType","getNextChar","escape","parseNumerator","word","parseDenominator","cmd","newCtx","char","expr","factor","obliqueExpr","aciExpr","aci","rgbExpr","relative","pattern","match","result","stop","isEscaped","parts","name","style","weight","part","indent","left","right","align","tabStops","parseFloatValue","adjustment","type","followupToken","nextToken","_a","letter","cmdStartIndex","hexCode","decodedChar","commandText","specialChar","data","MTextToken","count","offset","index","stroke","state"],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ (function(a,d){typeof exports=="object"&&typeof module<"u"?d(exports):typeof define=="function"&&define.amd?define(["exports"],d):(a=typeof globalThis<"u"?globalThis:a||self,d(a.MTextParser={}))})(this,function(a){"use strict";var d=(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))(d||{}),x=(s=>(s[s.BOTTOM=0]="BOTTOM",s[s.MIDDLE=1]="MIDDLE",s[s.TOP=2]="TOP",s))(x||{}),E=(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||{}),m=(s=>(s[s.NONE=0]="NONE",s[s.UNDERLINE=1]="UNDERLINE",s[s.OVERLINE=2]="OVERLINE",s[s.STRIKE_THROUGH=4]="STRIKE_THROUGH",s))(m||{});const w={c:"Ø",d:"°",p:"±"},_={l:1,r:2,c:3,j:4,d:5};function I(s){const[e,t,r]=s;return r<<16|t<<8|e}function b(s){const e=s&255,t=s>>8&255,r=s>>16&255;return[e,t,r]}function S(s){return s.replace(/\^(.)/g,(e,t)=>{const r=t.charCodeAt(0);return r===32?"^":r===73?" ":r===74?`
2
+ `:r===77?"":"▯"})}function C(s){return s.replace(/\r\n|\r|\n/g,"\\P")}function F(s){return s.replace(/\\P/g,"").replace(/\\~/g,"").includes("\\")}class N{constructor(e,t,r=!1){this.ctxStack=[],this.continueStroke=!1,this.scanner=new f(S(e)),this.ctx=t??new g,this.yieldPropertyCommands=r,this.decoder=new TextDecoder("gbk")}decodeMultiByteChar(e){try{const t=new Uint8Array([parseInt(e.substr(0,2),16),parseInt(e.substr(2,2),16)]);return this.decoder.decode(t)}catch{return"▯"}}pushCtx(){this.ctxStack.push(this.ctx)}popCtx(){this.ctxStack.length>0&&(this.ctx=this.ctxStack.pop())}parseStacking(){const e=new f(this.extractExpression(!0));let t="",r="",c="";const n=()=>{let i=e.peek(),h=!1;return i.charCodeAt(0)<32&&(i=" "),i==="\\"&&(h=!0,e.consume(1),i=e.peek()),e.consume(1),[i,h]},o=()=>{let i="";for(;e.hasData;){const[h,u]=n();if(!u&&"^/#".includes(h))return[i,h];i+=h}return[i,""]},p=()=>{let i="";for(;e.hasData;){const[h,u]=n();u&&h===";"?i+=";":i+=h}return i};return[t,c]=o(),c&&(r=p()),[2,[t,r,c]]}parseProperties(e){const t=this.ctx.copy();switch(e){case"L":t.underline=!0,this.continueStroke=!0;break;case"l":t.underline=!1,t.hasAnyStroke||(this.continueStroke=!1);break;case"O":t.overline=!0,this.continueStroke=!0;break;case"o":t.overline=!1,t.hasAnyStroke||(this.continueStroke=!1);break;case"K":t.strikeThrough=!0,this.continueStroke=!0;break;case"k":t.strikeThrough=!1,t.hasAnyStroke||(this.continueStroke=!1);break;case"A":this.parseAlign(t);break;case"C":this.parseAciColor(t);break;case"c":this.parseRgbColor(t);break;case"H":this.parseHeight(t);break;case"W":this.parseWidth(t);break;case"Q":this.parseOblique(t);break;case"T":this.parseCharTracking(t);break;case"p":this.parseParagraphProperties(t);break;case"f":case"F":this.parseFontProperties(t);break;default:throw new Error(`Unknown command: ${e}`)}t.continueStroke=this.continueStroke,this.ctx=t}parseAlign(e){const t=this.scanner.get();"012".includes(t)?e.align=parseInt(t):e.align=0,this.consumeOptionalTerminator()}parseHeight(e){const t=this.extractFloatExpression(!0);if(t)try{t.endsWith("x")?e.capHeight=Math.abs(parseFloat(t.slice(0,-1))):e.capHeight=Math.abs(parseFloat(t))}catch{this.scanner.consume(-t.length);return}this.consumeOptionalTerminator()}parseWidth(e){const t=this.extractFloatExpression(!0);if(t)try{t.endsWith("x")?e.widthFactor=Math.abs(parseFloat(t.slice(0,-1))):e.widthFactor=Math.abs(parseFloat(t))}catch{this.scanner.consume(-t.length);return}this.consumeOptionalTerminator()}parseCharTracking(e){const t=this.extractFloatExpression(!0);if(t)try{t.endsWith("x")?e.charTrackingFactor=Math.abs(parseFloat(t.slice(0,-1))):e.charTrackingFactor=Math.abs(parseFloat(t))}catch{this.scanner.consume(-t.length);return}this.consumeOptionalTerminator()}parseFloatValueOrFactor(e){const t=this.extractFloatExpression(!0);if(t)if(t.endsWith("x")){const r=parseFloat(t.slice(0,-1));e*=Math.abs(r)}else e=Math.abs(parseFloat(t));return e}parseOblique(e){const t=this.extractFloatExpression(!1);t&&(e.oblique=parseFloat(t)),this.consumeOptionalTerminator()}parseAciColor(e){const t=this.extractIntExpression();if(t){const r=parseInt(t);r<257&&(e.aci=r,e.rgb=null)}this.consumeOptionalTerminator()}parseRgbColor(e){const t=this.extractIntExpression();if(t){const r=parseInt(t)&16777215,[c,n,o]=b(r);e.rgb=[o,n,c]}this.consumeOptionalTerminator()}extractFloatExpression(e=!1){const t=e?/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?x?/:/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?/,r=this.scanner.tail.match(t);if(r){const c=r[0];return this.scanner.consume(c.length),c}return""}extractIntExpression(){const e=this.scanner.tail.match(/^\d+/);if(e){const t=e[0];return this.scanner.consume(t.length),t}return""}extractExpression(e=!1){const t=this.scanner.find(";",e);if(t<0){const o=this.scanner.tail;return this.scanner.consume(o.length),o}const c=this.scanner.peek(t-this.scanner.currentIndex-1)==="\\",n=this.scanner.tail.slice(0,t-this.scanner.currentIndex+(c?1:0));return this.scanner.consume(n.length+1),n}parseFontProperties(e){const t=this.extractExpression().split("|");if(t.length>0&&t[0]){const r=t[0];let c="Regular",n=400;for(const o of t.slice(1))o.startsWith("b1")?n=700:o.startsWith("i1")&&(c="Italic");e.fontFace={family:r,style:c,weight:n}}}parseParagraphProperties(e){const t=new f(this.extractExpression());let r=e.paragraph.indent,c=e.paragraph.left,n=e.paragraph.right,o=e.paragraph.align,p=[];const i=()=>{const h=t.tail.match(/^[+-]?\d+(?:\.\d*)?(?:[eE][+-]?\d+)?/);if(h){const u=parseFloat(h[0]);for(t.consume(h[0].length);t.peek()===",";)t.consume(1);return u}return 0};for(;t.hasData;)switch(t.get()){case"i":r=i();break;case"l":c=i();break;case"r":n=i();break;case"x":break;case"q":{const u=t.get();for(o=_[u]||0;t.peek()===",";)t.consume(1);break}case"t":for(p=[];t.hasData;){const u=t.peek();if(u==="r"||u==="c"){t.consume(1);const l=i();p.push(u+l.toString())}else{const l=i();isNaN(l)?t.consume(1):p.push(l)}}break}e.paragraph={indent:r,left:c,right:n,align:o,tab_stops:p}}consumeOptionalTerminator(){this.scanner.peek()===";"&&this.scanner.consume(1)}*parse(){let r=null;const c=()=>{var o;let n="";for(;this.scanner.hasData;){let p=!1,i=this.scanner.peek();const h=this.scanner.currentIndex;if(i.charCodeAt(0)<32){if(this.scanner.consume(1),i===" ")return[5,null];if(i===`
3
+ `)return[6,null];i=" "}if(i==="\\")if("\\{}".includes(this.scanner.peek(1)))p=!0,this.scanner.consume(1),i=this.scanner.peek();else{if(n)return[1,n];this.scanner.consume(1);const u=this.scanner.get();switch(u){case"~":return[4,null];case"P":return[6,null];case"N":return[7,null];case"X":return[8,null];case"S":return this.parseStacking();case"m":case"M":if(this.scanner.peek()==="+"){this.scanner.consume(1);const l=(o=this.scanner.tail.match(/^[0-9A-Fa-f]{4}/))==null?void 0:o[0];if(l){this.scanner.consume(4);const O=this.decodeMultiByteChar(l);return n?[1,n]:[1,O]}this.scanner.consume(-1)}n+="\\M";continue;default:if(u)try{if(this.parseProperties(u),this.yieldPropertyCommands)return[9,this.scanner.tail.slice(h,this.scanner.currentIndex)];continue}catch{const l=this.scanner.tail.slice(h,this.scanner.currentIndex);n+=l}}continue}if(i==="%"&&this.scanner.peek(1)==="%"){const u=this.scanner.peek(2).toLowerCase(),l=w[u];if(l){this.scanner.consume(3),n+=l;continue}else{this.scanner.consume(3);continue}}if(i===" ")return n?(this.scanner.consume(1),r=3,[1,n]):(this.scanner.consume(1),[3,null]);if(!p){if(i==="{"){if(n)return[1,n];this.scanner.consume(1),this.pushCtx();continue}else if(i==="}"){if(n)return[1,n];this.scanner.consume(1),this.popCtx();continue}}this.scanner.consume(1),i.charCodeAt(0)>=32&&(n+=i)}return n?[1,n]:[0,null]};for(;;){const[n,o]=c();if(n)yield new k(n,this.ctx,o),r&&(yield new k(r,this.ctx,null),r=null);else break}}}class f{constructor(e){this.text=e,this.textLen=e.length,this._index=0}get currentIndex(){return this._index}get isEmpty(){return this._index>=this.textLen}get hasData(){return this._index<this.textLen}get(){if(this.isEmpty)return"";const e=this.text[this._index];return this._index++,e}consume(e=1){this._index=Math.max(0,Math.min(this._index+e,this.textLen))}peek(e=0){const t=this._index+e;return t>=this.textLen||t<0?"":this.text[t]}find(e,t=!1){let r=this._index;for(;r<this.textLen;){if(t&&this.text[r]==="\\"){if(r+1<this.textLen){if(this.text[r+1]===e)return r+1;r+=2;continue}r++;continue}if(this.text[r]===e)return r;r++}return-1}get tail(){return this.text.slice(this._index)}isNextSpace(){return this.peek()===" "}consumeSpaces(){let e=0;for(;this.isNextSpace();)this.consume(),e++;return e}}class g{constructor(){this._stroke=0,this.continueStroke=!1,this._aci=7,this.rgb=null,this.align=0,this.fontFace={family:"",style:"Regular",weight:400},this.capHeight=1,this.widthFactor=1,this.charTrackingFactor=1,this.oblique=0,this.paragraph={indent:0,left:0,right:0,align:0,tab_stops:[]}}get aci(){return this._aci}set aci(e){if(e>=0&&e<=256)this._aci=e,this.rgb=null;else throw new Error("ACI not in range [0, 256]")}get underline(){return!!(this._stroke&1)}set underline(e){this._setStrokeState(1,e)}get strikeThrough(){return!!(this._stroke&4)}set strikeThrough(e){this._setStrokeState(4,e)}get overline(){return!!(this._stroke&2)}set overline(e){this._setStrokeState(2,e)}get hasAnyStroke(){return!!this._stroke}_setStrokeState(e,t=!0){t?this._stroke|=e:this._stroke&=~e}copy(){const e=new g;return e._stroke=this._stroke,e.continueStroke=this.continueStroke,e._aci=this._aci,e.rgb=this.rgb,e.align=this.align,e.fontFace={...this.fontFace},e.capHeight=this.capHeight,e.widthFactor=this.widthFactor,e.charTrackingFactor=this.charTrackingFactor,e.oblique=this.oblique,e.paragraph={...this.paragraph},e}}class k{constructor(e,t,r){this.type=e,this.ctx=t,this.data=r}}a.MTextContext=g,a.MTextLineAlignment=x,a.MTextParagraphAlignment=E,a.MTextParser=N,a.MTextStroke=m,a.MTextToken=k,a.TextScanner=f,a.TokenType=d,a.caretDecode=S,a.escapeDxfLineEndings=C,a.hasInlineFormattingCodes=F,a.int2rgb=b,a.rgb2int=I,Object.defineProperty(a,Symbol.toStringTag,{value:"Module"})});
4
+ //# sourceMappingURL=parser.umd.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.umd.js","sources":["../src/parser.ts"],"sourcesContent":["/**\n * Token types used in MText parsing\n */\nexport enum TokenType {\n /** No token */\n NONE = 0,\n /** Word token with string data */\n WORD = 1,\n /** Stack token with [numerator, denominator, type] data */\n STACK = 2,\n /** Space token with no data */\n SPACE = 3,\n /** Non-breaking space token with no data */\n NBSP = 4,\n /** Tab token with no data */\n TABULATOR = 5,\n /** New paragraph token with no data */\n NEW_PARAGRAPH = 6,\n /** New column token with no data */\n NEW_COLUMN = 7,\n /** Wrap at dimension line token with no data */\n WRAP_AT_DIMLINE = 8,\n /** Properties changed token with string data (full command) */\n PROPERTIES_CHANGED = 9,\n}\n\n/**\n * Type for token data based on token type\n */\nexport type TokenData = {\n [TokenType.NONE]: null;\n [TokenType.WORD]: string;\n [TokenType.STACK]: [string, string, string];\n [TokenType.SPACE]: null;\n [TokenType.NBSP]: null;\n [TokenType.TABULATOR]: null;\n [TokenType.NEW_PARAGRAPH]: null;\n [TokenType.NEW_COLUMN]: null;\n [TokenType.WRAP_AT_DIMLINE]: null;\n [TokenType.PROPERTIES_CHANGED]: string;\n};\n\n/**\n * Line alignment options for MText\n */\nexport enum MTextLineAlignment {\n /** Align text to bottom */\n BOTTOM = 0,\n /** Align text to middle */\n MIDDLE = 1,\n /** Align text to top */\n TOP = 2,\n}\n\n/**\n * Paragraph alignment options for MText\n */\nexport enum MTextParagraphAlignment {\n /** Default alignment */\n DEFAULT = 0,\n /** Left alignment */\n LEFT = 1,\n /** Right alignment */\n RIGHT = 2,\n /** Center alignment */\n CENTER = 3,\n /** Justified alignment */\n JUSTIFIED = 4,\n /** Distributed alignment */\n DISTRIBUTED = 5,\n}\n\n/**\n * Text stroke options for MText\n */\nexport enum MTextStroke {\n /** No stroke */\n NONE = 0,\n /** Underline stroke */\n UNDERLINE = 1,\n /** Overline stroke */\n OVERLINE = 2,\n /** Strike-through stroke */\n STRIKE_THROUGH = 4,\n}\n\n/**\n * RGB color tuple\n */\nexport type RGB = [number, number, number];\n\n/**\n * Font face properties\n */\nexport interface FontFace {\n /** Font family name */\n family: string;\n /** Font style (e.g., 'Regular', 'Italic') */\n style: string;\n /** Font weight (e.g., 400 for normal, 700 for bold) */\n weight: number;\n}\n\n/**\n * Font measurement properties\n */\nexport interface FontMeasurements {\n /** Height of capital letters */\n cap_height: number;\n /** Baseline position */\n baseline: number;\n /** Bottom position */\n bottom: number;\n /** Top position */\n top: number;\n /** Total height of the font */\n total_height: number;\n /**\n * Scale measurements by a factor\n * @param factor - The scaling factor\n * @returns New scaled measurements\n */\n scale(factor: number): FontMeasurements;\n}\n\n/**\n * Abstract font interface\n */\nexport interface AbstractFont {\n /**\n * Calculate text width\n * @param text - The text to measure\n * @returns Width of the text\n */\n text_width(text: string): number;\n /**\n * Get width of a space character\n * @returns Width of a space\n */\n space_width(): number;\n /** Font measurements */\n measurements: FontMeasurements;\n}\n\n/**\n * Paragraph properties\n */\nexport interface ParagraphProperties {\n /** Indentation value */\n indent: number;\n /** Left margin value */\n left: number;\n /** Right margin value */\n right: number;\n /** Paragraph alignment */\n align: MTextParagraphAlignment;\n /** Tab stop positions and types */\n tab_stops: (number | string)[];\n}\n\n/**\n * Special character encoding mapping\n */\nconst SPECIAL_CHAR_ENCODING: Record<string, string> = {\n c: 'Ø',\n d: '°',\n p: '±',\n};\n\n/**\n * Character to paragraph alignment mapping\n */\nconst CHAR_TO_ALIGN: Record<string, MTextParagraphAlignment> = {\n l: MTextParagraphAlignment.LEFT,\n r: MTextParagraphAlignment.RIGHT,\n c: MTextParagraphAlignment.CENTER,\n j: MTextParagraphAlignment.JUSTIFIED,\n d: MTextParagraphAlignment.DISTRIBUTED,\n};\n\n/**\n * Convert RGB tuple to integer color value\n * @param rgb - RGB color tuple\n * @returns Integer color value\n */\nexport function rgb2int(rgb: RGB): number {\n const [r, g, b] = rgb;\n return (b << 16) | (g << 8) | r;\n}\n\n/**\n * Convert integer color value to RGB tuple\n * @param value - Integer color value\n * @returns RGB color tuple\n */\nexport function int2rgb(value: number): RGB {\n const r = value & 0xff;\n const g = (value >> 8) & 0xff;\n const b = (value >> 16) & 0xff;\n return [r, g, b];\n}\n\n/**\n * DXF stores some special characters using caret notation. This function\n * decodes this notation to normalize the representation of special characters\n * in the string.\n * see: https://en.wikipedia.org/wiki/Caret_notation\n * @param text - Text to decode\n * @returns Decoded text\n */\nexport function caretDecode(text: string): string {\n return text.replace(/\\^(.)/g, (_, c) => {\n const code = c.charCodeAt(0);\n\n // Handle space after caret\n if (code === 32) {\n // Space\n return '^';\n }\n\n // Handle control characters\n if (code === 73) {\n // Tab (^I)\n return '\\t';\n }\n if (code === 74) {\n // Line Feed (^J)\n return '\\n';\n }\n if (code === 77) {\n // Carriage Return (^M)\n return '';\n }\n\n // Handle all other characters\n return '▯';\n });\n}\n\n/**\n * Escape DXF line endings\n * @param text - Text to escape\n * @returns Escaped text\n */\nexport function escapeDxfLineEndings(text: string): string {\n return text.replace(/\\r\\n|\\r|\\n/g, '\\\\P');\n}\n\n/**\n * Check if text contains inline formatting codes\n * @param text - Text to check\n * @returns True if text contains formatting codes\n */\nexport function hasInlineFormattingCodes(text: string): boolean {\n return text.replace(/\\\\P/g, '').replace(/\\\\~/g, '').includes('\\\\');\n}\n\n/**\n * Main parser class for MText content\n */\nexport class MTextParser {\n private scanner: TextScanner;\n private ctx: MTextContext;\n private ctxStack: MTextContext[] = [];\n private continueStroke: boolean = false;\n private yieldPropertyCommands: boolean;\n private decoder: TextDecoder;\n\n /**\n * Creates a new MTextParser instance\n * @param content - The MText content to parse\n * @param ctx - Optional initial MText context\n * @param yieldPropertyCommands - Whether to yield property change commands\n */\n constructor(content: string, ctx?: MTextContext, yieldPropertyCommands: boolean = false) {\n this.scanner = new TextScanner(caretDecode(content));\n this.ctx = ctx ?? new MTextContext();\n this.yieldPropertyCommands = yieldPropertyCommands;\n this.decoder = new TextDecoder('gbk');\n }\n\n /**\n * Decode multi-byte character from hex code\n * @param hex - Hex code string (e.g. \"C4E3\")\n * @returns Decoded character or empty square if invalid\n */\n private decodeMultiByteChar(hex: string): string {\n try {\n const bytes = new Uint8Array([\n parseInt(hex.substr(0, 2), 16),\n parseInt(hex.substr(2, 2), 16),\n ]);\n // TODO: handle BIG5 encoding too\n return this.decoder.decode(bytes);\n } catch {\n return '▯';\n }\n }\n\n /**\n * Push current context onto the stack\n */\n private pushCtx(): void {\n this.ctxStack.push(this.ctx);\n }\n\n /**\n * Pop context from the stack\n */\n private popCtx(): void {\n if (this.ctxStack.length > 0) {\n this.ctx = this.ctxStack.pop()!;\n }\n }\n\n /**\n * Parse stacking expression (numerator/denominator)\n * @returns Tuple of [TokenType.STACK, [numerator, denominator, type]]\n */\n private parseStacking(): [TokenType, [string, string, string]] {\n const scanner = new TextScanner(this.extractExpression(true));\n let numerator = '';\n let denominator = '';\n let stackingType = '';\n\n const getNextChar = (): [string, boolean] => {\n let c = scanner.peek();\n let escape = false;\n if (c.charCodeAt(0) < 32) {\n c = ' ';\n }\n if (c === '\\\\') {\n escape = true;\n scanner.consume(1);\n c = scanner.peek();\n }\n scanner.consume(1);\n return [c, escape];\n };\n\n const parseNumerator = (): [string, string] => {\n let word = '';\n while (scanner.hasData) {\n const [c, escape] = getNextChar();\n if (!escape && '^/#'.includes(c)) {\n return [word, c];\n }\n word += c;\n }\n return [word, ''];\n };\n\n const parseDenominator = (): string => {\n let word = '';\n while (scanner.hasData) {\n const [c, escape] = getNextChar();\n if (escape && c === ';') {\n word += ';';\n } else {\n word += c;\n }\n }\n return word;\n };\n\n [numerator, stackingType] = parseNumerator();\n if (stackingType) {\n denominator = parseDenominator();\n }\n\n return [TokenType.STACK, [numerator, denominator, stackingType]];\n }\n\n /**\n * Parse MText properties\n * @param cmd - The property command to parse\n */\n private parseProperties(cmd: string): void {\n const newCtx = this.ctx.copy();\n\n switch (cmd) {\n case 'L':\n newCtx.underline = true;\n this.continueStroke = true;\n break;\n case 'l':\n newCtx.underline = false;\n if (!newCtx.hasAnyStroke) {\n this.continueStroke = false;\n }\n break;\n case 'O':\n newCtx.overline = true;\n this.continueStroke = true;\n break;\n case 'o':\n newCtx.overline = false;\n if (!newCtx.hasAnyStroke) {\n this.continueStroke = false;\n }\n break;\n case 'K':\n newCtx.strikeThrough = true;\n this.continueStroke = true;\n break;\n case 'k':\n newCtx.strikeThrough = false;\n if (!newCtx.hasAnyStroke) {\n this.continueStroke = false;\n }\n break;\n case 'A':\n this.parseAlign(newCtx);\n break;\n case 'C':\n this.parseAciColor(newCtx);\n break;\n case 'c':\n this.parseRgbColor(newCtx);\n break;\n case 'H':\n this.parseHeight(newCtx);\n break;\n case 'W':\n this.parseWidth(newCtx);\n break;\n case 'Q':\n this.parseOblique(newCtx);\n break;\n case 'T':\n this.parseCharTracking(newCtx);\n break;\n case 'p':\n this.parseParagraphProperties(newCtx);\n break;\n case 'f':\n case 'F':\n this.parseFontProperties(newCtx);\n break;\n default:\n throw new Error(`Unknown command: ${cmd}`);\n }\n\n newCtx.continueStroke = this.continueStroke;\n this.ctx = newCtx;\n }\n\n /**\n * Parse alignment property\n * @param ctx - The context to update\n */\n private parseAlign(ctx: MTextContext): void {\n const char = this.scanner.get();\n if ('012'.includes(char)) {\n ctx.align = parseInt(char) as MTextLineAlignment;\n } else {\n ctx.align = MTextLineAlignment.BOTTOM;\n }\n this.consumeOptionalTerminator();\n }\n\n /**\n * Parse height property\n * @param ctx - The context to update\n */\n private parseHeight(ctx: MTextContext): void {\n const expr = this.extractFloatExpression(true);\n if (expr) {\n try {\n if (expr.endsWith('x')) {\n // For height command, treat x suffix as absolute value\n ctx.capHeight = Math.abs(parseFloat(expr.slice(0, -1)));\n } else {\n ctx.capHeight = Math.abs(parseFloat(expr));\n }\n } catch {\n // If parsing fails, treat the entire command as literal text\n this.scanner.consume(-expr.length); // Rewind to before the expression\n return;\n }\n }\n this.consumeOptionalTerminator();\n }\n\n /**\n * Parse width property\n * @param ctx - The context to update\n */\n private parseWidth(ctx: MTextContext): void {\n const expr = this.extractFloatExpression(true);\n if (expr) {\n try {\n if (expr.endsWith('x')) {\n // For width command, treat x suffix as absolute value\n ctx.widthFactor = Math.abs(parseFloat(expr.slice(0, -1)));\n } else {\n ctx.widthFactor = Math.abs(parseFloat(expr));\n }\n } catch {\n // If parsing fails, treat the entire command as literal text\n this.scanner.consume(-expr.length); // Rewind to before the expression\n return;\n }\n }\n this.consumeOptionalTerminator();\n }\n\n /**\n * Parse character tracking property\n * @param ctx - The context to update\n */\n private parseCharTracking(ctx: MTextContext): void {\n const expr = this.extractFloatExpression(true);\n if (expr) {\n try {\n if (expr.endsWith('x')) {\n // For tracking command, treat x suffix as absolute value\n ctx.charTrackingFactor = Math.abs(parseFloat(expr.slice(0, -1)));\n } else {\n ctx.charTrackingFactor = Math.abs(parseFloat(expr));\n }\n } catch {\n // If parsing fails, treat the entire command as literal text\n this.scanner.consume(-expr.length); // Rewind to before the expression\n return;\n }\n }\n this.consumeOptionalTerminator();\n }\n\n /**\n * Parse float value or factor\n * @param value - Current value to apply factor to\n * @returns New value\n */\n private parseFloatValueOrFactor(value: number): number {\n const expr = this.extractFloatExpression(true);\n if (expr) {\n if (expr.endsWith('x')) {\n const factor = parseFloat(expr.slice(0, -1));\n value *= Math.abs(factor);\n } else {\n value = Math.abs(parseFloat(expr));\n }\n }\n return value;\n }\n\n /**\n * Parse oblique angle property\n * @param ctx - The context to update\n */\n private parseOblique(ctx: MTextContext): void {\n const obliqueExpr = this.extractFloatExpression(false);\n if (obliqueExpr) {\n ctx.oblique = parseFloat(obliqueExpr);\n }\n this.consumeOptionalTerminator();\n }\n\n /**\n * Parse ACI color property\n * @param ctx - The context to update\n */\n private parseAciColor(ctx: MTextContext): void {\n const aciExpr = this.extractIntExpression();\n if (aciExpr) {\n const aci = parseInt(aciExpr);\n if (aci < 257) {\n ctx.aci = aci;\n ctx.rgb = null;\n }\n }\n this.consumeOptionalTerminator();\n }\n\n /**\n * Parse RGB color property\n * @param ctx - The context to update\n */\n private parseRgbColor(ctx: MTextContext): void {\n const rgbExpr = this.extractIntExpression();\n if (rgbExpr) {\n const value = parseInt(rgbExpr) & 0xffffff;\n const [b, g, r] = int2rgb(value);\n ctx.rgb = [r, g, b];\n }\n this.consumeOptionalTerminator();\n }\n\n /**\n * Extract float expression from scanner\n * @param relative - Whether to allow relative values (ending in 'x')\n * @returns Extracted expression\n */\n private extractFloatExpression(relative: boolean = false): string {\n const pattern = relative\n ? /^[+-]?(?:\\d+(?:\\.\\d*)?|\\.\\d+)(?:[eE][+-]?\\d+)?x?/\n : /^[+-]?(?:\\d+(?:\\.\\d*)?|\\.\\d+)(?:[eE][+-]?\\d+)?/;\n const match = this.scanner.tail.match(pattern);\n if (match) {\n const result = match[0];\n this.scanner.consume(result.length);\n return result;\n }\n return '';\n }\n\n /**\n * Extract integer expression from scanner\n * @returns Extracted expression\n */\n private extractIntExpression(): string {\n const match = this.scanner.tail.match(/^\\d+/);\n if (match) {\n const result = match[0];\n this.scanner.consume(result.length);\n return result;\n }\n return '';\n }\n\n /**\n * Extract expression until semicolon or end\n * @param escape - Whether to handle escaped semicolons\n * @returns Extracted expression\n */\n private extractExpression(escape: boolean = false): string {\n const stop = this.scanner.find(';', escape);\n if (stop < 0) {\n const expr = this.scanner.tail;\n this.scanner.consume(expr.length);\n return expr;\n }\n // Check if the semicolon is escaped by looking at the previous character\n const prevChar = this.scanner.peek(stop - this.scanner.currentIndex - 1);\n const isEscaped = prevChar === '\\\\';\n const expr = this.scanner.tail.slice(0, stop - this.scanner.currentIndex + (isEscaped ? 1 : 0));\n this.scanner.consume(expr.length + 1);\n return expr;\n }\n\n /**\n * Parse font properties\n * @param ctx - The context to update\n */\n private parseFontProperties(ctx: MTextContext): void {\n const parts = this.extractExpression().split('|');\n if (parts.length > 0 && parts[0]) {\n const name = parts[0];\n let style = 'Regular';\n let weight = 400;\n\n for (const part of parts.slice(1)) {\n if (part.startsWith('b1')) {\n weight = 700;\n } else if (part.startsWith('i1')) {\n style = 'Italic';\n }\n }\n\n ctx.fontFace = {\n family: name,\n style,\n weight,\n };\n }\n }\n\n /**\n * Parse paragraph properties from the MText content\n * Handles properties like indentation, alignment, and tab stops\n * @param ctx - The context to update\n */\n private parseParagraphProperties(ctx: MTextContext): void {\n const scanner = new TextScanner(this.extractExpression());\n /** Current indentation value */\n let indent = ctx.paragraph.indent;\n /** Left margin value */\n let left = ctx.paragraph.left;\n /** Right margin value */\n let right = ctx.paragraph.right;\n /** Current paragraph alignment */\n let align = ctx.paragraph.align;\n /** Array of tab stop positions and types */\n let tabStops: (number | string)[] = [];\n\n /**\n * Parse a floating point number from the scanner's current position\n * Handles optional sign, decimal point, and scientific notation\n * @returns The parsed float value, or 0 if no valid number is found\n */\n const parseFloatValue = (): number => {\n const match = scanner.tail.match(/^[+-]?\\d+(?:\\.\\d*)?(?:[eE][+-]?\\d+)?/);\n if (match) {\n const value = parseFloat(match[0]);\n scanner.consume(match[0].length);\n while (scanner.peek() === ',') {\n scanner.consume(1);\n }\n return value;\n }\n return 0;\n };\n\n while (scanner.hasData) {\n const cmd = scanner.get();\n switch (cmd) {\n case 'i': // Indentation\n indent = parseFloatValue();\n break;\n case 'l': // Left margin\n left = parseFloatValue();\n break;\n case 'r': // Right margin\n right = parseFloatValue();\n break;\n case 'x': // Skip\n break;\n case 'q': {\n // Alignment\n const adjustment = scanner.get();\n align = CHAR_TO_ALIGN[adjustment] || MTextParagraphAlignment.DEFAULT;\n while (scanner.peek() === ',') {\n scanner.consume(1);\n }\n break;\n }\n case 't': // Tab stops\n tabStops = [];\n while (scanner.hasData) {\n const type = scanner.peek();\n if (type === 'r' || type === 'c') {\n scanner.consume(1);\n const value = parseFloatValue();\n tabStops.push(type + value.toString());\n } else {\n const value = parseFloatValue();\n if (!isNaN(value)) {\n tabStops.push(value);\n } else {\n scanner.consume(1);\n }\n }\n }\n break;\n }\n }\n\n ctx.paragraph = {\n indent,\n left,\n right,\n align,\n tab_stops: tabStops,\n };\n }\n\n /**\n * Consume optional terminator (semicolon)\n */\n private consumeOptionalTerminator(): void {\n if (this.scanner.peek() === ';') {\n this.scanner.consume(1);\n }\n }\n\n /**\n * Parse MText content into tokens\n * @yields MTextToken objects\n */\n *parse(): Generator<MTextToken> {\n const wordToken = TokenType.WORD;\n const spaceToken = TokenType.SPACE;\n let followupToken: TokenType | null = null;\n\n const nextToken = (): [TokenType, TokenData[TokenType]] => {\n let word = '';\n while (this.scanner.hasData) {\n let escape = false;\n let letter = this.scanner.peek();\n const cmdStartIndex = this.scanner.currentIndex;\n\n // Handle control characters first\n if (letter.charCodeAt(0) < 32) {\n this.scanner.consume(1); // Always consume the control character\n if (letter === '\\t') {\n return [TokenType.TABULATOR, null];\n }\n if (letter === '\\n') {\n return [TokenType.NEW_PARAGRAPH, null];\n }\n letter = ' ';\n }\n\n if (letter === '\\\\') {\n if ('\\\\{}'.includes(this.scanner.peek(1))) {\n escape = true;\n this.scanner.consume(1);\n letter = this.scanner.peek();\n } else {\n if (word) {\n return [wordToken, word];\n }\n this.scanner.consume(1);\n const cmd = this.scanner.get();\n switch (cmd) {\n case '~':\n return [TokenType.NBSP, null];\n case 'P':\n return [TokenType.NEW_PARAGRAPH, null];\n case 'N':\n return [TokenType.NEW_COLUMN, null];\n case 'X':\n return [TokenType.WRAP_AT_DIMLINE, null];\n case 'S':\n return this.parseStacking();\n case 'm':\n case 'M':\n // Handle multi-byte character encoding\n if (this.scanner.peek() === '+') {\n this.scanner.consume(1); // Consume the '+'\n const hexCode = this.scanner.tail.match(/^[0-9A-Fa-f]{4}/)?.[0];\n if (hexCode) {\n this.scanner.consume(4);\n const decodedChar = this.decodeMultiByteChar(hexCode);\n if (word) {\n return [wordToken, word];\n }\n return [wordToken, decodedChar];\n }\n // If no valid hex code found, rewind the '+' character\n this.scanner.consume(-1);\n }\n // If not a valid multi-byte code, treat as literal text\n word += '\\\\M';\n continue;\n default:\n if (cmd) {\n try {\n this.parseProperties(cmd);\n if (this.yieldPropertyCommands) {\n return [\n TokenType.PROPERTIES_CHANGED,\n this.scanner.tail.slice(cmdStartIndex, this.scanner.currentIndex),\n ];\n }\n // After processing a property command, continue with normal parsing\n continue;\n } catch {\n const commandText = this.scanner.tail.slice(\n cmdStartIndex,\n this.scanner.currentIndex\n );\n word += commandText;\n }\n }\n }\n continue;\n }\n }\n\n if (letter === '%' && this.scanner.peek(1) === '%') {\n const code = this.scanner.peek(2).toLowerCase();\n const specialChar = SPECIAL_CHAR_ENCODING[code];\n if (specialChar) {\n this.scanner.consume(3);\n word += specialChar;\n continue;\n } else {\n // Skip invalid special character codes\n this.scanner.consume(3);\n continue;\n }\n }\n\n if (letter === ' ') {\n if (word) {\n this.scanner.consume(1);\n followupToken = spaceToken;\n return [wordToken, word];\n }\n this.scanner.consume(1);\n return [spaceToken, null];\n }\n\n if (!escape) {\n if (letter === '{') {\n if (word) {\n return [wordToken, word];\n }\n this.scanner.consume(1);\n this.pushCtx();\n continue;\n } else if (letter === '}') {\n if (word) {\n return [wordToken, word];\n }\n this.scanner.consume(1);\n this.popCtx();\n continue;\n }\n }\n\n this.scanner.consume(1);\n if (letter.charCodeAt(0) >= 32) {\n word += letter;\n }\n }\n\n if (word) {\n return [wordToken, word];\n }\n return [TokenType.NONE, null];\n };\n\n while (true) {\n const [type, data] = nextToken();\n if (type) {\n yield new MTextToken(type, this.ctx, data);\n if (followupToken) {\n yield new MTextToken(followupToken, this.ctx, null);\n followupToken = null;\n }\n } else {\n break;\n }\n }\n }\n}\n\n/**\n * Text scanner for parsing MText content\n */\nexport class TextScanner {\n private text: string;\n private textLen: number;\n private _index: number;\n\n /**\n * Create a new text scanner\n * @param text - The text to scan\n */\n constructor(text: string) {\n this.text = text;\n this.textLen = text.length;\n this._index = 0;\n }\n\n /**\n * Get the current index in the text\n */\n get currentIndex(): number {\n return this._index;\n }\n\n /**\n * Check if the scanner has reached the end of the text\n */\n get isEmpty(): boolean {\n return this._index >= this.textLen;\n }\n\n /**\n * Check if there is more text to scan\n */\n get hasData(): boolean {\n return this._index < this.textLen;\n }\n\n /**\n * Get the next character and advance the index\n * @returns The next character, or empty string if at end\n */\n get(): string {\n if (this.isEmpty) {\n return '';\n }\n const char = this.text[this._index];\n this._index++;\n return char;\n }\n\n /**\n * Advance the index by the specified count\n * @param count - Number of characters to advance\n */\n consume(count: number = 1): void {\n this._index = Math.max(0, Math.min(this._index + count, this.textLen));\n }\n\n /**\n * Look at a character without advancing the index\n * @param offset - Offset from current position\n * @returns The character at the offset position, or empty string if out of bounds\n */\n peek(offset: number = 0): string {\n const index = this._index + offset;\n if (index >= this.textLen || index < 0) {\n return '';\n }\n return this.text[index];\n }\n\n /**\n * Find the next occurrence of a character\n * @param char - The character to find\n * @param escape - Whether to handle escaped characters\n * @returns Index of the character, or -1 if not found\n */\n find(char: string, escape: boolean = false): number {\n let index = this._index;\n while (index < this.textLen) {\n if (escape && this.text[index] === '\\\\') {\n if (index + 1 < this.textLen) {\n if (this.text[index + 1] === char) {\n return index + 1;\n }\n index += 2;\n continue;\n }\n index++;\n continue;\n }\n if (this.text[index] === char) {\n return index;\n }\n index++;\n }\n return -1;\n }\n\n /**\n * Get the remaining text from the current position\n */\n get tail(): string {\n return this.text.slice(this._index);\n }\n\n /**\n * Check if the next character is a space\n */\n isNextSpace(): boolean {\n return this.peek() === ' ';\n }\n\n /**\n * Consume spaces until a non-space character is found\n * @returns Number of spaces consumed\n */\n consumeSpaces(): number {\n let count = 0;\n while (this.isNextSpace()) {\n this.consume();\n count++;\n }\n return count;\n }\n}\n\n/**\n * MText context class for managing text formatting state\n */\nexport class MTextContext {\n private _stroke: number = 0;\n /** Whether to continue stroke formatting */\n continueStroke: boolean = false;\n private _aci: number = 7;\n /** RGB color value, or null if using ACI */\n rgb: RGB | null = null;\n /** Line alignment */\n align: MTextLineAlignment = MTextLineAlignment.BOTTOM;\n /** Font face properties */\n fontFace: FontFace = { family: '', style: 'Regular', weight: 400 };\n /** Capital letter height */\n capHeight: number = 1.0;\n /** Character width factor */\n widthFactor: number = 1.0;\n /** Character tracking factor */\n charTrackingFactor: number = 1.0;\n /** Oblique angle */\n oblique: number = 0.0;\n /** Paragraph properties */\n paragraph: ParagraphProperties = {\n indent: 0,\n left: 0,\n right: 0,\n align: MTextParagraphAlignment.DEFAULT,\n tab_stops: [],\n };\n\n /**\n * Get the ACI color value\n */\n get aci(): number {\n return this._aci;\n }\n\n /**\n * Set the ACI color value\n * @param value - ACI color value (0-256)\n * @throws Error if value is out of range\n */\n set aci(value: number) {\n if (value >= 0 && value <= 256) {\n this._aci = value;\n this.rgb = null;\n } else {\n throw new Error('ACI not in range [0, 256]');\n }\n }\n\n /**\n * Get whether text is underlined\n */\n get underline(): boolean {\n return Boolean(this._stroke & MTextStroke.UNDERLINE);\n }\n\n /**\n * Set whether text is underlined\n * @param value - Whether to underline\n */\n set underline(value: boolean) {\n this._setStrokeState(MTextStroke.UNDERLINE, value);\n }\n\n /**\n * Get whether text has strike-through\n */\n get strikeThrough(): boolean {\n return Boolean(this._stroke & MTextStroke.STRIKE_THROUGH);\n }\n\n /**\n * Set whether text has strike-through\n * @param value - Whether to strike through\n */\n set strikeThrough(value: boolean) {\n this._setStrokeState(MTextStroke.STRIKE_THROUGH, value);\n }\n\n /**\n * Get whether text has overline\n */\n get overline(): boolean {\n return Boolean(this._stroke & MTextStroke.OVERLINE);\n }\n\n /**\n * Set whether text has overline\n * @param value - Whether to overline\n */\n set overline(value: boolean) {\n this._setStrokeState(MTextStroke.OVERLINE, value);\n }\n\n /**\n * Check if any stroke formatting is active\n */\n get hasAnyStroke(): boolean {\n return Boolean(this._stroke);\n }\n\n /**\n * Set the state of a stroke type\n * @param stroke - The stroke type to set\n * @param state - Whether to enable or disable the stroke\n */\n private _setStrokeState(stroke: MTextStroke, state: boolean = true): void {\n if (state) {\n this._stroke |= stroke;\n } else {\n this._stroke &= ~stroke;\n }\n }\n\n /**\n * Create a copy of this context\n * @returns A new context with the same properties\n */\n copy(): MTextContext {\n const ctx = new MTextContext();\n ctx._stroke = this._stroke;\n ctx.continueStroke = this.continueStroke;\n ctx._aci = this._aci;\n ctx.rgb = this.rgb;\n ctx.align = this.align;\n ctx.fontFace = { ...this.fontFace };\n ctx.capHeight = this.capHeight;\n ctx.widthFactor = this.widthFactor;\n ctx.charTrackingFactor = this.charTrackingFactor;\n ctx.oblique = this.oblique;\n ctx.paragraph = { ...this.paragraph };\n return ctx;\n }\n}\n\n/**\n * Token class for MText parsing\n */\nexport class MTextToken {\n /**\n * Create a new MText token\n * @param type - The token type\n * @param ctx - The text context at this token\n * @param data - Optional token data\n */\n constructor(\n public type: TokenType,\n public ctx: MTextContext,\n public data: TokenData[TokenType]\n ) {}\n}\n"],"names":["TokenType","MTextLineAlignment","MTextParagraphAlignment","MTextStroke","SPECIAL_CHAR_ENCODING","CHAR_TO_ALIGN","rgb2int","rgb","r","g","b","int2rgb","value","caretDecode","text","_","c","code","escapeDxfLineEndings","hasInlineFormattingCodes","MTextParser","content","ctx","yieldPropertyCommands","TextScanner","MTextContext","hex","bytes","scanner","numerator","denominator","stackingType","getNextChar","escape","parseNumerator","word","parseDenominator","cmd","newCtx","char","expr","factor","obliqueExpr","aciExpr","aci","rgbExpr","relative","pattern","match","result","stop","isEscaped","parts","name","style","weight","part","indent","left","right","align","tabStops","parseFloatValue","adjustment","type","followupToken","nextToken","letter","cmdStartIndex","hexCode","_a","decodedChar","commandText","specialChar","data","MTextToken","count","offset","index","stroke","state"],"mappings":"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"}
@@ -190,6 +190,7 @@ export declare class MTextParser {
190
190
  private ctxStack;
191
191
  private continueStroke;
192
192
  private yieldPropertyCommands;
193
+ private decoder;
193
194
  /**
194
195
  * Creates a new MTextParser instance
195
196
  * @param content - The MText content to parse
@@ -197,6 +198,12 @@ export declare class MTextParser {
197
198
  * @param yieldPropertyCommands - Whether to yield property change commands
198
199
  */
199
200
  constructor(content: string, ctx?: MTextContext, yieldPropertyCommands?: boolean);
201
+ /**
202
+ * Decode multi-byte character from hex code
203
+ * @param hex - Hex code string (e.g. "C4E3")
204
+ * @returns Decoded character or empty square if invalid
205
+ */
206
+ private decodeMultiByteChar;
200
207
  /**
201
208
  * Push current context onto the stack
202
209
  */
package/package.json CHANGED
@@ -1,9 +1,12 @@
1
1
  {
2
2
  "name": "@mlightcad/mtext-parser",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "AutoCAD MText parser written in TypeScript",
5
- "main": "dist/parser.js",
6
- "types": "dist/parser.d.ts",
5
+ "type": "module",
6
+ "main": "dist/parser.cjs.js",
7
+ "module": "dist/parser.es.js",
8
+ "browser": "dist/parser.umd.js",
9
+ "types": "dist/types/parser.d.ts",
7
10
  "author": "MLight Lee <mlight.lee@outlook.com>",
8
11
  "repository": {
9
12
  "type": "git",
@@ -26,19 +29,23 @@
26
29
  "@types/node": "^20.11.24",
27
30
  "@typescript-eslint/eslint-plugin": "^7.1.0",
28
31
  "@typescript-eslint/parser": "^7.1.0",
29
- "eslint": "^8.57.0",
30
- "eslint-config-prettier": "^9.1.0",
31
- "eslint-plugin-prettier": "^5.1.3",
32
+ "eslint": "^9.26.0",
33
+ "eslint-config-prettier": "^10.1.5",
34
+ "eslint-plugin-prettier": "^5.4.0",
32
35
  "jest": "^29.7.0",
33
36
  "prettier": "^3.2.5",
34
37
  "ts-jest": "^29.3.2",
35
38
  "ts-node": "^10.9.2",
36
- "typescript": "^5.3.3"
39
+ "typescript": "^5.3.3",
40
+ "vite": "^5.1.4",
41
+ "vite-plugin-dts": "^3.7.3"
37
42
  },
38
43
  "scripts": {
39
- "build": "tsc",
44
+ "build": "vite build",
45
+ "build:example": "vite build --config vite.config.ts --mode example",
46
+ "build:types": "tsc --emitDeclarationOnly",
40
47
  "test": "jest",
41
- "example": "npm run build && node dist/example.js",
48
+ "example": "npm run build && npm run build:example && node dist/node/example.cjs.js",
42
49
  "format": "prettier --write \"src/**/*.{ts,tsx,js,jsx,json}\"",
43
50
  "format:check": "prettier --check \"src/**/*.{ts,tsx,js,jsx,json}\"",
44
51
  "lint": "eslint ./src --ext .ts",
package/dist/example.js DELETED
@@ -1,125 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const parser_1 = require("./parser");
4
- // Example 1: Basic text with formatting
5
- const basicExample = `
6
- This is a test of the MText parser\\P
7
- \\C1;This text is red\\P
8
- \\H2x;This text is twice the height\\P
9
- \\S1/2;This is a fraction\\P
10
- `;
11
- // Example 2: Paragraph formatting
12
- const paragraphExample = `
13
- \\pi2,l4;This paragraph has indentation\\P
14
- \\pqc;This paragraph is centered\\P
15
- \\pqr;This paragraph is right-aligned\\P
16
- `;
17
- // Example 3: Text styling
18
- const stylingExample = `
19
- \\LThis text is underlined\\l\\P
20
- \\OThis text has an overline\\o\\P
21
- \\KThis text is struck through\\k\\P
22
- \\Q15;This text is oblique\\P
23
- \\T2;This text has increased tracking\\P
24
- `;
25
- // Example 4: Colors and special characters
26
- const colorExample = `
27
- \\c16711680;This text is blue (RGB)\\P
28
- %%c This is a diameter symbol\\P
29
- %%d This is a degree symbol\\P
30
- %%p This is a plus-minus symbol\\P
31
- `;
32
- // Example 5: Caret encoded characters
33
- const caretExample = `
34
- ^I This is a tab\\P
35
- ^J This is a line break\\P
36
- 1^ 2 This shows a caret\\P
37
- `;
38
- // Example 6: Complex formatting with context
39
- const complexExample = `
40
- {\\fArial|b1|i0;
41
- This text is in Arial Bold\\P
42
- \\H2.5;This text is larger\\P
43
- \\H.5x;This text is smaller\\P
44
- }
45
- `;
46
- // Function to process tokens and display their properties
47
- function processTokens(parser, title) {
48
- console.log(`\n=== ${title} ===\n`);
49
- for (const token of parser.parse()) {
50
- // Print token type and data
51
- switch (token.type) {
52
- case parser_1.TokenType.WORD:
53
- console.log('Word:', token.data);
54
- break;
55
- case parser_1.TokenType.SPACE:
56
- console.log('Space');
57
- break;
58
- case parser_1.TokenType.NEW_PARAGRAPH:
59
- console.log('New Paragraph');
60
- break;
61
- case parser_1.TokenType.STACK: {
62
- const [numerator, denominator, type] = token.data;
63
- console.log('Stack:', { numerator, denominator, type });
64
- break;
65
- }
66
- case parser_1.TokenType.PROPERTIES_CHANGED:
67
- console.log('Properties Changed:', token.data);
68
- break;
69
- case parser_1.TokenType.NBSP:
70
- console.log('Non-breaking space');
71
- break;
72
- case parser_1.TokenType.TABULATOR:
73
- console.log('Tab');
74
- break;
75
- case parser_1.TokenType.NEW_COLUMN:
76
- console.log('New Column');
77
- break;
78
- case parser_1.TokenType.WRAP_AT_DIMLINE:
79
- console.log('Wrap at dimension line');
80
- break;
81
- }
82
- // Print context properties if they differ from defaults
83
- const ctx = token.ctx;
84
- const contextProps = {
85
- font: ctx.fontFace.family ? ctx.fontFace : undefined,
86
- color: ctx.rgb ? `RGB(${ctx.rgb.join(',')})` : ctx.aci !== 7 ? `ACI(${ctx.aci})` : undefined,
87
- height: ctx.capHeight !== 1.0 ? ctx.capHeight : undefined,
88
- width: ctx.widthFactor !== 1.0 ? ctx.widthFactor : undefined,
89
- tracking: ctx.charTrackingFactor !== 1.0 ? ctx.charTrackingFactor : undefined,
90
- oblique: ctx.oblique !== 0.0 ? ctx.oblique : undefined,
91
- underline: ctx.underline ? true : undefined,
92
- overline: ctx.overline ? true : undefined,
93
- strikeThrough: ctx.strikeThrough ? true : undefined,
94
- paragraph: Object.keys(ctx.paragraph).some(key => ctx.paragraph[key] !==
95
- new parser_1.MTextContext().paragraph[key])
96
- ? ctx.paragraph
97
- : undefined,
98
- };
99
- // Only print non-default context properties
100
- const nonDefaultProps = Object.entries(contextProps)
101
- .filter(([_, value]) => value !== undefined)
102
- .reduce((obj, [key, value]) => ({ ...obj, [key]: value }), {});
103
- if (Object.keys(nonDefaultProps).length > 0) {
104
- console.log('Context:', nonDefaultProps);
105
- }
106
- console.log('---');
107
- }
108
- }
109
- // Process all examples
110
- processTokens(new parser_1.MTextParser(basicExample), 'Basic Formatting');
111
- processTokens(new parser_1.MTextParser(paragraphExample), 'Paragraph Formatting');
112
- processTokens(new parser_1.MTextParser(stylingExample), 'Text Styling');
113
- processTokens(new parser_1.MTextParser(colorExample), 'Colors and Special Characters');
114
- processTokens(new parser_1.MTextParser(caretExample), 'Caret Encoded Characters');
115
- processTokens(new parser_1.MTextParser(complexExample), 'Complex Formatting with Context');
116
- // Example of using custom context
117
- const customContext = new parser_1.MTextContext();
118
- customContext.capHeight = 2.0;
119
- customContext.widthFactor = 1.5;
120
- customContext.fontFace = { family: 'Times New Roman', style: 'Italic', weight: 400 };
121
- const customParser = new parser_1.MTextParser('Text with custom context', customContext);
122
- processTokens(customParser, 'Custom Context');
123
- // Example of parsing with property commands
124
- const propertyParser = new parser_1.MTextParser('\\C1;Red Text', undefined, true);
125
- processTokens(propertyParser, 'Property Commands');