UNPKG

@mlightcad/shx-parser

Version:

A TypeScript library for parsing AutoCAD SHX font files

1,055 lines (1,054 loc) 32.8 kB
class f { /** * Converts an unsigned byte to a signed byte as used in SHX format. * Values > 127 are converted to their signed equivalent (-128 to -1). * @param value - The unsigned byte value to convert * @returns The signed byte value */ static byteToSByte(t) { return (t & 127) - (t & 128 ? 128 : 0); } /** * Creates a new ShxFileReader instance. * @param arraybuffer - The ArrayBuffer to read from */ constructor(t) { this.position = 0, this.data = new DataView(t); } /** * Reads a specified number of bytes from the current position. * @param length - Number of bytes to read (optional) * @returns A Uint8Array containing the read bytes * @throws Error if reading beyond buffer bounds */ readBytes(t = 1) { this.data.byteLength < this.position + t && this.throwOutOfRangeError(this.position + t); const e = new Uint8Array(this.data.buffer, this.position, t); return this.position += t, e; } /** * Skips a specified number of bytes from the current position. * @param length - Number of bytes to skip * @throws Error if skipping beyond buffer bounds */ skip(t) { this.data.byteLength < this.position + t && this.throwOutOfRangeError(this.position + t), this.position += t; } /** * Reads an unsigned 8-bit integer. * @returns The read uint8 value * @throws Error if reading beyond buffer bounds */ readUint8() { this.data.byteLength < this.position + 1 && this.throwOutOfRangeError(this.position + 1); const t = this.data.getUint8(this.position); return this.position += 1, t; } /** * Reads a signed 8-bit integer. * @returns The read int8 value * @throws Error if reading beyond buffer bounds */ readInt8() { this.data.byteLength < this.position + 1 && this.throwOutOfRangeError(this.position + 1); const t = this.data.getInt8(this.position); return this.position += 1, t; } /** * Reads an unsigned 16-bit integer. * @param littleEndian If false, a big-endian value should be read. * @returns The read uint16 value * @throws Error if reading beyond buffer bounds */ readUint16(t = !0) { this.data.byteLength < this.position + 2 && this.throwOutOfRangeError(this.position + 2); const e = this.data.getUint16(this.position, t); return this.position += 2, e; } /** * Reads a signed 16-bit integer. * @returns The read int16 value * @throws Error if reading beyond buffer bounds */ readInt16() { this.data.byteLength < this.position + 2 && this.throwOutOfRangeError(this.position + 2); const t = this.data.getInt16(this.position, !0); return this.position += 2, t; } /** * Reads an unsigned 32-bit integer. * @returns The read uint32 value * @throws Error if reading beyond buffer bounds */ readUint32() { this.data.byteLength < this.position + 4 && this.throwOutOfRangeError(this.position + 4); const t = this.data.getUint32(this.position, !0); return this.position += 4, t; } /** * Reads a signed 32-bit integer. * @returns The read int32 value * @throws Error if reading beyond buffer bounds */ readInt32() { this.data.byteLength < this.position + 4 && this.throwOutOfRangeError(this.position + 4); const t = this.data.getInt32(this.position, !0); return this.position += 4, t; } /** * Reads a 32-bit floating point number. * @returns The read float32 value * @throws Error if reading beyond buffer bounds */ readFloat32() { this.data.byteLength < this.position + 4 && this.throwOutOfRangeError(this.position + 4); const t = this.data.getFloat32(this.position, !0); return this.position += 4, t; } /** * Reads a 64-bit floating point number. * @returns The read float64 value * @throws Error if reading beyond buffer bounds */ readFloat64() { this.data.byteLength < this.position + 8 && this.throwOutOfRangeError(this.position + 8); const t = this.data.getFloat64(this.position, !0); return this.position += 8, t; } /** * Sets the current read position in the buffer. * @param position - The new position to set */ setPosition(t) { this.data.byteLength < t && this.throwOutOfRangeError(t), this.position = t; } /** * Checks if the current position is at the end of the buffer. * @returns True if at the end of the buffer, false otherwise */ isEnd() { return this.position === this.data.byteLength - 1; } /** * Gets the current position in the buffer. * @returns The current position */ get currentPosition() { return this.position; } /** * Gets the total length of the buffer. * @returns The buffer length in bytes */ get length() { return this.data.byteLength; } /** * Throws an error when attempting to read beyond buffer bounds. * @param position - The position that caused the error * @throws Error with details about the out of range access */ throwOutOfRangeError(t) { throw new Error( `Position ${t} is out of range for the data length ${this.data.byteLength}!` ); } } var y = /* @__PURE__ */ ((d) => (d.SHAPES = "shapes", d.BIGFONT = "bigfont", d.UNIFONT = "unifont", d))(y || {}); class E { parse(t) { const i = this.parseHeader(t).split(" "), n = i[1].toLocaleLowerCase(); if (!Object.values(y).includes(n)) throw new Error(`Invalid font type: ${n}`); return { fileHeader: i[0], fontType: n, fileVersion: i[2] }; } parseHeader(t) { let e = "", n = 0; for (; t.currentPosition < t.length - 2 && n < 1024; ) { const s = t.readUint8(); if (s === 13) { const o = t.currentPosition, r = t.readUint8(), c = t.readUint8(); if (r === 10 && c === 26) break; t.setPosition(o), e += String.fromCharCode(s); } else e += String.fromCharCode(s); n++; } return e.trim(); } } class I { parse(t) { try { t.readBytes(4); const e = t.readInt16(); if (e <= 0) throw new Error("Invalid shape count in font file"); const i = []; for (let o = 0; o < e; o++) { const r = t.readUint16(), c = t.readUint16(); c > 0 && i.push({ code: r, length: c }); } const n = {}; for (const o of i) try { const r = t.readBytes(o.length); r.length === o.length && (n[o.code] = r); } catch { console.warn(`Failed to read shape data for code ${o.code}`); } const s = { data: n, info: "", baseUp: 8, // Default values baseDown: 2, orientation: "horizontal", isExtended: !1 }; if (0 in n) { const o = n[0]; try { const r = new TextDecoder().decode(o); let c = r.indexOf("\0"); c >= 0 && (s.info = r.substring(0, c), c + 3 < o.length && (s.baseUp = o[c + 1], s.baseDown = o[c + 2], s.orientation = o[c + 3] === 0 ? "horizontal" : "vertical")); } catch { console.warn("Failed to parse font info block"); } } return s; } catch (e) { return console.error("Error parsing shape font:", e), { data: {}, info: "Failed to parse font file", baseUp: 8, baseDown: 2, orientation: "horizontal", isExtended: !1 }; } } } class B { parse(t) { try { t.readInt16(); const e = t.readInt16(), i = t.readInt16(); if (e <= 0) throw new Error("Invalid character count in font file"); t.skip(i * 4); const n = []; for (let r = 0; r < e; r++) { const c = t.readUint16(!1), a = t.readUint16(), h = t.readUint32(); (c !== 0 || a !== 0 || h !== 0) && n.push({ code: c, length: a, offset: h }); } const s = {}; for (const r of n) try { t.setPosition(r.offset); const c = t.readBytes(r.length); c.length === r.length && (s[r.code] = c); } catch { console.warn(`Failed to read bigfont data for code ${r.code}`); } const o = { data: s, info: "", baseUp: 8, baseDown: 2, orientation: "horizontal", isExtended: !1 }; if (0 in s) { const r = s[0]; try { const c = this.utf8ArrayToStr(r); let a = c.indexOf("\0"); a >= 0 && (o.info = c.substring(0, a), a++, a + 3 < r.length && (r.length - a === 4 ? (o.baseUp = r[a++], o.baseDown = r[a++], o.orientation = r[a++] === 0 ? "horizontal" : "vertical") : (o.baseUp = r[a++], a++, o.orientation = r[a++] === 0 ? "horizontal" : "vertical", o.baseDown = r[a++], o.isExtended = !0))); } catch { console.warn("Failed to parse bigfont info block"); } } return o; } catch (e) { return console.error("Error parsing big font:", e), { data: {}, info: "Failed to parse font file", baseUp: 8, baseDown: 2, orientation: "horizontal", isExtended: !1 }; } } utf8ArrayToStr(t) { let e = "", i = 0; for (; i < t.length; ) { const n = t[i++]; switch (n >> 4) { case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: e += String.fromCharCode(n); break; case 12: case 13: { const s = t[i++]; e += String.fromCharCode((n & 31) << 6 | s & 63); break; } case 14: { const s = t[i++], o = t[i++]; e += String.fromCharCode( (n & 15) << 12 | (s & 63) << 6 | (o & 63) << 0 ); break; } } } return e; } } class U { parse(t) { try { const e = t.readInt32(); if (e <= 0) throw new Error("Invalid character count in font file"); const i = t.readInt16(), n = t.readBytes(i), s = { data: {}, info: "", baseUp: 8, baseDown: 2, orientation: "horizontal", isExtended: !1 }; try { const r = new TextDecoder().decode(n); let c = r.indexOf("\0"); c >= 0 && (s.info = r.substring(0, c), c + 3 < n.length && (s.baseUp = n[c + 1], s.baseDown = n[c + 2], s.orientation = n[c + 3] === 0 ? "horizontal" : "vertical")); } catch { console.warn("Failed to parse unifont info block"); } const o = {}; for (let r = 0; r < e - 1; r++) try { const c = t.readUint16(), a = t.readUint16(); if (a > 0) { const h = t.readBytes(a); h.length === a && (o[c] = h); } } catch { console.warn("Failed to read unifont character data"); break; } return s.data = o, s; } catch (e) { return console.error("Error parsing unifont:", e), { data: {}, info: "Failed to parse font file", baseUp: 8, baseDown: 2, orientation: "horizontal", isExtended: !1 }; } } } class T { static createParser(t) { switch (t) { case y.SHAPES: return new I(); case y.BIGFONT: return new B(); case y.UNIFONT: return new U(); default: throw new Error(`Unsupported font type: ${t}`); } } } class l { /** * Creates a new Point instance. * @param x - The x-coordinate (defaults to 0) * @param y - The y-coordinate (defaults to 0) */ constructor(t = 0, e = 0) { this.x = t, this.y = e; } /** * Sets the coordinates of the point. * @param x - The new x-coordinate * @param y - The new y-coordinate * @returns The point instance for method chaining */ set(t, e) { return this.x = t, this.y = e, this; } /** * Calculates the length (magnitude) of the vector from origin to this point. * @returns The length of the vector */ length() { return Math.sqrt(this.x * this.x + this.y * this.y); } /** * Normalizes the point vector to have a length of 1. * @returns The point instance for method chaining */ normalize() { const t = this.length(); return t !== 0 && (this.x /= t, this.y /= t), this; } /** * Creates a new Point instance with the same coordinates. * @returns A new Point instance with the same x and y values */ clone() { return new l(this.x, this.y); } /** * Adds another point's coordinates to this point. * @param point - The point to add * @returns The point instance for method chaining */ add(t) { return this.x += t.x, this.y += t.y, this; } /** * Subtracts another point's coordinates from this point. * @param point - The point to subtract * @returns The point instance for method chaining */ subtract(t) { return this.x -= t.x, this.y -= t.y, this; } /** * Multiplies both coordinates by a scalar value. * @param scalar - The scalar value to multiply by * @returns The point instance for method chaining */ multiply(t) { return this.x *= t, this.y *= t, this; } /** * Divides both coordinates by a scalar value. * @param scalar - The scalar value to divide by * @returns The point instance for method chaining */ divide(t) { return t !== 0 && (this.x /= t, this.y /= t), this; } /** * Multiplies x and y coordinates by different scalar values. * @param xScalar - The scalar value to multiply x-coordinate by * @param yScalar - The scalar value to multiply y-coordinate by * @returns The point instance for method chaining */ multiplyScalars(t, e) { return this.x *= t, this.y *= e, this; } /** * Divides x and y coordinates by different scalar values. * @param xScalar - The scalar value to divide x-coordinate by * @param yScalar - The scalar value to divide y-coordinate by * @returns The point instance for method chaining */ divideScalars(t, e) { return t !== 0 && (this.x /= t), e !== 0 && (this.y /= e), this; } /** * Calculates the Euclidean distance to another point. * @param point - The point to calculate distance to * @returns The distance between the two points */ distanceTo(t) { const e = this.x - t.x, i = this.y - t.y; return Math.sqrt(e * e + i * i); } } const A = Math.PI / 4; class C { /** * Creates a bulge-defined arc * @param start Start point * @param end End point * @param bulge Bulge factor (-1 to 1, where 1 is a semicircle) */ static fromBulge(t, e, i) { const n = Math.max(-1, Math.min(1, i)); return new C({ start: t, end: e, bulge: n }); } /** * Creates an octant-defined arc * @param center Center point of the arc * @param radius Radius of the arc * @param startOctant Starting octant (0-7) * @param octantCount Number of octants to span (0-8, where 0 means 8 octants) * @param isClockwise Whether the arc goes clockwise */ static fromOctant(t, e, i, n, s) { return new C({ center: t, radius: e, startOctant: i, octantCount: n, isClockwise: s }); } constructor(t) { if (t.start && t.end && t.bulge !== void 0) { this.start = t.start.clone(), this.end = t.end.clone(), this.bulge = t.bulge, this.isClockwise = t.bulge < 0; const e = this.end.clone().subtract(this.start), i = e.length(); if (Math.abs(this.bulge) * i / 2 === 0) { this.radius = 0, this.center = this.start.clone(), this.startAngle = Math.atan2(e.y, e.x), this.endAngle = this.startAngle; return; } const s = 4 * Math.atan(Math.abs(this.bulge)); this.radius = i / (2 * Math.sin(s / 2)); const o = this.start.clone().add(e.clone().divide(2)), r = new l(-e.y, e.x); r.normalize(), r.multiply(Math.abs(this.radius * Math.cos(s / 2))), this.center = o.clone(), this.isClockwise ? this.center.subtract(r) : this.center.add(r), this.startAngle = Math.atan2(this.start.y - this.center.y, this.start.x - this.center.x), this.endAngle = Math.atan2(this.end.y - this.center.y, this.end.x - this.center.x), this.isClockwise ? this.endAngle >= this.startAngle && (this.endAngle -= 2 * Math.PI) : this.endAngle <= this.startAngle && (this.endAngle += 2 * Math.PI); } else if (t.center && t.radius !== void 0 && t.startOctant !== void 0 && t.octantCount !== void 0 && t.isClockwise !== void 0) { this.center = t.center.clone(), this.radius = t.radius, this.isClockwise = t.isClockwise, this.startAngle = t.startOctant * A; const e = (t.octantCount === 0 ? 8 : t.octantCount) * A; this.endAngle = this.startAngle + (this.isClockwise ? -e : e), this.start = this.center.clone().add( new l( this.radius * Math.cos(this.startAngle), this.radius * Math.sin(this.startAngle) ) ), this.end = this.center.clone().add( new l(this.radius * Math.cos(this.endAngle), this.radius * Math.sin(this.endAngle)) ); } else throw new Error("Invalid arc parameters"); } /** * Tessellates the arc into a series of points that approximate the arc. * @param circleSpan The angle span between tessellated points (default Math.PI / 18) * @returns Array of points representing the tessellated arc */ tessellate(t = Math.PI / 18) { if (this.radius === 0) return [this.start.clone(), this.end.clone()]; const e = [this.start.clone()], i = Math.abs(this.endAngle - this.startAngle), n = Math.max(1, Math.floor(i / t)); for (let s = 1; s < n; s++) { const o = s / n, r = this.isClockwise ? this.startAngle - o * i : this.startAngle + o * i; e.push( this.center.clone().add(new l(this.radius * Math.cos(r), this.radius * Math.sin(r))) ); } return e.push( this.end ? this.end.clone() : this.center.clone().add( new l( this.radius * Math.cos(this.endAngle), this.radius * Math.sin(this.endAngle) ) ) ), e; } } class S { constructor(t, e = []) { this.lastPoint = t, this.polylines = e; } /** * Get the bounding box of the shape * @returns Bounding box of the shape */ get bbox() { let t = 1 / 0, e = -1 / 0, i = 1 / 0, n = -1 / 0; return this.polylines.forEach((s) => { s.forEach((o) => { t = Math.min(t, o.x), e = Math.max(e, o.x), i = Math.min(i, o.y), n = Math.max(n, o.y); }); }), { minX: t, minY: i, maxX: e, maxY: n }; } /** * Offset the shape by a point * @param p The point to offset the shape by * @param isNewInstance Whether to return a new instance of the shape or modify the current instance * @returns The offset shape */ offset(t, e = !0) { var i, n; return e ? new S( (i = this.lastPoint) == null ? void 0 : i.clone().add(t), this.polylines.map((s) => s.map((o) => o.clone().add(t))) ) : ((n = this.lastPoint) == null || n.add(t), this.polylines.forEach((s) => s.forEach((o) => o.add(t))), this); } /** * Converts the shape to an SVG string * @param options SVG rendering options * @returns SVG string */ toSVG(t = {}) { const { strokeWidth: e = "0.5%", strokeColor: i = "black", isAutoFit: n = !1 } = t; let s, o; if (n) { const r = this.bbox, c = 0.2, a = r.maxX - r.minX, h = r.maxY - r.minY, b = r.minX - a * c, p = r.maxX + a * c, g = r.minY - h * c, k = r.maxY + h * c; o = this.polylines.map((w) => { let M = ""; return w.forEach((x, P) => { const m = x.x, u = -x.y; M += P === 0 ? `M ${m} ${u} ` : `L ${m} ${u} `; }), `<path d="${M}" stroke="${i}" stroke-width="${e}" fill="none"/>`; }).join(""), s = `${b} ${-k} ${p - b} ${k - g}`; } else s = "0 0 20 20", o = this.polylines.map((r) => { let c = ""; return r.forEach((a, h) => { const b = a.x + 5, p = -a.y + 15; c += h === 0 ? `M ${b} ${p} ` : `L ${b} ${p} `; }), `<path d="${c}" stroke="${i}" stroke-width="${e}" fill="none"/>`; }).join(""); return `<svg width="100%" height="100%" viewBox="${s}" preserveAspectRatio="xMidYMid meet">${o}</svg>`; } } const F = Math.PI / 18, O = 12; class $ { constructor(t) { this.shapeCache = /* @__PURE__ */ new Map(), this.shapeData = /* @__PURE__ */ new Map(), this.fontData = t; } /** * Releases parsed shapes and cached shapes */ release() { this.shapeCache.clear(), this.shapeData.clear(); } /** * Parses a character's shape * @param code - The character code * @param size - The font size * @returns The parsed shape or undefined if the character is not found */ parse(t, e) { var o; const i = `${t}_${e}`; if (this.shapeCache.has(i)) return this.shapeCache.get(i); if (t === 0) return; const n = this.fontData.content.data; let s; if (!this.shapeData.has(t) && n[t]) { const r = n[t], c = O / this.fontData.content.baseUp; s = this.parseShape(r, c), this.shapeData.set(t, s); } if (this.shapeData.has(t)) { const r = e / O, c = this.shapeData.get(t); s = new S( (o = c.lastPoint) == null ? void 0 : o.clone().multiply(r), c.polylines.map((a) => a.map((h) => h.clone().multiply(r))) ); } return s; } /** * Parses the shape of a character. * @param data - The data of the character * @param scale - The scale of the font * @returns The parsed shape */ parseShape(t, e) { const c = { currentPoint: new l(), polylines: [], currentPolyline: [], sp: [], isPenDown: !1, scale: e }; for (let a = 0; a < t.length; a++) { const h = t[a]; h <= 15 ? a = this.handleSpecialCommand(h, t, a, c) : this.handleVectorCommand(h, c); } return new S(c.currentPoint, c.polylines); } /** * Please refer to special codes reference in the following link for more information. * https://help.autodesk.com/view/OARX/2023/ENU/?guid=GUID-06832147-16BE-4A66-A6D0-3ADF98DC8228 * @param command - The command byte * @param data - The data of the character * @param index - The index of the command byte * @param state - The state of the parser * @returns The index of the next command byte */ handleSpecialCommand(t, e, i, n) { let s = i; switch (t) { case 0: n.currentPolyline = [], n.isPenDown = !1; break; case 1: n.isPenDown = !0, n.currentPolyline.push(n.currentPoint.clone()); break; case 2: n.isPenDown = !1, n.currentPolyline.length > 1 && n.polylines.push(n.currentPolyline.slice()), n.currentPolyline = []; break; case 3: s++, n.scale /= e[s]; break; case 4: s++, n.scale *= e[s]; break; case 5: if (n.sp.length === 4) throw new Error("The position stack is only four locations deep"); n.sp.push(n.currentPoint.clone()); break; case 6: n.currentPoint = n.sp.pop() ?? n.currentPoint; break; case 7: s = this.handleSubshapeCommand(e, s, n); break; case 8: s = this.handleXYDisplacement(e, s, n); break; case 9: s = this.handleMultipleXYDisplacements(e, s, n); break; case 10: s = this.handleOctantArc(e, s, n); break; case 11: s = this.handleFractionalArc(e, s, n); break; case 12: s = this.handleBulgeArc(e, s, n); break; case 13: s = this.handleMultipleBulgeArcs(e, s, n); break; case 14: s = this.skipCode(e, ++s); break; } return s; } handleVectorCommand(t, e) { const i = (t & 240) >> 4, n = t & 15, s = this.getVectorForDirection(n); e.currentPoint.add(s.multiply(i * e.scale)), e.isPenDown && e.currentPolyline.push(e.currentPoint.clone()); } /** * Get the vector for the given direction code. Please refer to the following link for more information. * https://help.autodesk.com/view/OARX/2023/ENU/?guid=GUID-0A8E12A1-F4AB-44AD-8A9B-2140E0D5FD23 * @param dir - The direction code of the vector * @returns Returns the vector for the given direction code */ getVectorForDirection(t) { const e = new l(); switch (t) { case 0: e.x = 1; break; case 1: e.x = 1, e.y = 0.5; break; case 2: e.x = 1, e.y = 1; break; case 3: e.x = 0.5, e.y = 1; break; case 4: e.y = 1; break; case 5: e.x = -0.5, e.y = 1; break; case 6: e.x = -1, e.y = 1; break; case 7: e.x = -1, e.y = 0.5; break; case 8: e.x = -1; break; case 9: e.x = -1, e.y = -0.5; break; case 10: e.x = -1, e.y = -1; break; case 11: e.x = -0.5, e.y = -1; break; case 12: e.y = -1; break; case 13: e.x = 0.5, e.y = -1; break; case 14: e.x = 1, e.y = -1; break; case 15: e.x = 1, e.y = -0.5; break; } return e; } handleSubshapeCommand(t, e, i) { let n = e, s = 0, o, r = i.scale * this.fontData.content.baseUp, c = r; const a = i.currentPoint.clone(); switch (i.currentPolyline.length > 1 && (i.polylines.push(i.currentPolyline.slice()), i.currentPolyline = []), this.fontData.header.fontType) { case y.SHAPES: n++, s = t[n]; break; case y.BIGFONT: n++, s = t[n], s === 0 && (n++, s = t[n++] | t[n++] << 8, a.x = t[n++] * i.scale, a.y = t[n++] * i.scale, this.fontData.content.isExtended && (c = t[n++] * i.scale), r = t[n] * i.scale); break; case y.UNIFONT: n++, s = t[n++] | t[n++] << 8; break; } return s !== 0 && (o = this.getShapeByCodeWithOffset(s, c, r, a), o && (i.polylines.push(...o.polylines.slice()), i.currentPoint = o.lastPoint ? o.lastPoint.clone() : a.clone())), i.currentPolyline = [], n; } handleXYDisplacement(t, e, i) { let n = e; const s = new l(); return s.x = f.byteToSByte(t[++n]), s.y = f.byteToSByte(t[++n]), i.currentPoint.add(s.multiply(i.scale)), i.isPenDown && i.currentPolyline.push(i.currentPoint.clone()), n; } handleMultipleXYDisplacements(t, e, i) { let n = e; for (; ; ) { const s = new l(); if (s.x = f.byteToSByte(t[++n]), s.y = f.byteToSByte(t[++n]), s.x === 0 && s.y === 0) break; i.currentPoint.add(s.multiply(i.scale)), i.isPenDown && i.currentPolyline.push(i.currentPoint.clone()); } return n; } handleOctantArc(t, e, i) { var g; let n = e; const s = t[++n] * i.scale, o = f.byteToSByte(t[++n]), r = (o & 112) >> 4; let c = o & 7; const a = o < 0, h = Math.PI / 4 * r, b = i.currentPoint.clone().subtract(new l(Math.cos(h) * s, Math.sin(h) * s)), p = C.fromOctant(b, s, r, c, a); if (i.isPenDown) { const k = p.tessellate(); i.currentPolyline.pop(), i.currentPolyline.push(...k.slice()); } return i.currentPoint = (g = p.tessellate().pop()) == null ? void 0 : g.clone(), n; } handleFractionalArc(t, e, i) { let n = e; const s = t[++n], o = t[++n], r = t[++n], c = t[++n], a = (r * 255 + c) * i.scale, h = f.byteToSByte(t[++n]), b = (h & 112) >> 4; let p = h & 7; p === 0 && (p = 8), o !== 0 && p--; const g = Math.PI / 4; let k = g * p, w = F, M = 1; h < 0 && (w = -w, k = -k, M = -1); let x = g * b, P = x + k; x += g * s / 256 * M, P += g * o / 256 * M; const m = i.currentPoint.clone().subtract(new l(a * Math.cos(x), a * Math.sin(x))); if (i.currentPoint = m.clone().add(new l(a * Math.cos(P), a * Math.sin(P))), i.isPenDown) { let u = x; const D = []; if (D.push( m.clone().add(new l(a * Math.cos(u), a * Math.sin(u))) ), w > 0) for (; u + w < P; ) u += w, D.push( m.clone().add(new l(a * Math.cos(u), a * Math.sin(u))) ); else for (; u + w > P; ) u += w, D.push( m.clone().add(new l(a * Math.cos(u), a * Math.sin(u))) ); D.push(m.clone().add(new l(a * Math.cos(P), a * Math.sin(P)))), i.currentPolyline.push(...D); } return n; } handleBulgeArc(t, e, i) { let n = e; const s = new l(); s.x = f.byteToSByte(t[++n]), s.y = f.byteToSByte(t[++n]); const o = f.byteToSByte(t[++n]); return i.currentPoint = this.handleArcSegment( i.currentPoint, s, o, i.scale, i.isPenDown, i.currentPolyline ), n; } handleMultipleBulgeArcs(t, e, i) { let n = e; for (; ; ) { const s = new l(); if (s.x = f.byteToSByte(t[++n]), s.y = f.byteToSByte(t[++n]), s.x === 0 && s.y === 0) break; const o = f.byteToSByte(t[++n]); i.currentPoint = this.handleArcSegment( i.currentPoint, s, o, i.scale, i.isPenDown, i.currentPolyline ); } return n; } skipCode(t, e) { switch (t[e]) { case 0: break; case 1: break; case 2: break; case 3: case 4: e++; break; case 5: break; case 6: break; case 7: switch (this.fontData.header.fontType) { case y.SHAPES: e++; break; case y.BIGFONT: e++, t[e] === 0 && (e += 5); break; case y.UNIFONT: e += 2; break; } break; case 8: e += 2; break; case 9: for (; ; ) { const s = t[++e], o = t[++e]; if (s === 0 && o === 0) break; } break; case 10: e += 2; break; case 11: e += 5; break; case 12: e += 3; break; case 13: for (; ; ) { const s = t[++e], o = t[++e]; if (s === 0 && o === 0) break; e++; } break; } return e; } getShapeByCodeWithOffset(t, e, i, n) { var o; const s = this.parse(t, i); if (s) { if (e === i) return s.offset(n); { const r = (o = s.lastPoint) == null ? void 0 : o.clone(); r && (r.x *= e / i); const c = s.polylines.map((a) => a.map((h) => h.clone())); return c.forEach((a) => a.forEach((h) => h.x *= e / i)), new S( r == null ? void 0 : r.add(n), c.map((a) => a.map((h) => h.add(n))) ); } } } /** * Handles drawing an arc segment with the given vector and bulge * @param currentPoint The starting point of the arc * @param vec The displacement vector * @param bulge The bulge value (will be normalized by 127.0) * @param scale The current scale factor * @param isPenDown Whether the pen is currently down (drawing) * @param currentPolyline The current polyline being built * @returns The new current point after the arc */ handleArcSegment(t, e, i, n, s, o) { e.x *= n, e.y *= n, i < -127 && (i = -127); const r = t.clone(); if (s) if (i === 0) o.push(r.clone().add(e)); else { const c = r.clone().add(e), h = C.fromBulge(r, c, i / 127).tessellate(); o.push(...h.slice(1)); } return r.add(e), r; } } class v { /** * Creates a new ShxFont instance. * @param data - Either raw binary data of the SHX font file (ArrayBuffer) or pre-parsed font data (ShxFontData) * @throws {Error} If the font data is invalid or cannot be parsed */ constructor(t) { if (t instanceof ArrayBuffer) { const e = new f(t), n = new E().parse(e), o = T.createParser(n.fontType).parse(e); this.fontData = { header: n, content: o }; } else this.fontData = t; this.shapeParser = new $(this.fontData); } /** * Gets the shape data for a specific character at a given size. * @param code - The character code to get the shape for * @param size - The desired size of the character in drawing units * @returns The shape data for the character, or undefined if the character is not found in the font */ getCharShape(t, e) { return this.shapeParser.parse(t, e); } /** * Releases resources used by the font. * This should be called when the font is no longer needed to free up memory. */ release() { this.shapeParser.release(); } } export { l as Point, v as ShxFont, y as ShxFontType, S as ShxShape, $ as ShxShapeParser }; //# sourceMappingURL=index.es.js.map