@knowvah/dot-engine 2.6.8 → 2.6.10

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.
@@ -1,6 +1,6 @@
1
1
  /**
2
- * Base XML escape (gv_xml_escape with flags {0}): ids, titles, hrefs.
3
- * @see lib/util/xml.c gv_xml_escape / xml_core / xml_isentity
2
+ * Base XML escape (gv_xml_escape with flags {0}): ids, hrefs.
3
+ * @see lib/util/xml.c:gv_xml_escape
4
4
  */
5
5
  export declare function escapeXml(s: string): string;
6
6
  /**
@@ -12,10 +12,12 @@ export declare function escapeXml(s: string): string;
12
12
  */
13
13
  export declare function escapeXmlTitle(s: string): string;
14
14
  /**
15
- * Text/tooltip XML escape (gv_xml_escape with flags {raw, dash, nbsp}):
16
- * additionally escapes '-'→-, newline/CR→
/
, and 2nd+ consecutive
17
- * spaces→ . Used for textspans and tooltips.
18
- * @see plugin/core/gvrender_core_svg.c:546 (svg_textspan flags)
15
+ * Text/tooltip XML escape (gv_xml_escape with flags {raw, dash, nbsp}): raw
16
+ * escapes every '&' -- entity-looking or not -- and newline/CR to
17
+ * 
/
; also '-'→- and 2nd+ consecutive spaces→ . Used for
18
+ * textspans and xlink:title.
19
+ * @see plugin/core/gvrender_core_svg.c:svg_textspan, svg_begin_anchor
20
+ * @see lib/util/xml.c:xml_core (`flags.raw || !xml_isentity(s)`)
19
21
  */
20
22
  export declare function escapeXmlText(s: string): string;
21
23
  //# sourceMappingURL=xml-escape.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"xml-escape.d.ts","sourceRoot":"","sources":["../../src/render/xml-escape.ts"],"names":[],"mappings":"AAeA;;;GAGG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAO3C;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAKhD;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAO/C"}
1
+ {"version":3,"file":"xml-escape.d.ts","sourceRoot":"","sources":["../../src/render/xml-escape.ts"],"names":[],"mappings":"AAgBA;;;GAGG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAE3C;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAE/C"}
package/dist/render.js CHANGED
@@ -8436,6 +8436,9 @@ function interpretCRNL(str) {
8436
8436
  }
8437
8437
  return out;
8438
8438
  }
8439
+ function preprocessTooltip(s) {
8440
+ return interpretCRNL(htmlEntityUTF8(s));
8441
+ }
8439
8442
 
8440
8443
  // src/model/graphInfo.ts
