@mlightcad/mtext-parser 1.3.2 → 1.3.3

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.
package/README.md CHANGED
@@ -1,208 +1,208 @@
1
- # AutoCAD MText Parser
2
-
3
- TypeScript version of AutoCAD MText parser. It is based on [ezdxf dxf mtext parser](https://github.com/mozman/ezdxf/blob/aaa2d1b302c78a47fe1159bd6007254d1c2ebd22/src/ezdxf/tools/text.py) and ported by [Cursor](https://www.cursor.com/). Moveover, unit tests are added based on use case in the specification in next section.
4
-
5
- ## AutoCAD MText Specification
6
-
7
- The text formatting is done by inline codes. You can get more information from [this page](https://ezdxf.mozman.at/docs/dxfinternals/entities/mtext.html).
8
-
9
- ### caret encoded characters:
10
- - “^I” tabulator
11
- - “^J” (LF) is a valid line break like “\P”
12
- - “^M” (CR) is ignored
13
- - other characters render as empty square “▯”
14
- - a space “ “ after the caret renders the caret glyph: “1^ 2” renders “1^2”
15
-
16
- ### special encoded characters:
17
- - “%%c” and “%%C” renders “Ø” (alt-0216)
18
- - “%%d” and “%%D” renders “°” (alt-0176)
19
- - “%%p” and “%%P” renders “±” (alt-0177)
20
-
21
- ### Multi-byte character encoding commands "\M" and "\m":
22
- - Format: "\M+XXXX" or "\m+XXXX" where XXXX is a 4-character hex code
23
- - Example: "\M+C4E3" renders the GBK encoded character
24
- - If no valid hex code is found after "+", the command is treated as literal text
25
- - Currently supports GBK encoding, with plans to add BIG5 support
26
-
27
- ### Alignment command “\A”: argument “0”, “1” or “2” is expected
28
- - the terminator symbol “;” is optional
29
- - the arguments “3”, “4”, “5”, “6”, “7”, “8”, “9” and “-” default to 0
30
- - other characters terminate the command and will be printed: “\AX”, renders “X”
31
-
32
- ### ACI color command “\C”: int argument is expected
33
- - the terminator symbol “;” is optional
34
- - a leading “-” or “+” terminates the command, “\C+5” renders “\C+5”
35
- - arguments > 255, are ignored but consumed “\C1000” renders nothing, not even a “0”
36
- - a trailing “;” after integers is always consumed, even for much to big values, “\C10000;” renders nothing
37
-
38
- ### RGB color command “\c”: int argument is expected
39
- - the terminator symbol “;” is optional
40
- - a leading “-” or “+” terminates the command, “\c+255” renders “\c+255”
41
- - arguments >= 16777216 are masked by: value & 0xFFFFFF
42
- - a trailing “;” after integers is always consumed, even for much to big values, “\c9999999999;” renders nothing and switches the color to yellow (255, 227, 11)
43
-
44
- ### Height command “\H” and “\H…x”: float argument is expected
45
- - the terminator symbol “;” is optional
46
- - a leading “-” is valid, but negative values are ignored
47
- - a leading “+” is valid
48
- - a leading “.” is valid like “\H.5x” for height factor 0.5
49
- - exponential format is valid like “\H1e2” for height factor 100 and “\H1e-2” for 0.01
50
- - an invalid floating point value terminates the command, “\H1..5” renders “\H1..5”
51
-
52
- ### Other commands with floating point arguments like the height command:
53
- - Width commands “\W” and “\W…x”
54
- - Character tracking commands “\T” and “\T…x”, negative values are used
55
- - Slanting (oblique) command “\Q”
56
-
57
- ### Stacking command “\S”:
58
- - build fractions: “numerator (upr)” + “stacking type char (t)” + “denominator (lwr)” + “;”
59
- - divider chars: “^”, “/” or “#”
60
- - a space “ “ after the divider char “^” is mandatory to avoid caret decoding: “\S1^ 2;”
61
- - the terminator symbol “;” is mandatory to end the command, all chars beyond the “\S” until the next “;” or the end of the string are part of the fraction
62
- - backslash escape “\;” to render the terminator char
63
- - a space “ “ after the divider chars “/” and “#” is rendered as space “ ” in front of the denominator
64
- - the numerator and denominator can contain spaces
65
- - backslashes “\” inside the stacking command are ignored (except “\;”) “\S\N^ \P” render “N” over “P”, therefore property changes (color, text height, …) are not possible inside the stacking command
66
- - grouping chars “{” and “}” render as simple curly braces
67
- - in the context of the MText \S...; stack command, the ^ is always treated as a stack separator, regardless of what characters come before or after it — even if those characters look like caret-encoded control characters (^I, ^J, ^!, ^?, etc.) in other contexts.
68
- - a divider char after the first divider char, renders as the char itself: “\S1/2/3” renders the horizontal fraction “1” / “2/3”
69
-
70
- ### Font command “\f” and “\F”: export only “\f”, parse both, “\F” ignores some arguments
71
- - the terminator symbol “;” is mandatory to end the command, all chars beyond the “\f” until the next “;” or the end of the string are part of the command
72
- - the command arguments are separated by the pipe char “|”
73
- - arguments: “font family name” | “bold” | “italic” | “codepage” | “pitch”; example “\fArial|b0|i0|c0|p0;”
74
- - only the “font family name” argument is required, fonts which are not available on the system are replaced by the “TXT.SHX” shape font
75
- - the “font family name” is the font name shown in font selection widgets in desktop applications
76
- - “b1” to use the bold font style, any other second char is interpreted as “non bold”
77
- - “i1” to use an italic font style, any other second char is interpreted as “non italic”
78
- - “c???” change codepage, “c0” use the default codepage, because of the age of unicode no further investigations, also seems to be ignored by AutoCAD and BricsCAD
79
- - “p???” change pitch size, “p0” means don’t change, ignored by AutoCAD and BricsCAD, to change the text height use the “\H” command
80
- - the order is not important, but export always in the shown order: “\fArial|b0|i0;” the arguments “c0” and “p0” are not required
81
-
82
- ### Paragraph properties command “\p”
83
- - the terminator symbol “;” is mandatory to end the command, all chars beyond the “\p” until the next “;” or the end of the string are part of the command
84
- - the command arguments are separated by commas “,”
85
- - all values are factors for the initial char height of the MTEXT entity, example: char height = 2.5, “\pl1;” set the left paragraph indentation to 1 x 2.5 = 2.5 drawing units.
86
- - all values are floating point values, see height command
87
- - arguments are “i”, “l”, “r”, “q”, “t”
88
- - a “*” as argument value, resets the argument to the initial value: “i0”, “l0”, “r0”, the “q” argument most likely depends on the text direction; I haven’t seen “t*”. The sequence used by BricsCAD to reset all values is "\pi*,l*,r*,q*,t;"
89
- - “i” indentation of the first line relative to the “l” argument as floating point value, “\pi1.5”
90
- - “l” left paragraph indentation as floating point value, “\pl1.5”
91
- - “r” right paragraph indentation as floating point value, “\pr1.5”
92
- - “x” is required if a “q” or a “t” argument is present, the placement of the “x” has no obvious rules
93
- - “q” paragraph alignment
94
- - “ql” left paragraph alignment
95
- - “qr” right paragraph alignment
96
- - “qc” center paragraph alignment
97
- - “qj” justified paragraph alignment
98
- - “qd” distributed paragraph alignment
99
- - “t” tabulator stops as comma separated list, the default tabulator stops are located at 4, 8, 12, …, by defining at least one tabulator stop, the default tabulator stops will be ignored. There 3 kind of tabulator stops: left, right and center adjusted stops, e.g. “pxt1,r5,c8”:
100
- - a left adjusted stop has no leading char, two left adjusted stops “\pxt1,2;”
101
- - a right adjusted stop has a preceding “r” char, “\pxtr1,r2;”
102
- - a center adjusted stop has a preceding “c” char, “\pxtc1,c2;”
103
-
104
- - complex example to create a numbered list with two items: "pxi-3,l4t4;1.^Ifirst item\P2.^Isecond item"
105
- - a parser should be very flexible, I have seen several different orders of the arguments and placing the sometimes required “x” has no obvious rules.
106
- - exporting seems to be safe to follow these three rules:
107
- - the command starts with "\\px", the "x" does no harm, if not required
108
- - argument order "i", "l", "r", "q", "t", any of the arguments can be left off
109
- - terminate the command with a ";"
110
-
111
- ## Usage
112
-
113
- ```bash
114
- npm install @mlightcad/mtext-parser
115
- ```
116
-
117
- Here's how to use the MText parser in your TypeScript/JavaScript project:
118
-
119
- ```typescript
120
- import { MTextParser, TokenType } from '@mlightcad/mtext-parser';
121
-
122
- // Basic usage
123
- const parser = new MTextParser('Hello World');
124
- const tokens = Array.from(parser.parse());
125
- // tokens will contain:
126
- // - WORD token with "Hello"
127
- // - SPACE token
128
- // - WORD token with "World"
129
-
130
- // Parsing formatted text
131
- const formattedParser = new MTextParser('\\H2.5;Large\\H.5x;Small');
132
- const formattedTokens = Array.from(formattedParser.parse());
133
- // formattedTokens will contain:
134
- // - WORD token with "Large" and capHeight = 2.5
135
- // - WORD token with "Small" and capHeight = 0.5
136
-
137
- // Parsing special characters
138
- const specialParser = new MTextParser('Diameter: %%c, Angle: %%d, Tolerance: %%p');
139
- const specialTokens = Array.from(specialParser.parse());
140
- // specialTokens will contain:
141
- // - WORD token with "Diameter: Ø, Angle: °, Tolerance: ±"
142
-
143
- // Parsing with context
144
- const ctx = new MTextContext();
145
- ctx.capHeight = 2.0;
146
- ctx.widthFactor = 1.5;
147
- const contextParser = new MTextParser('Text with context', ctx);
148
- const contextTokens = Array.from(contextParser.parse());
149
- // contextTokens will contain tokens with the specified context
150
-
151
- // Parsing with property commands
152
- const propertyParser = new MTextParser('\\C1;Red Text', undefined, { yieldPropertyCommands: true });
153
- const propertyTokens = Array.from(propertyParser.parse());
154
- // propertyTokens will contain:
155
- // - PROPERTIES_CHANGED token with the color command
156
- // - WORD token with "Red Text" and aci = 1
157
-
158
- // Parsing with paragraph reset after new paragraph
159
- const resetParser = new MTextParser('Line1\\PLine2', undefined, { yieldPropertyCommands: true, resetParagraphParameters: true });
160
- const resetTokens = Array.from(resetParser.parse());
161
- // resetTokens will contain:
162
- // - WORD token with "Line1"
163
- // - NEW_PARAGRAPH token
164
- // - PROPERTIES_CHANGED token with paragraph reset
165
- // - WORD token with "Line2"
166
- ```
167
-
168
- ### Token Types
169
-
170
- The parser produces tokens of the following types:
171
-
172
- - `WORD`: Text content with associated formatting context
173
- - `SPACE`: Space character
174
- - `NBSP`: Non-breaking space
175
- - `TABULATOR`: Tab character
176
- - `NEW_PARAGRAPH`: Paragraph break
177
- - `NEW_COLUMN`: Column break
178
- - `WRAP_AT_DIMLINE`: Wrap at dimension line
179
- - `STACK`: Stacked fraction with [numerator, denominator, type] data
180
- - `PROPERTIES_CHANGED`: Property change command (only when yieldPropertyCommands is true)
181
-
182
- ### Context Properties
183
-
184
- Each token includes a context object (`MTextContext`) that contains the current formatting state:
185
-
186
- - `underline`: Whether text is underlined
187
- - `overline`: Whether text has overline
188
- - `strikeThrough`: Whether text has strike-through
189
- - `aci`: ACI color value (0-256)
190
- - `rgb`: RGB color value [r, g, b]
191
- - `align`: Line alignment (BOTTOM, MIDDLE, TOP)
192
- - `fontFace`: Font properties (family, style, weight)
193
- - `capHeight`: Capital letter height
194
- - `widthFactor`: Character width factor
195
- - `charTrackingFactor`: Character tracking factor
196
- - `oblique`: Oblique angle
197
- - `paragraph`: Paragraph properties (indent, margins, alignment, tab stops)
198
-
199
- ### Error Handling
200
-
201
- The parser handles invalid commands gracefully:
202
-
203
- - Invalid floating point values are treated as literal text
204
- - Invalid special character codes are ignored
205
- - Invalid property commands are treated as literal text
206
- - Invalid stacking commands are treated as literal text
207
-
1
+ # AutoCAD MText Parser
2
+
3
+ TypeScript version of AutoCAD MText parser. It is based on [ezdxf dxf mtext parser](https://github.com/mozman/ezdxf/blob/aaa2d1b302c78a47fe1159bd6007254d1c2ebd22/src/ezdxf/tools/text.py) and ported by [Cursor](https://www.cursor.com/). Moveover, unit tests are added based on use case in the specification in next section.
4
+
5
+ ## AutoCAD MText Specification
6
+
7
+ The text formatting is done by inline codes. You can get more information from [this page](https://ezdxf.mozman.at/docs/dxfinternals/entities/mtext.html).
8
+
9
+ ### caret encoded characters:
10
+ - “^I” tabulator
11
+ - “^J” (LF) is a valid line break like “\P”
12
+ - “^M” (CR) is ignored
13
+ - other characters render as empty square “▯”
14
+ - a space “ “ after the caret renders the caret glyph: “1^ 2” renders “1^2”
15
+
16
+ ### special encoded characters:
17
+ - “%%c” and “%%C” renders “Ø” (alt-0216)
18
+ - “%%d” and “%%D” renders “°” (alt-0176)
19
+ - “%%p” and “%%P” renders “±” (alt-0177)
20
+
21
+ ### Multi-byte character encoding commands "\M" and "\m":
22
+ - Format: "\M+XXXX" or "\m+XXXX" where XXXX is a 4-character hex code
23
+ - Example: "\M+C4E3" renders the GBK encoded character
24
+ - If no valid hex code is found after "+", the command is treated as literal text
25
+ - Currently supports GBK encoding, with plans to add BIG5 support
26
+
27
+ ### Alignment command “\A”: argument “0”, “1” or “2” is expected
28
+ - the terminator symbol “;” is optional
29
+ - the arguments “3”, “4”, “5”, “6”, “7”, “8”, “9” and “-” default to 0
30
+ - other characters terminate the command and will be printed: “\AX”, renders “X”
31
+
32
+ ### ACI color command “\C”: int argument is expected
33
+ - the terminator symbol “;” is optional
34
+ - a leading “-” or “+” terminates the command, “\C+5” renders “\C+5”
35
+ - arguments > 255, are ignored but consumed “\C1000” renders nothing, not even a “0”
36
+ - a trailing “;” after integers is always consumed, even for much to big values, “\C10000;” renders nothing
37
+
38
+ ### RGB color command “\c”: int argument is expected
39
+ - the terminator symbol “;” is optional
40
+ - a leading “-” or “+” terminates the command, “\c+255” renders “\c+255”
41
+ - arguments >= 16777216 are masked by: value & 0xFFFFFF
42
+ - a trailing “;” after integers is always consumed, even for much to big values, “\c9999999999;” renders nothing and switches the color to yellow (255, 227, 11)
43
+
44
+ ### Height command “\H” and “\H…x”: float argument is expected
45
+ - the terminator symbol “;” is optional
46
+ - a leading “-” is valid, but negative values are ignored
47
+ - a leading “+” is valid
48
+ - a leading “.” is valid like “\H.5x” for height factor 0.5
49
+ - exponential format is valid like “\H1e2” for height factor 100 and “\H1e-2” for 0.01
50
+ - an invalid floating point value terminates the command, “\H1..5” renders “\H1..5”
51
+
52
+ ### Other commands with floating point arguments like the height command:
53
+ - Width commands “\W” and “\W…x”
54
+ - Character tracking commands “\T” and “\T…x”, negative values are used
55
+ - Slanting (oblique) command “\Q”
56
+
57
+ ### Stacking command “\S”:
58
+ - build fractions: “numerator (upr)” + “stacking type char (t)” + “denominator (lwr)” + “;”
59
+ - divider chars: “^”, “/” or “#”
60
+ - a space “ “ after the divider char “^” is mandatory to avoid caret decoding: “\S1^ 2;”
61
+ - the terminator symbol “;” is mandatory to end the command, all chars beyond the “\S” until the next “;” or the end of the string are part of the fraction
62
+ - backslash escape “\;” to render the terminator char
63
+ - a space “ “ after the divider chars “/” and “#” is rendered as space “ ” in front of the denominator
64
+ - the numerator and denominator can contain spaces
65
+ - backslashes “\” inside the stacking command are ignored (except “\;”) “\S\N^ \P” render “N” over “P”, therefore property changes (color, text height, …) are not possible inside the stacking command
66
+ - grouping chars “{” and “}” render as simple curly braces
67
+ - in the context of the MText \S...; stack command, the ^ is always treated as a stack separator, regardless of what characters come before or after it — even if those characters look like caret-encoded control characters (^I, ^J, ^!, ^?, etc.) in other contexts.
68
+ - a divider char after the first divider char, renders as the char itself: “\S1/2/3” renders the horizontal fraction “1” / “2/3”
69
+
70
+ ### Font command “\f” and “\F”: export only “\f”, parse both, “\F” ignores some arguments
71
+ - the terminator symbol “;” is mandatory to end the command, all chars beyond the “\f” until the next “;” or the end of the string are part of the command
72
+ - the command arguments are separated by the pipe char “|”
73
+ - arguments: “font family name” | “bold” | “italic” | “codepage” | “pitch”; example “\fArial|b0|i0|c0|p0;”
74
+ - only the “font family name” argument is required, fonts which are not available on the system are replaced by the “TXT.SHX” shape font
75
+ - the “font family name” is the font name shown in font selection widgets in desktop applications
76
+ - “b1” to use the bold font style, any other second char is interpreted as “non bold”
77
+ - “i1” to use an italic font style, any other second char is interpreted as “non italic”
78
+ - “c???” change codepage, “c0” use the default codepage, because of the age of unicode no further investigations, also seems to be ignored by AutoCAD and BricsCAD
79
+ - “p???” change pitch size, “p0” means don’t change, ignored by AutoCAD and BricsCAD, to change the text height use the “\H” command
80
+ - the order is not important, but export always in the shown order: “\fArial|b0|i0;” the arguments “c0” and “p0” are not required
81
+
82
+ ### Paragraph properties command “\p”
83
+ - the terminator symbol “;” is mandatory to end the command, all chars beyond the “\p” until the next “;” or the end of the string are part of the command
84
+ - the command arguments are separated by commas “,”
85
+ - all values are factors for the initial char height of the MTEXT entity, example: char height = 2.5, “\pl1;” set the left paragraph indentation to 1 x 2.5 = 2.5 drawing units.
86
+ - all values are floating point values, see height command
87
+ - arguments are “i”, “l”, “r”, “q”, “t”
88
+ - a “*” as argument value, resets the argument to the initial value: “i0”, “l0”, “r0”, the “q” argument most likely depends on the text direction; I haven’t seen “t*”. The sequence used by BricsCAD to reset all values is "\pi*,l*,r*,q*,t;"
89
+ - “i” indentation of the first line relative to the “l” argument as floating point value, “\pi1.5”
90
+ - “l” left paragraph indentation as floating point value, “\pl1.5”
91
+ - “r” right paragraph indentation as floating point value, “\pr1.5”
92
+ - “x” is required if a “q” or a “t” argument is present, the placement of the “x” has no obvious rules
93
+ - “q” paragraph alignment
94
+ - “ql” left paragraph alignment
95
+ - “qr” right paragraph alignment
96
+ - “qc” center paragraph alignment
97
+ - “qj” justified paragraph alignment
98
+ - “qd” distributed paragraph alignment
99
+ - “t” tabulator stops as comma separated list, the default tabulator stops are located at 4, 8, 12, …, by defining at least one tabulator stop, the default tabulator stops will be ignored. There 3 kind of tabulator stops: left, right and center adjusted stops, e.g. “pxt1,r5,c8”:
100
+ - a left adjusted stop has no leading char, two left adjusted stops “\pxt1,2;”
101
+ - a right adjusted stop has a preceding “r” char, “\pxtr1,r2;”
102
+ - a center adjusted stop has a preceding “c” char, “\pxtc1,c2;”
103
+
104
+ - complex example to create a numbered list with two items: "pxi-3,l4t4;1.^Ifirst item\P2.^Isecond item"
105
+ - a parser should be very flexible, I have seen several different orders of the arguments and placing the sometimes required “x” has no obvious rules.
106
+ - exporting seems to be safe to follow these three rules:
107
+ - the command starts with "\\px", the "x" does no harm, if not required
108
+ - argument order "i", "l", "r", "q", "t", any of the arguments can be left off
109
+ - terminate the command with a ";"
110
+
111
+ ## Usage
112
+
113
+ ```bash
114
+ npm install @mlightcad/mtext-parser
115
+ ```
116
+
117
+ Here's how to use the MText parser in your TypeScript/JavaScript project:
118
+
119
+ ```typescript
120
+ import { MTextParser, TokenType } from '@mlightcad/mtext-parser';
121
+
122
+ // Basic usage
123
+ const parser = new MTextParser('Hello World');
124
+ const tokens = Array.from(parser.parse());
125
+ // tokens will contain:
126
+ // - WORD token with "Hello"
127
+ // - SPACE token
128
+ // - WORD token with "World"
129
+
130
+ // Parsing formatted text
131
+ const formattedParser = new MTextParser('\\H2.5;Large\\H.5x;Small');
132
+ const formattedTokens = Array.from(formattedParser.parse());
133
+ // formattedTokens will contain:
134
+ // - WORD token with "Large" and capHeight = 2.5
135
+ // - WORD token with "Small" and capHeight = 0.5
136
+
137
+ // Parsing special characters
138
+ const specialParser = new MTextParser('Diameter: %%c, Angle: %%d, Tolerance: %%p');
139
+ const specialTokens = Array.from(specialParser.parse());
140
+ // specialTokens will contain:
141
+ // - WORD token with "Diameter: Ø, Angle: °, Tolerance: ±"
142
+
143
+ // Parsing with context
144
+ const ctx = new MTextContext();
145
+ ctx.capHeight = 2.0;
146
+ ctx.widthFactor = 1.5;
147
+ const contextParser = new MTextParser('Text with context', ctx);
148
+ const contextTokens = Array.from(contextParser.parse());
149
+ // contextTokens will contain tokens with the specified context
150
+
151
+ // Parsing with property commands
152
+ const propertyParser = new MTextParser('\\C1;Red Text', undefined, { yieldPropertyCommands: true });
153
+ const propertyTokens = Array.from(propertyParser.parse());
154
+ // propertyTokens will contain:
155
+ // - PROPERTIES_CHANGED token with the color command
156
+ // - WORD token with "Red Text" and aci = 1
157
+
158
+ // Parsing with paragraph reset after new paragraph
159
+ const resetParser = new MTextParser('Line1\\PLine2', undefined, { yieldPropertyCommands: true, resetParagraphParameters: true });
160
+ const resetTokens = Array.from(resetParser.parse());
161
+ // resetTokens will contain:
162
+ // - WORD token with "Line1"
163
+ // - NEW_PARAGRAPH token
164
+ // - PROPERTIES_CHANGED token with paragraph reset
165
+ // - WORD token with "Line2"
166
+ ```
167
+
168
+ ### Token Types
169
+
170
+ The parser produces tokens of the following types:
171
+
172
+ - `WORD`: Text content with associated formatting context
173
+ - `SPACE`: Space character
174
+ - `NBSP`: Non-breaking space
175
+ - `TABULATOR`: Tab character
176
+ - `NEW_PARAGRAPH`: Paragraph break
177
+ - `NEW_COLUMN`: Column break
178
+ - `WRAP_AT_DIMLINE`: Wrap at dimension line
179
+ - `STACK`: Stacked fraction with [numerator, denominator, type] data
180
+ - `PROPERTIES_CHANGED`: Property change command (only when yieldPropertyCommands is true)
181
+
182
+ ### Context Properties
183
+
184
+ Each token includes a context object (`MTextContext`) that contains the current formatting state:
185
+
186
+ - `underline`: Whether text is underlined
187
+ - `overline`: Whether text has overline
188
+ - `strikeThrough`: Whether text has strike-through
189
+ - `aci`: ACI color value (0-256)
190
+ - `rgb`: RGB color value [r, g, b]
191
+ - `align`: Line alignment (BOTTOM, MIDDLE, TOP)
192
+ - `fontFace`: Font properties (family, style, weight)
193
+ - `capHeight`: Capital letter height
194
+ - `widthFactor`: Character width factor
195
+ - `charTrackingFactor`: Character tracking factor
196
+ - `oblique`: Oblique angle
197
+ - `paragraph`: Paragraph properties (indent, margins, alignment, tab stops)
198
+
199
+ ### Error Handling
200
+
201
+ The parser handles invalid commands gracefully:
202
+
203
+ - Invalid floating point values are treated as literal text
204
+ - Invalid special character codes are ignored
205
+ - Invalid property commands are treated as literal text
206
+ - Invalid stacking commands are treated as literal text
207
+
208
208
  This makes the parser robust for handling real-world MText content that may contain errors or unexpected formatting.
@@ -1,3 +1,3 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});var _=(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=(s=>(s[s.BOTTOM=0]="BOTTOM",s[s.MIDDLE=1]="MIDDLE",s[s.TOP=2]="TOP",s))(S||{}),F=(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))(F||{}),E=(s=>(s[s.NONE=0]="NONE",s[s.UNDERLINE=1]="UNDERLINE",s[s.OVERLINE=2]="OVERLINE",s[s.STRIKE_THROUGH=4]="STRIKE_THROUGH",s))(E||{});const R={c:"Ø",d:"°",p:"±","%":"%"},y={l:1,r:2,c:3,j:4,d:5};function v(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 O(s){return s.replace(/\r\n|\r|\n/g,"\\P")}function N(s){return s.replace(/\\P/g,"").replace(/\\~/g,"").includes("\\")}function x(s,t=!1){const e=new Set,r=/\\[fF](.*?)[;|]/g;return[...s.matchAll(r)].forEach(a=>{let h=a[1].toLowerCase();t&&(h=h.replace(/\.(ttf|otf|woff|shx)$/,"")),e.add(h)}),e}class T{constructor(t){this.stack=[],this.stack.push(t)}push(t){this.stack.push(t)}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}get current(){return this.stack[this.stack.length-1]}get depth(){return this.stack.length-1}get root(){return this.stack[0]}setCurrent(t){this.stack[this.stack.length-1]=t}}class w{constructor(t,e,r={}){this.continueStroke=!1,this.inStackContext=!1,this.scanner=new f(t);const a=e??new b;this.ctxStack=new T(a),this.yieldPropertyCommands=r.yieldPropertyCommands??!1,this.resetParagraphParameters=r.resetParagraphParameters??!1,this.mifDecoder=r.mifDecoder??this.decodeMultiByteChar.bind(this),this.mifCodeLength=r.mifCodeLength??"auto"}decodeMultiByteChar(t){try{if(t.length===5){const e=t[0];let r="gbk";e==="1"?r="shift-jis":e==="2"&&(r="big5");const a=new Uint8Array([parseInt(t.substr(1,2),16),parseInt(t.substr(3,2),16)]);return new TextDecoder(r).decode(a)}else if(t.length===4){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);if(i!=="▯")return i}return"▯"}catch{return"▯"}}extractMifCode(t){var e,r,a;if(t==="auto"){const h=(e=this.scanner.tail.match(/^[0-9A-Fa-f]{5}/))==null?void 0:e[0];if(h)return h;const i=(r=this.scanner.tail.match(/^[0-9A-Fa-f]{4}/))==null?void 0:r[0];return i||null}else return((a=this.scanner.tail.match(new RegExp(`^[0-9A-Fa-f]{${t}}`)))==null?void 0:a[0])||null}pushCtx(){this.ctxStack.push(this.ctxStack.current)}popCtx(){this.ctxStack.pop()}parseStacking(){const t=new f(this.extractExpression(!0));let e="",r="",a="";const h=()=>{let n=t.peek(),l=!1;return n.charCodeAt(0)<32&&(n=" "),n==="\\"&&(l=!0,t.consume(1),n=t.peek()),t.consume(1),[n,l]},i=()=>{let n="";for(;t.hasData;){const[l,o]=h();if(!o&&(l==="/"||l==="#"||l==="^"))return[n,l];n+=l}return[n,""]},u=n=>{let l="",o=n;for(;t.hasData;){const[c,g]=h();if(!(o&&c===" ")){if(o=!1,!g&&c===";")break;l+=c}}return l};return[e,a]=i(),a&&(r=u(a==="^")),e===""&&r.includes("I/")?[2,[" "," ","/"]]:a==="^"?[2,[e,r,"^"]]:[2,[e,r,a]]}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}}}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}parseAlign(t){const e=this.scanner.get();"012".includes(e)?t.align=parseInt(e):t.align=0,this.consumeOptionalTerminator()}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()}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()}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()}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}parseOblique(t){const e=this.extractFloatExpression(!1);e&&(t.oblique=parseFloat(e)),this.consumeOptionalTerminator()}parseAciColor(t){const e=this.extractIntExpression();if(e){const r=parseInt(e);r<257&&(t.color.aci=r)}this.consumeOptionalTerminator()}parseRgbColor(t){const e=this.extractIntExpression();if(e){const r=parseInt(e)&16777215;t.color.rgbValue=r}this.consumeOptionalTerminator()}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""}extractIntExpression(){const t=this.scanner.tail.match(/^\d+/);if(t){const e=t[0];return this.scanner.consume(e.length),e}return""}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)==="\\",h=this.scanner.tail.slice(0,e-this.scanner.currentIndex+(a?1:0));return this.scanner.consume(h.length+1),h}parseFontProperties(t){const e=this.extractExpression().split("|");if(e.length>0&&e[0]){const r=e[0];let a="Regular",h=400;for(const i of e.slice(1))i.startsWith("b1")?h=700:i==="i"||i.startsWith("i1")?a="Italic":(i==="i0"||i.startsWith("i0"))&&(a="Regular");t.fontFace={family:r,style:a,weight:h}}}parseParagraphProperties(t){const e=new f(this.extractExpression());let r=t.paragraph.indent,a=t.paragraph.left,h=t.paragraph.right,i=t.paragraph.align,u=[];const n=()=>{const l=e.tail.match(/^[+-]?\d+(?:\.\d*)?(?:[eE][+-]?\d+)?/);if(l){const o=parseFloat(l[0]);for(e.consume(l[0].length);e.peek()===",";)e.consume(1);return o}return 0};for(;e.hasData;)switch(e.get()){case"i":r=n();break;case"l":a=n();break;case"r":h=n();break;case"x":break;case"q":{const o=e.get();for(i=y[o]||0;e.peek()===",";)e.consume(1);break}case"t":for(u=[];e.hasData;){const o=e.peek();if(o==="r"||o==="c"){e.consume(1);const c=n();u.push(o+c.toString())}else{const c=n();isNaN(c)?e.consume(1):u.push(c)}}break}t.paragraph={indent:r,left:a,right:h,align:i,tabs:u}}consumeOptionalTerminator(){this.scanner.peek()===";"&&this.scanner.consume(1)}*parse(){let r=null;function a(i){const u={...i.paragraph};i.paragraph={indent:0,left:0,right:0,align:0,tabs:[]};const n={};return u.indent!==0&&(n.indent=0),u.left!==0&&(n.left=0),u.right!==0&&(n.right=0),u.align!==0&&(n.align=0),JSON.stringify(u.tabs)!==JSON.stringify([])&&(n.tabs=[]),n}const h=()=>{let i="";for(;this.scanner.hasData;){let u=!1,n=this.scanner.peek();const l=this.scanner.currentIndex;if(n.charCodeAt(0)<32){if(this.scanner.consume(1),n===" ")return[5,null];if(n===`
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});var _=(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=(s=>(s[s.BOTTOM=0]="BOTTOM",s[s.MIDDLE=1]="MIDDLE",s[s.TOP=2]="TOP",s))(S||{}),F=(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))(F||{}),E=(s=>(s[s.NONE=0]="NONE",s[s.UNDERLINE=1]="UNDERLINE",s[s.OVERLINE=2]="OVERLINE",s[s.STRIKE_THROUGH=4]="STRIKE_THROUGH",s))(E||{});const R={c:"Ø",d:"°",p:"±","%":"%"},y={l:1,r:2,c:3,j:4,d:5};function v(s){const[t,e,r]=s;return t<<16|e<<8|r}function I(s){const t=s>>16&255,e=s>>8&255,r=s&255;return[t,e,r]}function O(s){return s.replace(/\r\n|\r|\n/g,"\\P")}function N(s){return s.replace(/\\P/g,"").replace(/\\~/g,"").includes("\\")}function x(s,t=!1){const e=new Set,r=/\\[fF](.*?)[;|]/g;return[...s.matchAll(r)].forEach(a=>{let h=a[1].toLowerCase();t&&(h=h.replace(/\.(ttf|otf|woff|shx)$/,"")),e.add(h)}),e}class T{constructor(t){this.stack=[],this.stack.push(t)}push(t){this.stack.push(t)}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}get current(){return this.stack[this.stack.length-1]}get depth(){return this.stack.length-1}get root(){return this.stack[0]}setCurrent(t){this.stack[this.stack.length-1]=t}}class w{constructor(t,e,r={}){this.continueStroke=!1,this.inStackContext=!1,this.scanner=new f(t);const a=e??new b;this.ctxStack=new T(a),this.yieldPropertyCommands=r.yieldPropertyCommands??!1,this.resetParagraphParameters=r.resetParagraphParameters??!1,this.mifDecoder=r.mifDecoder??this.decodeMultiByteChar.bind(this),this.mifCodeLength=r.mifCodeLength??"auto"}decodeMultiByteChar(t){try{if(t.length===5){const e=t[0];let r="gbk";e==="1"?r="shift-jis":e==="2"&&(r="big5");const a=new Uint8Array([parseInt(t.substr(1,2),16),parseInt(t.substr(3,2),16)]);return new TextDecoder(r).decode(a)}else if(t.length===4){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);if(i!=="▯")return i}return"▯"}catch{return"▯"}}extractMifCode(t){var e,r,a;if(t==="auto"){const h=(e=this.scanner.tail.match(/^[0-9A-Fa-f]{5}/))==null?void 0:e[0];if(h)return h;const i=(r=this.scanner.tail.match(/^[0-9A-Fa-f]{4}/))==null?void 0:r[0];return i||null}else return((a=this.scanner.tail.match(new RegExp(`^[0-9A-Fa-f]{${t}}`)))==null?void 0:a[0])||null}pushCtx(){this.ctxStack.push(this.ctxStack.current)}popCtx(){this.ctxStack.pop()}parseStacking(){const t=new f(this.extractExpression(!0));let e="",r="",a="";const h=()=>{let n=t.peek(),l=!1;return n.charCodeAt(0)<32&&(n=" "),n==="\\"&&(l=!0,t.consume(1),n=t.peek()),t.consume(1),[n,l]},i=()=>{let n="";for(;t.hasData;){const[l,o]=h();if(!o&&(l==="/"||l==="#"||l==="^"))return[n,l];n+=l}return[n,""]},u=n=>{let l="",o=n;for(;t.hasData;){const[c,g]=h();if(!(o&&c===" ")){if(o=!1,!g&&c===";")break;l+=c}}return l};return[e,a]=i(),a&&(r=u(a==="^")),e===""&&r.includes("I/")?[2,[" "," ","/"]]:a==="^"?[2,[e,r,"^"]]:[2,[e,r,a]]}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}}}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}parseAlign(t){const e=this.scanner.get();"012".includes(e)?t.align=parseInt(e):t.align=0,this.consumeOptionalTerminator()}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()}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()}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()}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}parseOblique(t){const e=this.extractFloatExpression(!1);e&&(t.oblique=parseFloat(e)),this.consumeOptionalTerminator()}parseAciColor(t){const e=this.extractIntExpression();if(e){const r=parseInt(e);r<257&&(t.color.aci=r)}this.consumeOptionalTerminator()}parseRgbColor(t){const e=this.extractIntExpression();if(e){const r=parseInt(e)&16777215;t.color.rgbValue=r}this.consumeOptionalTerminator()}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""}extractIntExpression(){const t=this.scanner.tail.match(/^\d+/);if(t){const e=t[0];return this.scanner.consume(e.length),e}return""}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)==="\\",h=this.scanner.tail.slice(0,e-this.scanner.currentIndex+(a?1:0));return this.scanner.consume(h.length+1),h}parseFontProperties(t){const e=this.extractExpression().split("|");if(e.length>0&&e[0]){const r=e[0];let a="Regular",h=400;for(const i of e.slice(1))i.startsWith("b1")?h=700:i==="i"||i.startsWith("i1")?a="Italic":(i==="i0"||i.startsWith("i0"))&&(a="Regular");t.fontFace={family:r,style:a,weight:h}}}parseParagraphProperties(t){const e=new f(this.extractExpression());let r=t.paragraph.indent,a=t.paragraph.left,h=t.paragraph.right,i=t.paragraph.align,u=[];const n=()=>{const l=e.tail.match(/^[+-]?\d+(?:\.\d*)?(?:[eE][+-]?\d+)?/);if(l){const o=parseFloat(l[0]);for(e.consume(l[0].length);e.peek()===",";)e.consume(1);return o}return 0};for(;e.hasData;)switch(e.get()){case"i":r=n();break;case"l":a=n();break;case"r":h=n();break;case"x":break;case"q":{const o=e.get();for(i=y[o]||0;e.peek()===",";)e.consume(1);break}case"t":for(u=[];e.hasData;){const o=e.peek();if(o==="r"||o==="c"){e.consume(1);const c=n();u.push(o+c.toString())}else{const c=n();isNaN(c)?e.consume(1):u.push(c)}}break}t.paragraph={indent:r,left:a,right:h,align:i,tabs:u}}consumeOptionalTerminator(){this.scanner.peek()===";"&&this.scanner.consume(1)}*parse(){let r=null;function a(i){const u={...i.paragraph};i.paragraph={indent:0,left:0,right:0,align:0,tabs:[]};const n={};return u.indent!==0&&(n.indent=0),u.left!==0&&(n.left=0),u.right!==0&&(n.right=0),u.align!==0&&(n.align=0),JSON.stringify(u.tabs)!==JSON.stringify([])&&(n.tabs=[]),n}const h=()=>{let i="";for(;this.scanner.hasData;){let u=!1,n=this.scanner.peek();const l=this.scanner.currentIndex;if(n.charCodeAt(0)<32){if(this.scanner.consume(1),n===" ")return[5,null];if(n===`
2
2
  `)return[6,null];n=" "}if(n==="\\")if("\\{}".includes(this.scanner.peek(1)))u=!0,this.scanner.consume(1),n=this.scanner.peek();else{if(i)return[1,i];this.scanner.consume(1);const o=this.scanner.get();switch(o){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 c=this.parseStacking();return this.inStackContext=!1,c}case"m":case"M":if(this.scanner.peek()==="+"){this.scanner.consume(1);const c=this.extractMifCode(this.mifCodeLength);if(c){this.scanner.consume(c.length);const g=this.mifDecoder(c);return i?[1,i]:[1,g]}this.scanner.consume(-1)}i+="\\M";continue;case"U":if(this.scanner.peek()==="+"){this.scanner.consume(1);const c=this.scanner.tail.match(/^[0-9A-Fa-f]{4,8}/);if(c){const g=c[0];this.scanner.consume(g.length);const p=parseInt(g,16);let m="";try{m=String.fromCodePoint(p)}catch{m="▯"}return i?[1,i]:[1,m]}this.scanner.consume(-1)}i+="\\U";continue;default:if(o)try{const c=this.parseProperties(o);if(this.yieldPropertyCommands&&c)return[9,c];continue}catch{const c=this.scanner.tail.slice(l,this.scanner.currentIndex);i+=c}}continue}if(n==="%"&&this.scanner.peek(1)==="%"){const o=this.scanner.peek(2).toLowerCase(),c=R[o];if(c){this.scanner.consume(3),i+=c;continue}else{const g=[o,this.scanner.peek(3),this.scanner.peek(4)];if(g.every(p=>p>="0"&&p<="9")){const p=Number.parseInt(g.join(""),10);this.scanner.consume(5),i+=String.fromCharCode(p)}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(!u){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 o=this.ctxStack.current;this.popCtx();const c=this.getPropertyChanges(o,this.ctxStack.current);if(Object.keys(c).length>0)return[9,{command:void 0,changes:c,depth:this.ctxStack.depth}]}else this.popCtx();continue}}if(!this.inStackContext&&n==="^"){const o=this.scanner.peek(1);if(o){const c=o.charCodeAt(0);if(this.scanner.consume(2),c===32)i+="^";else{if(c===73)return i?[1,i]:[5,null];if(c===74)return i?[1,i]:[6,null];if(c===77)continue;i+="▯"}continue}}this.scanner.consume(1),n.charCodeAt(0)>=32&&(i+=n)}return i?[1,i]:[0,null]};for(;;){const[i,u]=h.call(this);if(i){if(yield new d(i,this.ctxStack.current.copy(),u),i===6&&this.resetParagraphParameters){const n=this.ctxStack.current,l=a(n);this.yieldPropertyCommands&&Object.keys(l).length>0&&(yield new d(9,n.copy(),{command:void 0,changes:{paragraph:l},depth:this.ctxStack.depth}))}r&&(yield new d(r,this.ctxStack.current.copy(),null),r=null)}else break}}}class f{constructor(t){this.text=t,this.textLen=t.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 t=this.text[this._index];return this._index++,t}consume(t=1){this._index=Math.max(0,Math.min(this._index+t,this.textLen))}peek(t=0){const e=this._index+t;return e>=this.textLen||e<0?"":this.text[e]}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 tail(){return this.text.slice(this._index)}isNextSpace(){return this.peek()===" "}consumeSpaces(){let t=0;for(;this.isNextSpace();)this.consume(),t++;return t}}class k{constructor(t){this._aci=256,this._rgbValue=null,Array.isArray(t)?this.rgb=t:typeof t=="number"?this.aci=t:this.aci=256}get aci(){return this._aci}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 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 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}get isRgb(){return this._rgbValue!==null}get isAci(){return this._rgbValue===null&&this._aci!==null}get rgbValue(){return this._rgbValue}set rgbValue(t){t===null?this._rgbValue=null:(this._rgbValue=t&16777215,this._aci=null)}copy(){const t=new k;return t._aci=this._aci,t._rgbValue=this._rgbValue,t}toObject(){return{aci:this._aci,rgb:this.rgb,rgbValue:this._rgbValue}}equals(t){return this._aci===t._aci&&this._rgbValue===t._rgbValue}}class b{constructor(){this._stroke=0,this.continueStroke=!1,this.color=new k,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 capHeight(){return this._capHeight}set capHeight(t){this._capHeight={value:Math.abs(t.value),isRelative:t.isRelative}}get widthFactor(){return this._widthFactor}set widthFactor(t){this._widthFactor={value:Math.abs(t.value),isRelative:t.isRelative}}get charTrackingFactor(){return this._charTrackingFactor}set charTrackingFactor(t){this._charTrackingFactor={value:Math.abs(t.value),isRelative:t.isRelative}}get aci(){return this.color.aci}set aci(t){this.color.aci=t}get rgb(){return this.color.rgb}set rgb(t){this.color.rgb=t}get italic(){return this.fontFace.style==="Italic"}set italic(t){this.fontFace.style=t?"Italic":"Regular"}get bold(){return(this.fontFace.weight||400)>=700}set bold(t){this.fontFace.weight=t?700:400}get underline(){return!!(this._stroke&1)}set underline(t){this._setStrokeState(1,t)}get strikeThrough(){return!!(this._stroke&4)}set strikeThrough(t){this._setStrokeState(4,t)}get overline(){return!!(this._stroke&2)}set overline(t){this._setStrokeState(2,t)}get hasAnyStroke(){return!!this._stroke}_setStrokeState(t,e=!0){e?this._stroke|=t:this._stroke&=~t}copy(){const t=new b;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 d{constructor(t,e,r){this.type=t,this.ctx=e,this.data=r}}exports.MTextColor=k;exports.MTextContext=b;exports.MTextLineAlignment=S;exports.MTextParagraphAlignment=F;exports.MTextParser=w;exports.MTextStroke=E;exports.MTextToken=d;exports.TextScanner=f;exports.TokenType=_;exports.escapeDxfLineEndings=O;exports.getFonts=x;exports.hasInlineFormattingCodes=N;exports.int2rgb=I;exports.rgb2int=v;
3
3
  //# sourceMappingURL=parser.cjs.js.map