@knowvah/dot-engine 2.6.7 → 2.6.9

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.
Files changed (45) hide show
  1. package/dist/common/htmltable-attr-warn.d.ts +10 -4
  2. package/dist/common/htmltable-attr-warn.d.ts.map +1 -1
  3. package/dist/common/htmltable-label.d.ts +37 -0
  4. package/dist/common/htmltable-label.d.ts.map +1 -0
  5. package/dist/common/htmltable-lex-attvalue.d.ts +59 -0
  6. package/dist/common/htmltable-lex-attvalue.d.ts.map +1 -0
  7. package/dist/common/htmltable-lex-expat.d.ts +15 -4
  8. package/dist/common/htmltable-lex-expat.d.ts.map +1 -1
  9. package/dist/common/htmltable-lex.d.ts.map +1 -1
  10. package/dist/common/htmltable-parse-attrs.d.ts +3 -1
  11. package/dist/common/htmltable-parse-attrs.d.ts.map +1 -1
  12. package/dist/common/htmltable-parse.d.ts +6 -5
  13. package/dist/common/htmltable-parse.d.ts.map +1 -1
  14. package/dist/common/htmltable-pos.d.ts +3 -26
  15. package/dist/common/htmltable-pos.d.ts.map +1 -1
  16. package/dist/common/htmltable-types.d.ts +3 -1
  17. package/dist/common/htmltable-types.d.ts.map +1 -1
  18. package/dist/common/htmltable.d.ts +3 -2
  19. package/dist/common/htmltable.d.ts.map +1 -1
  20. package/dist/common/make-label.d.ts.map +1 -1
  21. package/dist/common/subst.d.ts +8 -0
  22. package/dist/common/subst.d.ts.map +1 -1
  23. package/dist/gvc/anchor.d.ts.map +1 -1
  24. package/dist/index.js +308 -105
  25. package/dist/index.js.map +4 -4
  26. package/dist/render/xml-escape.d.ts +8 -6
  27. package/dist/render/xml-escape.d.ts.map +1 -1
  28. package/dist/render.js +308 -105
  29. package/dist/render.js.map +4 -4
  30. package/package.json +1 -1
  31. package/src/common/htmltable-attr-warn.ts +24 -7
  32. package/src/common/htmltable-label.ts +109 -0
  33. package/src/common/htmltable-lex-attvalue.ts +141 -0
  34. package/src/common/htmltable-lex-expat.ts +59 -27
  35. package/src/common/htmltable-lex.ts +9 -10
  36. package/src/common/htmltable-parse-attrs.ts +10 -13
  37. package/src/common/htmltable-parse.ts +16 -29
  38. package/src/common/htmltable-pos.ts +5 -88
  39. package/src/common/htmltable-types.ts +4 -1
  40. package/src/common/htmltable.ts +16 -16
  41. package/src/common/make-label.ts +8 -3
  42. package/src/common/poly-gencode.ts +2 -2
  43. package/src/common/subst.ts +12 -0
  44. package/src/gvc/anchor.ts +6 -5
  45. package/src/render/xml-escape.ts +23 -33
package/dist/index.js CHANGED
@@ -10358,9 +10358,11 @@ var StderrLines = class {
10358
10358
  writeLine;
10359
10359
  pending = "";
10360
10360
  write(text) {
10361
- const parts = (this.pending + text).split("\n");
10362
- this.pending = parts.pop() ?? "";
10363
- for (const line of parts) this.writeLine(line);
10361
+ this.pending += text;
10362
+ if (!this.pending.endsWith("\n")) return;
10363
+ const line = this.pending.slice(0, -1);
10364
+ this.pending = "";
10365
+ this.writeLine(line);
10364
10366
  }
10365
10367
  flush() {
10366
10368
  if (this.pending === "") return;
@@ -10368,6 +10370,16 @@ var StderrLines = class {
10368
10370
  this.pending = "";
10369
10371
  }
10370
10372
  };
10373
+ var consoleLine = (line) => {
10374
+ if (line.startsWith("Warning")) console.warn(line);
10375
+ else console.error(line);
10376
+ };
10377
+ var labelStderr = () => new StderrLines(consoleLine);
10378
+ var agerrLine = (out, msg) => {
10379
+ if (out === void 0) consoleLine(msg);
10380
+ else out.write(`${msg}
10381
+ `);
10382
+ };
10371
10383
 
10372
10384
  // src/common/htmltable-parse-state.ts
10373
10385
  var peek = (s) => s.tokens[s.pos];
@@ -10672,11 +10684,87 @@ function htmlEntityUTF8(s) {
10672
10684
  });
10673
10685
  }
10674
10686
 
