@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/render.js CHANGED
@@ -5653,9 +5653,11 @@ var StderrLines = class {
5653
5653
  writeLine;
5654
5654
  pending = "";
5655
5655
  write(text) {
5656
- const parts = (this.pending + text).split("\n");
5657
- this.pending = parts.pop() ?? "";
5658
- for (const line of parts) this.writeLine(line);
5656
+ this.pending += text;
5657
+ if (!this.pending.endsWith("\n")) return;
5658
+ const line = this.pending.slice(0, -1);
5659
+ this.pending = "";
5660
+ this.writeLine(line);
5659
5661
  }
5660
5662
  flush() {
5661
5663
  if (this.pending === "") return;
@@ -5663,6 +5665,16 @@ var StderrLines = class {
5663
5665
  this.pending = "";
5664
5666
  }
5665
5667
  };
5668
+ var consoleLine = (line) => {
5669
+ if (line.startsWith("Warning")) console.warn(line);
5670
+ else console.error(line);
5671
+ };
5672
+ var labelStderr = () => new StderrLines(consoleLine);
5673
+ var agerrLine = (out, msg) => {
5674
+ if (out === void 0) consoleLine(msg);
5675
+ else out.write(`${msg}
5676
+ `);
5677
+ };
5666
5678
 
5667
5679
  // src/common/htmltable-parse-state.ts
5668
5680
  var peek = (s) => s.tokens[s.pos];
@@ -5967,11 +5979,87 @@ function htmlEntityUTF8(s) {
5967
5979
  });
5968
5980
  }
5969
5981
 
5982
+ // src/common/htmltable-lex-attvalue.ts
5983
+ var XML_ERROR_UNDEFINED_ENTITY = "undefined entity";
5984
+ var XML_ERROR_BAD_CHAR_REF = "reference to invalid character number";
5985
+ var NAME_START = /[A-Za-z_:\u0080-￿]/;
5986
+ var NAME_CHAR = /[\w.:\u0080-￿-]/;
5987
+ var DEC_DIGIT = /[0-9]/;
5988
+ var HEX_DIGIT = /[0-9A-Fa-f]/;
5989
+ var NON_XML = /[\x00-\x08\x0b\x0c\x0e-\x1f]/;
5990
+ function scanDigits(text, i, digit) {
5991
+ if (i >= text.length) return { partial: true };
5992
+ if (!digit.test(text[i])) return { invalid: i };
5993
+ for (let k = i + 1; k < text.length; k++) {
5994
+ if (text[k] === ";") return k + 1;
5995
+ if (!digit.test(text[k])) return { invalid: k };
5996
+ }
5997
+ return { partial: true };
5998
+ }
5999
+ function scanRef(text, i) {
6000
+ const c = text[i];
6001
+ if (c === void 0) return { partial: true };
6002
+ if (c === "#") return text[i + 1] === "x" ? scanDigits(text, i + 2, HEX_DIGIT) : scanDigits(text, i + 1, DEC_DIGIT);
6003
+ if (!NAME_START.test(c)) return { invalid: i };
6004
+ for (let k = i + 1; k < text.length; k++) {
6005
+ if (text[k] === ";") return k + 1;
6006
+ if (!NAME_CHAR.test(text[k])) return { invalid: k };
6007
+ }
6008
+ return { partial: true };
6009
+ }
6010
+ function scanAttValue(text, j) {
6011
+ const q = text[j];
6012
+ let k = j + 1;
6013
+ for (; ; ) {
6014
+ const c = text[k];
6015
+ if (c === void 0) return { partial: true };
6016
+ if (c === q) return { close: k };
6017
+ if (c === "<" || NON_XML.test(c)) return { invalid: k };
6018
+ if (c !== "&") {
6019
+ k++;
6020
+ continue;
6021
+ }
6022
+ const r = scanRef(text, k + 1);
6023
+ if (typeof r !== "number") return r;
6024
+ k = r;
6025
+ }
6026
+ }
6027
+ function charRefNumber(ref) {
6028
+ const hex = ref.startsWith("&#x");
6029
+ const n = parseInt(ref.slice(hex ? 3 : 2, -1), hex ? 16 : 10);
6030
+ if (n >= 1114112) return -1;
6031
+ if (n >= 55296 && n <= 57343) return -1;
6032
+ if (n < 32 && NON_XML.test(String.fromCharCode(n))) return -1;
6033
+ return n === 65534 || n === 65535 ? -1 : n;
6034
+ }
6035
+ var PREDEFINED = { lt: "<", gt: ">", amp: "&", quot: '"', apos: "'" };
6036
+ var VALUE_PIECE = /&#x?[0-9A-Fa-f]+;|&[^;]+;|\r\n|[\t\n\r]/g;
6037
+ function attValueError(raw, base) {
6038
+ for (const m of raw.matchAll(VALUE_PIECE)) {
6039
+ const ref = m[0];
6040
+ if (!ref.startsWith("&")) continue;
6041
+ if (ref.startsWith("&#")) {
6042
+ if (charRefNumber(ref) < 0) return { message: XML_ERROR_BAD_CHAR_REF, offset: base + m.index };
6043
+ } else if (!Object.hasOwn(PREDEFINED, ref.slice(1, -1))) {
6044
+ return { message: XML_ERROR_UNDEFINED_ENTITY };
6045
+ }
6046
+ }
6047
+ return void 0;
6048
+ }
6049
+ function decodeAttValue(raw) {
6050
+ return raw.replace(VALUE_PIECE, (m) => {
6051
+ if (!m.startsWith("&")) return " ";
6052
+ if (m.startsWith("&#")) return String.fromCodePoint(charRefNumber(m));
6053
+ return PREDEFINED[m.slice(1, -1)];
6054
+ });
6055
+ }
6056
+
5970
6057
  // src/common/htmltable-lex-expat.ts
