@knowvah/dot-engine 2.6.2 → 2.6.4

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 (46) hide show
  1. package/dist/common/htmltable-lex.d.ts +38 -0
  2. package/dist/common/htmltable-lex.d.ts.map +1 -1
  3. package/dist/common/htmltable-parse-attrs.d.ts +41 -0
  4. package/dist/common/htmltable-parse-attrs.d.ts.map +1 -0
  5. package/dist/common/htmltable-parse.d.ts +3 -0
  6. package/dist/common/htmltable-parse.d.ts.map +1 -1
  7. package/dist/common/htmltable-pos.d.ts +17 -0
  8. package/dist/common/htmltable-pos.d.ts.map +1 -1
  9. package/dist/common/htmltable.d.ts +13 -1
  10. package/dist/common/htmltable.d.ts.map +1 -1
  11. package/dist/common/make-label.d.ts.map +1 -1
  12. package/dist/common/spline-midpoint.d.ts +8 -0
  13. package/dist/common/spline-midpoint.d.ts.map +1 -1
  14. package/dist/index.js +621 -323
  15. package/dist/index.js.map +4 -4
  16. package/dist/label/node.d.ts +7 -1
  17. package/dist/label/node.d.ts.map +1 -1
  18. package/dist/layout/circo/position-helpers.d.ts +15 -3
  19. package/dist/layout/circo/position-helpers.d.ts.map +1 -1
  20. package/dist/layout/circo/position.d.ts +17 -1
  21. package/dist/layout/circo/position.d.ts.map +1 -1
  22. package/dist/layout/dot/ortho-adapter.d.ts.map +1 -1
  23. package/dist/layout/dot/splines-flat-labeled.d.ts.map +1 -1
  24. package/dist/layout/neato/multispline.d.ts.map +1 -1
  25. package/dist/layout/pack/poly-pack.d.ts +2 -18
  26. package/dist/layout/pack/poly-pack.d.ts.map +1 -1
  27. package/dist/pathplan/shortest.d.ts.map +1 -1
  28. package/dist/render.js +774 -476
  29. package/dist/render.js.map +4 -4
  30. package/package.json +1 -1
  31. package/src/common/htmltable-lex.ts +265 -5
  32. package/src/common/htmltable-parse-attrs.ts +166 -0
  33. package/src/common/htmltable-parse.ts +262 -242
  34. package/src/common/htmltable-pos.ts +37 -38
  35. package/src/common/htmltable.ts +139 -45
  36. package/src/common/make-label.ts +19 -3
  37. package/src/common/spline-midpoint.ts +30 -9
  38. package/src/label/node.ts +8 -2
  39. package/src/layout/circo/position-helpers.ts +28 -17
  40. package/src/layout/circo/position.ts +28 -7
  41. package/src/layout/dot/__fixtures__/2796-flat-labeled-lost.dot +161 -0
  42. package/src/layout/dot/ortho-adapter.ts +20 -9
  43. package/src/layout/dot/splines-flat-labeled.ts +3 -1
  44. package/src/layout/neato/multispline.ts +17 -5
  45. package/src/layout/pack/poly-pack.ts +5 -31
  46. package/src/pathplan/shortest.ts +15 -2
package/dist/render.js CHANGED
@@ -5854,16 +5854,152 @@ function parseOpenToken(inner) {
5854
5854
  checkTag(tag);
5855
5855
  return { type: "open", tag: normalizeTag(tag), attrs: parseAttrs(inner.slice(sp + 1)) };
5856
5856
  }