10687
+ // src/common/htmltable-lex-attvalue.ts
10688
+ var XML_ERROR_UNDEFINED_ENTITY = "undefined entity";
10689
+ var XML_ERROR_BAD_CHAR_REF = "reference to invalid character number";
10690
+ var NAME_START = /[A-Za-z_:\u0080-￿]/;
10691
+ var NAME_CHAR = /[\w.:\u0080-￿-]/;
10692
+ var DEC_DIGIT = /[0-9]/;
10693
+ var HEX_DIGIT = /[0-9A-Fa-f]/;
10694
+ var NON_XML = /[\x00-\x08\x0b\x0c\x0e-\x1f]/;
10695
+ function scanDigits(text, i, digit) {
10696
+ if (i >= text.length) return { partial: true };
10697
+ if (!digit.test(text[i])) return { invalid: i };
10698
+ for (let k = i + 1; k < text.length; k++) {
10699
+ if (text[k] === ";") return k + 1;
10700
+ if (!digit.test(text[k])) return { invalid: k };
10701
+ }
10702
+ return { partial: true };
10703
+ }
10704
+ function scanRef(text, i) {
10705
+ const c = text[i];
10706
+ if (c === void 0) return { partial: true };
10707
+ if (c === "#") return text[i + 1] === "x" ? scanDigits(text, i + 2, HEX_DIGIT) : scanDigits(text, i + 1, DEC_DIGIT);
10708
+ if (!NAME_START.test(c)) return { invalid: i };
10709
+ for (let k = i + 1; k < text.length; k++) {
10710
+ if (text[k] === ";") return k + 1;
10711
+ if (!NAME_CHAR.test(text[k])) return { invalid: k };
10712
+ }
10713
+ return { partial: true };
10714
+ }
10715
+ function scanAttValue(text, j) {
10716
+ const q = text[j];
10717
+ let k = j + 1;
10718
+ for (; ; ) {
10719
+ const c = text[k];
10720
+ if (c === void 0) return { partial: true };
10721
+ if (c === q) return { close: k };
10722
+ if (c === "<" || NON_XML.test(c)) return { invalid: k };
10723
+ if (c !== "&") {
10724
+ k++;
10725
+ continue;
10726
+ }
10727
+ const r = scanRef(text, k + 1);
10728
+ if (typeof r !== "number") return r;
10729
+ k = r;
10730
+ }
10731
+ }
10732
+ function charRefNumber(ref) {
10733
+ const hex = ref.startsWith("&#x");
10734
+ const n = parseInt(ref.slice(hex ? 3 : 2, -1), hex ? 16 : 10);
10735
+ if (n >= 1114112) return -1;
10736
+ if (n >= 55296 && n <= 57343) return -1;
10737
+ if (n < 32 && NON_XML.test(String.fromCharCode(n))) return -1;
10738
+ return n === 65534 || n === 65535 ? -1 : n;
10739
+ }
10740
+ var PREDEFINED = { lt: "<", gt: ">", amp: "&", quot: '"', apos: "'" };
10741
+ var VALUE_PIECE = /&#x?[0-9A-Fa-f]+;|&[^;]+;|\r\n|[\t\n\r]/g;
10742
+ function attValueError(raw, base) {
10743
+ for (const m of raw.matchAll(VALUE_PIECE)) {
10744
+ const ref = m[0];
10745
+ if (!ref.startsWith("&")) continue;
10746
+ if (ref.startsWith("&#")) {
10747
+ if (charRefNumber(ref) < 0) return { message: XML_ERROR_BAD_CHAR_REF, offset: base + m.index };
10748
+ } else if (!Object.hasOwn(PREDEFINED, ref.slice(1, -1))) {
10749
+ return { message: XML_ERROR_UNDEFINED_ENTITY };
10750
+ }
10751
+ }
10752
+ return void 0;
10753
+ }
10754
+ function decodeAttValue(raw) {
10755
+ return raw.replace(VALUE_PIECE, (m) => {
10756
+ if (!m.startsWith("&")) return " ";
10757
+ if (m.startsWith("&#")) return String.fromCodePoint(charRefNumber(m));
10758
+ return PREDEFINED[m.slice(1, -1)];
10759
+ });
10760
+ }
10761
+
10675
10762
  // src/common/htmltable-lex-expat.ts
10676
10763
  var XML_ERROR_INVALID_TOKEN = "not well-formed (invalid token)";
10677
10764
  var XML_CONTEXT_BYTES = 1024;
10678
10765
  var INIT_BUFFER_SIZE = 1024;
10679
- var COMPLETE_END = (end, dup) => dup === void 0 ? { kind: "complete", end } : { kind: "complete", end, dup };
10766
+ var XML_ERROR_DUPLICATE_ATTRIBUTE = "duplicate attribute";
10767
+ var COMPLETE_END = (end, attError) => attError === void 0 ? { kind: "complete", end } : { kind: "complete", end, attError };
10680
10768
  var PARTIAL = { kind: "partial" };
10681
10769
  var INVALID = (offset) => ({ kind: "invalid", offset });
10682
10770
  var XML_NAME = /^[A-Za-z_:\u0080-\uFFFF][\w.:\u0080-\uFFFF-]*/;