5971
6058
  var XML_ERROR_INVALID_TOKEN = "not well-formed (invalid token)";
5972
6059
  var XML_CONTEXT_BYTES = 1024;
5973
6060
  var INIT_BUFFER_SIZE = 1024;
5974
- var COMPLETE_END = (end, dup) => dup === void 0 ? { kind: "complete", end } : { kind: "complete", end, dup };
6061
+ var XML_ERROR_DUPLICATE_ATTRIBUTE = "duplicate attribute";
6062
+ var COMPLETE_END = (end, attError) => attError === void 0 ? { kind: "complete", end } : { kind: "complete", end, attError };
5975
6063
  var PARTIAL = { kind: "partial" };
5976
6064
  var INVALID = (offset) => ({ kind: "invalid", offset });
5977
6065
  var XML_NAME = /^[A-Za-z_:\u0080-\uFFFF][\w.:\u0080-\uFFFF-]*/;
@@ -5994,14 +6082,13 @@ function fedBytes(unit, isText) {
5994
6082
  function scanValue(text, j) {
5995
6083
  const q = text[j];
5996
6084
  if (q !== '"' && q !== "'") return INVALID(j);
5997
- const close = text.indexOf(q, j + 1);
5998
- if (close < 0) {
5999
- const lt = text.indexOf("<", j + 1);
6000
- return lt >= 0 ? INVALID(lt) : PARTIAL;
6001
- }
6085
+ const v = scanAttValue(text, j);
6086
+ if ("partial" in v) return PARTIAL;
6087
+ if ("invalid" in v) return INVALID(v.invalid);
6088
+ const close = v.close;
6002
6089
  const next = skipSpace(text, close + 1);
6003
6090
  if (next === close + 1 && !">/".includes(text[next] ?? ">")) return INVALID(next);
6004
- return next;
6091
+ return close;
6005
6092
  }
6006
6093
  function scanPair(text, i) {
6007
6094
  const name = XML_NAME.exec(text.slice(i))?.[0];
@@ -6011,8 +6098,9 @@ function scanPair(text, i) {
6011
6098
  if (text[eq] !== "=") return INVALID(eq);
6012
6099
  const j = skipSpace(text, eq + 1);
6013
6100
  if (j >= text.length) return PARTIAL;
6014
- const next = scanValue(text, j);
6015
- return typeof next === "number" ? { name, next } : next;
6101
+ const close = scanValue(text, j);
6102
+ if (typeof close !== "number") return close;
6103
+ return { name, next: skipSpace(text, close + 1), value: { start: j + 1, end: close } };
6016
6104
  }
6017
6105
  function tagEdge(text, i) {
6018
6106
  const c = text[i];
@@ -6022,18 +6110,23 @@ function tagEdge(text, i) {
6022
6110
  if (i + 1 >= text.length) return PARTIAL;
6023
6111
  return text[i + 1] === ">" ? COMPLETE_END(i + 2) : void 0;
6024
6112
  }
6025
- function scanAttributes(text, from) {
6113
+ function storeAttError(text, p, at, pair, seen) {
6114
+ if (seen.has(pair.name)) return { message: XML_ERROR_DUPLICATE_ATTRIBUTE, offset: at };
6115
+ seen.add(pair.name);
6116
+ const err = attValueError(text.slice(pair.value.start, pair.value.end), pair.value.start);
6117
+ return err === void 0 ? void 0 : { message: err.message, offset: err.offset ?? p };
6118
+ }
6119
+ function scanAttributes(text, p, from) {
6026
6120
  const seen = /* @__PURE__ */ new Set();
6027
- let dup;
6121
+ let attError;
6028
6122
  let i = skipSpace(text, from);
6029
6123
  for (; ; ) {
6030
6124
  const edge = tagEdge(text, i);
6031
- if (edge?.kind === "complete") return COMPLETE_END(edge.end, dup);
6125
+ if (edge?.kind === "complete") return COMPLETE_END(edge.end, attError);
6032
6126
  if (edge !== void 0) return edge;
6033
6127
  const pair = scanPair(text, i);
6034
6128
  if ("kind" in pair) return pair;
6035
- if (seen.has(pair.name)) dup ??= i;
6036
- seen.add(pair.name);
6129
+ attError ??= storeAttError(text, p, i, pair, seen);
6037
6130
  i = pair.next;
6038
6131
  }
6039
6132
  }
@@ -6048,7 +6141,7 @@ function scanStartTag(text, p) {
6048
6141
  const c = text[name.next];
6049
6142
  if (c === void 0) return PARTIAL;
6050
6143
  if (!isSpace(c) && c !== ">" && c !== "/") return INVALID(name.next);
6051
- return scanAttributes(text, name.next);
6144
+ return scanAttributes(text, p, name.next);
6052
6145
  }
6053
6146
  function scanEndTag(text, p) {
6054
6147
  const name = scanName(text, p + 2);
@@ -6125,7 +6218,7 @@ function scanContent(text, base, inCdata, dataOf) {
6125
6218
  if (scan.kind === "invalid") return { tokens, consumed: p, invalid: base + scan.offset, inCdata: false };
6126
6219
  if (scan.kind === "partial") return { tokens, consumed: p, inCdata: false };
6127
6220
  const tok = { kind, start: base + p, end: base + scan.end };
6128
- if (scan.dup !== void 0) tok.dup = base + scan.dup;
6221
+ if (scan.attError !== void 0) tok.attError = { ...scan.attError, offset: base + scan.attError.offset };
6129
6222
  tokens.push(tok);
6130
6223
  p = scan.end;
6131
6224
  }
@@ -6219,17 +6312,22 @@ var ExpatFeed = class {
6219
6312
  };
6220
6313
  var XML_PREDEFINED_ENTITIES = /* @__PURE__ */ new Set(["lt", "gt", "amp", "quot", "apos"]);
6221
6314
  var ENTITY_NAME = /^[A-Za-z_:][\w.:-]*$/;
6222
- function findUndefinedEntity(text) {
6315
+ var CHAR_REF = /^&#(?:x[0-9A-Fa-f]+|[0-9]+);/;
6316
+ function textRefError(text) {
6223
6317
  for (let t = text.indexOf("&"); t >= 0; t = text.indexOf("&", t + 1)) {
6224
- if (text[t + 1] === "#") continue;
6318
+ if (text[t + 1] === "#") {
6319
+ const ref = CHAR_REF.exec(text.slice(t))?.[0];
6320
+ if (ref !== void 0 && charRefNumber(ref) < 0) return { message: XML_ERROR_BAD_CHAR_REF, offset: t };
6321
+ continue;
6322
+ }
6225
6323
  const semi = text.indexOf(";", t + 1);
6226
6324
  if (semi < 0) continue;
6227
6325
  const key = text.slice(t + 1, semi);
6228
6326
  if (ENTITY_NAME.test(key) && !Object.hasOwn(HTML_ENTITIES, key) && !XML_PREDEFINED_ENTITIES.has(key)) {
6229
- return t;
6327
+ return { message: XML_ERROR_UNDEFINED_ENTITY, offset: t };
6230
6328
  }
6231
6329
  }
6232
- return -1;
6330
+ return void 0;
6233
6331
  }
6234
6332
  function parseTagChunk(chunk) {
6235
6333
  if (!chunk.endsWith(">")) return void 0;
@@ -6369,7 +6467,7 @@ function parseAttrPairs(raw) {
6369
6467
  if (q !== '"' && q !== "'") throw new HtmlParseError(name);
6370
6468
  const end = raw.indexOf(q, i + 1);
6371
6469
  if (end < 0) throw new HtmlParseError(name);
6372
- out.push([name, raw.slice(i + 1, end)]);
6470
+ out.push([name, decodeAttValue(raw.slice(i + 1, end))]);
6373
6471
  i = end + 1;
6374
6472
  }
6375
6473
  return out;
@@ -6411,9 +6509,7 @@ function parseOpenToken(inner) {
6411
6509
  checkTag(tag);
6412
6510
  return openToken(normalizeTag(tag), sp === -1 ? "" : inner.slice(sp + 1));
6413
6511
  }
6414
- var XML_ERROR_UNDEFINED_ENTITY = "undefined entity";
6415
6512
  var XML_ERROR_TAG_MISMATCH = "mismatched tag";
6416
- var XML_ERROR_DUPLICATE_ATTRIBUTE = "duplicate attribute";
6417
6513
  var SYNTAX_ERROR = "syntax error";
6418
6514
  var HtmlLexError = class extends HtmlParseError {
6419
6515
  diagnostics;
@@ -6456,7 +6552,8 @@ function checkTagToken(site) {
6456
6552
  throw htmlErrorInLine(XML_ERROR_TAG_MISMATCH, lineAt(src, tok.start), context);
6457
6553
  }
6458
6554
  } else {
6459
- if (tok.dup !== void 0) throw htmlErrorInLine(XML_ERROR_DUPLICATE_ATTRIBUTE, lineAt(src, tok.dup), context);
6555
+ const err = tok.attError;
6556
+ if (err !== void 0) throw htmlErrorInLine(err.message, lineAt(src, err.offset), context);
6460
6557
  if (!tag.selfClose) st.stack.push(tag.name);
6461
6558
  }
6462
6559
  if (KNOWN_TAGS.has(tag.name.toUpperCase())) return;
@@ -6472,10 +6569,8 @@ function eventTokens(site) {
6472
6569
  checkTagToken(site);
6473
6570
  return tagTokens(text);
6474
6571
  }
6475
- const off = findUndefinedEntity(text);
6476
- if (off >= 0) {
6477
- throw htmlErrorInLine(XML_ERROR_UNDEFINED_ENTITY, lineAt(src, tok.start + off), st.prev + st.curr);
6478
- }
6572
+ const err = textRefError(text);
6573
+ if (err !== void 0) throw htmlErrorInLine(err.message, lineAt(src, tok.start + err.offset), st.prev + st.curr);
6479
6574
  return textTokens(text);
6480
6575
  }
6481
6576
  function attachCarry(carry, first) {
@@ -6585,9 +6680,6 @@ function tokenizeDeferred(src, label = { start: 0, end: src.length }) {
6585
6680
  }
6586
6681
 
6587
6682
  // src/common/htmltable-parse-attrs.ts
6588
- function anchorAttr(v) {
6589
- return v === void 0 ? void 0 : htmlEntityUTF8(v);
6590
- }
6591
6683
  var parseAlign = (v) => {
6592
6684
  const u = v.toUpperCase();
6593
6685
  if (u === "LEFT") return "left";
@@ -6595,6 +6687,7 @@ var parseAlign = (v) => {
6595
6687
  if (u === "CENTER") return "center";
6596
6688
  return void 0;
6597
6689
  };
6690
+ var parseCellAlign = (v) => v.toUpperCase() === "TEXT" ? "text" : parseAlign(v);
6598
6691
  var parseVAlign = (v) => {
6599
6692
  const u = v.toUpperCase();
6600
6693
  if (u === "TOP") return "top";
@@ -6628,7 +6721,7 @@ var buildCell = (content, a) => ({
6628
6721
  kind: "cell",
6629
6722
  content,
6630
6723
  port: a["port"],
6631
- align: parseAlign(a["align"] !== void 0 ? a["align"] : ""),
6724
+ align: parseCellAlign(a["align"] !== void 0 ? a["align"] : ""),
6632
6725
  valign: parseVAlign(a["valign"] !== void 0 ? a["valign"] : ""),
6633
6726
  balign: parseAlign(a["balign"] !== void 0 ? a["balign"] : ""),
6634
6727
  bgcolor: a["bgcolor"],
@@ -6644,9 +6737,9 @@ var buildCell = (content, a) => ({
6644
6737
  fixedsize: parseFixedSize(a),
6645
6738
  colspan: num(a["colspan"]),
6646
6739
  rowspan: num(a["rowspan"]),
6647
- href: anchorAttr(a["href"]),
6740
+ href: a["href"],
6648
6741
  title: parseTitle(a),
6649
- target: anchorAttr(a["target"]),
6742
+ target: a["target"],
6650
6743
  id: a["id"]
6651
6744
  });
6652
6745
  var buildTable = (rows, a) => ({
@@ -6664,9 +6757,9 @@ var buildTable = (rows, a) => ({
6664
6757
  fixedsize: parseFixedSize(a),
6665
6758
  width: num(a["width"]),
6666
6759
  height: num(a["height"]),
6667
- href: anchorAttr(a["href"]),
6760
+ href: a["href"],
6668
6761
  title: parseTitle(a),
6669
- target: anchorAttr(a["target"]),
6762
+ target: a["target"],
6670
6763
  id: a["id"],
6671
6764
  port: a["port"],
6672
6765
  sides: parseSides(a["sides"]),
@@ -6838,11 +6931,11 @@ var rejectTableAfterTag = (s, start) => {
6838
6931
  throw parseError(s, nxt, MSG_SYNTAX_ERROR);
6839
6932
  }
6840
6933
  };
6841
- var parseImgContent = (s, t, cellAttrs) => {
6934
+ var parseImgContent = (s, t) => {
6842
6935
  consume(s);
6843
6936
  const img = {
6844
6937
  kind: "image",
6845
- src: cellAttrs["src"] ?? t.attrs["src"] ?? "",
6938
+ src: t.attrs["src"] ?? "",
6846
6939
  scale: t.attrs["scale"]
6847
6940
  };
6848
6941
  consumeClose(s, "IMG");
@@ -6870,12 +6963,12 @@ var scanAfterTable = (s, t, st) => {
6870
6963
  if (nonSpace(t.value)) st.afterNonSpace = true;
6871
6964
  return [];
6872
6965
  };
6873
- var scanCellImage = (s, t, cellAttrs, st) => {
6966
+ var scanCellImage = (s, t, st) => {
6874
6967
  if (st.kind !== "none") throw parseError(s, t, MSG_SYNTAX_ERROR);
6875
6968
  st.kind = "image";
6876
- return [parseImgContent(s, t, cellAttrs)];
6969
+ return [parseImgContent(s, t)];
6877
6970
  };
6878
- var scanCellItem = (s, t, cellAttrs, fontStack, st) => {
6971
+ var scanCellItem = (s, t, fontStack, st) => {
6879
6972
  if (st.kind === "fonttable") throw parseError(s, t, MSG_SYNTAX_ERROR);
6880
6973
  if (st.kind === "table") return scanAfterTable(s, t, st);
6881
6974
  const wrapper = st.kind === "none" ? fontTableWrapper(s) : void 0;
@@ -6884,7 +6977,7 @@ var scanCellItem = (s, t, cellAttrs, fontStack, st) => {
6884
6977
  return [parseFontTable(s, wrapper, fontStack)];
6885
6978
  }
6886
6979
  if (t.type === "open" && t.tag === "TABLE") return scanCellTable(s, t, fontStack, st);
6887
- if (t.type === "open" && t.tag === "IMG") return scanCellImage(s, t, cellAttrs, st);
6980
+ if (t.type === "open" && t.tag === "IMG") return scanCellImage(s, t, st);
6888
6981
  if (st.kind === "image") throw parseError(s, t, MSG_SYNTAX_ERROR);
6889
6982
  return scanCellText(s, t, fontStack, st);
6890
6983
  };
@@ -6894,12 +6987,12 @@ var endsCell = (s, t, st) => {
6894
6987
  if (t.tag === "TD" && !afterText) return true;
6895
6988
  throw parseError(s, t, afterText ? MSG_TEXT_AFTER_TABLE : MSG_SYNTAX_ERROR);
6896
6989
  };
6897
- var parseCellContent = (s, cellAttrs, fontStack) => {
6990
+ var parseCellContent = (s, fontStack) => {
6898
6991
  const content = [];
6899
6992
  const st = { kind: "none", textNonSpace: false, afterNonSpace: false };
6900
6993
  for (let t = peek(s); t !== void 0; t = peek(s)) {
6901
6994
  if (endsCell(s, t, st)) break;
6902
- content.push(...scanCellItem(s, t, cellAttrs, fontStack, st));
6995
+ content.push(...scanCellItem(s, t, fontStack, st));
6903
6996
  }
6904
6997
  const nxt = peek(s);
6905
6998
  if (nxt?.type === "close" && nxt.tag === "TD") consume(s);
@@ -6907,7 +7000,7 @@ var parseCellContent = (s, cellAttrs, fontStack) => {
6907
7000
  if (tbl !== void 0) return [tbl];
6908
7001
  return content;
6909
7002
  };
6910
- var parseCell = (s, attrs, fontStack) => buildCell(parseCellContent(s, attrs, fontStack), attrs);
7003
+ var parseCell = (s, attrs, fontStack) => buildCell(parseCellContent(s, fontStack), attrs);
6911
7004
  var parseRowVr = (s, t, cells) => {
6912
7005
  const prev = cells[cells.length - 1];
6913
7006
  if (prev === void 0) throw parseError(s, t, MSG_SYNTAX_ERROR);
@@ -7068,17 +7161,9 @@ var parseTokens = (s) => {
7068
7161
  if (firstReal?.type === "open" && firstReal.tag === "TABLE") return parseTableLabel(s, fontStack);
7069
7162
  return parseTextLabel(s, fontStack);
7070
7163
  };
7071
- var parseHtmlLabelRv = (src, writeLine = (line) => console.warn(line)) => {
7164
+ var parseHtmlLabelRv = (src, stderr) => {
7072
7165
  const { tokens, error, eof } = tokenizeDeferred(`<HTML>${src}</HTML>`, { start: 6, end: 6 + src.length });
7073
- const s = {
7074
- tokens,
7075
- pos: 0,
7076
- pending: error,
7077
- stderr: new StderrLines(writeLine),
7078
- emitted: /* @__PURE__ */ new WeakSet(),
7079
- warn: false,
7080
- eof
7081
- };
7166
+ const s = { tokens, pos: 0, pending: error, stderr, emitted: /* @__PURE__ */ new WeakSet(), warn: false, eof };
7082
7167
  try {
7083
7168
  const label = parseTokens(s);
7084
7169
  if (error !== void 0) throw error;
@@ -7089,11 +7174,9 @@ var parseHtmlLabelRv = (src, writeLine = (line) => console.warn(line)) => {
7089
7174
  throw error;
7090
7175
  }
7091
7176
  throw e;
7092
- } finally {
7093
- s.stderr?.flush();
7094
7177
  }
7095
7178
  };
7096
- var parseHtmlLabel = (src) => parseHtmlLabelRv(src, () => void 0).label;
7179
+ var parseHtmlLabel = (src) => parseHtmlLabelRv(src, new StderrLines(() => void 0)).label;
7097
7180
 
7098
7181
  // src/gvc/usershape.ts
7099
7182
  var activeSizer = null;
@@ -7289,7 +7372,7 @@ var sizeTextContent = (texts, measurer, env = defaultSizeEnv()) => {
7289
7372
  return { w, h };
7290
7373
  };
7291
7374
  var missingImages = /* @__PURE__ */ new WeakSet();
7292
- var sizeHtmlImg = (img, imageSizer) => {
7375
+ var sizeHtmlImg = (img, { imageSizer, stderr }) => {
7293
7376
  if (img.width !== void 0 && img.height !== void 0) return missingImages.has(img) ? 1 : 0;
7294
7377
  const sz = imageSizer !== void 0 ? imageSizer(img.src) : findImageSize(img.src);
7295
7378
  if (sz !== null) {
@@ -7297,7 +7380,7 @@ var sizeHtmlImg = (img, imageSizer) => {
7297
7380
  img.height = sz.h;
7298
7381
  return 0;
7299
7382
  }
7300
- console.error(`Error: No or improper image file="${img.src}"`);
7383
+ agerrLine(stderr, `Error: No or improper image file="${img.src}"`);
7301
7384
  img.width = 0;
7302
7385
  img.height = 0;
7303
7386
  missingImages.add(img);
@@ -7310,7 +7393,7 @@ var sizeContentItem = (item, measurer, env) => {
7310
7393
  return { ...item.dimen !== void 0 ? item.dimen : { w: 0, h: 0 }, rv };
7311
7394
  }
7312
7395
  if (item.kind === "image") {
7313
- const rv = sizeHtmlImg(item, env.imageSizer);
7396
+ const rv = sizeHtmlImg(item, env);
7314
7397
  return { w: item.width ?? 0, h: item.height ?? 0, rv };
7315
7398
  }
7316
7399
  return { w: 0, h: 0, rv: 0 };
@@ -7340,14 +7423,14 @@ var WARN_CELL_UNSPECIFIED = "Warning: fixed cell size with unspecified width or
7340
7423
  var WARN_TABLE_TOO_SMALL = "Warning: table size too small for content";
7341
7424
  var WARN_TABLE_UNSPECIFIED = "Warning: fixed table size with unspecified width or height";
7342
7425
  var sizeWarned = /* @__PURE__ */ new WeakSet();
7343
- var fixedCellCheck = (cell2, sz, warn) => {
7426
+ var fixedCellCheck = (cell2, sz, warn, out) => {
7344
7427
  if (!cell2.fixedsize) return { rv: 0, zero: false };
7345
7428
  if (cell2.width && cell2.height) {
7346
7429
  const small = (cell2.width < sz.w || cell2.height < sz.h) && cell2.content[0]?.kind !== "image";
7347
- if (small && warn) console.warn(WARN_CELL_TOO_SMALL);
7430
+ if (small && warn) agerrLine(out, WARN_CELL_TOO_SMALL);
7348
7431
  return { rv: small ? 1 : 0, zero: true };
7349
7432
  }
7350
- if (warn) console.warn(WARN_CELL_UNSPECIFIED);
7433
+ if (warn) agerrLine(out, WARN_CELL_UNSPECIFIED);
7351
7434
  return { rv: 1, zero: false };
7352
7435
  };
7353
7436
  var getCellSize = (cell2, tbl, measurer, env) => {
@@ -7363,7 +7446,7 @@ var getCellSize = (cell2, tbl, measurer, env) => {
7363
7446
  let szy = ch + margin;
7364
7447
  const warn = !sizeWarned.has(cell2);
7365
7448
  sizeWarned.add(cell2);
7366
- const fixed = fixedCellCheck(cell2, { w: szx, h: szy }, warn);
7449
+ const fixed = fixedCellCheck(cell2, { w: szx, h: szy }, warn, env.stderr);
7367
7450
  rv |= fixed.rv;
7368
7451
  if (fixed.zero) {
7369
7452
  szx = 0;
@@ -7457,22 +7540,22 @@ var layoutHtmlTable = (tbl, measurer, env = defaultSizeEnv()) => {
7457
7540
  const heights = setRowHeights(entries, nrows, spacing);
7458
7541
  return { entries, widths, heights, spacing, border: border2, rv };
7459
7542
  };
7460
- var warnFixedTable = (tbl, dims) => {
7543
+ var warnFixedTable = (tbl, dims, out) => {
7461
7544
  const warn = !sizeWarned.has(tbl);
7462
7545
  sizeWarned.add(tbl);
7463
7546
  if (tbl.fixedsize !== true) return 0;
7464
7547
  if (tbl.width && tbl.height) {
7465
7548
  if (tbl.width >= dims.wd && tbl.height >= dims.ht) return 0;
7466
- if (warn) console.warn(WARN_TABLE_TOO_SMALL);
7549
+ if (warn) agerrLine(out, WARN_TABLE_TOO_SMALL);
7467
7550
  return 1;
7468
7551
  }
7469
- if (warn) console.warn(WARN_TABLE_UNSPECIFIED);
7552
+ if (warn) agerrLine(out, WARN_TABLE_UNSPECIFIED);
7470
7553
  return 1;
7471
7554
  };
7472
7555
  var sizeTableBody = (tbl, measurer, env) => {
7473
7556
  const { entries, widths, heights, spacing, border: border2, rv } = layoutHtmlTable(tbl, measurer, env);
7474
7557
  if (entries.length === 0) {
7475
- const fixed2 = warnFixedTable(tbl, sumTableDims({ widths, heights, ncols: 0, nrows: 0, spacing, border: border2 }));
7558
+ const fixed2 = warnFixedTable(tbl, sumTableDims({ widths, heights, ncols: 0, nrows: 0, spacing, border: border2 }), env.stderr);
7476
7559
  tbl.dimen = { w: 0, h: 0 };
7477
7560
  return rv | fixed2;
7478
7561
  }
@@ -7482,7 +7565,7 @@ var sizeTableBody = (tbl, measurer, env) => {
7482
7565
  const { wd, ht } = sumTableDims({ widths, heights, ncols, nrows, spacing, border: border2 });
7483
7566
  const baseWd = tbl.width !== void 0 ? tbl.width : 0;
7484
7567
  const baseHt = tbl.height !== void 0 ? tbl.height : 0;
7485
- const fixed = warnFixedTable(tbl, { wd, ht });
7568
+ const fixed = warnFixedTable(tbl, { wd, ht }, env.stderr);
7486
7569
  const zeroContent = isTblFixed(tbl) && baseWd !== 0 && baseHt !== 0;
7487
7570
  tbl.dimen = {
7488
7571
  w: Math.max(zeroContent ? 0 : wd, baseWd),
@@ -7687,7 +7770,7 @@ function placeComplexRuns(runs, box, finfo, measurer) {
7687
7770
  function alignContentBox(box, cell2, w, h) {
7688
7771
  const out = { ll: { ...box.ll }, ur: { ...box.ur } };
7689
7772
  const delx = out.ur.x - out.ll.x - w;
7690
- if (delx > 0) {
7773
+ if (delx > 0 && cell2.align !== "text") {
7691
7774
  if (cell2.align === "left") out.ur.x -= delx;
7692
7775
  else if (cell2.align === "right") out.ll.x += delx;
7693
7776
  else {
@@ -7942,21 +8025,25 @@ function posHtmlLabel(label, finfo, measurer) {
7942
8025
  if (label.kind === "table") return withTableFont(env, label.table.font, "emit", () => posHtmlTable(label.table, env, measurer));
7943
8026
  return posTextLabel(label, finfo, measurer);
7944
8027
  }
8028
+
8029
+ // src/common/htmltable-label.ts
7945
8030
  var HTML_PARSE_FATAL = 3;
7946
- function makeHtmlLabelRv(content, finfo, measurer) {
8031
+ function makeHtmlLabelTo(content, finfo, measurer, stderr) {
7947
8032
  const { fontname, fontsize, fontcolor, imageSizer, pencolor } = finfo;
7948
8033
  let lbl, lexWarn;
7949
8034
  try {
7950
- ({ label: lbl, warn: lexWarn } = parseHtmlLabelRv(content));
8035
+ ({ label: lbl, warn: lexWarn } = parseHtmlLabelRv(content, stderr));
7951
8036
  } catch (e) {
7952
- if (e instanceof HtmlLexError) for (const line of e.diagnostics) console.error(line);
8037
+ if (e instanceof HtmlLexError) for (const line of e.diagnostics) stderr.write(`${line}
8038
+ `);
7953
8039
  return { label: makeLabel("", fontname, fontsize, fontcolor, measurer), rv: HTML_PARSE_FATAL };
7954
8040
  }
7955
8041
  if (lbl.kind === "table") inheritTablePenColor(lbl.table, pencolor);
7956
8042
  const rv = Number(lexWarn) | sizeHtmlLabel(lbl, measurer, {
7957
8043
  fontsize,
7958
8044
  fontname,
7959
- imageSizer
8045
+ imageSizer,
8046
+ stderr
7960
8047
  });
7961
8048
  const placed = posHtmlLabel(lbl, { fontname, fontsize, fontcolor }, measurer);
7962
8049
  const dim = lbl.dimen ?? { w: 0, h: 0 };
@@ -8349,6 +8436,9 @@ function interpretCRNL(str) {
8349
8436
  }
8350
8437
  return out;
8351
8438
  }
8439
+ function preprocessTooltip(s) {
8440
+ return interpretCRNL(htmlEntityUTF8(s));
8441
+ }
8352
8442
 
8353
8443
  // src/model/graphInfo.ts
8354
8444
  function makeGraphInfo() {
@@ -8593,8 +8683,11 @@ function makeLabel(text, fontname, fontsize, fontcolor, measurer) {
8593
8683
  function makeAnyLabel(content, isHtml, font, measurer, obj) {
8594
8684
  if (isHtml && content !== "") {
8595
8685
  const imageSizer = obj !== void 0 ? rootOf(obj).info.gvc?.imageSizer : void 0;
8596
- const { label, rv } = makeHtmlLabelRv(content, { ...font, pencolor: getPenColor(obj), imageSizer }, measurer);
8597
- if (rv !== 0 && obj !== void 0) console.error(inLabelOf(obj));
8686
+ const stderr = labelStderr();
8687
+ const { label, rv } = makeHtmlLabelTo(content, { ...font, pencolor: getPenColor(obj), imageSizer }, measurer, stderr);
8688
+ if (rv !== 0 && obj !== void 0) stderr.write(`${inLabelOf(obj)}
8689
+ `);
8690
+ stderr.flush();
8598
8691
  return label;
8599
8692
  }
8600
8693
  const subst2 = obj !== void 0 ? substObj(content, obj, false) : content;
@@ -15608,31 +15701,141 @@ function stripedBox(job, pts, clrs, rotate, renderer) {
15608
15701
  return error;
15609
15702
  }
15610
15703
 
15704
+ // src/util/xml.ts
15705
+ function gvXmlEscape(s, flags) {
15706
+ return XmlEscaper.escape(s, flags);
15707
+ }
15708
+ var XmlEscaper = class _XmlEscaper {
15709
+ static escape(s, flags) {
15710
+ let result = "";
15711
+ let previous = "";
15712
+ let pos = 0;
15713
+ while (pos < s.length) {
15714
+ const prev = s[pos];
15715
+ const [fragment, consumed] = _XmlEscaper.core(previous, s, pos, flags);
15716
+ result += fragment;
15717
+ previous = prev;
15718
+ pos += consumed;
15719
+ }
15720
+ return result;
15721
+ }
15722
+ /**
15723
+ * Escape one character/codepoint at s[pos].
15724
+ * Returns [escaped_fragment, characters_consumed].
15725
+ * Delegates to focused helpers to stay within CCN limit.
15726
+ * @see lib/util/xml.c:xml_core
15727
+ */
15728
+ static core(previous, s, pos, flags) {
15729
+ const special = _XmlEscaper.coreSpecial(previous, s, pos, flags);
15730
+ if (special !== null) return special;
15731
+ const code = s.charCodeAt(pos);
15732
+ if (code > 127 && flags.utf8) return _XmlEscaper.utf8Entity(s, pos);
15733
+ return [s[pos], 1];
15734
+ }
15735
+ /**
15736
+ * Handle all the fixed-escape cases (& < > - space " ' \n \r).
15737
+ * Returns null when none of those cases applies.
15738
+ * @see lib/util/xml.c:xml_core
15739
+ */
15740
+ static coreSpecial(previous, s, pos, flags) {
15741
+ const c = s[pos];
15742
+ if (c === "&" && (flags.raw || !_XmlEscaper.isEntity(s, pos))) {
15743
+ return ["&amp;", 1];
15744
+ }
15745
+ if (c === "<") return ["&lt;", 1];
15746
+ if (c === ">") return ["&gt;", 1];
15747
+ if (c === '"') return ["&quot;", 1];
15748
+ if (c === "'") return ["&#39;", 1];
15749
+ if (c === "-" && flags.dash) return ["&#45;", 1];
15750
+ if (c === " " && previous === " " && flags.nbsp) return ["&#160;", 1];
15751
+ if (c === "\n" && flags.raw) return ["&#10;", 1];
15752
+ if (c === "\r" && flags.raw) return ["&#13;", 1];
15753
+ return null;
15754
+ }
15755
+ /**
15756
+ * Return true if s[pos] (which is '&') starts a valid XML entity:
15757
+ * &[A-Za-z]+; e.g. &Ccedil;
15758
+ * &#[0-9]*; e.g. &#38;
15759
+ * &#x[0-9a-fA-F]*; e.g. &#x6C34;
15760
+ * @see lib/util/xml.c:xml_isentity
15761
+ */
15762
+ static isEntity(s, pos) {
15763
+ let i = pos + 1;
15764
+ if (i >= s.length || s[i] === ";") return false;
15765
+ i = s[i] === "#" ? _XmlEscaper.scanNumericRef(s, i) : _XmlEscaper.scanNamedRef(s, i);
15766
+ return i < s.length && s[i] === ";";
15767
+ }
15768
+ /** Scan a numeric entity body starting just after the '#'. */
15769
+ static scanNumericRef(s, hashPos) {
15770
+ let i = hashPos + 1;
15771
+ if (i < s.length && (s[i] === "x" || s[i] === "X")) {
15772
+ i++;
15773
+ while (i < s.length && _XmlEscaper.isXDigit(s[i])) i++;
15774
+ } else {
15775
+ while (i < s.length && _XmlEscaper.isDecDigit(s[i])) i++;
15776
+ }
15777
+ return i;
15778
+ }
15779
+ /** Scan a named entity body (alpha chars after the '&'). */
15780
+ static scanNamedRef(s, start) {
15781
+ let i = start;
15782
+ while (i < s.length && _XmlEscaper.isAlpha(s[i])) i++;
15783
+ return i;
15784
+ }
15785
+ /**
15786
+ * Emit &#xNNNN; for the Unicode code point at s[pos]. JS strings are
15787
+ * UTF-16, so the code point (what C decodes from UTF-8) comes from
15788
+ * codePointAt, and an astral character consumes two units.
15789
+ * @see lib/util/xml.c:xml_core (UTF-8 block)
15790
+ */
15791
+ static utf8Entity(s, pos) {
15792
+ const cp = _XmlEscaper.decodeUtf8(s, pos);
15793
+ const consumed = _XmlEscaper.utf8ByteLen(cp);
15794
+ return [`&#x${cp.toString(16)};`, consumed];
15795
+ }
15796
+ /**
15797
+ * Return the code point at s[pos]. A lone surrogate is the analogue of
15798
+ * malformed UTF-8, where C reports an error and exits (xml.c:135).
15799
+ * @see lib/util/xml.c:xml_core (fprintf + graphviz_exit at xml.c:135)
15800
+ */
15801
+ static decodeUtf8(s, pos) {
15802
+ const cp = s.codePointAt(pos);
15803
+ if (cp >= 55296 && cp <= 57343) {
15804
+ throw new RenderError(`gvXmlEscape: malformed UTF-8 at position ${pos}`, "RENDER_ERROR");
15805
+ }
15806
+ return cp;
15807
+ }
15808
+ /**
15809
+ * Return how many UTF-16 units encode the code point (C: the UTF-8 byte
15810
+ * length from the leading byte).
15811
+ * @see lib/util/xml.c:xml_core (length computation)
15812
+ */
15813
+ static utf8ByteLen(cp) {
15814
+ return cp > 65535 ? 2 : 1;
15815
+ }
15816
+ static isDecDigit(c) {
15817
+ return c >= "0" && c <= "9";
15818
+ }
15819
+ static isAlpha(c) {
15820
+ return c >= "a" && c <= "z" || c >= "A" && c <= "Z";
15821
+ }
15822
+ static isXDigit(c) {
15823
+ return _XmlEscaper.isDecDigit(c) || c >= "a" && c <= "f" || c >= "A" && c <= "F";
15824
+ }
15825
+ };
15826
+
15611
15827
  // src/render/xml-escape.ts
15612
- var RE_DQUOTE = new RegExp('"', "g");
15613
- var RE_SQUOTE = new RegExp("'", "g");
15614
- var RE_AMP_NONENTITY = /&(?![A-Za-z]+;|#[0-9]+;|#x[0-9A-Fa-f]+;)/g;
15828
+ var FLAGS_BASE = { raw: false, dash: false, nbsp: false, utf8: false };
15829
+ var FLAGS_TITLE = { raw: false, dash: true, nbsp: true, utf8: false };
15830
+ var FLAGS_TEXT = { raw: true, dash: true, nbsp: true, utf8: false };
15615
15831
  function escapeXml(s) {
15616
- let r = s.replace(RE_AMP_NONENTITY, "&amp;");
15617
- r = r.replace(/</g, "&lt;");
15618
- r = r.replace(/>/g, "&gt;");
15619
- r = r.replace(RE_DQUOTE, "&quot;");
15620
- r = r.replace(RE_SQUOTE, "&#39;");
15621
- return r;
15832
+ return gvXmlEscape(s, FLAGS_BASE);
15622
15833
  }
15623
15834
  function escapeXmlTitle(s) {
15624
- let r = escapeXml(s);
15625
- r = r.replace(/-/g, "&#45;");
15626
- r = r.replace(/ {2,}/g, (m) => " " + "&#160;".repeat(m.length - 1));
15627
- return r;
15835
+ return gvXmlEscape(s, FLAGS_TITLE);
15628
15836
  }
15629
15837
  function escapeXmlText(s) {
15630
- let r = escapeXml(s);
15631
- r = r.replace(/-/g, "&#45;");
15632
- r = r.replace(/\n/g, "&#10;");
15633
- r = r.replace(/\r/g, "&#13;");
15634
- r = r.replace(/ {2,}/g, (m) => " " + "&#160;".repeat(m.length - 1));
15635
- return r;
15838
+ return gvXmlEscape(s, FLAGS_TEXT);
15636
15839
  }
15637
15840
 
15638
15841
  // src/gvc/device-cluster.ts
@@ -15808,23 +16011,23 @@ function renderNodeLabel(n, coord, renderer, job) {
15808
16011
  renderLabel(label, coord, renderer, job);
15809
16012
  }
15810
16013
  }
15811
- function anchorAttr2(n, key) {
16014
+ function anchorAttr(n, key) {
15812
16015
  const v = nodeAttr(n, n.root, key);
15813
16016
  return v !== void 0 && v !== "" ? v : void 0;
15814
16017
  }
15815
16018
  function resolveTooltip(n, tooltip) {
15816
- if (tooltip !== void 0) return substObjAnchor(interpretCRNL(tooltip), n);
16019
+ if (tooltip !== void 0) return substObjAnchor(preprocessTooltip(tooltip), n);
15817
16020
  const label = n.info.label;
15818
16021
  return label?.text ?? "";
15819
16022
  }
15820
16023
  function beginNodeAnchor(n, renderer, job) {
15821
- const url = anchorAttr2(n, "href") ?? anchorAttr2(n, "URL");
15822
- const tooltip = anchorAttr2(n, "tooltip");
16024
+ const url = anchorAttr(n, "href") ?? anchorAttr(n, "URL");
16025
+ const tooltip = anchorAttr(n, "tooltip");
15823
16026
  if (url === void 0 && tooltip === void 0) return false;
15824
16027
  renderer.beginAnchor?.(
15825
16028
  url !== void 0 ? substObjAnchor(url, n) : "",
15826
16029
  resolveTooltip(n, tooltip),
15827
- anchorAttr2(n, "target") ?? "",
16030
+ anchorAttr(n, "target") ?? "",
15828
16031
  svgNodeId(n, job),
15829
16032
  job
15830
16033
  );
@@ -19951,7 +20154,7 @@ function subst(s, obj) {
19951
20154
  return substObjAnchor(s, obj);
19952
20155
  }
19953
20156
  function tip(s, obj) {
19954
- return substObjAnchor(interpretCRNL(s), obj);
20157
+ return substObjAnchor(preprocessTooltip(s), obj);
19955
20158
  }
19956
20159
  function textOf(lp) {
19957
20160
  return lp?.text ?? null;