5857
- function tokenize(src) {
5857
+ var XML_ERROR_UNDEFINED_ENTITY = "undefined entity";
5858
+ var XML_ERROR_TAG_MISMATCH = "mismatched tag";
5859
+ var XML_ERROR_INVALID_TOKEN = "not well-formed (invalid token)";
5860
+ var XML_ERROR_DUPLICATE_ATTRIBUTE = "duplicate attribute";
5861
+ var XML_PREDEFINED_ENTITIES = /* @__PURE__ */ new Set(["lt", "gt", "amp", "quot", "apos"]);
5862
+ var XML_NAME = /^[A-Za-z_:][\w.:-]*$/;
5863
+ var HtmlLexError = class extends HtmlParseError {
5864
+ diagnostics;
5865
+ constructor(tag, message, diagnostics) {
5866
+ super(tag);
5867
+ this.message = message;
5868
+ this.diagnostics = diagnostics;
5869
+ }
5870
+ };
5871
+ function htmlErrorInLine(message, line, context) {
5872
+ const text = `${message} in line ${line} `;
5873
+ return new HtmlLexError(message, text, [`Error: ${text}`, `... ${context} ...`]);
5874
+ }
5875
+ var chunkInfo = /* @__PURE__ */ new WeakMap();
5876
+ function lineAt(src, pos) {
5877
+ let line = 1;
5878
+ for (let k = 0; k < pos; k++) {
5879
+ if (src[k] === "\n" || src[k] === "\r" && src[k + 1] !== "\n") line++;
5880
+ }
5881
+ return line;
5882
+ }
5883
+ function chunkLine(info) {
5884
+ if (info.src[info.pos] === "<") return lineAt(info.src, info.pos);
5885
+ const last = info.src.lastIndexOf("\n", info.end - 1);
5886
+ if (last < info.pos) return lineAt(info.src, info.pos);
5887
+ return lineAt(info.src, last === info.end - 1 ? last : last + 1);
5888
+ }
5889
+ function htmlErrorAtToken(message, tok) {
5890
+ const info = tok === void 0 ? void 0 : chunkInfo.get(tok);
5891
+ if (info === void 0) return void 0;
5892
+ return htmlErrorInLine(message, chunkLine(info), info.context);
5893
+ }
5894
+ function chunkEnd(src, i) {
5895
+ const j = src[i] === "<" ? src.indexOf(">", i + 1) : src.indexOf("<", i);
5896
+ if (j < 0) return src.length;
5897
+ return src[i] === "<" ? j + 1 : j;
5898
+ }
5899
+ function findUndefinedEntity(text) {
5900
+ for (let t = text.indexOf("&"); t >= 0; t = text.indexOf("&", t + 1)) {
5901
+ if (text[t + 1] === "#") continue;
5902
+ const semi = text.indexOf(";", t + 1);
5903
+ if (semi < 0) continue;
5904
+ const key = text.slice(t + 1, semi);
5905
+ if (XML_NAME.test(key) && !Object.hasOwn(HTML_ENTITIES, key) && !XML_PREDEFINED_ENTITIES.has(key)) {
5906
+ return t;
5907
+ }
5908
+ }
5909
+ return -1;
5910
+ }
5911
+ function parseTagChunk(chunk) {
5912
+ if (!chunk.endsWith(">")) return void 0;
5913
+ const body = chunk.slice(1, -1);
5914
+ if (body.startsWith("!")) return void 0;
5915
+ const close = body.startsWith("/");
5916
+ const name = /^[^\s/>]*/.exec(close ? body.slice(1) : body)?.[0] ?? "";
5917
+ return { close, selfClose: !close && body.endsWith("/"), name };
5918
+ }
5919
+ var ATTR_NAME = /^[A-Za-z_:][\w.:-]*/;
5920
+ var isSpace = (c) => c !== void 0 && /\s/.test(c);
5921
+ var invalidToken = (offset) => ({ message: XML_ERROR_INVALID_TOKEN, offset });
5922
+ function skipSpace(chunk, i) {
5923
+ let j = i;
5924
+ while (isSpace(chunk[j])) j++;
5925
+ return j;
5926
+ }
5927
+ function readAttrPair(chunk, i) {
5928
+ const name = ATTR_NAME.exec(chunk.slice(i))?.[0];
5929
+ if (name === void 0) return invalidToken(i);
5930
+ let j = skipSpace(chunk, i + name.length);
5931
+ if (chunk[j] !== "=") return invalidToken(j);
5932
+ j = skipSpace(chunk, j + 1);
5933
+ const q = chunk[j];
5934
+ if (q !== '"' && q !== "'") return invalidToken(j);
5935
+ const end = chunk.indexOf(q, j + 1);
5936
+ return end < 0 ? void 0 : { name, next: end + 1 };
5937
+ }
5938
+ function findAttrError(chunk, from) {
5939
+ const seen = /* @__PURE__ */ new Set();
5940
+ let dup;
5941
+ let i = skipSpace(chunk, from);
5942
+ while (chunk[i] !== ">" && !(chunk[i] === "/" && chunk[i + 1] === ">")) {
5943
+ const pair = readAttrPair(chunk, i);
5944
+ if (pair === void 0) return void 0;
5945
+ if ("message" in pair) return pair;
5946
+ if (seen.has(pair.name) && dup === void 0) dup = { message: XML_ERROR_DUPLICATE_ATTRIBUTE, offset: i };
5947
+ seen.add(pair.name);
5948
+ i = skipSpace(chunk, pair.next);
5949
+ }
5950
+ return dup;
5951
+ }
5952
+ function checkTagChunk(src, start, st) {
5953
+ const tag = parseTagChunk(st.curr);
5954
+ if (tag === void 0) return;
5955
+ if (tag.close) {
5956
+ if (st.stack.length > 0 && st.stack.pop() !== tag.name) {
5957
+ throw htmlErrorInLine(XML_ERROR_TAG_MISMATCH, lineAt(src, start), st.prev + st.curr);
5958
+ }
5959
+ } else {
5960
+ const bad = findAttrError(st.curr, 1 + tag.name.length);
5961
+ if (bad !== void 0) {
5962
+ throw htmlErrorInLine(bad.message, lineAt(src, start + bad.offset), st.prev + st.curr);
5963
+ }
5964
+ if (!tag.selfClose) st.stack.push(tag.name);
5965
+ }
5966
+ if (KNOWN_TAGS.has(tag.name.toUpperCase())) return;
5967
+ const text = `Unknown HTML element <${tag.name}> on line ${lineAt(src, start)} `;
5968
+ throw new HtmlLexError(tag.name.toUpperCase(), text, [`Error: ${text}`]);
5969
+ }
5970
+ function checkChunk(src, start, st) {
5971
+ if (st.curr.startsWith("<")) {
5972
+ checkTagChunk(src, start, st);
5973
+ return;
5974
+ }
5975
+ const off = findUndefinedEntity(st.curr);
5976
+ if (off >= 0) {
5977
+ throw htmlErrorInLine(XML_ERROR_UNDEFINED_ENTITY, lineAt(src, start + off), st.prev + st.curr);
5978
+ }
5979
+ }
5980
+ function tokenizeDeferred(src) {
5858
5981
  const tokens = [];
5982
+ const st = { stack: [], prev: "", curr: "" };
5859
5983
  let i = 0;
5860
- while (i < src.length) {
5861
- i = src[i] === "<" ? scanTag(src, i, tokens) : scanText(src, i, tokens);
5984
+ try {
5985
+ while (i < src.length) {
5986
+ const first = tokens.length;
5987
+ const start = i;
5988
+ st.prev = st.curr;
5989
+ st.curr = src.slice(i, chunkEnd(src, i));
5990
+ checkChunk(src, i, st);
5991
+ i = src[i] === "<" ? scanTag(src, i, tokens) : scanText(src, i, tokens);
5992
+ const info = { src, pos: start, end: i, context: st.prev + st.curr };
5993
+ for (let k = first; k < tokens.length; k++) chunkInfo.set(tokens[k], info);
5994
+ }
5995
+ } catch (e) {
5996
+ if (e instanceof HtmlParseError) return { tokens, error: e };
5997
+ throw e;
5862
5998
  }
5863
- return tokens;
5999
+ return { tokens };
5864
6000
  }
5865
6001
 
5866
- // src/common/htmltable-parse.ts
6002
+ // src/common/htmltable-parse-attrs.ts
5867
6003
  function anchorAttr(v) {
5868
6004
  return v === void 0 ? void 0 : htmlEntityUTF8(v);
5869
6005
  }
@@ -5886,6 +6022,81 @@ var num = (s) => {
5886
6022
  const n = parseInt(s, 10);
5887
6023
  return isNaN(n) ? void 0 : n;
5888
6024
  };
6025
+ var parseFixedSize = (a) => a["fixedsize"]?.toUpperCase() === "TRUE" ? true : void 0;
6026
+ var parseTitle = (a) => a["title"] !== void 0 ? a["title"] : a["tooltip"];
6027
+ var parseSides = (v) => {
6028
+ if (v === void 0) return void 0;
6029
+ let flags = 0;
6030
+ for (const c of v.toLowerCase()) {
6031
+ if (c === "l") flags |= BORDER_LEFT;
6032
+ else if (c === "t") flags |= BORDER_TOP;
6033
+ else if (c === "r") flags |= BORDER_RIGHT;
6034
+ else if (c === "b") flags |= BORDER_BOTTOM;
6035
+ }
6036
+ if (flags === 0 || flags === BORDER_MASK) return void 0;
6037
+ return flags;
6038
+ };
6039
+ var parseGradientAngle = (v) => {
6040
+ if (v === void 0) return void 0;
6041
+ const n = parseInt(v, 10);
6042
+ if (isNaN(n) || String(n) !== v.trim()) return void 0;
6043
+ if (n < 0 || n > 360) return void 0;
6044
+ return n;
6045
+ };
6046
+ var parseColumns = (v) => v === "*" ? true : void 0;
6047
+ var parseRows = (v) => v === "*" ? true : void 0;
6048
+ var buildCell = (content, a) => ({
6049
+ kind: "cell",
6050
+ content,
6051
+ port: a["port"],
6052
+ align: parseAlign(a["align"] !== void 0 ? a["align"] : ""),
6053
+ valign: parseVAlign(a["valign"] !== void 0 ? a["valign"] : ""),
6054
+ balign: parseAlign(a["balign"] !== void 0 ? a["balign"] : ""),
6055
+ bgcolor: a["bgcolor"],
6056
+ color: a["color"],
6057
+ border: num(a["border"]),
6058
+ cellpadding: num(a["cellpadding"]),
6059
+ cellspacing: num(a["cellspacing"]),
6060
+ sides: parseSides(a["sides"]),
6061
+ gradientangle: parseGradientAngle(a["gradientangle"]),
6062
+ style: a["style"],
6063
+ width: num(a["width"]),
6064
+ height: num(a["height"]),
6065
+ fixedsize: parseFixedSize(a),
6066
+ colspan: num(a["colspan"]),
6067
+ rowspan: num(a["rowspan"]),
6068
+ href: anchorAttr(a["href"]),
6069
+ title: parseTitle(a),
6070
+ target: anchorAttr(a["target"]),
6071
+ id: a["id"]
6072
+ });
6073
+ var buildTable = (rows, a) => ({
6074
+ kind: "table",
6075
+ rows,
6076
+ border: num(a["border"]),
6077
+ cellborder: num(a["cellborder"]),
6078
+ cellspacing: num(a["cellspacing"]),
6079
+ cellpadding: num(a["cellpadding"]),
6080
+ bgcolor: a["bgcolor"],
6081
+ color: a["color"],
6082
+ style: a["style"],
6083
+ align: parseAlign(a["align"] !== void 0 ? a["align"] : ""),
6084
+ valign: parseVAlign(a["valign"] !== void 0 ? a["valign"] : ""),
6085
+ fixedsize: parseFixedSize(a),
6086
+ width: num(a["width"]),
6087
+ height: num(a["height"]),
6088
+ href: anchorAttr(a["href"]),
6089
+ title: parseTitle(a),
6090
+ target: anchorAttr(a["target"]),
6091
+ id: a["id"],
6092
+ port: a["port"],
6093
+ sides: parseSides(a["sides"]),
6094
+ gradientangle: parseGradientAngle(a["gradientangle"]),
6095
+ vrule: parseColumns(a["columns"]),
6096
+ hrule: parseRows(a["rows"])
6097
+ });
6098
+
6099
+ // src/common/htmltable-parse.ts
5889
6100
  var definedOf = (s) => Object.fromEntries(
5890
6101
  Object.entries(s).filter(([, v]) => v !== void 0)
5891
6102
  );
@@ -5909,6 +6120,11 @@ var fontItemProps = (f) => {
5909
6120
  if (f.fontSize !== void 0) out.fontSize = f.fontSize;
5910
6121
  return out;
5911
6122
  };
6123
+ var MSG_SYNTAX_ERROR = "syntax error";
6124
+ var MSG_TEXT_BEFORE_TABLE = "Syntax error: non-space string used before <TABLE>";
6125
+ var MSG_TEXT_AFTER_TABLE = "Syntax error: non-space string used after </TABLE>";
6126
+ var nonSpace = (text) => /[^ \t\n\r]/.test(text);
6127
+ var parseError = (s, tok, message) => (tok === void 0 ? s.pending : htmlErrorAtToken(message, tok)) ?? new HtmlParseError(message);
5912
6128
  var peek = (s) => s.tokens[s.pos];
5913
6129
  var consume = (s) => {
5914
6130
  const t = s.tokens[s.pos];
@@ -5927,12 +6143,20 @@ var inlineFontState = (tag, _attrs) => {
5927
6143
  if (tag === "SUP") fs.superscript = true;
5928
6144
  return fs;
5929
6145
  };
6146
+ var consumeClose = (s, tag) => {
6147
+ const nxt = peek(s);
6148
+ if (nxt === void 0) return;
6149
+ if (nxt.type === "close" && nxt.tag === tag) {
6150
+ consume(s);
6151
+ return;
6152
+ }
6153
+ throw parseError(s, nxt, MSG_SYNTAX_ERROR);
6154
+ };
5930
6155
  var applyBrTag = (s, t, fontStack, items) => {
5931
6156
  consume(s);
5932
6157
  const font = fontStack[fontStack.length - 1] ?? {};
5933
6158
  items.push({ br: true, brAlign: parseAlign(t.attrs["align"] ?? ""), ...fontItemProps(font) });
5934
- const nxt = peek(s);
5935
- if (nxt?.type === "close" && nxt.tag === "BR") consume(s);
6159
+ consumeClose(s, "BR");
5936
6160
  };
5937
6161
  var applyFontTag = (s, t, fontStack, items) => {
5938
6162
  consume(s);
@@ -5989,12 +6213,6 @@ var parseText = (s, fontStack) => {
5989
6213
  }
5990
6214
  return { kind: "text", items };
5991
6215
  };
5992
- var parseHrContent = (s) => {
5993
- consume(s);
5994
- const nxt = peek(s);
5995
- if (nxt?.type === "close" && nxt.tag === "HR") consume(s);
5996
- return { kind: "hr" };
5997
- };
5998
6216
  var parseImgContent = (s, t, cellAttrs) => {
5999
6217
  consume(s);
6000
6218
  const img = {
@@ -6002,24 +6220,54 @@ var parseImgContent = (s, t, cellAttrs) => {
6002
6220
  src: cellAttrs["src"] ?? t.attrs["src"] ?? "",
6003
6221
  scale: t.attrs["scale"]
6004
6222
  };
6005
- const nxt = peek(s);
6006
- if (nxt?.type === "close" && nxt.tag === "IMG") consume(s);
6223
+ consumeClose(s, "IMG");
6007
6224
  return img;
6008
6225
  };
6009
- var parseCellItem = (s, t, cellAttrs, fontStack) => {
6010
- if (t.type === "open" && t.tag === "TABLE") return [parseTable(s, fontStack)];
6011
- if (t.type === "open" && t.tag === "IMG") return [parseImgContent(s, t, cellAttrs)];
6012
- if (t.type === "open" && t.tag === "HR") return [parseHrContent(s)];
6226
+ var scanCellTable = (s, t, fontStack, st) => {
6227
+ if (st.kind === "image" || st.kind === "table") throw parseError(s, t, MSG_SYNTAX_ERROR);
6228
+ if (st.textNonSpace) throw parseError(s, t, MSG_TEXT_BEFORE_TABLE);
6229
+ const tbl = parseTable(s, fontStack);
6230
+ st.kind = "table";
6231
+ return [tbl];
6232
+ };
6233
+ var scanCellText = (s, t, fontStack, st) => {
6234
+ const start = s.pos;
6013
6235
  const txt = parseText(s, fontStack);
6236
+ if (s.pos === start) throw parseError(s, t, MSG_SYNTAX_ERROR);
6237
+ if (txt.items.some((i) => i.text !== void 0 && nonSpace(i.text))) st.textNonSpace = true;
6238
+ st.kind = "text";
6014
6239
  return txt.items.length > 0 ? [txt] : [];
6015
6240
  };
6241
+ var scanAfterTable = (s, t, st) => {
6242
+ if (t.type !== "text") throw parseError(s, t, st.afterNonSpace ? MSG_TEXT_AFTER_TABLE : MSG_SYNTAX_ERROR);
6243
+ consume(s);
6244
+ if (nonSpace(t.value)) st.afterNonSpace = true;
6245
+ return [];
6246
+ };
6247
+ var scanCellImage = (s, t, cellAttrs, st) => {
6248
+ if (st.kind !== "none") throw parseError(s, t, MSG_SYNTAX_ERROR);
6249
+ st.kind = "image";
6250
+ return [parseImgContent(s, t, cellAttrs)];
6251
+ };
6252
+ var scanCellItem = (s, t, cellAttrs, fontStack, st) => {
6253
+ if (st.kind === "table") return scanAfterTable(s, t, st);
6254
+ if (t.type === "open" && t.tag === "TABLE") return scanCellTable(s, t, fontStack, st);
6255
+ if (t.type === "open" && t.tag === "IMG") return scanCellImage(s, t, cellAttrs, st);
6256
+ if (st.kind === "image") throw parseError(s, t, MSG_SYNTAX_ERROR);
6257
+ return scanCellText(s, t, fontStack, st);
6258
+ };
6259
+ var endsCell = (s, t, st) => {
6260
+ if (t.type !== "close") return false;
6261
+ const afterText = st.kind === "table" && st.afterNonSpace;
6262
+ if (t.tag === "TD" && !afterText) return true;
6263
+ throw parseError(s, t, afterText ? MSG_TEXT_AFTER_TABLE : MSG_SYNTAX_ERROR);
6264
+ };
6016
6265
  var parseCellContent = (s, cellAttrs, fontStack) => {
6017
6266
  const content = [];
6018
- while (s.pos < s.tokens.length) {
6019
- const t = peek(s);
6020
- if (!t) break;
6021
- if (t.type === "close" && t.tag === "TD") break;
6022
- content.push(...parseCellItem(s, t, cellAttrs, fontStack));
6267
+ const st = { kind: "none", textNonSpace: false, afterNonSpace: false };
6268
+ for (let t = peek(s); t !== void 0; t = peek(s)) {
6269
+ if (endsCell(s, t, st)) break;
6270
+ content.push(...scanCellItem(s, t, cellAttrs, fontStack, st));
6023
6271
  }
6024
6272
  const nxt = peek(s);
6025
6273
  if (nxt?.type === "close" && nxt.tag === "TD") consume(s);
@@ -6027,134 +6275,85 @@ var parseCellContent = (s, cellAttrs, fontStack) => {
6027
6275
  if (tbl !== void 0) return [tbl];
6028
6276
  return content;
6029
6277
  };
6030
- var parseFixedSize = (a) => a["fixedsize"]?.toUpperCase() === "TRUE" ? true : void 0;
6031
- var parseTitle = (a) => a["title"] !== void 0 ? a["title"] : a["tooltip"];
6032
- var parseSides = (v) => {
6033
- if (v === void 0) return void 0;
6034
- let flags = 0;
6035
- for (const c of v.toLowerCase()) {
6036
- if (c === "l") flags |= BORDER_LEFT;
6037
- else if (c === "t") flags |= BORDER_TOP;
6038
- else if (c === "r") flags |= BORDER_RIGHT;
6039
- else if (c === "b") flags |= BORDER_BOTTOM;
6040
- }
6041
- if (flags === 0 || flags === BORDER_MASK) return void 0;
6042
- return flags;
6043
- };
6044
- var parseGradientAngle = (v) => {
6045
- if (v === void 0) return void 0;
6046
- const n = parseInt(v, 10);
6047
- if (isNaN(n) || String(n) !== v.trim()) return void 0;
6048
- if (n < 0 || n > 360) return void 0;
6049
- return n;
6050
- };
6051
- var parseColumns = (v) => v === "*" ? true : void 0;
6052
- var parseRows = (v) => v === "*" ? true : void 0;
6053
- var buildCell = (content, a) => ({
6054
- kind: "cell",
6055
- content,
6056
- port: a["port"],
6057
- align: parseAlign(a["align"] !== void 0 ? a["align"] : ""),
6058
- valign: parseVAlign(a["valign"] !== void 0 ? a["valign"] : ""),
6059
- balign: parseAlign(a["balign"] !== void 0 ? a["balign"] : ""),
6060
- bgcolor: a["bgcolor"],
6061
- color: a["color"],
6062
- border: num(a["border"]),
6063
- cellpadding: num(a["cellpadding"]),
6064
- cellspacing: num(a["cellspacing"]),
6065
- sides: parseSides(a["sides"]),
6066
- gradientangle: parseGradientAngle(a["gradientangle"]),
6067
- style: a["style"],
6068
- width: num(a["width"]),
6069
- height: num(a["height"]),
6070
- fixedsize: parseFixedSize(a),
6071
- colspan: num(a["colspan"]),
6072
- rowspan: num(a["rowspan"]),
6073
- href: anchorAttr(a["href"]),
6074
- title: parseTitle(a),
6075
- target: anchorAttr(a["target"]),
6076
- id: a["id"]
6077
- });
6078
6278
  var parseCell = (s, attrs, fontStack) => buildCell(parseCellContent(s, attrs, fontStack), attrs);
6079
- var processRowToken = (s, t, cells, fontStack) => {
6279
+ var parseRowVr = (s, t, cells) => {
6280
+ const prev = cells[cells.length - 1];
6281
+ if (prev === void 0) throw parseError(s, t, MSG_SYNTAX_ERROR);
6282
+ consume(s);
6283
+ consumeClose(s, "VR");
6284
+ prev.vruled = true;
6285
+ };
6286
+ var scanRowCell = (s, t, fontStack, rs) => {
6080
6287
  if (t.type === "open" && t.tag === "TD") {
6081
6288
  consume(s);
6082
- cells.push(parseCell(s, t.attrs, fontStack));
6083
- return;
6289
+ rs.cells.push(parseCell(s, t.attrs, fontStack));
6290
+ rs.needCell = false;
6291
+ } else if (t.type === "open" && t.tag === "VR" && !rs.needCell) {
6292
+ parseRowVr(s, t, rs.cells);
6293
+ rs.needCell = true;
6294
+ } else {
6295
+ throw parseError(s, t, MSG_SYNTAX_ERROR);
6084
6296
  }
6085
- if (t.type === "open" && t.tag === "VR") {
6297
+ };
6298
+ var scanRowToken = (s, t, fontStack, rs) => {
6299
+ if (t.type === "text") {
6086
6300
  consume(s);
6087
- const nxt = peek(s);
6088
- if (nxt?.type === "close" && nxt.tag === "VR") consume(s);
6089
- const prev = cells[cells.length - 1];
6090
- if (prev === void 0) throw new HtmlParseError("VR");
6091
- prev.vruled = true;
6092
- return;
6301
+ return false;
6093
6302
  }
6094
- consume(s);
6303
+ if (t.type !== "close" || t.tag !== "TR") {
6304
+ scanRowCell(s, t, fontStack, rs);
6305
+ return false;
6306
+ }
6307
+ if (rs.cells.length === 0 || rs.needCell) throw parseError(s, t, MSG_SYNTAX_ERROR);
6308
+ return true;
6095
6309
  };
6096
6310
  var parseRow = (s, fontStack) => {
6097
- const cells = [];
6098
- while (s.pos < s.tokens.length) {
6099
- const t = peek(s);
6100
- if (!t) break;
6101
- if (t.type === "close" && t.tag === "TR") break;
6102
- processRowToken(s, t, cells, fontStack);
6311
+ const rs = { cells: [], needCell: false };
6312
+ for (let t = peek(s); t !== void 0; t = peek(s)) {
6313
+ if (scanRowToken(s, t, fontStack, rs)) break;
6103
6314
  }
6104
6315
  const nxt = peek(s);
6105
6316
  if (nxt?.type === "close" && nxt.tag === "TR") consume(s);
6106
- return { cells };
6317
+ return { cells: rs.cells };
6318
+ };
6319
+ var scanTableRow = (s, t, fontStack, ts) => {
6320
+ if (t.type === "open" && t.tag === "TR") {
6321
+ consume(s);
6322
+ ts.rows.push(parseTableRow(s, fontStack, ts));
6323
+ ts.needRow = false;
6324
+ } else if (t.type === "open" && t.tag === "HR" && ts.rows.length > 0 && !ts.needRow) {
6325
+ consumeRowHr(s, ts.rows);
6326
+ ts.needRow = true;
6327
+ } else {
6328
+ throw parseError(s, t, MSG_SYNTAX_ERROR);
6329
+ }
6330
+ };
6331
+ var scanTableToken = (s, t, fontStack, ts) => {
6332
+ if (t.type === "text") {
6333
+ consume(s);
6334
+ return false;
6335
+ }
6336
+ if (t.type !== "close" || t.tag !== "TABLE") {
6337
+ scanTableRow(s, t, fontStack, ts);
6338
+ return false;
6339
+ }
6340
+ if (ts.rows.length === 0 || ts.needRow) throw parseError(s, t, MSG_SYNTAX_ERROR);
6341
+ consume(s);
6342
+ return true;
6107
6343
  };
6108
- var buildTable = (rows, a) => ({
6109
- kind: "table",
6110
- rows,
6111
- border: num(a["border"]),
6112
- cellborder: num(a["cellborder"]),
6113
- cellspacing: num(a["cellspacing"]),
6114
- cellpadding: num(a["cellpadding"]),
6115
- bgcolor: a["bgcolor"],
6116
- color: a["color"],
6117
- style: a["style"],
6118
- align: parseAlign(a["align"] !== void 0 ? a["align"] : ""),
6119
- valign: parseVAlign(a["valign"] !== void 0 ? a["valign"] : ""),
6120
- fixedsize: parseFixedSize(a),
6121
- width: num(a["width"]),
6122
- height: num(a["height"]),
6123
- href: anchorAttr(a["href"]),
6124
- title: parseTitle(a),
6125
- target: anchorAttr(a["target"]),
6126
- id: a["id"],
6127
- port: a["port"],
6128
- sides: parseSides(a["sides"]),
6129
- gradientangle: parseGradientAngle(a["gradientangle"]),
6130
- vrule: parseColumns(a["columns"]),
6131
- hrule: parseRows(a["rows"])
6132
- });
6133
6344
  var parseTable = (s, fontStack) => {
6134
6345
  const open = consume(s);
6135
6346
  if (open.type !== "open" || open.tag !== "TABLE") throw new HtmlParseError("TABLE");
6136
- const hrule = parseRows(open.attrs["rows"]) === true;
6137
- const vrule = parseColumns(open.attrs["columns"]) === true;
6138
- const rows = [];
6139
- while (s.pos < s.tokens.length) {
6140
- const t = peek(s);
6141
- if (!t) break;
6142
- if (t.type === "close" && t.tag === "TABLE") {
6143
- consume(s);
6144
- break;
6145
- }
6146
- if (t.type === "open" && t.tag === "TR") {
6147
- consume(s);
6148
- rows.push(parseTableRow(s, fontStack, { hrule, vrule }));
6149
- continue;
6150
- }
6151
- if (t.type === "open" && t.tag === "HR") {
6152
- consumeRowHr(s, rows);
6153
- continue;
6154
- }
6155
- consume(s);
6347
+ const ts = {
6348
+ rows: [],
6349
+ needRow: false,
6350
+ hrule: parseRows(open.attrs["rows"]) === true,
6351
+ vrule: parseColumns(open.attrs["columns"]) === true
6352
+ };
6353
+ for (let t = peek(s); t !== void 0; t = peek(s)) {
6354
+ if (scanTableToken(s, t, fontStack, ts)) break;
6156
6355
  }
6157
- return buildTable(rows, open.attrs);
6356
+ return buildTable(ts.rows, open.attrs);
6158
6357
  };
6159
6358
  var parseTableRow = (s, fontStack, opts) => {
6160
6359
  const row = parseRow(s, fontStack);
@@ -6164,11 +6363,9 @@ var parseTableRow = (s, fontStack, opts) => {
6164
6363
  };
6165
6364
  var consumeRowHr = (s, rows) => {
6166
6365
  consume(s);
6167
- const nxt = peek(s);
6168
- if (nxt?.type === "close" && nxt.tag === "HR") consume(s);
6366
+ consumeClose(s, "HR");
6169
6367
  const prev = rows[rows.length - 1];
6170
- if (prev === void 0) throw new HtmlParseError("HR");
6171
- prev.ruled = true;
6368
+ if (prev !== void 0) prev.ruled = true;
6172
6369
  };
6173
6370
  var findFirstRealToken = (tokens, start) => {
6174
6371
  for (let i = start; i < tokens.length; i++) {
@@ -6184,34 +6381,61 @@ var skipLeadingWs = (s) => {
6184
6381
  consume(s);
6185
6382
  }
6186
6383
  };
6384
+ var finishTableLabel = (s) => {
6385
+ let afterNonSpace = false;
6386
+ for (let t = peek(s); t?.type === "text"; t = peek(s)) {
6387
+ consume(s);
6388
+ if (nonSpace(t.value)) afterNonSpace = true;
6389
+ }
6390
+ const nxt = peek(s);
6391
+ if (nxt === void 0) return;
6392
+ if (afterNonSpace) throw parseError(s, nxt, MSG_TEXT_AFTER_TABLE);
6393
+ if (nxt.type !== "close" || nxt.tag !== "HTML") throw parseError(s, nxt, MSG_SYNTAX_ERROR);
6394
+ consume(s);
6395
+ };
6187
6396
  var parseTableLabel = (s, fontStack) => {
6188
6397
  skipLeadingWs(s);
6189
6398
  const table = parseTable(s, fontStack);
6190
- const nxt = peek(s);
6191
- if (nxt?.type === "close" && nxt.tag === "HTML") consume(s);
6399
+ finishTableLabel(s);
6192
6400
  return { kind: "table", table };
6193
6401
  };
6194
- var parseHtmlLabel = (src) => {
6195
- const wrapped = `<HTML>${src}</HTML>`;
6196
- const tokens = tokenize(wrapped);
6197
- const s = { tokens, pos: 0 };
6198
- const fontStack = [{}];
6199
- const htmlOpen = consume(s);
6200
- if (htmlOpen.type !== "open" || htmlOpen.tag !== "HTML") throw new HtmlParseError("HTML");
6201
- const firstReal = findFirstRealToken(tokens, s.pos);
6202
- if (firstReal?.type === "open" && firstReal.tag === "TABLE") {
6203
- return parseTableLabel(s, fontStack);
6204
- }
6402
+ var parseTextLabel = (s, fontStack) => {
6205
6403
  const texts = [];
6206
6404
  const txt = parseText(s, fontStack);
6207
6405
  if (txt.items.length > 0) texts.push(txt);
6208
6406
  const nxt = peek(s);
6209
- if (nxt?.type === "open" && nxt.tag === "TABLE") {
6210
- throw new HtmlParseError("non-space string used before <TABLE>");
6407
+ if (nxt === void 0) return { kind: "text", texts };
6408
+ if (nxt.type === "open" && nxt.tag === "TABLE") {
6409
+ if (txt.items.some((i) => i.text !== void 0 && nonSpace(i.text))) {
6410
+ throw parseError(s, nxt, MSG_TEXT_BEFORE_TABLE);
6411
+ }
6412
+ throw new HtmlParseError("TABLE");
6211
6413
  }
6212
- if (nxt?.type === "close" && nxt.tag === "HTML") consume(s);
6414
+ if (nxt.type !== "close" || nxt.tag !== "HTML") throw parseError(s, nxt, MSG_SYNTAX_ERROR);
6415
+ consume(s);
6213
6416
  return { kind: "text", texts };
6214
6417
  };
6418
+ var parseTokens = (s) => {
6419
+ const fontStack = [{}];
6420
+ const htmlOpen = consume(s);
6421
+ if (htmlOpen.type !== "open" || htmlOpen.tag !== "HTML") throw new HtmlParseError("HTML");
6422
+ const firstReal = findFirstRealToken(s.tokens, s.pos);
6423
+ if (firstReal?.type === "open" && firstReal.tag === "TABLE") return parseTableLabel(s, fontStack);
6424
+ return parseTextLabel(s, fontStack);
6425
+ };
6426
+ var parseHtmlLabel = (src) => {
6427
+ const { tokens, error } = tokenizeDeferred(`<HTML>${src}</HTML>`);
6428
+ const s = { tokens, pos: 0, pending: error };
6429
+ let label;
6430
+ try {
6431
+ label = parseTokens(s);
6432
+ } catch (e) {
6433
+ if (error !== void 0 && s.pos >= tokens.length) throw error;
6434
+ throw e;
6435
+ }
6436
+ if (error !== void 0) throw error;
6437
+ return label;
6438
+ };
6215
6439
 
6216
6440
  // src/gvc/usershape.ts
6217
6441
  var activeSizer = null;
@@ -6219,28 +6443,80 @@ function findImageSize(src) {
6219
6443
  return activeSizer !== null ? activeSizer(src) : null;
6220
6444
  }
6221
6445
 
6446
+ // src/common/pointset.ts
6447
+ function cmpPair(a, b) {
6448
+ if (a.x > b.x) return 1;
6449
+ if (a.x < b.x) return -1;
6450
+ if (a.y > b.y) return 1;
6451
+ if (a.y < b.y) return -1;
6452
+ return 0;
6453
+ }
6454
+ var PointSet = class {
6455
+ byX = /* @__PURE__ */ new Map();
6456
+ n = 0;
6457
+ /**
6458
+ * Insert (x, y). A duplicate keeps the point inserted first, as `dtinsert`
6459
+ * returns the existing object and the new one is freed.
6460
+ * @see lib/common/pointset.c:addPS
6461
+ */
6462
+ addPS(x, y) {
6463
+ let col = this.byX.get(x);
6464
+ if (col === void 0) {
6465
+ col = /* @__PURE__ */ new Map();
6466
+ this.byX.set(x, col);
6467
+ }
6468
+ if (col.has(y)) return;
6469
+ col.set(y, { x, y });
6470
+ this.n++;
6471
+ }
6472
+ /** @see lib/common/pointset.c:inPS */
6473
+ inPS(pt) {
6474
+ return this.byX.get(pt.x)?.has(pt.y) ?? false;
6475
+ }
6476
+ /** @see lib/common/pointset.c:isInPS */
6477
+ isInPS(x, y) {
6478
+ return this.byX.get(x)?.has(y) ?? false;
6479
+ }
6480
+ /** @see lib/common/pointset.c:sizeOf */
6481
+ sizeOf() {
6482
+ return this.n;
6483
+ }
6484
+ /**
6485
+ * The points in dictionary order (`dtflatten` walks the ordered set).
6486
+ * @see lib/common/pointset.c:pointsOf
6487
+ */
6488
+ pointsOf() {
6489
+ const pts = [];
6490
+ for (const col of this.byX.values()) {
6491
+ for (const p of col.values()) pts.push({ x: p.x, y: p.y });
6492
+ }
6493
+ return pts.sort(cmpPair);
6494
+ }
6495
+ };
6496
+
6222
6497
  // src/common/htmltable.ts
6223
6498
  var DEFAULT_BORDER = 1;
6224
6499
  var DEFAULT_CELLPADDING = 2;
6225
6500
  var DEFAULT_CELLSPACING = 2;
6226
6501
  var DEFAULT_FONTSIZE = 14;
6227
- var findCol = (used, row, startCol, colspan, rowspan) => {
6502
+ var findCol = (ps, row, startCol, colspan, rowspan) => {
6228
6503
  let col = startCol;
6229
- for (; ; ) {
6230
- let conflict = false;
6231
- for (let c = col; c < col + colspan && !conflict; c++) {
6232
- for (let r = row; r < row + rowspan && !conflict; r++) {
6233
- if (used.has(`${c},${r}`)) {
6234
- conflict = true;
6235
- col = c + 1;
6236
- }
6237
- }
6504
+ let notFound = true;
6505
+ const end = colspan - 1;
6506
+ while (notFound) {
6507
+ const lastc = col + end;
6508
+ let c;
6509
+ for (c = lastc; c >= col; c--) {
6510
+ if (ps.isInPS(c, row)) break;
6511
+ }
6512
+ if (c >= col) col = c + 1;
6513
+ else notFound = false;
6514
+ }
6515
+ for (let j = col; j < col + colspan; j++) {
6516
+ for (let i = row; i < row + rowspan; i++) {
6517
+ ps.addPS(j, i);
6238
6518
  }
6239
- if (!conflict) break;
6240
6519
  }
6241
- for (let c = col; c < col + colspan; c++)
6242
- for (let r = row; r < row + rowspan; r++)
6243
- used.add(`${c},${r}`);
6244
6520
  return col;
6245
6521
  };
6246
6522
  var DEFAULT_SIZE_ENV = { fontsize: DEFAULT_FONTSIZE, fontname: "" };
@@ -6319,29 +6595,32 @@ var sizeTextContent = (texts, measurer, env = DEFAULT_SIZE_ENV) => {
6319
6595
  if (lines.length === 1) h = lines[0].items.length > 0 ? lines[0].mxysize : DEFAULT_FONTSIZE;
6320
6596
  return { w, h };
6321
6597
  };
6598
+ var missingImages = /* @__PURE__ */ new WeakSet();
6322
6599
  var sizeHtmlImg = (img, imageSizer) => {
6323
- if (img.width !== void 0 && img.height !== void 0) return;
6600
+ if (img.width !== void 0 && img.height !== void 0) return missingImages.has(img) ? 1 : 0;
6324
6601
  const sz = imageSizer !== void 0 ? imageSizer(img.src) : findImageSize(img.src);
6325
6602
  if (sz !== null) {
6326
6603
  img.width = sz.w;
6327
6604
  img.height = sz.h;
6328
- return;
6605
+ return 0;
6329
6606
  }
6330
6607
  console.error(`Error: No or improper image file="${img.src}"`);
6331
6608
  img.width = 0;
6332
6609
  img.height = 0;
6610
+ missingImages.add(img);
6611
+ return 1;
6333
6612
  };
6334
6613
  var sizeContentItem = (item, measurer, env) => {
6335
- if (item.kind === "text") return sizeTextContent([item], measurer, env);
6614
+ if (item.kind === "text") return { ...sizeTextContent([item], measurer, env), rv: 0 };
6336
6615
  if (item.kind === "table") {
6337
- sizeTableInner(item, measurer, env);
6338
- return item.dimen !== void 0 ? item.dimen : { w: 0, h: 0 };
6616
+ const rv = sizeTableInner(item, measurer, env);
6617
+ return { ...item.dimen !== void 0 ? item.dimen : { w: 0, h: 0 }, rv };
6339
6618
  }
6340
6619
  if (item.kind === "image") {
6341
- sizeHtmlImg(item, env.imageSizer);
6342
- return { w: item.width ?? 0, h: item.height ?? 0 };
6620
+ const rv = sizeHtmlImg(item, env.imageSizer);
6621
+ return { w: item.width ?? 0, h: item.height ?? 0, rv };
6343
6622
  }
6344
- return { w: 0, h: 0 };
6623
+ return { w: 0, h: 0, rv: 0 };
6345
6624
  };
6346
6625
  var cellPad = (cell2, tbl) => cell2.cellpadding !== void 0 ? cell2.cellpadding : tbl.cellpadding !== void 0 ? tbl.cellpadding : DEFAULT_CELLPADDING;
6347
6626
  var cellBorder = (cell2, tbl) => {
@@ -6351,27 +6630,60 @@ var cellBorder = (cell2, tbl) => {
6351
6630
  if (tbl.border !== void 0) return tbl.border;
6352
6631
  return DEFAULT_BORDER;
6353
6632
  };
6633
+ function inheritTablePenColor(tbl, parentColor) {
6634
+ if (tbl.color === void 0) tbl.color = parentColor;
6635
+ const tblColor = tbl.color;
6636
+ for (const row of tbl.rows) {
6637
+ for (const cell2 of row.cells) {
6638
+ if (cell2.color === void 0) cell2.color = tblColor;
6639
+ for (const item of cell2.content) {
6640
+ if (item.kind === "table") inheritTablePenColor(item, cell2.color);
6641
+ }
6642
+ }
6643
+ }
6644
+ }
6645
+ var WARN_CELL_TOO_SMALL = "Warning: cell size too small for content";
6646
+ var WARN_CELL_UNSPECIFIED = "Warning: fixed cell size with unspecified width or height";
6647
+ var WARN_TABLE_TOO_SMALL = "Warning: table size too small for content";
6648
+ var WARN_TABLE_UNSPECIFIED = "Warning: fixed table size with unspecified width or height";
6649
+ var sizeWarned = /* @__PURE__ */ new WeakSet();
6650
+ var fixedCellCheck = (cell2, sz, warn) => {
6651
+ if (!cell2.fixedsize) return { rv: 0, zero: false };
6652
+ if (cell2.width && cell2.height) {
6653
+ const small = (cell2.width < sz.w || cell2.height < sz.h) && cell2.content[0]?.kind !== "image";
6654
+ if (small && warn) console.warn(WARN_CELL_TOO_SMALL);
6655
+ return { rv: small ? 1 : 0, zero: true };
6656
+ }
6657
+ if (warn) console.warn(WARN_CELL_UNSPECIFIED);
6658
+ return { rv: 1, zero: false };
6659
+ };
6354
6660
  var getCellSize = (cell2, tbl, measurer, env) => {
6355
6661
  const margin = 2 * (cellPad(cell2, tbl) + cellBorder(cell2, tbl));
6356
- let cw = 0, ch = 0;
6662
+ let cw = 0, ch = 0, rv = 0;
6357
6663
  for (const item of cell2.content) {
6358
6664
  const s = sizeContentItem(item, measurer, env);
6359
6665
  cw = Math.max(cw, s.w);
6360
6666
  ch += s.h;
6667
+ rv |= s.rv;
6361
6668
  }
6362
6669
  let szx = cw + margin;
6363
6670
  let szy = ch + margin;
6364
- if (cell2.fixedsize && cell2.width && cell2.height) {
6671
+ const warn = !sizeWarned.has(cell2);
6672
+ sizeWarned.add(cell2);
6673
+ const fixed = fixedCellCheck(cell2, { w: szx, h: szy }, warn);
6674
+ rv |= fixed.rv;
6675
+ if (fixed.zero) {
6365
6676
  szx = 0;
6366
6677
  szy = 0;
6367
6678
  }
6368
6679
  const bw = cell2.width !== void 0 ? cell2.width : 0;
6369
6680
  const bh = cell2.height !== void 0 ? cell2.height : 0;
6370
- return { w: Math.max(szx, bw), h: Math.max(szy, bh) };
6681
+ return { w: Math.max(szx, bw), h: Math.max(szy, bh), rv };
6371
6682
  };
6372
6683
  var buildLayouts = (tbl, measurer, env) => {
6373
6684
  const entries = [];
6374
- const used = /* @__PURE__ */ new Set();
6685
+ let rv = 0;
6686
+ const ps = new PointSet();
6375
6687
  let rowIdx = 0;
6376
6688
  for (const row of tbl.rows) {
6377
6689
  let col = 0;
@@ -6381,14 +6693,15 @@ var buildLayouts = (tbl, measurer, env) => {
6381
6693
  }
6382
6694
  const colspan = c.colspan !== void 0 ? c.colspan : 1;
6383
6695
  const rowspan = c.rowspan !== void 0 ? c.rowspan : 1;
6384
- const { w, h } = getCellSize(c, tbl, measurer, env);
6385
- col = findCol(used, rowIdx, col, colspan, rowspan);
6696
+ const { w, h, rv: cellRv } = getCellSize(c, tbl, measurer, env);
6697
+ rv |= cellRv;
6698
+ col = findCol(ps, rowIdx, col, colspan, rowspan);
6386
6699
  entries.push({ cell: c, row: rowIdx, col, colspan, rowspan, w, h });
6387
6700
  col += colspan;
6388
6701
  }
6389
6702
  rowIdx++;
6390
6703
  }
6391
- return entries;
6704
+ return { entries, rv };
6392
6705
  };
6393
6706
  var setColWidths = (entries, ncols, spacing) => {
6394
6707
  const widths = new Array(ncols + 1).fill(0);
@@ -6441,21 +6754,34 @@ var sumTableDims = (a) => {
6441
6754
  var layoutHtmlTable = (tbl, measurer, env = DEFAULT_SIZE_ENV) => {
6442
6755
  const spacing = getSpacing(tbl);
6443
6756
  const border = getBorder(tbl);
6444
- const entries = buildLayouts(tbl, measurer, env);
6757
+ const { entries, rv } = buildLayouts(tbl, measurer, env);
6445
6758
  if (entries.length === 0) {
6446
- return { entries, widths: [], heights: [], spacing, border };
6759
+ return { entries, widths: [], heights: [], spacing, border, rv };
6447
6760
  }
6448
6761
  const ncols = Math.max(...entries.map((e) => e.col + e.colspan));
6449
6762
  const nrows = Math.max(...entries.map((e) => e.row + e.rowspan));
6450
6763
  const widths = setColWidths(entries, ncols, spacing);
6451
6764
  const heights = setRowHeights(entries, nrows, spacing);
6452
- return { entries, widths, heights, spacing, border };
6765
+ return { entries, widths, heights, spacing, border, rv };
6766
+ };
6767
+ var warnFixedTable = (tbl, dims) => {
6768
+ const warn = !sizeWarned.has(tbl);
6769
+ sizeWarned.add(tbl);
6770
+ if (tbl.fixedsize !== true) return 0;
6771
+ if (tbl.width && tbl.height) {
6772
+ if (tbl.width >= dims.wd && tbl.height >= dims.ht) return 0;
6773
+ if (warn) console.warn(WARN_TABLE_TOO_SMALL);
6774
+ return 1;
6775
+ }
6776
+ if (warn) console.warn(WARN_TABLE_UNSPECIFIED);
6777
+ return 1;
6453
6778
  };
6454
6779
  var sizeTableInner = (tbl, measurer, env) => {
6455
- const { entries, widths, heights, spacing, border } = layoutHtmlTable(tbl, measurer, env);
6780
+ const { entries, widths, heights, spacing, border, rv } = layoutHtmlTable(tbl, measurer, env);
6456
6781
  if (entries.length === 0) {
6782
+ const fixed2 = warnFixedTable(tbl, sumTableDims({ widths, heights, ncols: 0, nrows: 0, spacing, border }));
6457
6783
  tbl.dimen = { w: 0, h: 0 };
6458
- return;
6784
+ return rv | fixed2;
6459
6785
  }
6460
6786
  const ncols = Math.max(...entries.map((e) => e.col + e.colspan));
6461
6787
  const nrows = Math.max(...entries.map((e) => e.row + e.rowspan));
@@ -6463,19 +6789,22 @@ var sizeTableInner = (tbl, measurer, env) => {
6463
6789
  const { wd, ht } = sumTableDims({ widths, heights, ncols, nrows, spacing, border });
6464
6790
  const baseWd = tbl.width !== void 0 ? tbl.width : 0;
6465
6791
  const baseHt = tbl.height !== void 0 ? tbl.height : 0;
6792
+ const fixed = warnFixedTable(tbl, { wd, ht });
6466
6793
  const zeroContent = isTblFixed(tbl) && baseWd !== 0 && baseHt !== 0;
6467
6794
  tbl.dimen = {
6468
6795
  w: Math.max(zeroContent ? 0 : wd, baseWd),
6469
6796
  h: Math.max(zeroContent ? 0 : ht, baseHt)
6470
6797
  };
6798
+ return rv | fixed;
6471
6799
  };
6472
6800
  var sizeHtmlLabel = (label, measurer, env = DEFAULT_SIZE_ENV) => {
6473
6801
  if (label.kind === "table") {
6474
- sizeTableInner(label.table, measurer, env);
6802
+ const rv = sizeTableInner(label.table, measurer, env);
6475
6803
  label.dimen = label.table.dimen;
6476
- } else {
6477
- label.dimen = sizeTextContent(label.texts, measurer, env);
6804
+ return rv;
6478
6805
  }
6806
+ label.dimen = sizeTextContent(label.texts, measurer, env);
6807
+ return 0;
6479
6808
  };
6480
6809
 
6481
6810
  // src/common/splines-constants.ts
@@ -6918,28 +7247,18 @@ function posHtmlLabel(label, finfo, measurer) {
6918
7247
  if (label.kind === "table") return posHtmlTable(label.table, finfo, measurer);
6919
7248
  return posTextLabel(label, finfo, measurer);
6920
7249
  }
6921
- function inheritTablePenColor(tbl, parentColor) {
6922
- if (tbl.color === void 0) tbl.color = parentColor;
6923
- const tblColor = tbl.color;
6924
- for (const row of tbl.rows) {
6925
- for (const cell2 of row.cells) {
6926
- if (cell2.color === void 0) cell2.color = tblColor;
6927
- for (const item of cell2.content) {
6928
- if (item.kind === "table") inheritTablePenColor(item, cell2.color);
6929
- }
6930
- }
6931
- }
6932
- }
6933
- function makeHtmlLabel(content, finfo, measurer) {
7250
+ var HTML_PARSE_FATAL = 3;
7251
+ function makeHtmlLabelRv(content, finfo, measurer) {
6934
7252
  const { fontname, fontsize, fontcolor, imageSizer, pencolor } = finfo;
6935
7253
  let lbl;
6936
7254
  try {
6937
7255
  lbl = parseHtmlLabel(content);
6938
- } catch {
6939
- return makeLabel("", fontname, fontsize, fontcolor, measurer);
7256
+ } catch (e) {
7257
+ if (e instanceof HtmlLexError) for (const line of e.diagnostics) console.error(line);
7258
+ return { label: makeLabel("", fontname, fontsize, fontcolor, measurer), rv: HTML_PARSE_FATAL };
6940
7259
  }
6941
7260
  if (lbl.kind === "table") inheritTablePenColor(lbl.table, pencolor);
6942
- sizeHtmlLabel(lbl, measurer, {
7261
+ const rv = sizeHtmlLabel(lbl, measurer, {
6943
7262
  fontsize,
6944
7263
  fontname,
6945
7264
  imageSizer
@@ -6947,7 +7266,7 @@ function makeHtmlLabel(content, finfo, measurer) {
6947
7266
  const placed = posHtmlLabel(lbl, { fontname, fontsize, fontcolor }, measurer);
6948
7267
  const dim = lbl.dimen ?? { w: 0, h: 0 };
6949
7268
  const text = lbl.kind === "table" ? "<TABLE>" : content;
6950
- return htmlTextLabel(text, { fontname, fontsize, fontcolor }, placed, dim);
7269
+ return { label: htmlTextLabel(text, { fontname, fontsize, fontcolor }, placed, dim), rv };
6951
7270
  }
6952
7271
  function htmlTextLabel(text, finfo, placed, dim) {
6953
7272
  return {
@@ -7336,6 +7655,159 @@ function interpretCRNL(str) {
7336
7655
  return out;
7337
7656
  }
7338
7657
 
7658
+ // src/model/graphInfo.ts
7659
+ function makeGraphInfo() {
7660
+ return {
7661
+ bb: { ll: { x: 0, y: 0 }, ur: { x: 0, y: 0 } },
7662
+ rankdir: 0,
7663
+ flags: 0,
7664
+ charset: 0,
7665
+ gui_state: 0,
7666
+ has_labels: 0,
7667
+ has_images: false
7668
+ };
7669
+ }
7670
+
7671
+ // src/model/graph.ts
7672
+ var Graph = class {
7673
+ /** Graph name; agnameof equivalent. @see lib/cgraph/cgraph.h:agnameof */
7674
+ name;
7675
+ /**
7676
+ * True when the graph was declared with NO name (`digraph {`), as opposed to
7677
+ * an explicit empty name (`digraph "" {`). Both store `name === ''`, but
7678
+ * cgraph gives the truly-anonymous root the internal id `%1` (agnameof) while
7679
+ * the empty-named one keeps `""`. Only the imagemap `<map id/name>` reads this
7680
+ * (map.ts:mapGraphName); dot/xdot re-serialization keeps `name` so an
7681
+ * anonymous root round-trips as `digraph {`, not `digraph %1 {`.
7682
+ * @see lib/cgraph/id.c:idmap (anon → `%1`) ; lib/cgraph/agraph.c:agnameof
7683
+ */
7684
+ anonymous;
7685
+ /**
7686
+ * Directed/strict classification; mirrors Agdesc_t.directed and
7687
+ * Agdesc_t.strict. @see lib/cgraph/cgraph.h:Agdesc_s
7688
+ */
7689
+ kind;
7690
+ /**
7691
+ * Node set in insertion order, keyed by name.
7692
+ * Mirrors Agraph_t.n_seq (sequence dict) merged with n_id access.
7693
+ * @see lib/cgraph/cgraph.h:Agraph_s
7694
+ */
7695
+ nodes;
7696
+ /**
7697
+ * All edges owned by this graph (root graph only for ownership; subgraphs
7698
+ * reference a subset). Mirrors Agraph_t.e_seq.
7699
+ * @see lib/cgraph/cgraph.h:Agraph_s
7700
+ */
7701
+ edges;
7702
+ /**
7703
+ * Named subgraphs, keyed by name. Mirrors Agraph_t.g_seq/g_id.
7704
+ * @see lib/cgraph/cgraph.h:Agraph_s
7705
+ */
7706
+ subgraphs;
7707
+ /**
7708
+ * String attributes (agget/agset equivalents).
7709
+ * Replaces the Agattr_t / Agsym_t machinery for simple key-value access.
7710
+ * @see lib/cgraph/cgraph.h:Agattr_s
7711
+ */
7712
+ attrs;
7713
+ /** Default attributes applied to all nodes in this graph scope. */
7714
+ nodeDefaults;
7715
+ /** Default attributes applied to all edges in this graph scope. */
7716
+ edgeDefaults;
7717
+ /**
7718
+ * Snapshot of the enclosing graphs' graph-attribute defaults (this graph ->
7719
+ * root, inner wins) captured when this subgraph was OPENED during parsing.
7720
+ * Graph attributes set on an ancestor AFTER this subgraph was created are not
7721
+ * included — mirroring cgraph's parse-time agsubg defval copy. Used for
7722
+ * order-correct label/font inheritance (e.g. a root `label=` declared after a
7723
+ * cluster must NOT apply to that cluster). Undefined on the root graph.
7724
+ * @see lib/cgraph/graph.c:agsubg (attr defval copy)
7725
+ */
7726
+ graphDefaultsSnapshot;
7727
+ /**
7728
+ * Keys in `attrs` the BUILDER seeded from `graphDefaultsSnapshot` rather than
7729
+ * the input declaring them in this scope (see builder.ts GRAPH_LABEL_INHERIT_
7730
+ * KEYS). cgraph distinguishes these structurally — a seeded value is inherited
7731
+ * through the dict view and has no local symbol, while a local declaration
7732
+ * creates one even when its value equals the inherited value — so the
7733
+ * serializer needs the provenance to decide what write_dict would print.
7734
+ * Undefined on the root graph and on any scope that seeded nothing.
7735
+ * @see lib/cgraph/write.c:262 write_dict
7736
+ */
7737
+ seededAttrs;
7738
+ /**
7739
+ * Graph-attribute keys whose declaration in THIS scope was the first anywhere
7740
+ * in the graph. cgraph's `setattr` splits on exactly this: a key with no
7741
+ * existing symbol takes the global-declaration branch, which ends up seeding
7742
+ * every direct subgraph that already exists with a local empty value (see
7743
+ * `eagerEmptyParts`); a key some earlier scope already declared takes the
7744
+ * "new local definition" branch and touches no subgraph at all. So an
7745
+ * identical `graph [fill=x]` statement makes a child print `fill=""` or print
7746
+ * nothing purely according to whether a SIBLING declared `fill` first.
7747
+ * Undefined on any scope that declared nothing new.
7748
+ * @see lib/cgraph/attr.c:257 setattr (lsym / psym / global branches)
7749
+ */
7750
+ firstGraphDecl;
7751
+ /**
7752
+ * Layout-engine info; replaces GD_* macros via agbindrec (AD-1).
7753
+ * @see lib/cgraph/cgraph.h:agbindrec
7754
+ */
7755
+ info;
7756
+ /**
7757
+ * Immediate parent graph. null for root graphs.
7758
+ * @see lib/cgraph/cgraph.h:Agraph_s
7759
+ */
7760
+ parent;
7761
+ /**
7762
+ * Root (main) graph. Self-referential for root graphs;
7763
+ * points up for subgraphs.
7764
+ * @see lib/cgraph/cgraph.h:Agraph_s
7765
+ */
7766
+ root;
7767
+ /**
7768
+ * Subgraph sequence number (AGSEQ). A global counter on the root, assigned
7769
+ * at creation in source order, counting anonymous subgraphs. The root keeps
7770
+ * the default 0 (par == NULL never calls agnextseq). Consumed by getObjId to
7771
+ * emit cluster ids `clust<seq>`.
7772
+ * @see lib/cgraph/cgraph.h:AGSEQ
7773
+ * @see lib/cgraph/graph.c:agopen (AGSEQ(g) = agnextseq(par, AGRAPH))
7774
+ */
7775
+ seq = 0;
7776
+ /**
7777
+ * Root-only counter mirroring `clos->seq[AGRAPH]`: the running maximum AGSEQ
7778
+ * handed out to subgraphs. Meaningful only on the root graph; `assignSubgSeq`
7779
+ * pre-increments it. Other graphs leave it at 0.
7780
+ * @see lib/cgraph/graph.c:agnextseq (++clos->seq[AGRAPH])
7781
+ */
7782
+ subgSeqCounter = 0;
7783
+ /**
7784
+ * Names of graph attributes (AGRAPH) declared anywhere in this hierarchy —
7785
+ * the port's stand-in for cgraph's `Agsym_t` symbol table. Populated on the
7786
+ * ROOT only, at parse time, whenever a graph attribute is assigned on any
7787
+ * (sub)graph: setting `ordering=out` on a subgraph declares "ordering"
7788
+ * graph-wide with an empty default, so `agGraphAttr(root, "ordering")` returns
7789
+ * `""` (not undefined) — mirroring `agfindgraphattr`/`late_string`. Read via
7790
+ * `agGraphAttr` (model/cgraph-ops.ts); empty on non-root graphs.
7791
+ * @see lib/cgraph/attr.c:agattr (graph-wide declaration, "" default)
7792
+ */
7793
+ declaredGraphAttrs = /* @__PURE__ */ new Set();
7794
+ /** @see lib/cgraph/graph.c:agopen */
7795
+ constructor(name, kind, anonymous = false) {
7796
+ this.name = name;
7797
+ this.anonymous = anonymous;
7798
+ this.kind = kind;
7799
+ this.nodes = /* @__PURE__ */ new Map();
7800
+ this.edges = [];
7801
+ this.subgraphs = /* @__PURE__ */ new Map();
7802
+ this.attrs = /* @__PURE__ */ new Map();
7803
+ this.nodeDefaults = /* @__PURE__ */ new Map();
7804
+ this.edgeDefaults = /* @__PURE__ */ new Map();
7805
+ this.info = makeGraphInfo();
7806
+ this.parent = null;
7807
+ this.root = this;
7808
+ }
7809
+ };
7810
+
7339
7811
  // src/common/make-label.ts
7340
7812
  var DEFAULT_FONTSIZE2 = 14;
7341
7813
  var DEFAULT_FONTNAME = "Times-Roman";
@@ -7351,6 +7823,14 @@ function getPenColor(obj) {
7351
7823
  function rootOf(obj) {
7352
7824
  return obj instanceof Edge ? obj.tail.root : obj.root;
7353
7825
  }
7826
+ function inLabelOf(obj) {
7827
+ if (obj instanceof Edge) {
7828
+ const sep = obj.head.root.kind.endsWith("undirected") ? "--" : "->";
7829
+ return `in label of edge ${obj.tail.name} ${sep} ${obj.head.name}`;
7830
+ }
7831
+ if (obj instanceof Graph) return `in label of graph ${obj.name}`;
7832
+ return `in label of node ${obj.name}`;
7833
+ }
7354
7834
  function buildSpan(text, font, measured) {
7355
7835
  return {
7356
7836
  str: text,
@@ -7418,7 +7898,9 @@ function makeLabel(text, fontname, fontsize, fontcolor, measurer) {
7418
7898
  function makeAnyLabel(content, isHtml, font, measurer, obj) {
7419
7899
  if (isHtml && content !== "") {
7420
7900
  const imageSizer = obj !== void 0 ? rootOf(obj).info.gvc?.imageSizer : void 0;
7421
- return makeHtmlLabel(content, { ...font, pencolor: getPenColor(obj), imageSizer }, measurer);
7901
+ const { label, rv } = makeHtmlLabelRv(content, { ...font, pencolor: getPenColor(obj), imageSizer }, measurer);
7902
+ if (rv !== 0 && obj !== void 0) console.error(inLabelOf(obj));
7903
+ return label;
7422
7904
  }
7423
7905
  const subst2 = obj !== void 0 ? substObj(content, obj, false) : content;
7424
7906
  const text = htmlEntityUTF8(subst2);
@@ -8887,19 +9369,6 @@ var RenderJob = class {
8887
9369
  }
8888
9370
  };
8889
9371
 
8890
- // src/model/graphInfo.ts
8891
- function makeGraphInfo() {
8892
- return {
8893
- bb: { ll: { x: 0, y: 0 }, ur: { x: 0, y: 0 } },
8894
- rankdir: 0,
8895
- flags: 0,
8896
- charset: 0,
8897
- gui_state: 0,
8898
- has_labels: 0,
8899
- has_images: false
8900
- };
8901
- }
8902
-
8903
9372
  // src/common/shapeData.ts
8904
9373
  var DOGEAR = 1;
8905
9374
  var TAB = 2;
@@ -15489,146 +15958,6 @@ function ncross(ctx) {
15489
15958
  return total;
15490
15959
  }
15491
15960
 
15492
- // src/model/graph.ts
15493
- var Graph = class {
15494
- /** Graph name; agnameof equivalent. @see lib/cgraph/cgraph.h:agnameof */
15495
- name;
15496
- /**
15497
- * True when the graph was declared with NO name (`digraph {`), as opposed to
15498
- * an explicit empty name (`digraph "" {`). Both store `name === ''`, but
15499
- * cgraph gives the truly-anonymous root the internal id `%1` (agnameof) while
15500
- * the empty-named one keeps `""`. Only the imagemap `<map id/name>` reads this
15501
- * (map.ts:mapGraphName); dot/xdot re-serialization keeps `name` so an
15502
- * anonymous root round-trips as `digraph {`, not `digraph %1 {`.
15503
- * @see lib/cgraph/id.c:idmap (anon → `%1`) ; lib/cgraph/agraph.c:agnameof
15504
- */
15505
- anonymous;
15506
- /**
15507
- * Directed/strict classification; mirrors Agdesc_t.directed and
15508
- * Agdesc_t.strict. @see lib/cgraph/cgraph.h:Agdesc_s
15509
- */
15510
- kind;
15511
- /**
15512
- * Node set in insertion order, keyed by name.
15513
- * Mirrors Agraph_t.n_seq (sequence dict) merged with n_id access.
15514
- * @see lib/cgraph/cgraph.h:Agraph_s
15515
- */
15516
- nodes;
15517
- /**
15518
- * All edges owned by this graph (root graph only for ownership; subgraphs
15519
- * reference a subset). Mirrors Agraph_t.e_seq.
15520
- * @see lib/cgraph/cgraph.h:Agraph_s
15521
- */
15522
- edges;
15523
- /**
15524
- * Named subgraphs, keyed by name. Mirrors Agraph_t.g_seq/g_id.
15525
- * @see lib/cgraph/cgraph.h:Agraph_s
15526
- */
15527
- subgraphs;
15528
- /**
15529
- * String attributes (agget/agset equivalents).
15530
- * Replaces the Agattr_t / Agsym_t machinery for simple key-value access.
15531
- * @see lib/cgraph/cgraph.h:Agattr_s
15532
- */
15533
- attrs;
15534
- /** Default attributes applied to all nodes in this graph scope. */
15535
- nodeDefaults;
15536
- /** Default attributes applied to all edges in this graph scope. */
15537
- edgeDefaults;
15538
- /**
15539
- * Snapshot of the enclosing graphs' graph-attribute defaults (this graph ->
15540
- * root, inner wins) captured when this subgraph was OPENED during parsing.
15541
- * Graph attributes set on an ancestor AFTER this subgraph was created are not
15542
- * included — mirroring cgraph's parse-time agsubg defval copy. Used for
15543
- * order-correct label/font inheritance (e.g. a root `label=` declared after a
15544
- * cluster must NOT apply to that cluster). Undefined on the root graph.
15545
- * @see lib/cgraph/graph.c:agsubg (attr defval copy)
15546
- */
15547
- graphDefaultsSnapshot;
15548
- /**
15549
- * Keys in `attrs` the BUILDER seeded from `graphDefaultsSnapshot` rather than
15550
- * the input declaring them in this scope (see builder.ts GRAPH_LABEL_INHERIT_
15551
- * KEYS). cgraph distinguishes these structurally — a seeded value is inherited
15552
- * through the dict view and has no local symbol, while a local declaration
15553
- * creates one even when its value equals the inherited value — so the
15554
- * serializer needs the provenance to decide what write_dict would print.
15555
- * Undefined on the root graph and on any scope that seeded nothing.
15556
- * @see lib/cgraph/write.c:262 write_dict
15557
- */
15558
- seededAttrs;
15559
- /**
15560
- * Graph-attribute keys whose declaration in THIS scope was the first anywhere
15561
- * in the graph. cgraph's `setattr` splits on exactly this: a key with no
15562
- * existing symbol takes the global-declaration branch, which ends up seeding
15563
- * every direct subgraph that already exists with a local empty value (see
15564
- * `eagerEmptyParts`); a key some earlier scope already declared takes the
15565
- * "new local definition" branch and touches no subgraph at all. So an
15566
- * identical `graph [fill=x]` statement makes a child print `fill=""` or print
15567
- * nothing purely according to whether a SIBLING declared `fill` first.
15568
- * Undefined on any scope that declared nothing new.
15569
- * @see lib/cgraph/attr.c:257 setattr (lsym / psym / global branches)
15570
- */
15571
- firstGraphDecl;
15572
- /**
15573
- * Layout-engine info; replaces GD_* macros via agbindrec (AD-1).
15574
- * @see lib/cgraph/cgraph.h:agbindrec
15575
- */
15576
- info;
15577
- /**
15578
- * Immediate parent graph. null for root graphs.
15579
- * @see lib/cgraph/cgraph.h:Agraph_s
15580
- */
15581
- parent;
15582
- /**
15583
- * Root (main) graph. Self-referential for root graphs;
15584
- * points up for subgraphs.
15585
- * @see lib/cgraph/cgraph.h:Agraph_s
15586
- */
15587
- root;
15588
- /**
15589
- * Subgraph sequence number (AGSEQ). A global counter on the root, assigned
15590
- * at creation in source order, counting anonymous subgraphs. The root keeps
15591
- * the default 0 (par == NULL never calls agnextseq). Consumed by getObjId to
15592
- * emit cluster ids `clust<seq>`.
15593
- * @see lib/cgraph/cgraph.h:AGSEQ
15594
- * @see lib/cgraph/graph.c:agopen (AGSEQ(g) = agnextseq(par, AGRAPH))
15595
- */
15596
- seq = 0;
15597
- /**
15598
- * Root-only counter mirroring `clos->seq[AGRAPH]`: the running maximum AGSEQ
15599
- * handed out to subgraphs. Meaningful only on the root graph; `assignSubgSeq`
15600
- * pre-increments it. Other graphs leave it at 0.
15601
- * @see lib/cgraph/graph.c:agnextseq (++clos->seq[AGRAPH])
15602
- */
15603
- subgSeqCounter = 0;
15604
- /**
15605
- * Names of graph attributes (AGRAPH) declared anywhere in this hierarchy —
15606
- * the port's stand-in for cgraph's `Agsym_t` symbol table. Populated on the
15607
- * ROOT only, at parse time, whenever a graph attribute is assigned on any
15608
- * (sub)graph: setting `ordering=out` on a subgraph declares "ordering"
15609
- * graph-wide with an empty default, so `agGraphAttr(root, "ordering")` returns
15610
- * `""` (not undefined) — mirroring `agfindgraphattr`/`late_string`. Read via
15611
- * `agGraphAttr` (model/cgraph-ops.ts); empty on non-root graphs.
15612
- * @see lib/cgraph/attr.c:agattr (graph-wide declaration, "" default)
15613
- */
15614
- declaredGraphAttrs = /* @__PURE__ */ new Set();
15615
- /** @see lib/cgraph/graph.c:agopen */
15616
- constructor(name, kind, anonymous = false) {
15617
- this.name = name;
15618
- this.anonymous = anonymous;
15619
- this.kind = kind;
15620
- this.nodes = /* @__PURE__ */ new Map();
15621
- this.edges = [];
15622
- this.subgraphs = /* @__PURE__ */ new Map();
15623
- this.attrs = /* @__PURE__ */ new Map();
15624
- this.nodeDefaults = /* @__PURE__ */ new Map();
15625
- this.edgeDefaults = /* @__PURE__ */ new Map();
15626
- this.info = makeGraphInfo();
15627
- this.parent = null;
15628
- this.root = this;
15629
- }
15630
- };
15631
-
15632
15961
  // src/model/cgraph-ops.ts
15633
15962
  var LOCALNAMEPREFIX = "%";
15634
15963
  function freshNodeId(root) {
@@ -25057,18 +25386,22 @@ function dotSameports(g) {
25057
25386
  function dist2(p, q) {
25058
25387
  const dx = p.x - q.x;
25059
25388
  const dy = p.y - q.y;
25060
- return dx * dx + dy * dy;
25389
+ return fma(dx, dx, dy * dy);
25061
25390
  }
25062
25391
  function distPt(p, q) {
25063
25392
  return Math.sqrt(dist2(p, q));
25064
25393
  }
25065
- function evalBez4(pts, t) {
25394
+ function bezier(v, t) {
25066
25395
  const u = 1 - t;
25067
- const [p0, p1, p2, p3] = pts;
25068
- return {
25069
- x: u * u * u * p0.x + 3 * u * u * t * p1.x + 3 * u * t * t * p2.x + t * t * t * p3.x,
25070
- y: u * u * u * p0.y + 3 * u * u * t * p1.y + 3 * u * t * t * p2.y + t * t * t * p3.y
25071
- };
25396
+ const x = [v[0].x, v[1].x, v[2].x, v[3].x];
25397
+ const y = [v[0].y, v[1].y, v[2].y, v[3].y];
25398
+ for (let i = 1; i <= 3; i++) {
25399
+ for (let j = 0; j <= 3 - i; j++) {
25400
+ x[j] = fma(x[j], u, t * x[j + 1]);
25401
+ y[j] = fma(y[j], u, t * y[j + 1]);
25402
+ }
25403
+ }
25404
+ return { x: x[0], y: y[0] };
25072
25405
  }
25073
25406
  function dotneatoClosest(spl, pt) {
25074
25407
  let besti = 0;
@@ -25098,7 +25431,7 @@ function dotneatoClosest(spl, pt) {
25098
25431
  let pt2;
25099
25432
  for (; ; ) {
25100
25433
  const t = (lo + hi) / 2;
25101
- pt2 = evalBez4(c, t);
25434
+ pt2 = bezier(c, t);
25102
25435
  if (Math.abs(dlo2 - dhi2) < 1 || Math.abs(hi - lo) < 1e-5) break;
25103
25436
  if (dlo2 < dhi2) {
25104
25437
  hi = t;
@@ -26010,9 +26343,19 @@ var ShortestHelper = class _ShortestHelper {
26010
26343
  _ShortestHelper.connectAll(tris);
26011
26344
  const ftrii = _ShortestHelper.findTri(tris, eps[0]);
26012
26345
  const ltrii = _ShortestHelper.findTri(tris, eps[1]);
26013
- if (ftrii === NO_TRI || ltrii === NO_TRI) return null;
26346
+ if (ftrii === NO_TRI) {
26347
+ console.warn("source point not in any triangle");
26348
+ return null;
26349
+ }
26350
+ if (ltrii === NO_TRI) {
26351
+ console.warn("destination point not in any triangle");
26352
+ return null;
26353
+ }
26014
26354
  if (ftrii === ltrii) return [eps[0], eps[1]];
26015
- if (!_ShortestHelper.markPath(tris, ftrii, ltrii)) return [eps[0], eps[1]];
26355
+ if (!_ShortestHelper.markPath(tris, ftrii, ltrii)) {
26356
+ console.warn("cannot find triangle path");
26357
+ return [eps[0], eps[1]];
26358
+ }
26016
26359
  const epnls = [{ pp: eps[0], link: null }, { pp: eps[1], link: null }];
26017
26360
  const dq = _ShortestHelper.initDeque(pnlps.length);
26018
26361
  _ShortestHelper.add2dq(dq, DQ_FRONT, epnls[0]);
@@ -27994,7 +28337,7 @@ function nodeCover(n) {
27994
28337
  for (let i = 0; i < NODECARD; i++) {
27995
28338
  if (n.branch[i].child !== null) {
27996
28339
  if (flag) {
27997
- r = n.branch[i].rect;
28340
+ r = { boundary: [...n.branch[i].rect.boundary] };
27998
28341
  flag = false;
27999
28342
  } else {
28000
28343
  r = combineRect(r, n.branch[i].rect);
@@ -29947,7 +30290,10 @@ function makeFlatLabeledEdge(g, e) {
29947
30290
  info: { ...e.info, tail_port: e.info.head_port, head_port: e.info.tail_port }
29948
30291
  };
29949
30292
  const ps = routeFlatLabeledChannel(g, sample, ln, et);
29950
- if (ps === null || ps.length === 0) return true;
30293
+ if (ps === null || ps.length === 0) {
30294
+ e.info.lost = true;
30295
+ return true;
30296
+ }
29951
30297
  if (!tailIsLeft) ps.reverse();
29952
30298
  clipAndInstall(e, e.head, ps, ps.length, { ...buildDotSinfo(), ignoreSwap: true });
29953
30299
  return true;
@@ -30260,8 +30606,8 @@ function nodeInsideFn2(box) {
30260
30606
  const pw = box.penwidth ?? DEFAULT_NODEPENWIDTH;
30261
30607
  return box.isEllipse ? makeEllipseInsideFn(halfW, halfH, pw) : makeBoxInsideFn(halfW, halfH, pw);
30262
30608
  }
30263
- function clipToNodes(bezier, tailBox, headBox, penwidth = DEFAULT_NODEPENWIDTH, clip = CLIP_BOTH) {
30264
- const step1 = clip.tail ? bezierClipNode(bezier, tailBox.center.x, tailBox.center.y, nodeInsideFn2(tailBox), true) : bezier;
30609
+ function clipToNodes(bezier2, tailBox, headBox, penwidth = DEFAULT_NODEPENWIDTH, clip = CLIP_BOTH) {
30610
+ const step1 = clip.tail ? bezierClipNode(bezier2, tailBox.center.x, tailBox.center.y, nodeInsideFn2(tailBox), true) : bezier2;
30265
30611
  const step2 = clip.head ? bezierClipNode(step1, headBox.center.x, headBox.center.y, nodeInsideFn2(headBox), false) : step1;
30266
30612
  const arrowTip = step2[3];
30267
30613
  const elen = normalArrowLen(penwidth);
@@ -30275,9 +30621,9 @@ function clipToNodes(bezier, tailBox, headBox, penwidth = DEFAULT_NODEPENWIDTH,
30275
30621
  function routeWithRank(tailBox, headBox, rank3, penwidth = DEFAULT_NODEPENWIDTH, port) {
30276
30622
  const corridor = port ? { tailP: port.tailP, headP: port.headP } : void 0;
30277
30623
  const { startPt, endPt, boxes } = buildRankCorridor(tailBox, headBox, rank3, corridor);
30278
- const bezier = computeSpline(boxes, startPt, endPt);
30624
+ const bezier2 = computeSpline(boxes, startPt, endPt);
30279
30625
  const clip = port ? { tail: port.clipTail, head: port.clipHead } : CLIP_BOTH;
30280
- const { clipped, arrowTip, arrowDir } = clipToNodes(bezier, tailBox, headBox, penwidth, clip);
30626
+ const { clipped, arrowTip, arrowDir } = clipToNodes(bezier2, tailBox, headBox, penwidth, clip);
30281
30627
  return { bezierPts: clipped, arrowTip, arrowDir };
30282
30628
  }
30283
30629
  function routeSimple(tailBox, headBox, routeBezierFn) {
@@ -32870,24 +33216,27 @@ function buildNodes(nodeArr) {
32870
33216
  ysize: n.info.ht
32871
33217
  }));
32872
33218
  }
33219
+ function isConcentrateDup(ps, e) {
33220
+ const ti = e.tail.id;
33221
+ const hi = e.head.id;
33222
+ const lo = ti <= hi ? ti : hi;
33223
+ const up = ti <= hi ? hi : ti;
33224
+ if (ps.isInPS(lo, up)) return true;
33225
+ ps.addPS(lo, up);
33226
+ return false;
33227
+ }
32873
33228
  function buildEdges(g, nodeArr, orthoNodes) {
32874
33229
  const concentrate = dotRoot(g).info.concentrate ?? false;
32875
33230
  const nodeIndex = /* @__PURE__ */ new Map();
32876
33231
  nodeArr.forEach((n, i) => nodeIndex.set(n, i));
32877
33232
  const outIndex = buildOutEdgeIndex(g);
32878
- const seen = /* @__PURE__ */ new Set();
33233
+ const ps = concentrate ? new PointSet() : null;
32879
33234
  const result = [];
32880
33235
  for (const n of nodeArr) {
32881
33236
  const oes = outIndex.get(n);
32882
33237
  if (oes === void 0) continue;
32883
33238
  for (const e of oes) {
32884
- if (concentrate) {
32885
- const ti = e.tail.id;
32886
- const hi = e.head.id;
32887
- const key = ti <= hi ? `${ti},${hi}` : `${hi},${ti}`;
32888
- if (seen.has(key)) continue;
32889
- seen.add(key);
32890
- }
33239
+ if (ps !== null && isConcentrateDup(ps, e)) continue;
32891
33240
  result.push({
32892
33241
  tail: orthoNodes[nodeIndex.get(e.tail)],
32893
33242
  head: orthoNodes[nodeIndex.get(e.head)],
@@ -33975,15 +34324,6 @@ function spiralSearch(info, ctx, wide) {
33975
34324
 
33976
34325
  // src/layout/pack/poly-pack.ts
33977
34326
  var C2 = 100;
33978
- function psKey(x, y) {
33979
- return `${cround(x)},${cround(y)}`;
33980
- }
33981
- function inPS(ps, p) {
33982
- return ps.has(psKey(p.x, p.y));
33983
- }
33984
- function insertPS(ps, p) {
33985
- ps.add(psKey(p.x, p.y));
33986
- }
33987
34327
  function computeStep(ng, bbs, margin) {
33988
34328
  let b = 0;
33989
34329
  let c = 0;
@@ -34030,14 +34370,14 @@ function genBox2(p) {
34030
34370
  function hasCollision(cells, dx, dy, ps) {
34031
34371
  for (let i = 0; i < cells.length; i++) {
34032
34372
  const cell2 = cells[i];
34033
- if (cell2 !== void 0 && inPS(ps, { x: cell2.x + dx, y: cell2.y + dy })) return true;
34373
+ if (cell2 !== void 0 && ps.inPS({ x: cell2.x + dx, y: cell2.y + dy })) return true;
34034
34374
  }
34035
34375
  return false;
34036
34376
  }
34037
34377
  function markCells(cells, dx, dy, ps) {
34038
34378
  for (let i = 0; i < cells.length; i++) {
34039
34379
  const cell2 = cells[i];
34040
- if (cell2 !== void 0) insertPS(ps, { x: cell2.x + dx, y: cell2.y + dy });
34380
+ if (cell2 !== void 0) ps.addPS(cell2.x + dx, cell2.y + dy);
34041
34381
  }
34042
34382
  }
34043
34383
  function fits(x, y, info, ctx) {
@@ -34082,7 +34422,7 @@ function polyRects(ng, bbs, pinfo) {
34082
34422
  infos.push(genBox2({ bb, ssize: step, margin: pinfo.margin, idx: i }));
34083
34423
  }
34084
34424
  const sorted = gvQsort([...infos], cmpByGPerimeter);
34085
- const ctx = { ps: /* @__PURE__ */ new Set(), step, bbs };
34425
+ const ctx = { ps: new PointSet(), step, bbs };
34086
34426
  const places = new Array(ng).fill({ x: 0, y: 0 });
34087
34427
  for (let i = 0; i < sorted.length; i++) {
34088
34428
  const info = sorted[i];
@@ -34092,57 +34432,6 @@ function polyRects(ng, bbs, pinfo) {
34092
34432
  return places;
34093
34433
  }
34094
34434
 
34095
- // src/common/pointset.ts
34096
- function cmpPair(a, b) {
34097
- if (a.x > b.x) return 1;
34098
- if (a.x < b.x) return -1;
34099
- if (a.y > b.y) return 1;
34100
- if (a.y < b.y) return -1;
34101
- return 0;
34102
- }
34103
- var PointSet = class {
34104
- byX = /* @__PURE__ */ new Map();
34105
- n = 0;
34106
- /**
34107
- * Insert (x, y). A duplicate keeps the point inserted first, as `dtinsert`
34108
- * returns the existing object and the new one is freed.
34109
- * @see lib/common/pointset.c:addPS
34110
- */
34111
- addPS(x, y) {
34112
- let col = this.byX.get(x);
34113
- if (col === void 0) {
34114
- col = /* @__PURE__ */ new Map();
34115
- this.byX.set(x, col);
34116
- }
34117
- if (col.has(y)) return;
34118
- col.set(y, { x, y });
34119
- this.n++;
34120
- }
34121
- /** @see lib/common/pointset.c:inPS */
34122
- inPS(pt) {
34123
- return this.byX.get(pt.x)?.has(pt.y) ?? false;
34124
- }
34125
- /** @see lib/common/pointset.c:isInPS */
34126
- isInPS(x, y) {
34127
- return this.byX.get(x)?.has(y) ?? false;
34128
- }
34129
- /** @see lib/common/pointset.c:sizeOf */
34130
- sizeOf() {
34131
- return this.n;
34132
- }
34133
- /**
34134
- * The points in dictionary order (`dtflatten` walks the ordered set).
34135
- * @see lib/common/pointset.c:pointsOf
34136
- */
34137
- pointsOf() {
34138
- const pts = [];
34139
- for (const col of this.byX.values()) {
34140
- for (const p of col.values()) pts.push({ x: p.x, y: p.y });
34141
- }
34142
- return pts.sort(cmpPair);
34143
- }
34144
- };
34145
-
34146
34435
  // src/layout/pack/poly-place.ts
34147
34436
  var C_AVG = 100;
34148
34437
  function grid(x, s) {
@@ -37983,6 +38272,12 @@ function chainOf(e) {
37983
38272
  }
37984
38273
  return list;
37985
38274
  }
38275
+ function warnNoControlPoints(e) {
38276
+ console.warn(
38277
+ `Warning: Could not create control points for multiple spline for edge (${e.tail.name},${e.head.name})`
38278
+ );
38279
+ return 1;
38280
+ }
37986
38281
  function genroute(g, trip, t, e, doPolyline) {
37987
38282
  const eps = [
37988
38283
  { x: trip.ps[0].x, y: trip.ps[0].y },
@@ -37992,7 +38287,7 @@ function genroute(g, trip, t, e, doPolyline) {
37992
38287
  const mult = e.info.count ?? 1;
37993
38288
  const head = e.head;
37994
38289
  const pl = shortestPath({ ps: trip.ps }, eps);
37995
- if (pl === null) return 1;
38290
+ if (pl === null) return warnNoControlPoints(e);
37996
38291
  if (pl.length === 2) {
37997
38292
  const chain = chainOf(e);
37998
38293
  makeStraightEdges(g, chain, chain.length, doPolyline ? 1 : 0, MSINFO);
@@ -38000,7 +38295,7 @@ function genroute(g, trip, t, e, doPolyline) {
38000
38295
  }
38001
38296
  if (mult === 1 || concentrate) {
38002
38297
  const spl = routeMember(trip.ps, t, pl, doPolyline);
38003
- if (spl === null) return 1;
38298
+ if (spl === null) return warnNoControlPoints(e);
38004
38299
  finishEdge(e, spl, e.head !== head);
38005
38300
  return 0;
38006
38301
  }
@@ -38008,7 +38303,7 @@ function genroute(g, trip, t, e, doPolyline) {
38008
38303
  const cpts = [];
38009
38304
  for (let i = 0; i + 2 < pl.length; i++) {
38010
38305
  const cp = mkCtrlPts(t, mult + 1, pl[i], pl[i + 1], pl[i + 2], trip);
38011
- if (cp === null) return 1;
38306
+ if (cp === null) return warnNoControlPoints(e);
38012
38307
  cpts.push(cp);
38013
38308
  }
38014
38309
  let cur = e;
@@ -38023,9 +38318,9 @@ function genroute(g, trip, t, e, doPolyline) {
38023
38318
  poly[pn - j] = cpts[j - 1][i + 1];
38024
38319
  }
38025
38320
  const mmpl = shortestPath({ ps: poly }, eps);
38026
- if (mmpl === null) return 1;
38321
+ if (mmpl === null) return warnNoControlPoints(cur);
38027
38322
  const spl = routeMember(poly, pl.length - 1, mmpl, doPolyline);
38028
- if (spl === null) return 1;
38323
+ if (spl === null) return warnNoControlPoints(cur);
38029
38324
  finishEdge(cur, spl, cur.head !== head);
38030
38325
  cur = cur.info.to_virt;
38031
38326
  }
@@ -47249,22 +47544,22 @@ function freeBlocktree(bp) {
47249
47544
 
47250
47545
  // src/layout/circo/position-helpers.ts
47251
47546
  function rotatePoint(x, y, cosR, sinR) {
47252
- return [x * cosR - y * sinR, x * sinR + y * cosR];
47547
+ return [fma(x, cosR, -sinR * y), fma(x, sinR, cosR * y)];
47253
47548
  }
47254
47549
  function nodePsi(n) {
47255
47550
  return n.info.alg?.psi ?? 0;
47256
47551
  }
47257
47552
  function rotFromParentPos(sn, theta) {
47258
- let r = theta + Math.PI - sn.parentPos;
47553
+ let r = theta + (Math.PI - sn.parentPos);
47259
47554
  if (r < 0) r += 2 * Math.PI;
47260
47555
  return r;
47261
47556
  }
47262
47557
  function closestNode(sn, x, y, skip) {
47263
47558
  let best = skip;
47264
- let bestDist = Math.hypot(skip.info.pos[0] + x, skip.info.pos[1] + y);
47559
+ let bestDist = libmHypot(skip.info.pos[0] + x, skip.info.pos[1] + y);
47265
47560
  for (const dn of sn.subGraph.nodes) {
47266
47561
  if (dn.orig === skip) continue;
47267
- const d = Math.hypot(dn.orig.info.pos[0] + x, dn.orig.info.pos[1] + y);
47562
+ const d = libmHypot(dn.orig.info.pos[0] + x, dn.orig.info.pos[1] + y);
47268
47563
  if (d < bestDist) {
47269
47564
  bestDist = d;
47270
47565
  best = dn.orig;
@@ -47277,22 +47572,19 @@ function rotCoalesced(sn, x, y, nbr, theta) {
47277
47572
  const r = sn.radius - rho;
47278
47573
  const nx = nbr.info.pos[0];
47279
47574
  const ny = nbr.info.pos[1];
47280
- if (-r >= nx) {
47281
- const phi2 = Math.atan2(ny, nx);
47282
- return theta + Math.PI - phi2 - nodePsi(nbr);
47283
- }
47284
- const R2 = Math.hypot(x, y);
47575
+ const R2 = libmHypot(x, y);
47285
47576
  const phi = Math.atan2(ny, nx + r);
47286
47577
  const l = r - rho / Math.cos(phi);
47287
- return theta + Math.PI / 2 - phi - Math.asin(l / R2 * Math.cos(phi));
47578
+ return theta + (Math.PI / 2 - phi - Math.asin(l / R2 * Math.cos(phi)));
47288
47579
  }
47289
47580
  function rotGeneral(sn, x, y, nbr, theta) {
47290
47581
  if (closestNode(sn, x, y, nbr) === nbr) return 0;
47291
- if (isCoalesced(sn)) return rotCoalesced(sn, x, y, nbr, theta);
47582
+ const r = sn.radius - sn.rad0;
47583
+ if (isCoalesced(sn) && -r < nbr.info.pos[0]) return rotCoalesced(sn, x, y, nbr, theta);
47292
47584
  const phi = Math.atan2(nbr.info.pos[1], nbr.info.pos[0]);
47293
- let r = theta + Math.PI - phi - nodePsi(nbr);
47294
- if (r > 2 * Math.PI) r -= 2 * Math.PI;
47295
- return r;
47585
+ let t = theta + (Math.PI - phi - nodePsi(nbr));
47586
+ if (t > 2 * Math.PI) t -= 2 * Math.PI;
47587
+ return t;
47296
47588
  }
47297
47589
  function applyDelta(sn, x, y, rotate) {
47298
47590
  const cosR = Math.cos(rotate);
@@ -47335,7 +47627,7 @@ function getInfo(pi, stp, minDist) {
47335
47627
  }
47336
47628
  function setInfo(p0, p1, delta2) {
47337
47629
  const t = Math.max(
47338
- (p0.diameter * p1.minRadius + p1.diameter * p0.minRadius) / (2 * delta2 * p0.minRadius * p1.minRadius),
47630
+ fma(p0.diameter, p1.minRadius, p1.diameter * p0.minRadius) / (2 * delta2 * p0.minRadius * p1.minRadius),
47339
47631
  1
47340
47632
  );
47341
47633
  p0.scale = Math.max(p0.scale, t);
@@ -47345,7 +47637,13 @@ function placeChild(child, ca, ctx) {
47345
47637
  const dx = ctx.childRadius * Math.cos(ca);
47346
47638
  const dy = ctx.childRadius * Math.sin(ca);
47347
47639
  applyDelta(child, dx, dy, getRotation(child, dx, dy, ca));
47348
- return ctx.length === 1 ? ca + ctx.incAngle + ctx.mindistAngle : ca + ctx.incAngle + ctx.mindistAngle / 2;
47640
+ return nextChildAngle(ca, ctx.incAngle, ctx.mindistAngle, ctx.length);
47641
+ }
47642
+ function nextChildAngle(ca, inc2, mda, length) {
47643
+ return length === 1 ? ca + (inc2 + mda) : ca + (inc2 + mda / 2);
47644
+ }
47645
+ function leftoverArc(childRadius, diameter) {
47646
+ return fma(2 * Math.PI, childRadius, -diameter);
47349
47647
  }
47350
47648
  function advanceAngleL1(ca, inc2, total, cnt, stp) {
47351
47649
  const next = cnt === 0 ? 0 : total === 2 ? Math.PI : ca + inc2;
@@ -47355,7 +47653,7 @@ function advanceAngleL1(ca, inc2, total, cnt, stp) {
47355
47653
  }
47356
47654
  function advanceAngleLN(ca, inc2, mda, total, theta) {
47357
47655
  if (total === 1) return theta;
47358
- return ca + inc2 + mda / 2;
47656
+ return ca + (inc2 + mda / 2);
47359
47657
  }
47360
47658
  function maybeWritePsi(n, stp, ca, isMid) {
47361
47659
  if (isMid && n === stp.neighbor) {
@@ -47370,7 +47668,7 @@ function positionChildren(pi, stp, length, minDist) {
47370
47668
  let md = minDist;
47371
47669
  if (length === 1) {
47372
47670
  cr = Math.max(cr, pi.diameter / (2 * Math.PI));
47373
- const d = 2 * Math.PI * cr - pi.diameter;
47671
+ const d = leftoverArc(cr, pi.diameter);
47374
47672
  if (d > 0) md += d / pi.childCount;
47375
47673
  }
47376
47674
  stp.subtreeR = Math.max(stp.subtreeR, cr + pi.maxRadius);
@@ -54399,11 +54697,11 @@ function lexToken(s, i) {
54399
54697
  function scanNumberAmbiguities(src, stripped) {
54400
54698
  const out = [];
54401
54699
  let line = 1;
54402
- let lineAt = 0;
54700
+ let lineAt2 = 0;
54403
54701
  for (let i = 0; i < stripped.length; ) {
54404
54702
  const { next, bad } = lexToken(stripped, i);
54405
54703
  if (bad !== null) {
54406
- for (; lineAt < i; lineAt++) if (src[lineAt] === "\n") line++;
54704
+ for (; lineAt2 < i; lineAt2++) if (src[lineAt2] === "\n") line++;
54407
54705
  out.push({ text: bad, line, offset: i });
54408
54706
  }
54409
54707
  i = next;