8441
8444
  function makeGraphInfo() {
@@ -11289,6 +11292,37 @@ function underlineDraw(ring, coord, filled, ctx) {
11289
11292
  renderShapePolyline([ring[2], ring[3]], coord, ctx);
11290
11293
  }
11291
11294
 
11295
+ // src/common/htmltable-style.ts
11296
+ function htmlStyle(style2) {
11297
+ const s = { radial: false, rounded: false, invisible: false, dotted: false, dashed: false };
11298
+ if (style2 === void 0) return s;
11299
+ for (const tk of style2.split(/[ ,]+/)) {
11300
+ switch (tk.toUpperCase()) {
11301
+ case "ROUNDED":
11302
+ s.rounded = true;
11303
+ break;
11304
+ case "RADIAL":
11305
+ s.radial = true;
11306
+ break;
11307
+ case "SOLID":
11308
+ s.dotted = false;
11309
+ s.dashed = false;
11310
+ break;
11311
+ case "INVISIBLE":
11312
+ case "INVIS":
11313
+ s.invisible = true;
11314
+ break;
11315
+ case "DOTTED":
11316
+ s.dotted = true;
11317
+ break;
11318
+ case "DASHED":
11319
+ s.dashed = true;
11320
+ break;
11321
+ }
11322
+ }
11323
+ return s;
11324
+ }
11325
+
11292
11326
  // src/common/htmltable-emit-rules.ts
11293
11327
  function emitHtmlRules(ctx) {
11294
11328
  withHtmlPaint({ fill: ctx.color, pen: ctx.color, penWidth: 1 }, ctx.job, () => {
@@ -11435,14 +11469,14 @@ function bgFillPaint(ctx) {
11435
11469
  fill: spec[0],
11436
11470
  stop: spec[1],
11437
11471
  gradientAngle: ctx.gradientangle ?? 0,
11438
- radial: ctx.style !== void 0 && ctx.style.includes("radial"),
11472
+ radial: htmlStyle(ctx.style).radial,
11439
11473
  penWidth
11440
11474
  };
11441
11475
  }
11442
11476
  function emitBgFill(ctx) {
11443
11477
  const { box, pos, border: border2, renderer, job } = ctx;
11444
11478
  withHtmlPaint(bgFillPaint(ctx), job, () => {
11445
- if (parseStyleFlags(ctx.style).rounded) {
11479
+ if (htmlStyle(ctx.style).rounded) {
11446
11480
  emitRoundedBezier(mkPts(box, border2, pos), { x: 0, y: 0 }, true, { renderer, job });
11447
11481
  return;
11448
11482
  }
@@ -11508,13 +11542,6 @@ function emitHtmlImg(img, pos, renderer, job) {
11508
11542
  }
11509
11543
  renderer.usershape?.(img.src, b, job);
11510
11544
  }
11511
- function htmlStyleInvisible(style2) {
11512
- if (style2 === void 0) return false;
11513
- return style2.split(/[ ,]+/).some((tok) => {
11514
- const t = tok.toUpperCase();
11515
- return t === "INVIS" || t === "INVISIBLE";
11516
- });
11517
- }
11518
11545
  function emitHtmlCell(cell2, pos, renderer, job) {
11519
11546
  const inAnchor = initHtmlAnchor(
11520
11547
  { href: cell2.href, title: cell2.title, target: cell2.target, id: cell2.id },
@@ -11522,7 +11549,7 @@ function emitHtmlCell(cell2, pos, renderer, job) {
11522
11549
  renderer,
11523
11550
  job
11524
11551
  );
11525
- if (htmlStyleInvisible(cell2.style)) {
11552
+ if (htmlStyle(cell2.style).invisible) {
11526
11553
  if (inAnchor) endHtmlAnchor(renderer, job);
11527
11554
  return;
11528
11555
  }
@@ -11558,7 +11585,7 @@ function emitHtmlLabel(placed, pos, renderer, job) {
11558
11585
  const tblBox = absBox(placed.box, pos);
11559
11586
  const clustersLast = (job.flags & EMIT_CLUSTERS_LAST) !== 0;
11560
11587
  const inAnchor = clustersLast ? false : initHtmlAnchor(anchorData, tblBox, renderer, job);
11561
- const tblInvisible = htmlStyleInvisible(placed.style);
11588
+ const tblInvisible = htmlStyle(placed.style).invisible;
11562
11589
  if (!tblInvisible) {
11563
11590
  if (placed.bgcolor !== void 0) {
11564
11591
  emitBgFill({
@@ -15698,31 +15725,141 @@ function stripedBox(job, pts, clrs, rotate, renderer) {
15698
15725
  return error;
15699
15726
  }
15700
15727
 
15728
+ // src/util/xml.ts
15729
+ function gvXmlEscape(s, flags) {
15730
+ return XmlEscaper.escape(s, flags);
15731
+ }
15732
+ var XmlEscaper = class _XmlEscaper {
15733
+ static escape(s, flags) {
15734
+ let result = "";
15735
+ let previous = "";
15736
+ let pos = 0;
15737
+ while (pos < s.length) {
15738
+ const prev = s[pos];
15739
+ const [fragment, consumed] = _XmlEscaper.core(previous, s, pos, flags);
15740
+ result += fragment;
15741
+ previous = prev;
15742
+ pos += consumed;
15743
+ }
15744
+ return result;
15745
+ }
15746
+ /**
15747
+ * Escape one character/codepoint at s[pos].
15748
+ * Returns [escaped_fragment, characters_consumed].
15749
+ * Delegates to focused helpers to stay within CCN limit.
15750
+ * @see lib/util/xml.c:xml_core
15751
+ */
15752
+ static core(previous, s, pos, flags) {
15753
+ const special = _XmlEscaper.coreSpecial(previous, s, pos, flags);
15754
+ if (special !== null) return special;
15755
+ const code = s.charCodeAt(pos);
15756
+ if (code > 127 && flags.utf8) return _XmlEscaper.utf8Entity(s, pos);
15757
+ return [s[pos], 1];
15758
+ }
15759
+ /**
15760
+ * Handle all the fixed-escape cases (& < > - space " ' \n \r).
15761
+ * Returns null when none of those cases applies.
15762
+ * @see lib/util/xml.c:xml_core
15763
+ */
15764
+ static coreSpecial(previous, s, pos, flags) {
15765
+ const c = s[pos];
15766
+ if (c === "&" && (flags.raw || !_XmlEscaper.isEntity(s, pos))) {
15767
+ return ["&amp;", 1];
15768
+ }
15769
+ if (c === "<") return ["&lt;", 1];
15770
+ if (c === ">") return ["&gt;", 1];
15771
+ if (c === '"') return ["&quot;", 1];
15772
+ if (c === "'") return ["&#39;", 1];
15773
+ if (c === "-" && flags.dash) return ["&#45;", 1];
15774
+ if (c === " " && previous === " " && flags.nbsp) return ["&#160;", 1];
15775
+ if (c === "\n" && flags.raw) return ["&#10;", 1];
15776
+ if (c === "\r" && flags.raw) return ["&#13;", 1];
15777
+ return null;
15778
+ }
15779
+ /**
15780
+ * Return true if s[pos] (which is '&') starts a valid XML entity:
15781
+ * &[A-Za-z]+; e.g. &Ccedil;
15782
+ * &#[0-9]*; e.g. &#38;
15783
+ * &#x[0-9a-fA-F]*; e.g. &#x6C34;
15784
+ * @see lib/util/xml.c:xml_isentity
15785
+ */
15786
+ static isEntity(s, pos) {
15787
+ let i = pos + 1;
15788
+ if (i >= s.length || s[i] === ";") return false;
15789
+ i = s[i] === "#" ? _XmlEscaper.scanNumericRef(s, i) : _XmlEscaper.scanNamedRef(s, i);
15790
+ return i < s.length && s[i] === ";";
15791
+ }
15792
+ /** Scan a numeric entity body starting just after the '#'. */
15793
+ static scanNumericRef(s, hashPos) {
15794
+ let i = hashPos + 1;
15795
+ if (i < s.length && (s[i] === "x" || s[i] === "X")) {
15796
+ i++;
15797
+ while (i < s.length && _XmlEscaper.isXDigit(s[i])) i++;
15798
+ } else {
15799
+ while (i < s.length && _XmlEscaper.isDecDigit(s[i])) i++;
15800
+ }
15801
+ return i;
15802
+ }
15803
+ /** Scan a named entity body (alpha chars after the '&'). */
15804
+ static scanNamedRef(s, start) {
15805
+ let i = start;
15806
+ while (i < s.length && _XmlEscaper.isAlpha(s[i])) i++;
15807
+ return i;
15808
+ }
15809
+ /**
15810
+ * Emit &#xNNNN; for the Unicode code point at s[pos]. JS strings are
15811
+ * UTF-16, so the code point (what C decodes from UTF-8) comes from
15812
+ * codePointAt, and an astral character consumes two units.
15813
+ * @see lib/util/xml.c:xml_core (UTF-8 block)
15814
+ */
15815
+ static utf8Entity(s, pos) {
15816
+ const cp = _XmlEscaper.decodeUtf8(s, pos);
15817
+ const consumed = _XmlEscaper.utf8ByteLen(cp);
15818
+ return [`&#x${cp.toString(16)};`, consumed];
15819
+ }
15820
+ /**
15821
+ * Return the code point at s[pos]. A lone surrogate is the analogue of
15822
+ * malformed UTF-8, where C reports an error and exits (xml.c:135).
15823
+ * @see lib/util/xml.c:xml_core (fprintf + graphviz_exit at xml.c:135)
15824
+ */
15825
+ static decodeUtf8(s, pos) {
15826
+ const cp = s.codePointAt(pos);
15827
+ if (cp >= 55296 && cp <= 57343) {
15828
+ throw new RenderError(`gvXmlEscape: malformed UTF-8 at position ${pos}`, "RENDER_ERROR");
15829
+ }
15830
+ return cp;
15831
+ }
15832
+ /**
15833
+ * Return how many UTF-16 units encode the code point (C: the UTF-8 byte
15834
+ * length from the leading byte).
15835
+ * @see lib/util/xml.c:xml_core (length computation)
15836
+ */
15837
+ static utf8ByteLen(cp) {
15838
+ return cp > 65535 ? 2 : 1;
15839
+ }
15840
+ static isDecDigit(c) {
15841
+ return c >= "0" && c <= "9";
15842
+ }
15843
+ static isAlpha(c) {
15844
+ return c >= "a" && c <= "z" || c >= "A" && c <= "Z";
15845
+ }
15846
+ static isXDigit(c) {
15847
+ return _XmlEscaper.isDecDigit(c) || c >= "a" && c <= "f" || c >= "A" && c <= "F";
15848
+ }
15849
+ };
15850
+
15701
15851
  // src/render/xml-escape.ts
15702
- var RE_DQUOTE = new RegExp('"', "g");
15703
- var RE_SQUOTE = new RegExp("'", "g");
15704
- var RE_AMP_NONENTITY = /&(?![A-Za-z]+;|#[0-9]+;|#x[0-9A-Fa-f]+;)/g;
15852
+ var FLAGS_BASE = { raw: false, dash: false, nbsp: false, utf8: false };
15853
+ var FLAGS_TITLE = { raw: false, dash: true, nbsp: true, utf8: false };
15854
+ var FLAGS_TEXT = { raw: true, dash: true, nbsp: true, utf8: false };
15705
15855
  function escapeXml(s) {
15706
- let r = s.replace(RE_AMP_NONENTITY, "&amp;");
15707
- r = r.replace(/</g, "&lt;");
15708
- r = r.replace(/>/g, "&gt;");
15709
- r = r.replace(RE_DQUOTE, "&quot;");
15710
- r = r.replace(RE_SQUOTE, "&#39;");
15711
- return r;
15856
+ return gvXmlEscape(s, FLAGS_BASE);
15712
15857
  }
15713
15858
  function escapeXmlTitle(s) {
15714
- let r = escapeXml(s);
15715
- r = r.replace(/-/g, "&#45;");
15716
- r = r.replace(/ {2,}/g, (m) => " " + "&#160;".repeat(m.length - 1));
15717
- return r;
15859
+ return gvXmlEscape(s, FLAGS_TITLE);
15718
15860
  }
15719
15861
  function escapeXmlText(s) {
15720
- let r = escapeXml(s);
15721
- r = r.replace(/-/g, "&#45;");
15722
- r = r.replace(/\n/g, "&#10;");
15723
- r = r.replace(/\r/g, "&#13;");
15724
- r = r.replace(/ {2,}/g, (m) => " " + "&#160;".repeat(m.length - 1));
15725
- return r;
15862
+ return gvXmlEscape(s, FLAGS_TEXT);
15726
15863
  }
15727
15864
 
15728
15865
  // src/gvc/device-cluster.ts
@@ -15903,7 +16040,7 @@ function anchorAttr(n, key) {
15903
16040
  return v !== void 0 && v !== "" ? v : void 0;
15904
16041
  }
15905
16042
  function resolveTooltip(n, tooltip) {
15906
- if (tooltip !== void 0) return substObjAnchor(interpretCRNL(tooltip), n);
16043
+ if (tooltip !== void 0) return substObjAnchor(preprocessTooltip(tooltip), n);
15907
16044
  const label = n.info.label;
15908
16045
  return label?.text ?? "";
15909
16046
  }
@@ -20041,7 +20178,7 @@ function subst(s, obj) {
20041
20178
  return substObjAnchor(s, obj);
20042
20179
  }
20043
20180
  function tip(s, obj) {
20044
- return substObjAnchor(interpretCRNL(s), obj);
20181
+ return substObjAnchor(preprocessTooltip(s), obj);
20045
20182
  }
20046
20183
  function textOf(lp) {
20047
20184
  return lp?.text ?? null;
@@ -20820,8 +20957,9 @@ function resetHtmlFillPenWidth() {
20820
20957
  htmlFillPen = 1;
20821
20958
  }
20822
20959
  function penTypeOf(style2) {
20823
- if (style2 !== void 0 && style2.includes("dashed")) return 1 /* Dashed */;
20824
- if (style2 !== void 0 && style2.includes("dotted")) return 2 /* Dotted */;
20960
+ const st = htmlStyle(style2);
20961
+ if (st.dashed) return 1 /* Dashed */;
20962
+ if (st.dotted) return 2 /* Dotted */;
20825
20963
  return 3 /* Solid */;
20826
20964
  }
20827
20965
  function withHtmlPaint(paint, job, drawFn) {
@@ -20888,7 +21026,7 @@ function doBorder(spec, renderer, job) {
20888
21026
  const { box, pos, border: border2, color, sides: sides2, style: style2 } = spec;
20889
21027
  htmlFillPen = border2;
20890
21028
  withHtmlPaint({ pen: color, penWidth: border2, penStyle: style2 }, job, () => {
20891
- if (parseStyleFlags(style2).rounded) {
21029
+ if (htmlStyle(style2).rounded) {
20892
21030
  emitRoundedBezier(mkPts(box, border2, pos), { x: 0, y: 0 }, false, { renderer, job });
20893
21031
  return;
20894
21032
  }