@@ -10699,14 +10787,13 @@ function fedBytes(unit, isText) {
10699
10787
  function scanValue(text, j) {
10700
10788
  const q = text[j];
10701
10789
  if (q !== '"' && q !== "'") return INVALID(j);
10702
- const close = text.indexOf(q, j + 1);
10703
- if (close < 0) {
10704
- const lt = text.indexOf("<", j + 1);
10705
- return lt >= 0 ? INVALID(lt) : PARTIAL;
10706
- }
10790
+ const v = scanAttValue(text, j);
10791
+ if ("partial" in v) return PARTIAL;
10792
+ if ("invalid" in v) return INVALID(v.invalid);
10793
+ const close = v.close;
10707
10794
  const next = skipSpace(text, close + 1);
10708
10795
  if (next === close + 1 && !">/".includes(text[next] ?? ">")) return INVALID(next);
10709
- return next;
10796
+ return close;
10710
10797
  }
10711
10798
  function scanPair(text, i) {
10712
10799
  const name = XML_NAME.exec(text.slice(i))?.[0];
@@ -10716,8 +10803,9 @@ function scanPair(text, i) {
10716
10803
  if (text[eq] !== "=") return INVALID(eq);
10717
10804
  const j = skipSpace(text, eq + 1);
10718
10805
  if (j >= text.length) return PARTIAL;
10719
- const next = scanValue(text, j);
10720
- return typeof next === "number" ? { name, next } : next;
10806
+ const close = scanValue(text, j);
10807
+ if (typeof close !== "number") return close;
10808
+ return { name, next: skipSpace(text, close + 1), value: { start: j + 1, end: close } };
10721
10809
  }
10722
10810
  function tagEdge(text, i) {
10723
10811
  const c = text[i];
@@ -10727,18 +10815,23 @@ function tagEdge(text, i) {
10727
10815
  if (i + 1 >= text.length) return PARTIAL;
10728
10816
  return text[i + 1] === ">" ? COMPLETE_END(i + 2) : void 0;
10729
10817
  }
10730
- function scanAttributes(text, from) {
10818
+ function storeAttError(text, p, at, pair, seen) {
10819
+ if (seen.has(pair.name)) return { message: XML_ERROR_DUPLICATE_ATTRIBUTE, offset: at };
10820
+ seen.add(pair.name);
10821
+ const err = attValueError(text.slice(pair.value.start, pair.value.end), pair.value.start);
10822
+ return err === void 0 ? void 0 : { message: err.message, offset: err.offset ?? p };
10823
+ }
10824
+ function scanAttributes(text, p, from) {
10731
10825
  const seen = /* @__PURE__ */ new Set();
10732
- let dup;
10826
+ let attError;
10733
10827
  let i = skipSpace(text, from);
10734
10828
  for (; ; ) {
10735
10829
  const edge = tagEdge(text, i);
10736
- if (edge?.kind === "complete") return COMPLETE_END(edge.end, dup);
10830
+ if (edge?.kind === "complete") return COMPLETE_END(edge.end, attError);
10737
10831
  if (edge !== void 0) return edge;
10738
10832
  const pair = scanPair(text, i);
10739
10833
  if ("kind" in pair) return pair;
10740
- if (seen.has(pair.name)) dup ??= i;
10741
- seen.add(pair.name);
10834
+ attError ??= storeAttError(text, p, i, pair, seen);
10742
10835
  i = pair.next;
10743
10836
  }
10744
10837
  }
@@ -10753,7 +10846,7 @@ function scanStartTag(text, p) {
10753
10846
  const c = text[name.next];
10754
10847
  if (c === void 0) return PARTIAL;
10755
10848
  if (!isSpace(c) && c !== ">" && c !== "/") return INVALID(name.next);
10756
- return scanAttributes(text, name.next);
10849
+ return scanAttributes(text, p, name.next);
10757
10850
  }
10758
10851
  function scanEndTag(text, p) {
10759
10852
  const name = scanName(text, p + 2);
@@ -10830,7 +10923,7 @@ function scanContent(text, base, inCdata, dataOf) {
10830
10923
  if (scan.kind === "invalid") return { tokens, consumed: p, invalid: base + scan.offset, inCdata: false };
10831
10924
  if (scan.kind === "partial") return { tokens, consumed: p, inCdata: false };
10832
10925
  const tok = { kind, start: base + p, end: base + scan.end };
10833
- if (scan.dup !== void 0) tok.dup = base + scan.dup;
10926
+ if (scan.attError !== void 0) tok.attError = { ...scan.attError, offset: base + scan.attError.offset };
10834
10927
  tokens.push(tok);
10835
10928
  p = scan.end;
10836
10929
  }
@@ -10924,17 +11017,22 @@ var ExpatFeed = class {
10924
11017
  };
10925
11018
  var XML_PREDEFINED_ENTITIES = /* @__PURE__ */ new Set(["lt", "gt", "amp", "quot", "apos"]);
10926
11019
  var ENTITY_NAME = /^[A-Za-z_:][\w.:-]*$/;
10927
- function findUndefinedEntity(text) {
11020
+ var CHAR_REF = /^&#(?:x[0-9A-Fa-f]+|[0-9]+);/;
11021
+ function textRefError(text) {
10928
11022
  for (let t = text.indexOf("&"); t >= 0; t = text.indexOf("&", t + 1)) {
10929
- if (text[t + 1] === "#") continue;
11023
+ if (text[t + 1] === "#") {
11024
+ const ref = CHAR_REF.exec(text.slice(t))?.[0];
11025
+ if (ref !== void 0 && charRefNumber(ref) < 0) return { message: XML_ERROR_BAD_CHAR_REF, offset: t };
11026
+ continue;
11027
+ }
10930
11028
  const semi = text.indexOf(";", t + 1);
10931
11029
  if (semi < 0) continue;
10932
11030
  const key = text.slice(t + 1, semi);
10933
11031
  if (ENTITY_NAME.test(key) && !Object.hasOwn(HTML_ENTITIES, key) && !XML_PREDEFINED_ENTITIES.has(key)) {
10934
- return t;
11032
+ return { message: XML_ERROR_UNDEFINED_ENTITY, offset: t };
10935
11033
  }
10936
11034
  }
10937
- return -1;
11035
+ return void 0;
10938
11036
  }
10939
11037
  function parseTagChunk(chunk) {
10940
11038
  if (!chunk.endsWith(">")) return void 0;
@@ -11074,7 +11172,7 @@ function parseAttrPairs(raw) {
11074
11172
  if (q !== '"' && q !== "'") throw new HtmlParseError(name);
11075
11173
  const end = raw.indexOf(q, i + 1);
11076
11174
  if (end < 0) throw new HtmlParseError(name);
11077
- out.push([name, raw.slice(i + 1, end)]);
11175
+ out.push([name, decodeAttValue(raw.slice(i + 1, end))]);
11078
11176
  i = end + 1;
11079
11177
  }
11080
11178
  return out;
@@ -11116,9 +11214,7 @@ function parseOpenToken(inner) {
11116
11214
  checkTag(tag);
11117
11215
  return openToken(normalizeTag(tag), sp === -1 ? "" : inner.slice(sp + 1));
11118
11216
  }
11119
- var XML_ERROR_UNDEFINED_ENTITY = "undefined entity";
11120
11217
  var XML_ERROR_TAG_MISMATCH = "mismatched tag";
11121
- var XML_ERROR_DUPLICATE_ATTRIBUTE = "duplicate attribute";
11122
11218
  var SYNTAX_ERROR = "syntax error";
11123
11219
  var HtmlLexError = class extends HtmlParseError {
11124
11220
  diagnostics;
@@ -11161,7 +11257,8 @@ function checkTagToken(site) {
11161
11257
  throw htmlErrorInLine(XML_ERROR_TAG_MISMATCH, lineAt(src, tok.start), context);
11162
11258
  }
11163
11259
  } else {
11164
- if (tok.dup !== void 0) throw htmlErrorInLine(XML_ERROR_DUPLICATE_ATTRIBUTE, lineAt(src, tok.dup), context);
11260
+ const err = tok.attError;
11261
+ if (err !== void 0) throw htmlErrorInLine(err.message, lineAt(src, err.offset), context);
11165
11262
  if (!tag.selfClose) st.stack.push(tag.name);
11166
11263
  }
11167
11264
  if (KNOWN_TAGS.has(tag.name.toUpperCase())) return;
@@ -11177,10 +11274,8 @@ function eventTokens(site) {
11177
11274
  checkTagToken(site);
11178
11275
  return tagTokens(text);
11179
11276
  }
11180
- const off = findUndefinedEntity(text);
11181
- if (off >= 0) {
11182
- throw htmlErrorInLine(XML_ERROR_UNDEFINED_ENTITY, lineAt(src, tok.start + off), st.prev + st.curr);
11183
- }
11277
+ const err = textRefError(text);
11278
+ if (err !== void 0) throw htmlErrorInLine(err.message, lineAt(src, tok.start + err.offset), st.prev + st.curr);
11184
11279
  return textTokens(text);
11185
11280
  }
11186
11281
  function attachCarry(carry, first) {
@@ -11290,9 +11385,6 @@ function tokenizeDeferred(src, label = { start: 0, end: src.length }) {
11290
11385
  }
11291
11386
 
11292
11387
  // src/common/htmltable-parse-attrs.ts
11293
- function anchorAttr(v) {
11294
- return v === void 0 ? void 0 : htmlEntityUTF8(v);
11295
- }
11296
11388
  var parseAlign = (v) => {
11297
11389
  const u = v.toUpperCase();
11298
11390
  if (u === "LEFT") return "left";
@@ -11300,6 +11392,7 @@ var parseAlign = (v) => {
11300
11392
  if (u === "CENTER") return "center";
11301
11393
  return void 0;
11302
11394
  };
11395
+ var parseCellAlign = (v) => v.toUpperCase() === "TEXT" ? "text" : parseAlign(v);
11303
11396
  var parseVAlign = (v) => {
11304
11397
  const u = v.toUpperCase();
11305
11398
  if (u === "TOP") return "top";
@@ -11333,7 +11426,7 @@ var buildCell = (content, a) => ({
11333
11426
  kind: "cell",
11334
11427
  content,
11335
11428
  port: a["port"],
11336
- align: parseAlign(a["align"] !== void 0 ? a["align"] : ""),
11429
+ align: parseCellAlign(a["align"] !== void 0 ? a["align"] : ""),
11337
11430
  valign: parseVAlign(a["valign"] !== void 0 ? a["valign"] : ""),
11338
11431
  balign: parseAlign(a["balign"] !== void 0 ? a["balign"] : ""),
11339
11432
  bgcolor: a["bgcolor"],
@@ -11349,9 +11442,9 @@ var buildCell = (content, a) => ({
11349
11442
  fixedsize: parseFixedSize(a),
11350
11443
  colspan: num(a["colspan"]),
11351
11444
  rowspan: num(a["rowspan"]),
11352
- href: anchorAttr(a["href"]),
11445
+ href: a["href"],
11353
11446
  title: parseTitle(a),
11354
- target: anchorAttr(a["target"]),
11447
+ target: a["target"],
11355
11448
  id: a["id"]
11356
11449
  });
11357
11450
  var buildTable = (rows, a) => ({
@@ -11369,9 +11462,9 @@ var buildTable = (rows, a) => ({
11369
11462
  fixedsize: parseFixedSize(a),
11370
11463
  width: num(a["width"]),
11371
11464
  height: num(a["height"]),
11372
- href: anchorAttr(a["href"]),
11465
+ href: a["href"],
11373
11466
  title: parseTitle(a),
11374
- target: anchorAttr(a["target"]),
11467
+ target: a["target"],
11375
11468
  id: a["id"],
11376
11469
  port: a["port"],
11377
11470
  sides: parseSides(a["sides"]),
@@ -11543,11 +11636,11 @@ var rejectTableAfterTag = (s, start) => {
11543
11636
  throw parseError(s, nxt, MSG_SYNTAX_ERROR);
11544
11637
  }
11545
11638
  };
11546
- var parseImgContent = (s, t, cellAttrs) => {
11639
+ var parseImgContent = (s, t) => {
11547
11640
  consume(s);
11548
11641
  const img = {
11549
11642
  kind: "image",
11550
- src: cellAttrs["src"] ?? t.attrs["src"] ?? "",
11643
+ src: t.attrs["src"] ?? "",
11551
11644
  scale: t.attrs["scale"]
11552
11645
  };
11553
11646
  consumeClose(s, "IMG");
@@ -11575,12 +11668,12 @@ var scanAfterTable = (s, t, st) => {
11575
11668
  if (nonSpace(t.value)) st.afterNonSpace = true;
11576
11669
  return [];
11577
11670
  };
11578
- var scanCellImage = (s, t, cellAttrs, st) => {
11671
+ var scanCellImage = (s, t, st) => {
11579
11672
  if (st.kind !== "none") throw parseError(s, t, MSG_SYNTAX_ERROR);
11580
11673
  st.kind = "image";
11581
- return [parseImgContent(s, t, cellAttrs)];
11674
+ return [parseImgContent(s, t)];
11582
11675
  };
11583
- var scanCellItem = (s, t, cellAttrs, fontStack, st) => {
11676
+ var scanCellItem = (s, t, fontStack, st) => {
11584
11677
  if (st.kind === "fonttable") throw parseError(s, t, MSG_SYNTAX_ERROR);
11585
11678
  if (st.kind === "table") return scanAfterTable(s, t, st);
11586
11679
  const wrapper = st.kind === "none" ? fontTableWrapper(s) : void 0;
@@ -11589,7 +11682,7 @@ var scanCellItem = (s, t, cellAttrs, fontStack, st) => {
11589
11682
  return [parseFontTable(s, wrapper, fontStack)];
11590
11683
  }
11591
11684
  if (t.type === "open" && t.tag === "TABLE") return scanCellTable(s, t, fontStack, st);
11592
- if (t.type === "open" && t.tag === "IMG") return scanCellImage(s, t, cellAttrs, st);
11685
+ if (t.type === "open" && t.tag === "IMG") return scanCellImage(s, t, st);
11593
11686
  if (st.kind === "image") throw parseError(s, t, MSG_SYNTAX_ERROR);
11594
11687
  return scanCellText(s, t, fontStack, st);
11595
11688
  };
@@ -11599,12 +11692,12 @@ var endsCell = (s, t, st) => {
11599
11692
  if (t.tag === "TD" && !afterText) return true;
11600
11693
  throw parseError(s, t, afterText ? MSG_TEXT_AFTER_TABLE : MSG_SYNTAX_ERROR);
11601
11694
  };
11602
- var parseCellContent = (s, cellAttrs, fontStack) => {
11695
+ var parseCellContent = (s, fontStack) => {
11603
11696
  const content = [];
11604
11697
  const st = { kind: "none", textNonSpace: false, afterNonSpace: false };
11605
11698
  for (let t = peek(s); t !== void 0; t = peek(s)) {
11606
11699
  if (endsCell(s, t, st)) break;
11607
- content.push(...scanCellItem(s, t, cellAttrs, fontStack, st));
11700
+ content.push(...scanCellItem(s, t, fontStack, st));
11608
11701
  }
11609
11702
  const nxt = peek(s);
11610
11703
  if (nxt?.type === "close" && nxt.tag === "TD") consume(s);
@@ -11612,7 +11705,7 @@ var parseCellContent = (s, cellAttrs, fontStack) => {
11612
11705
  if (tbl !== void 0) return [tbl];
11613
11706
  return content;
11614
11707
  };
11615
- var parseCell = (s, attrs, fontStack) => buildCell(parseCellContent(s, attrs, fontStack), attrs);
11708
+ var parseCell = (s, attrs, fontStack) => buildCell(parseCellContent(s, fontStack), attrs);
11616
11709
  var parseRowVr = (s, t, cells) => {
11617
11710
  const prev = cells[cells.length - 1];
11618
11711
  if (prev === void 0) throw parseError(s, t, MSG_SYNTAX_ERROR);
@@ -11773,17 +11866,9 @@ var parseTokens = (s) => {
11773
11866
  if (firstReal?.type === "open" && firstReal.tag === "TABLE") return parseTableLabel(s, fontStack);
11774
11867
  return parseTextLabel(s, fontStack);
11775
11868
  };
11776
- var parseHtmlLabelRv = (src, writeLine = (line) => console.warn(line)) => {
11869
+ var parseHtmlLabelRv = (src, stderr) => {
11777
11870
  const { tokens, error, eof } = tokenizeDeferred(`<HTML>${src}</HTML>`, { start: 6, end: 6 + src.length });
11778
- const s = {
11779
- tokens,
11780
- pos: 0,
11781
- pending: error,
11782
- stderr: new StderrLines(writeLine),
11783
- emitted: /* @__PURE__ */ new WeakSet(),
11784
- warn: false,
11785
- eof
11786
- };
11871
+ const s = { tokens, pos: 0, pending: error, stderr, emitted: /* @__PURE__ */ new WeakSet(), warn: false, eof };
11787
11872
  try {
11788
11873
  const label = parseTokens(s);
11789
11874
  if (error !== void 0) throw error;
@@ -11794,11 +11879,9 @@ var parseHtmlLabelRv = (src, writeLine = (line) => console.warn(line)) => {
11794
11879
  throw error;
11795
11880
  }
11796
11881
  throw e;
11797
- } finally {
11798
- s.stderr?.flush();
11799
11882
  }
11800
11883
  };
11801
- var parseHtmlLabel = (src) => parseHtmlLabelRv(src, () => void 0).label;
11884
+ var parseHtmlLabel = (src) => parseHtmlLabelRv(src, new StderrLines(() => void 0)).label;
11802
11885
 
11803
11886
  // src/gvc/usershape.ts
11804
11887
  var activeSizer = null;
@@ -12000,7 +12083,7 @@ var sizeTextContent = (texts, measurer, env = defaultSizeEnv()) => {
12000
12083
  return { w, h };
12001
12084
  };
12002
12085
  var missingImages = /* @__PURE__ */ new WeakSet();
12003
- var sizeHtmlImg = (img, imageSizer) => {
12086
+ var sizeHtmlImg = (img, { imageSizer, stderr }) => {
12004
12087
  if (img.width !== void 0 && img.height !== void 0) return missingImages.has(img) ? 1 : 0;
12005
12088
  const sz = imageSizer !== void 0 ? imageSizer(img.src) : findImageSize(img.src);
12006
12089
  if (sz !== null) {
@@ -12008,7 +12091,7 @@ var sizeHtmlImg = (img, imageSizer) => {
12008
12091
  img.height = sz.h;
12009
12092
  return 0;
12010
12093
  }
12011
- console.error(`Error: No or improper image file="${img.src}"`);
12094
+ agerrLine(stderr, `Error: No or improper image file="${img.src}"`);
12012
12095
  img.width = 0;
12013
12096
  img.height = 0;
12014
12097
  missingImages.add(img);
@@ -12021,7 +12104,7 @@ var sizeContentItem = (item, measurer, env) => {
12021
12104
  return { ...item.dimen !== void 0 ? item.dimen : { w: 0, h: 0 }, rv };
12022
12105
  }
12023
12106
  if (item.kind === "image") {
12024
- const rv = sizeHtmlImg(item, env.imageSizer);
12107
+ const rv = sizeHtmlImg(item, env);
12025
12108
  return { w: item.width ?? 0, h: item.height ?? 0, rv };
12026
12109
  }
12027
12110
  return { w: 0, h: 0, rv: 0 };
@@ -12051,14 +12134,14 @@ var WARN_CELL_UNSPECIFIED = "Warning: fixed cell size with unspecified width or
12051
12134
  var WARN_TABLE_TOO_SMALL = "Warning: table size too small for content";
12052
12135
  var WARN_TABLE_UNSPECIFIED = "Warning: fixed table size with unspecified width or height";
12053
12136
  var sizeWarned = /* @__PURE__ */ new WeakSet();
12054
- var fixedCellCheck = (cell2, sz, warn) => {
12137
+ var fixedCellCheck = (cell2, sz, warn, out) => {
12055
12138
  if (!cell2.fixedsize) return { rv: 0, zero: false };
12056
12139
  if (cell2.width && cell2.height) {
12057
12140
  const small = (cell2.width < sz.w || cell2.height < sz.h) && cell2.content[0]?.kind !== "image";
12058
- if (small && warn) console.warn(WARN_CELL_TOO_SMALL);
12141
+ if (small && warn) agerrLine(out, WARN_CELL_TOO_SMALL);
12059
12142
  return { rv: small ? 1 : 0, zero: true };
12060
12143
  }
12061
- if (warn) console.warn(WARN_CELL_UNSPECIFIED);
12144
+ if (warn) agerrLine(out, WARN_CELL_UNSPECIFIED);
12062
12145
  return { rv: 1, zero: false };
12063
12146
  };
12064
12147
  var getCellSize = (cell2, tbl, measurer, env) => {
@@ -12074,7 +12157,7 @@ var getCellSize = (cell2, tbl, measurer, env) => {
12074
12157
  let szy = ch + margin;
12075
12158
  const warn = !sizeWarned.has(cell2);
12076
12159
  sizeWarned.add(cell2);
12077
- const fixed = fixedCellCheck(cell2, { w: szx, h: szy }, warn);
12160
+ const fixed = fixedCellCheck(cell2, { w: szx, h: szy }, warn, env.stderr);
12078
12161
  rv |= fixed.rv;
12079
12162
  if (fixed.zero) {
12080
12163
  szx = 0;
@@ -12168,22 +12251,22 @@ var layoutHtmlTable = (tbl, measurer, env = defaultSizeEnv()) => {
12168
12251
  const heights = setRowHeights(entries, nrows, spacing);
12169
12252
  return { entries, widths, heights, spacing, border: border2, rv };
12170
12253
  };
12171
- var warnFixedTable = (tbl, dims) => {
12254
+ var warnFixedTable = (tbl, dims, out) => {
12172
12255
  const warn = !sizeWarned.has(tbl);
12173
12256
  sizeWarned.add(tbl);
12174
12257
  if (tbl.fixedsize !== true) return 0;
12175
12258
  if (tbl.width && tbl.height) {
12176
12259
  if (tbl.width >= dims.wd && tbl.height >= dims.ht) return 0;
12177
- if (warn) console.warn(WARN_TABLE_TOO_SMALL);
12260
+ if (warn) agerrLine(out, WARN_TABLE_TOO_SMALL);
12178
12261
  return 1;
12179
12262
  }
12180
- if (warn) console.warn(WARN_TABLE_UNSPECIFIED);
12263
+ if (warn) agerrLine(out, WARN_TABLE_UNSPECIFIED);
12181
12264
  return 1;
12182
12265
  };
12183
12266
  var sizeTableBody = (tbl, measurer, env) => {
12184
12267
  const { entries, widths, heights, spacing, border: border2, rv } = layoutHtmlTable(tbl, measurer, env);
12185
12268
  if (entries.length === 0) {
12186
- const fixed2 = warnFixedTable(tbl, sumTableDims({ widths, heights, ncols: 0, nrows: 0, spacing, border: border2 }));
12269
+ const fixed2 = warnFixedTable(tbl, sumTableDims({ widths, heights, ncols: 0, nrows: 0, spacing, border: border2 }), env.stderr);
12187
12270
  tbl.dimen = { w: 0, h: 0 };
12188
12271
  return rv | fixed2;
12189
12272
  }
@@ -12193,7 +12276,7 @@ var sizeTableBody = (tbl, measurer, env) => {
12193
12276
  const { wd, ht } = sumTableDims({ widths, heights, ncols, nrows, spacing, border: border2 });
12194
12277
  const baseWd = tbl.width !== void 0 ? tbl.width : 0;
12195
12278
  const baseHt = tbl.height !== void 0 ? tbl.height : 0;
12196
- const fixed = warnFixedTable(tbl, { wd, ht });
12279
+ const fixed = warnFixedTable(tbl, { wd, ht }, env.stderr);
12197
12280
  const zeroContent = isTblFixed(tbl) && baseWd !== 0 && baseHt !== 0;
12198
12281
  tbl.dimen = {
12199
12282
  w: Math.max(zeroContent ? 0 : wd, baseWd),
@@ -17525,7 +17608,7 @@ function placeComplexRuns(runs, box, finfo, measurer) {
17525
17608
  function alignContentBox(box, cell2, w, h) {
17526
17609
  const out = { ll: { ...box.ll }, ur: { ...box.ur } };
17527
17610
  const delx = out.ur.x - out.ll.x - w;
17528
- if (delx > 0) {
17611
+ if (delx > 0 && cell2.align !== "text") {
17529
17612
  if (cell2.align === "left") out.ur.x -= delx;
17530
17613
  else if (cell2.align === "right") out.ll.x += delx;
17531
17614
  else {
@@ -17780,21 +17863,25 @@ function posHtmlLabel(label, finfo, measurer) {
17780
17863
  if (label.kind === "table") return withTableFont(env, label.table.font, "emit", () => posHtmlTable(label.table, env, measurer));
17781
17864
  return posTextLabel(label, finfo, measurer);
17782
17865
  }
17866
+
17867
+ // src/common/htmltable-label.ts
17783
17868
  var HTML_PARSE_FATAL = 3;
17784
- function makeHtmlLabelRv(content, finfo, measurer) {
17869
+ function makeHtmlLabelTo(content, finfo, measurer, stderr) {
17785
17870
  const { fontname, fontsize, fontcolor, imageSizer, pencolor } = finfo;
17786
17871
  let lbl, lexWarn;
17787
17872
  try {
17788
- ({ label: lbl, warn: lexWarn } = parseHtmlLabelRv(content));
17873
+ ({ label: lbl, warn: lexWarn } = parseHtmlLabelRv(content, stderr));
17789
17874
  } catch (e) {
17790
- if (e instanceof HtmlLexError) for (const line of e.diagnostics) console.error(line);
17875
+ if (e instanceof HtmlLexError) for (const line of e.diagnostics) stderr.write(`${line}
17876
+ `);
17791
17877
  return { label: makeLabel("", fontname, fontsize, fontcolor, measurer), rv: HTML_PARSE_FATAL };
17792
17878
  }
17793
17879
  if (lbl.kind === "table") inheritTablePenColor(lbl.table, pencolor);
17794
17880
  const rv = Number(lexWarn) | sizeHtmlLabel(lbl, measurer, {
17795
17881
  fontsize,
17796
17882
  fontname,
17797
- imageSizer
17883
+ imageSizer,
17884
+ stderr
17798
17885
  });
17799
17886
  const placed = posHtmlLabel(lbl, { fontname, fontsize, fontcolor }, measurer);
17800
17887
  const dim = lbl.dimen ?? { w: 0, h: 0 };
@@ -17902,6 +17989,9 @@ function interpretCRNL(str) {
17902
17989
  }
17903
17990
  return out;
17904
17991
  }
17992
+ function preprocessTooltip(s) {
17993
+ return interpretCRNL(htmlEntityUTF8(s));
17994
+ }
17905
17995
 
17906
17996
  // src/common/make-label.ts
17907
17997
  var DEFAULT_FONTSIZE2 = 14;
@@ -17993,8 +18083,11 @@ function makeLabel(text, fontname, fontsize, fontcolor, measurer) {
17993
18083
  function makeAnyLabel(content, isHtml, font, measurer, obj) {
17994
18084
  if (isHtml && content !== "") {
17995
18085
  const imageSizer = obj !== void 0 ? rootOf(obj).info.gvc?.imageSizer : void 0;
17996
- const { label, rv } = makeHtmlLabelRv(content, { ...font, pencolor: getPenColor(obj), imageSizer }, measurer);
17997
- if (rv !== 0 && obj !== void 0) console.error(inLabelOf(obj));
18086
+ const stderr = labelStderr();
18087
+ const { label, rv } = makeHtmlLabelTo(content, { ...font, pencolor: getPenColor(obj), imageSizer }, measurer, stderr);
18088
+ if (rv !== 0 && obj !== void 0) stderr.write(`${inLabelOf(obj)}
18089
+ `);
18090
+ stderr.flush();
17998
18091
  return label;
17999
18092
  }
18000
18093
  const subst2 = obj !== void 0 ? substObj(content, obj, false) : content;
@@ -24762,31 +24855,141 @@ function stripedBox(job, pts, clrs, rotate, renderer) {
24762
24855
  return error;
24763
24856
  }
24764
24857
 
24858
+ // src/util/xml.ts
24859
+ function gvXmlEscape(s, flags) {
24860
+ return XmlEscaper.escape(s, flags);
24861
+ }
24862
+ var XmlEscaper = class _XmlEscaper {
24863
+ static escape(s, flags) {
24864
+ let result = "";
24865
+ let previous = "";
24866
+ let pos = 0;
24867
+ while (pos < s.length) {
24868
+ const prev = s[pos];
24869
+ const [fragment, consumed] = _XmlEscaper.core(previous, s, pos, flags);
24870
+ result += fragment;
24871
+ previous = prev;
24872
+ pos += consumed;
24873
+ }
24874
+ return result;
24875
+ }
24876
+ /**
24877
+ * Escape one character/codepoint at s[pos].
24878
+ * Returns [escaped_fragment, characters_consumed].
24879
+ * Delegates to focused helpers to stay within CCN limit.
24880
+ * @see lib/util/xml.c:xml_core
24881
+ */
24882
+ static core(previous, s, pos, flags) {
24883
+ const special = _XmlEscaper.coreSpecial(previous, s, pos, flags);
24884
+ if (special !== null) return special;
24885
+ const code = s.charCodeAt(pos);
24886
+ if (code > 127 && flags.utf8) return _XmlEscaper.utf8Entity(s, pos);
24887
+ return [s[pos], 1];
24888
+ }
24889
+ /**
24890
+ * Handle all the fixed-escape cases (& < > - space " ' \n \r).
24891
+ * Returns null when none of those cases applies.
24892
+ * @see lib/util/xml.c:xml_core
24893
+ */
24894
+ static coreSpecial(previous, s, pos, flags) {
24895
+ const c = s[pos];
24896
+ if (c === "&" && (flags.raw || !_XmlEscaper.isEntity(s, pos))) {
24897
+ return ["&amp;", 1];
24898
+ }
24899
+ if (c === "<") return ["&lt;", 1];
24900
+ if (c === ">") return ["&gt;", 1];
24901
+ if (c === '"') return ["&quot;", 1];
24902
+ if (c === "'") return ["&#39;", 1];
24903
+ if (c === "-" && flags.dash) return ["&#45;", 1];
24904
+ if (c === " " && previous === " " && flags.nbsp) return ["&#160;", 1];
24905
+ if (c === "\n" && flags.raw) return ["&#10;", 1];
24906
+ if (c === "\r" && flags.raw) return ["&#13;", 1];
24907
+ return null;
24908
+ }
24909
+ /**
24910
+ * Return true if s[pos] (which is '&') starts a valid XML entity:
24911
+ * &[A-Za-z]+; e.g. &Ccedil;
24912
+ * &#[0-9]*; e.g. &#38;
24913
+ * &#x[0-9a-fA-F]*; e.g. &#x6C34;
24914
+ * @see lib/util/xml.c:xml_isentity
24915
+ */
24916
+ static isEntity(s, pos) {
24917
+ let i = pos + 1;
24918
+ if (i >= s.length || s[i] === ";") return false;
24919
+ i = s[i] === "#" ? _XmlEscaper.scanNumericRef(s, i) : _XmlEscaper.scanNamedRef(s, i);
24920
+ return i < s.length && s[i] === ";";
24921
+ }
24922
+ /** Scan a numeric entity body starting just after the '#'. */
24923
+ static scanNumericRef(s, hashPos) {
24924
+ let i = hashPos + 1;
24925
+ if (i < s.length && (s[i] === "x" || s[i] === "X")) {
24926
+ i++;
24927
+ while (i < s.length && _XmlEscaper.isXDigit(s[i])) i++;
24928
+ } else {
24929
+ while (i < s.length && _XmlEscaper.isDecDigit(s[i])) i++;
24930
+ }
24931
+ return i;
24932
+ }
24933
+ /** Scan a named entity body (alpha chars after the '&'). */
24934
+ static scanNamedRef(s, start) {
24935
+ let i = start;
24936
+ while (i < s.length && _XmlEscaper.isAlpha(s[i])) i++;
24937
+ return i;
24938
+ }
24939
+ /**
24940
+ * Emit &#xNNNN; for the Unicode code point at s[pos]. JS strings are
24941
+ * UTF-16, so the code point (what C decodes from UTF-8) comes from
24942
+ * codePointAt, and an astral character consumes two units.
24943
+ * @see lib/util/xml.c:xml_core (UTF-8 block)
24944
+ */
24945
+ static utf8Entity(s, pos) {
24946
+ const cp = _XmlEscaper.decodeUtf8(s, pos);
24947
+ const consumed = _XmlEscaper.utf8ByteLen(cp);
24948
+ return [`&#x${cp.toString(16)};`, consumed];
24949
+ }
24950
+ /**
24951
+ * Return the code point at s[pos]. A lone surrogate is the analogue of
24952
+ * malformed UTF-8, where C reports an error and exits (xml.c:135).
24953
+ * @see lib/util/xml.c:xml_core (fprintf + graphviz_exit at xml.c:135)
24954
+ */
24955
+ static decodeUtf8(s, pos) {
24956
+ const cp = s.codePointAt(pos);
24957
+ if (cp >= 55296 && cp <= 57343) {
24958
+ throw new RenderError(`gvXmlEscape: malformed UTF-8 at position ${pos}`, "RENDER_ERROR");
24959
+ }
24960
+ return cp;
24961
+ }
24962
+ /**
24963
+ * Return how many UTF-16 units encode the code point (C: the UTF-8 byte
24964
+ * length from the leading byte).
24965
+ * @see lib/util/xml.c:xml_core (length computation)
24966
+ */
24967
+ static utf8ByteLen(cp) {
24968
+ return cp > 65535 ? 2 : 1;
24969
+ }
24970
+ static isDecDigit(c) {
24971
+ return c >= "0" && c <= "9";
24972
+ }
24973
+ static isAlpha(c) {
24974
+ return c >= "a" && c <= "z" || c >= "A" && c <= "Z";
24975
+ }
24976
+ static isXDigit(c) {
24977
+ return _XmlEscaper.isDecDigit(c) || c >= "a" && c <= "f" || c >= "A" && c <= "F";
24978
+ }
24979
+ };
24980
+
24765
24981
  // src/render/xml-escape.ts
24766
- var RE_DQUOTE = new RegExp('"', "g");
24767
- var RE_SQUOTE = new RegExp("'", "g");
24768
- var RE_AMP_NONENTITY = /&(?![A-Za-z]+;|#[0-9]+;|#x[0-9A-Fa-f]+;)/g;
24982
+ var FLAGS_BASE = { raw: false, dash: false, nbsp: false, utf8: false };
24983
+ var FLAGS_TITLE = { raw: false, dash: true, nbsp: true, utf8: false };
24984
+ var FLAGS_TEXT = { raw: true, dash: true, nbsp: true, utf8: false };
24769
24985
  function escapeXml(s) {
24770
- let r = s.replace(RE_AMP_NONENTITY, "&amp;");
24771
- r = r.replace(/</g, "&lt;");
24772
- r = r.replace(/>/g, "&gt;");
24773
- r = r.replace(RE_DQUOTE, "&quot;");
24774
- r = r.replace(RE_SQUOTE, "&#39;");
24775
- return r;
24986
+ return gvXmlEscape(s, FLAGS_BASE);
24776
24987
  }
24777
24988
  function escapeXmlTitle(s) {
24778
- let r = escapeXml(s);
24779
- r = r.replace(/-/g, "&#45;");
24780
- r = r.replace(/ {2,}/g, (m) => " " + "&#160;".repeat(m.length - 1));
24781
- return r;
24989
+ return gvXmlEscape(s, FLAGS_TITLE);
24782
24990
  }
24783
24991
  function escapeXmlText(s) {
24784
- let r = escapeXml(s);
24785
- r = r.replace(/-/g, "&#45;");
24786
- r = r.replace(/\n/g, "&#10;");
24787
- r = r.replace(/\r/g, "&#13;");
24788
- r = r.replace(/ {2,}/g, (m) => " " + "&#160;".repeat(m.length - 1));
24789
- return r;
24992
+ return gvXmlEscape(s, FLAGS_TEXT);
24790
24993
  }
24791
24994
 
24792
24995
  // src/gvc/device-cluster.ts
@@ -24962,23 +25165,23 @@ function renderNodeLabel(n, coord, renderer, job) {
24962
25165
  renderLabel(label, coord, renderer, job);
24963
25166
  }
24964
25167
  }
24965
- function anchorAttr2(n, key) {
25168
+ function anchorAttr(n, key) {
24966
25169
  const v = nodeAttr(n, n.root, key);
24967
25170
  return v !== void 0 && v !== "" ? v : void 0;
24968
25171
  }
24969
25172
  function resolveTooltip(n, tooltip) {
24970
- if (tooltip !== void 0) return substObjAnchor(interpretCRNL(tooltip), n);
25173
+ if (tooltip !== void 0) return substObjAnchor(preprocessTooltip(tooltip), n);
24971
25174
  const label = n.info.label;
24972
25175
  return label?.text ?? "";
24973
25176
  }
24974
25177
  function beginNodeAnchor(n, renderer, job) {
24975
- const url = anchorAttr2(n, "href") ?? anchorAttr2(n, "URL");
24976
- const tooltip = anchorAttr2(n, "tooltip");
25178
+ const url = anchorAttr(n, "href") ?? anchorAttr(n, "URL");
25179
+ const tooltip = anchorAttr(n, "tooltip");
24977
25180
  if (url === void 0 && tooltip === void 0) return false;
24978
25181
  renderer.beginAnchor?.(
24979
25182
  url !== void 0 ? substObjAnchor(url, n) : "",
24980
25183
  resolveTooltip(n, tooltip),
24981
- anchorAttr2(n, "target") ?? "",
25184
+ anchorAttr(n, "target") ?? "",
24982
25185
  svgNodeId(n, job),
24983
25186
  job
24984
25187
  );
@@ -27484,7 +27687,7 @@ function subst(s, obj) {
27484
27687
  return substObjAnchor(s, obj);
27485
27688
  }
27486
27689
  function tip(s, obj) {
27487
- return substObjAnchor(interpretCRNL(s), obj);
27690
+ return substObjAnchor(preprocessTooltip(s), obj);
27488
27691
  }
27489
27692
  function textOf(lp) {
27490
27693
  return lp?.text ?? null;