@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.
package/dist/index.js CHANGED
@@ -9431,250 +9431,35 @@ function underlineDraw(ring, coord, filled, ctx) {
9431
9431
  renderShapePolyline([ring[2], ring[3]], coord, ctx);
9432
9432
  }
9433
9433
 
9434
- // src/common/multicolor.ts
9435
- var EPS = 1e-5;
9436
- function aeq0(x) {
9437
- return x < EPS && x > -EPS;
9438
- }
9439
- function getSegLen(token) {
9440
- const semi = token.indexOf(";");
9441
- if (semi === -1) return { color: token, v: 0 };
9442
- const color = token.slice(0, semi);
9443
- const rest = token.slice(semi + 1);
9444
- if (rest.length === 0) return { color, v: -1 };
9445
- const parsed = parseFloat(rest);
9446
- return !isNaN(parsed) && parsed >= 0 ? { color, v: parsed } : { color, v: -1 };
9447
- }
9448
- function appendSeg(token, left2, segs, warnedExceeded) {
9449
- const { color, v } = getSegLen(token);
9450
- if (v < 0) return null;
9451
- let t = v;
9452
- let rval = 0;
9453
- const del = t - left2;
9454
- if (del > 0) {
9455
- if (!aeq0(del) && !warnedExceeded.v) {
9456
- warnedExceeded.v = true;
9457
- rval = 3;
9458
- }
9459
- t = left2;
9460
- }
9461
- left2 -= t;
9462
- segs.push({ color: color.length > 0 ? color : null, t, hasFraction: t > 0 && v > 0 });
9463
- return { left: left2, rval };
9464
- }
9465
- function distributeLeft(segs, left2) {
9466
- if (left2 <= 0) return;
9467
- const zeroCount = segs.filter((s) => s.t <= 0).length;
9468
- if (zeroCount > 0) {
9469
- const delta2 = left2 / zeroCount;
9470
- for (const s of segs) {
9471
- if (s.t <= 0) s.t = delta2;
9472
- }
9473
- } else if (segs.length > 0) {
9474
- segs[segs.length - 1].t += left2;
9475
- }
9476
- }
9477
- function trimTrailing(segs) {
9478
- while (segs.length > 0 && (segs[segs.length - 1]?.t ?? 0) <= 0) {
9479
- segs.pop();
9480
- }
9481
- }
9482
- function parseSegs(colorlist) {
9483
- const segs = [];
9484
- let left2 = 1;
9485
- let rval = 0;
9486
- let badFloatCount = 0;
9487
- const warnedExceeded = { v: false };
9488
- for (const token of colorlist.split(":")) {
9489
- const result = appendSeg(token, left2, segs, warnedExceeded);
9490
- if (result === null) {
9491
- const errCode = badFloatCount === 0 ? 2 : 1;
9492
- badFloatCount++;
9493
- return { segs: [], error: errCode };
9494
- }
9495
- if (result.rval === 3) rval = 3;
9496
- left2 = result.left;
9497
- if (aeq0(left2)) {
9498
- left2 = 0;
9499
- break;
9500
- }
9501
- }
9502
- distributeLeft(segs, left2);
9503
- trimTrailing(segs);
9504
- return { segs, error: rval };
9505
- }
9506
-
9507
- // src/common/style-resolve.ts
9508
- var DEFAULT_COLOR = "black";
9509
- var DEFAULT_FILL = "lightgrey";
9510
- var PENWIDTH_BOLD = 2;
9511
- var TOKEN_FLAG = {
9512
- filled: "filled",
9513
- dashed: "dashed",
9514
- dotted: "dotted",
9515
- bold: "bold",
9516
- invis: "invis",
9517
- diagonals: "diagonals",
9518
- rounded: "rounded",
9519
- striped: "striped",
9520
- wedged: "wedged",
9521
- underline: "underline"
9522
- };
9523
- function zeroFlags() {
9524
- return {
9525
- filled: false,
9526
- dashed: false,
9527
- dotted: false,
9528
- bold: false,
9529
- invis: false,
9530
- diagonals: false,
9531
- rounded: false,
9532
- radial: false,
9533
- striped: false,
9534
- wedged: false,
9535
- underline: false,
9536
- setLineWidth: null
9537
- };
9538
- }
9539
- var FUNLIMIT = 64;
9540
- function styleTokens(style2) {
9541
- if (!style2) return [];
9542
- const out = [];
9543
- let fun = 0;
9544
- for (const raw of style2.split(",")) {
9545
- const token = raw.trim();
9546
- if (token.length === 0) continue;
9547
- if (fun === FUNLIMIT - 1) return [];
9548
- fun++;
9549
- out.push(token);
9550
- }
9551
- return out;
9552
- }
9553
- function parseSetLineWidth(token) {
9554
- const m = /^setlinewidth\s*\(\s*([^)]*)\)\s*$/.exec(token);
9555
- if (m === null) return null;
9556
- const v = parseFloat(m[1]);
9557
- return Number.isNaN(v) ? 0 : v;
9558
- }
9559
- function applyToken(flags, token) {
9560
- if (token === "radial") {
9561
- flags.radial = true;
9562
- flags.filled = true;
9563
- return;
9564
- }
9565
- const key = TOKEN_FLAG[token];
9566
- if (key !== void 0) flags[key] = true;
9567
- }
9568
- function nonEmpty(s) {
9569
- return s !== void 0 && s.length > 0;
9570
- }
9571
- function orElse(s, fallback) {
9572
- return nonEmpty(s) ? s : fallback;
9573
- }
9574
- function firstSolidColor(raw) {
9575
- const grad = parseGradientSpec(raw);
9576
- return grad !== null ? grad[0] : raw;
9577
- }
9578
- function parseStyleFlags(style2) {
9579
- const flags = zeroFlags();
9580
- if (!style2) return flags;
9581
- let fun = 0;
9582
- for (const raw of style2.split(",")) {
9583
- const token = raw.trim();
9584
- if (token.length === 0) continue;
9585
- if (fun === FUNLIMIT - 1) return zeroFlags();
9586
- fun++;
9587
- const lw = parseSetLineWidth(token);
9588
- if (lw !== null) {
9589
- flags.setLineWidth = lw;
9590
- continue;
9434
+ // src/common/htmltable-style.ts
9435
+ function htmlStyle(style2) {
9436
+ const s = { radial: false, rounded: false, invisible: false, dotted: false, dashed: false };
9437
+ if (style2 === void 0) return s;
9438
+ for (const tk of style2.split(/[ ,]+/)) {
9439
+ switch (tk.toUpperCase()) {
9440
+ case "ROUNDED":
9441
+ s.rounded = true;
9442
+ break;
9443
+ case "RADIAL":
9444
+ s.radial = true;
9445
+ break;
9446
+ case "SOLID":
9447
+ s.dotted = false;
9448
+ s.dashed = false;
9449
+ break;
9450
+ case "INVISIBLE":
9451
+ case "INVIS":
9452
+ s.invisible = true;
9453
+ break;
9454
+ case "DOTTED":
9455
+ s.dotted = true;
9456
+ break;
9457
+ case "DASHED":
9458
+ s.dashed = true;
9459
+ break;
9591
9460
  }
9592
- if (!token.includes("(")) applyToken(flags, token);
9593
- }
9594
- return flags;
9595
- }
9596
- function styleHasInvisibleAlias(style2) {
9597
- if (!style2) return false;
9598
- let fun = 0;
9599
- for (const raw of style2.split(",")) {
9600
- const token = raw.trim();
9601
- if (token.length === 0) continue;
9602
- if (fun === FUNLIMIT - 1) return false;
9603
- fun++;
9604
- if (token === "invisible") return true;
9605
- }
9606
- return false;
9607
- }
9608
- function resolvePenType(flags) {
9609
- if (flags.invis) return 0 /* None */;
9610
- if (flags.dashed) return 1 /* Dashed */;
9611
- if (flags.dotted) return 2 /* Dotted */;
9612
- return 3 /* Solid */;
9613
- }
9614
- function resolvePenWidth(flags, penwidthAttr) {
9615
- if (penwidthAttr && penwidthAttr.length > 0) {
9616
- const v = parseFloat(penwidthAttr);
9617
- if (isFinite(v)) return v;
9618
- }
9619
- if (flags.setLineWidth !== null) return flags.setLineWidth;
9620
- if (flags.bold) return PENWIDTH_BOLD;
9621
- return 1;
9622
- }
9623
- function resolvePenColor(colorAttr) {
9624
- if (!colorAttr || colorAttr.length === 0) return DEFAULT_COLOR;
9625
- return firstSolidColor(colorAttr);
9626
- }
9627
- function clusterColorAttrs(attrs) {
9628
- const base = nonEmpty(attrs.color) ? attrs.color : void 0;
9629
- const pencolor = orElse(attrs.pencolor, base ?? "");
9630
- const fillcolor = orElse(attrs.fillcolor, base ?? "");
9631
- return {
9632
- fillcolor: nonEmpty(fillcolor) ? fillcolor : void 0,
9633
- pencolor: nonEmpty(pencolor) ? pencolor : void 0
9634
- };
9635
- }
9636
- function applyBgcolor(attrs, filled, fillcolor) {
9637
- if ((!filled || !fillcolor) && nonEmpty(attrs.bgcolor)) {
9638
- return { filled: true, fillcolor: attrs.bgcolor };
9639
9461
  }
9640
- return { filled, fillcolor };
9641
- }
9642
- function findStopColor(colorlist) {
9643
- const { segs, error } = parseSegs(colorlist);
9644
- if (error === 1 || error === 2) return null;
9645
- if (segs.length < 2) return null;
9646
- const seg0 = segs[0];
9647
- const seg1 = segs[1];
9648
- if (seg0.color === null) return null;
9649
- const fillColor = seg0.color;
9650
- const stopColor = seg1.color ?? DEFAULT_COLOR;
9651
- const frac = seg0.hasFraction ? seg0.t : seg1.hasFraction ? 1 - seg1.t : 0;
9652
- return { fillColor, stopColor, frac };
9653
- }
9654
- function parseAngle(gradientangle2) {
9655
- if (!gradientangle2 || gradientangle2.length === 0) return 0;
9656
- const v = parseInt(gradientangle2, 10);
9657
- return isFinite(v) ? v : 0;
9658
- }
9659
- function buildResolvedFill(rawColor, flags, angle) {
9660
- const stop = findStopColor(rawColor);
9661
- if (stop === null) return { kind: "solid", color: rawColor };
9662
- const kind = flags.radial ? "radial" : "linear";
9663
- return { kind, fillColor: stop.fillColor, stopColor: stop.stopColor, frac: stop.frac, angle };
9664
- }
9665
- function resolveNodeFillEx(attrs) {
9666
- const flags = parseStyleFlags(attrs.style);
9667
- if (!flags.filled) return { kind: "none" };
9668
- const raw = nonEmpty(attrs.fillcolor) ? attrs.fillcolor : nonEmpty(attrs.color) ? attrs.color : DEFAULT_FILL;
9669
- return buildResolvedFill(raw, flags, parseAngle(attrs.gradientangle));
9670
- }
9671
- function resolveClusterFillEx(attrs) {
9672
- const flags = parseStyleFlags(attrs.style);
9673
- const colors = clusterColorAttrs(attrs);
9674
- const bg = applyBgcolor(attrs, flags.filled, colors.fillcolor);
9675
- if (!bg.filled) return { kind: "none" };
9676
- const raw = bg.fillcolor ?? DEFAULT_FILL;
9677
- return buildResolvedFill(raw, flags, parseAngle(attrs.gradientangle));
9462
+ return s;
9678
9463
  }
9679
9464
 
9680
9465
  // src/common/htmltable-types.ts
@@ -9759,8 +9544,9 @@ function resetHtmlFillPenWidth() {
9759
9544
  htmlFillPen = 1;
9760
9545
  }
9761
9546
  function penTypeOf(style2) {
9762
- if (style2 !== void 0 && style2.includes("dashed")) return 1 /* Dashed */;
9763
- if (style2 !== void 0 && style2.includes("dotted")) return 2 /* Dotted */;
9547
+ const st = htmlStyle(style2);
9548
+ if (st.dashed) return 1 /* Dashed */;
9549
+ if (st.dotted) return 2 /* Dotted */;
9764
9550
  return 3 /* Solid */;
9765
9551
  }
9766
9552
  function withHtmlPaint(paint, job, drawFn) {
@@ -9827,7 +9613,7 @@ function doBorder(spec, renderer, job) {
9827
9613
  const { box, pos, border: border2, color, sides: sides2, style: style2 } = spec;
9828
9614
  htmlFillPen = border2;
9829
9615
  withHtmlPaint({ pen: color, penWidth: border2, penStyle: style2 }, job, () => {
9830
- if (parseStyleFlags(style2).rounded) {
9616
+ if (htmlStyle(style2).rounded) {
9831
9617
  emitRoundedBezier(mkPts(box, border2, pos), { x: 0, y: 0 }, false, { renderer, job });
9832
9618
  return;
9833
9619
  }
@@ -10032,14 +9818,14 @@ function bgFillPaint(ctx) {
10032
9818
  fill: spec[0],
10033
9819
  stop: spec[1],
10034
9820
  gradientAngle: ctx.gradientangle ?? 0,
10035
- radial: ctx.style !== void 0 && ctx.style.includes("radial"),
9821
+ radial: htmlStyle(ctx.style).radial,
10036
9822
  penWidth
10037
9823
  };
10038
9824
  }
10039
9825
  function emitBgFill(ctx) {
10040
9826
  const { box, pos, border: border2, renderer, job } = ctx;
10041
9827
  withHtmlPaint(bgFillPaint(ctx), job, () => {
10042
- if (parseStyleFlags(ctx.style).rounded) {
9828
+ if (htmlStyle(ctx.style).rounded) {
10043
9829
  emitRoundedBezier(mkPts(box, border2, pos), { x: 0, y: 0 }, true, { renderer, job });
10044
9830
  return;
10045
9831
  }
@@ -10105,13 +9891,6 @@ function emitHtmlImg(img, pos, renderer, job) {
10105
9891
  }
10106
9892
  renderer.usershape?.(img.src, b, job);
10107
9893
  }
10108
- function htmlStyleInvisible(style2) {
10109
- if (style2 === void 0) return false;
10110
- return style2.split(/[ ,]+/).some((tok) => {
10111
- const t = tok.toUpperCase();
10112
- return t === "INVIS" || t === "INVISIBLE";
10113
- });
10114
- }
10115
9894
  function emitHtmlCell(cell2, pos, renderer, job) {
10116
9895
  const inAnchor = initHtmlAnchor(
10117
9896
  { href: cell2.href, title: cell2.title, target: cell2.target, id: cell2.id },
@@ -10119,7 +9898,7 @@ function emitHtmlCell(cell2, pos, renderer, job) {
10119
9898
  renderer,
10120
9899
  job
10121
9900
  );
10122
- if (htmlStyleInvisible(cell2.style)) {
9901
+ if (htmlStyle(cell2.style).invisible) {
10123
9902
  if (inAnchor) endHtmlAnchor(renderer, job);
10124
9903
  return;
10125
9904
  }
@@ -10155,7 +9934,7 @@ function emitHtmlLabel(placed, pos, renderer, job) {
10155
9934
  const tblBox = absBox(placed.box, pos);
10156
9935
  const clustersLast = (job.flags & EMIT_CLUSTERS_LAST) !== 0;
10157
9936
  const inAnchor = clustersLast ? false : initHtmlAnchor(anchorData, tblBox, renderer, job);
10158
- const tblInvisible = htmlStyleInvisible(placed.style);
9937
+ const tblInvisible = htmlStyle(placed.style).invisible;
10159
9938
  if (!tblInvisible) {
10160
9939
  if (placed.bgcolor !== void 0) {
10161
9940
  emitBgFill({
@@ -10232,7 +10011,7 @@ var sides = (v) => {
10232
10011
  };
10233
10012
  var STYLE_WORDS = ["rounded", "radial", "solid", "invisible", "invis", "dotted", "dashed"];
10234
10013
  var STYLE_DELIM = " ,";
10235
- function styleTokens2(v) {
10014
+ function styleTokens(v) {
10236
10015
  const out = [];
10237
10016
  const span = (from, member) => {
10238
10017
  let i = from;
@@ -10251,7 +10030,7 @@ function styleTokens2(v) {
10251
10030
  var style = (v) => {
10252
10031
  const legal = [];
10253
10032
  const text = [];
10254
- for (const tk of styleTokens2(v)) {
10033
+ for (const tk of styleTokens(v)) {
10255
10034
  if (STYLE_WORDS.includes(lower(tk))) legal.push(tk);
10256
10035
  else text.push(warning(`Illegal value ${tk} for STYLE - ignored
10257
10036
  `));
@@ -17989,11 +17768,14 @@ function interpretCRNL(str) {
17989
17768
  }
17990
17769
  return out;
17991
17770
  }
17771
+ function preprocessTooltip(s) {
17772
+ return interpretCRNL(htmlEntityUTF8(s));
17773
+ }
17992
17774
 
17993
17775
  // src/common/make-label.ts
17994
17776
  var DEFAULT_FONTSIZE2 = 14;
17995
17777
  var DEFAULT_FONTNAME = "Times-Roman";
17996
- var DEFAULT_COLOR2 = "black";
17778
+ var DEFAULT_COLOR = "black";
17997
17779
  function getPenColor(obj) {
17998
17780
  if (obj === void 0) return void 0;
17999
17781
  const pc = obj.attrs.get("pencolor");
@@ -18615,11 +18397,257 @@ function assembleResult(p, dimen, grown, fixedshape) {
18615
18397
  };
18616
18398
  }
18617
18399
 
18618
- // src/layout/dot/fastgr.ts
18619
- var VIRTUAL = 1;
18620
- var NORMAL = 0;
18621
- var FLATORDER = 4;
18622
- var REVERSED = 3;
18400
+ // src/common/multicolor.ts
18401
+ var EPS = 1e-5;
18402
+ function aeq0(x) {
18403
+ return x < EPS && x > -EPS;
18404
+ }
18405
+ function getSegLen(token) {
18406
+ const semi = token.indexOf(";");
18407
+ if (semi === -1) return { color: token, v: 0 };
18408
+ const color = token.slice(0, semi);
18409
+ const rest = token.slice(semi + 1);
18410
+ if (rest.length === 0) return { color, v: -1 };
18411
+ const parsed = parseFloat(rest);
18412
+ return !isNaN(parsed) && parsed >= 0 ? { color, v: parsed } : { color, v: -1 };
18413
+ }
18414
+ function appendSeg(token, left2, segs, warnedExceeded) {
18415
+ const { color, v } = getSegLen(token);
18416
+ if (v < 0) return null;
18417
+ let t = v;
18418
+ let rval = 0;
18419
+ const del = t - left2;
18420
+ if (del > 0) {
18421
+ if (!aeq0(del) && !warnedExceeded.v) {
18422
+ warnedExceeded.v = true;
18423
+ rval = 3;
18424
+ }
18425
+ t = left2;
18426
+ }
18427
+ left2 -= t;
18428
+ segs.push({ color: color.length > 0 ? color : null, t, hasFraction: t > 0 && v > 0 });
18429
+ return { left: left2, rval };
18430
+ }
18431
+ function distributeLeft(segs, left2) {
18432
+ if (left2 <= 0) return;
18433
+ const zeroCount = segs.filter((s) => s.t <= 0).length;
18434
+ if (zeroCount > 0) {
18435
+ const delta2 = left2 / zeroCount;
18436
+ for (const s of segs) {
18437
+ if (s.t <= 0) s.t = delta2;
18438
+ }
18439
+ } else if (segs.length > 0) {
18440
+ segs[segs.length - 1].t += left2;
18441
+ }
18442
+ }
18443
+ function trimTrailing(segs) {
18444
+ while (segs.length > 0 && (segs[segs.length - 1]?.t ?? 0) <= 0) {
18445
+ segs.pop();
18446
+ }
18447
+ }
18448
+ function parseSegs(colorlist) {
18449
+ const segs = [];
18450
+ let left2 = 1;
18451
+ let rval = 0;
18452
+ let badFloatCount = 0;
18453
+ const warnedExceeded = { v: false };
18454
+ for (const token of colorlist.split(":")) {
18455
+ const result = appendSeg(token, left2, segs, warnedExceeded);
18456
+ if (result === null) {
18457
+ const errCode = badFloatCount === 0 ? 2 : 1;
18458
+ badFloatCount++;
18459
+ return { segs: [], error: errCode };
18460
+ }
18461
+ if (result.rval === 3) rval = 3;
18462
+ left2 = result.left;
18463
+ if (aeq0(left2)) {
18464
+ left2 = 0;
18465
+ break;
18466
+ }
18467
+ }
18468
+ distributeLeft(segs, left2);
18469
+ trimTrailing(segs);
18470
+ return { segs, error: rval };
18471
+ }
18472
+
18473
+ // src/common/style-resolve.ts
18474
+ var DEFAULT_COLOR2 = "black";
18475
+ var DEFAULT_FILL = "lightgrey";
18476
+ var PENWIDTH_BOLD = 2;
18477
+ var TOKEN_FLAG = {
18478
+ filled: "filled",
18479
+ dashed: "dashed",
18480
+ dotted: "dotted",
18481
+ bold: "bold",
18482
+ invis: "invis",
18483
+ diagonals: "diagonals",
18484
+ rounded: "rounded",
18485
+ striped: "striped",
18486
+ wedged: "wedged",
18487
+ underline: "underline"
18488
+ };
18489
+ function zeroFlags() {
18490
+ return {
18491
+ filled: false,
18492
+ dashed: false,
18493
+ dotted: false,
18494
+ bold: false,
18495
+ invis: false,
18496
+ diagonals: false,
18497
+ rounded: false,
18498
+ radial: false,
18499
+ striped: false,
18500
+ wedged: false,
18501
+ underline: false,
18502
+ setLineWidth: null
18503
+ };
18504
+ }
18505
+ var FUNLIMIT = 64;
18506
+ function styleTokens2(style2) {
18507
+ if (!style2) return [];
18508
+ const out = [];
18509
+ let fun = 0;
18510
+ for (const raw of style2.split(",")) {
18511
+ const token = raw.trim();
18512
+ if (token.length === 0) continue;
18513
+ if (fun === FUNLIMIT - 1) return [];
18514
+ fun++;
18515
+ out.push(token);
18516
+ }
18517
+ return out;
18518
+ }
18519
+ function parseSetLineWidth(token) {
18520
+ const m = /^setlinewidth\s*\(\s*([^)]*)\)\s*$/.exec(token);
18521
+ if (m === null) return null;
18522
+ const v = parseFloat(m[1]);
18523
+ return Number.isNaN(v) ? 0 : v;
18524
+ }
18525
+ function applyToken(flags, token) {
18526
+ if (token === "radial") {
18527
+ flags.radial = true;
18528
+ flags.filled = true;
18529
+ return;
18530
+ }
18531
+ const key = TOKEN_FLAG[token];
18532
+ if (key !== void 0) flags[key] = true;
18533
+ }
18534
+ function nonEmpty(s) {
18535
+ return s !== void 0 && s.length > 0;
18536
+ }
18537
+ function orElse(s, fallback) {
18538
+ return nonEmpty(s) ? s : fallback;
18539
+ }
18540
+ function firstSolidColor(raw) {
18541
+ const grad = parseGradientSpec(raw);
18542
+ return grad !== null ? grad[0] : raw;
18543
+ }
18544
+ function parseStyleFlags(style2) {
18545
+ const flags = zeroFlags();
18546
+ if (!style2) return flags;
18547
+ let fun = 0;
18548
+ for (const raw of style2.split(",")) {
18549
+ const token = raw.trim();
18550
+ if (token.length === 0) continue;
18551
+ if (fun === FUNLIMIT - 1) return zeroFlags();
18552
+ fun++;
18553
+ const lw = parseSetLineWidth(token);
18554
+ if (lw !== null) {
18555
+ flags.setLineWidth = lw;
18556
+ continue;
18557
+ }
18558
+ if (!token.includes("(")) applyToken(flags, token);
18559
+ }
18560
+ return flags;
18561
+ }
18562
+ function styleHasInvisibleAlias(style2) {
18563
+ if (!style2) return false;
18564
+ let fun = 0;
18565
+ for (const raw of style2.split(",")) {
18566
+ const token = raw.trim();
18567
+ if (token.length === 0) continue;
18568
+ if (fun === FUNLIMIT - 1) return false;
18569
+ fun++;
18570
+ if (token === "invisible") return true;
18571
+ }
18572
+ return false;
18573
+ }
18574
+ function resolvePenType(flags) {
18575
+ if (flags.invis) return 0 /* None */;
18576
+ if (flags.dashed) return 1 /* Dashed */;
18577
+ if (flags.dotted) return 2 /* Dotted */;
18578
+ return 3 /* Solid */;
18579
+ }
18580
+ function resolvePenWidth(flags, penwidthAttr) {
18581
+ if (penwidthAttr && penwidthAttr.length > 0) {
18582
+ const v = parseFloat(penwidthAttr);
18583
+ if (isFinite(v)) return v;
18584
+ }
18585
+ if (flags.setLineWidth !== null) return flags.setLineWidth;
18586
+ if (flags.bold) return PENWIDTH_BOLD;
18587
+ return 1;
18588
+ }
18589
+ function resolvePenColor(colorAttr) {
18590
+ if (!colorAttr || colorAttr.length === 0) return DEFAULT_COLOR2;
18591
+ return firstSolidColor(colorAttr);
18592
+ }
18593
+ function clusterColorAttrs(attrs) {
18594
+ const base = nonEmpty(attrs.color) ? attrs.color : void 0;
18595
+ const pencolor = orElse(attrs.pencolor, base ?? "");
18596
+ const fillcolor = orElse(attrs.fillcolor, base ?? "");
18597
+ return {
18598
+ fillcolor: nonEmpty(fillcolor) ? fillcolor : void 0,
18599
+ pencolor: nonEmpty(pencolor) ? pencolor : void 0
18600
+ };
18601
+ }
18602
+ function applyBgcolor(attrs, filled, fillcolor) {
18603
+ if ((!filled || !fillcolor) && nonEmpty(attrs.bgcolor)) {
18604
+ return { filled: true, fillcolor: attrs.bgcolor };
18605
+ }
18606
+ return { filled, fillcolor };
18607
+ }
18608
+ function findStopColor(colorlist) {
18609
+ const { segs, error } = parseSegs(colorlist);
18610
+ if (error === 1 || error === 2) return null;
18611
+ if (segs.length < 2) return null;
18612
+ const seg0 = segs[0];
18613
+ const seg1 = segs[1];
18614
+ if (seg0.color === null) return null;
18615
+ const fillColor = seg0.color;
18616
+ const stopColor = seg1.color ?? DEFAULT_COLOR2;
18617
+ const frac = seg0.hasFraction ? seg0.t : seg1.hasFraction ? 1 - seg1.t : 0;
18618
+ return { fillColor, stopColor, frac };
18619
+ }
18620
+ function parseAngle(gradientangle2) {
18621
+ if (!gradientangle2 || gradientangle2.length === 0) return 0;
18622
+ const v = parseInt(gradientangle2, 10);
18623
+ return isFinite(v) ? v : 0;
18624
+ }
18625
+ function buildResolvedFill(rawColor, flags, angle) {
18626
+ const stop = findStopColor(rawColor);
18627
+ if (stop === null) return { kind: "solid", color: rawColor };
18628
+ const kind = flags.radial ? "radial" : "linear";
18629
+ return { kind, fillColor: stop.fillColor, stopColor: stop.stopColor, frac: stop.frac, angle };
18630
+ }
18631
+ function resolveNodeFillEx(attrs) {
18632
+ const flags = parseStyleFlags(attrs.style);
18633
+ if (!flags.filled) return { kind: "none" };
18634
+ const raw = nonEmpty(attrs.fillcolor) ? attrs.fillcolor : nonEmpty(attrs.color) ? attrs.color : DEFAULT_FILL;
18635
+ return buildResolvedFill(raw, flags, parseAngle(attrs.gradientangle));
18636
+ }
18637
+ function resolveClusterFillEx(attrs) {
18638
+ const flags = parseStyleFlags(attrs.style);
18639
+ const colors = clusterColorAttrs(attrs);
18640
+ const bg = applyBgcolor(attrs, flags.filled, colors.fillcolor);
18641
+ if (!bg.filled) return { kind: "none" };
18642
+ const raw = bg.fillcolor ?? DEFAULT_FILL;
18643
+ return buildResolvedFill(raw, flags, parseAngle(attrs.gradientangle));
18644
+ }
18645
+
18646
+ // src/layout/dot/fastgr.ts
18647
+ var VIRTUAL = 1;
18648
+ var NORMAL = 0;
18649
+ var FLATORDER = 4;
18650
+ var REVERSED = 3;
18623
18651
  var MC_SCALE = 256;
18624
18652
  function elistAppend(el, e) {
18625
18653
  el.list[el.size++] = e;
@@ -20724,7 +20752,7 @@ function readFontParams(sg) {
20724
20752
  return {
20725
20753
  fontsize: parseFloat(graphAttrInherited(sg, "fontsize") ?? "") || DEFAULT_FONTSIZE2,
20726
20754
  fontname: graphAttrInherited(sg, "fontname") ?? DEFAULT_FONTNAME,
20727
- fontcolor: graphAttrInherited(sg, "fontcolor") ?? DEFAULT_COLOR2
20755
+ fontcolor: graphAttrInherited(sg, "fontcolor") ?? DEFAULT_COLOR
20728
20756
  };
20729
20757
  }
20730
20758
  function applyLabelBorder(sg, dimen) {
@@ -24641,7 +24669,7 @@ function readFontAttrs(n, g) {
24641
24669
  return {
24642
24670
  fontname: nodeAttr(n, g, "fontname") ?? DEFAULT_FONTNAME,
24643
24671
  fontsize: parseFloat(nodeAttr(n, g, "fontsize") ?? String(DEFAULT_FONTSIZE2)),
24644
- fontcolor: nodeAttr(n, g, "fontcolor") ?? DEFAULT_COLOR2
24672
+ fontcolor: nodeAttr(n, g, "fontcolor") ?? DEFAULT_COLOR
24645
24673
  };
24646
24674
  }
24647
24675
  function assignShapeInfo(n, polyDesc) {
@@ -24852,31 +24880,141 @@ function stripedBox(job, pts, clrs, rotate, renderer) {
24852
24880
  return error;
24853
24881
  }
24854
24882
 
24883
+ // src/util/xml.ts
24884
+ function gvXmlEscape(s, flags) {
24885
+ return XmlEscaper.escape(s, flags);
24886
+ }
24887
+ var XmlEscaper = class _XmlEscaper {
24888
+ static escape(s, flags) {
24889
+ let result = "";
24890
+ let previous = "";
24891
+ let pos = 0;
24892
+ while (pos < s.length) {
24893
+ const prev = s[pos];
24894
+ const [fragment, consumed] = _XmlEscaper.core(previous, s, pos, flags);
24895
+ result += fragment;
24896
+ previous = prev;
24897
+ pos += consumed;
24898
+ }
24899
+ return result;
24900
+ }
24901
+ /**
24902
+ * Escape one character/codepoint at s[pos].
24903
+ * Returns [escaped_fragment, characters_consumed].
24904
+ * Delegates to focused helpers to stay within CCN limit.
24905
+ * @see lib/util/xml.c:xml_core
24906
+ */
24907
+ static core(previous, s, pos, flags) {
24908
+ const special = _XmlEscaper.coreSpecial(previous, s, pos, flags);
24909
+ if (special !== null) return special;
24910
+ const code = s.charCodeAt(pos);
24911
+ if (code > 127 && flags.utf8) return _XmlEscaper.utf8Entity(s, pos);
24912
+ return [s[pos], 1];
24913
+ }
24914
+ /**
24915
+ * Handle all the fixed-escape cases (& < > - space " ' \n \r).
24916
+ * Returns null when none of those cases applies.
24917
+ * @see lib/util/xml.c:xml_core
24918
+ */
24919
+ static coreSpecial(previous, s, pos, flags) {
24920
+ const c = s[pos];
24921
+ if (c === "&" && (flags.raw || !_XmlEscaper.isEntity(s, pos))) {
24922
+ return ["&amp;", 1];
24923
+ }
24924
+ if (c === "<") return ["&lt;", 1];
24925
+ if (c === ">") return ["&gt;", 1];
24926
+ if (c === '"') return ["&quot;", 1];
24927
+ if (c === "'") return ["&#39;", 1];
24928
+ if (c === "-" && flags.dash) return ["&#45;", 1];
24929
+ if (c === " " && previous === " " && flags.nbsp) return ["&#160;", 1];
24930
+ if (c === "\n" && flags.raw) return ["&#10;", 1];
24931
+ if (c === "\r" && flags.raw) return ["&#13;", 1];
24932
+ return null;
24933
+ }
24934
+ /**
24935
+ * Return true if s[pos] (which is '&') starts a valid XML entity:
24936
+ * &[A-Za-z]+; e.g. &Ccedil;
24937
+ * &#[0-9]*; e.g. &#38;
24938
+ * &#x[0-9a-fA-F]*; e.g. &#x6C34;
24939
+ * @see lib/util/xml.c:xml_isentity
24940
+ */
24941
+ static isEntity(s, pos) {
24942
+ let i = pos + 1;
24943
+ if (i >= s.length || s[i] === ";") return false;
24944
+ i = s[i] === "#" ? _XmlEscaper.scanNumericRef(s, i) : _XmlEscaper.scanNamedRef(s, i);
24945
+ return i < s.length && s[i] === ";";
24946
+ }
24947
+ /** Scan a numeric entity body starting just after the '#'. */
24948
+ static scanNumericRef(s, hashPos) {
24949
+ let i = hashPos + 1;
24950
+ if (i < s.length && (s[i] === "x" || s[i] === "X")) {
24951
+ i++;
24952
+ while (i < s.length && _XmlEscaper.isXDigit(s[i])) i++;
24953
+ } else {
24954
+ while (i < s.length && _XmlEscaper.isDecDigit(s[i])) i++;
24955
+ }
24956
+ return i;
24957
+ }
24958
+ /** Scan a named entity body (alpha chars after the '&'). */
24959
+ static scanNamedRef(s, start) {
24960
+ let i = start;
24961
+ while (i < s.length && _XmlEscaper.isAlpha(s[i])) i++;
24962
+ return i;
24963
+ }
24964
+ /**
24965
+ * Emit &#xNNNN; for the Unicode code point at s[pos]. JS strings are
24966
+ * UTF-16, so the code point (what C decodes from UTF-8) comes from
24967
+ * codePointAt, and an astral character consumes two units.
24968
+ * @see lib/util/xml.c:xml_core (UTF-8 block)
24969
+ */
24970
+ static utf8Entity(s, pos) {
24971
+ const cp = _XmlEscaper.decodeUtf8(s, pos);
24972
+ const consumed = _XmlEscaper.utf8ByteLen(cp);
24973
+ return [`&#x${cp.toString(16)};`, consumed];
24974
+ }
24975
+ /**
24976
+ * Return the code point at s[pos]. A lone surrogate is the analogue of
24977
+ * malformed UTF-8, where C reports an error and exits (xml.c:135).
24978
+ * @see lib/util/xml.c:xml_core (fprintf + graphviz_exit at xml.c:135)
24979
+ */
24980
+ static decodeUtf8(s, pos) {
24981
+ const cp = s.codePointAt(pos);
24982
+ if (cp >= 55296 && cp <= 57343) {
24983
+ throw new RenderError(`gvXmlEscape: malformed UTF-8 at position ${pos}`, "RENDER_ERROR");
24984
+ }
24985
+ return cp;
24986
+ }
24987
+ /**
24988
+ * Return how many UTF-16 units encode the code point (C: the UTF-8 byte
24989
+ * length from the leading byte).
24990
+ * @see lib/util/xml.c:xml_core (length computation)
24991
+ */
24992
+ static utf8ByteLen(cp) {
24993
+ return cp > 65535 ? 2 : 1;
24994
+ }
24995
+ static isDecDigit(c) {
24996
+ return c >= "0" && c <= "9";
24997
+ }
24998
+ static isAlpha(c) {
24999
+ return c >= "a" && c <= "z" || c >= "A" && c <= "Z";
25000
+ }
25001
+ static isXDigit(c) {
25002
+ return _XmlEscaper.isDecDigit(c) || c >= "a" && c <= "f" || c >= "A" && c <= "F";
25003
+ }
25004
+ };
25005
+
24855
25006
  // src/render/xml-escape.ts
24856
- var RE_DQUOTE = new RegExp('"', "g");
24857
- var RE_SQUOTE = new RegExp("'", "g");
24858
- var RE_AMP_NONENTITY = /&(?![A-Za-z]+;|#[0-9]+;|#x[0-9A-Fa-f]+;)/g;
25007
+ var FLAGS_BASE = { raw: false, dash: false, nbsp: false, utf8: false };
25008
+ var FLAGS_TITLE = { raw: false, dash: true, nbsp: true, utf8: false };
25009
+ var FLAGS_TEXT = { raw: true, dash: true, nbsp: true, utf8: false };
24859
25010
  function escapeXml(s) {
24860
- let r = s.replace(RE_AMP_NONENTITY, "&amp;");
24861
- r = r.replace(/</g, "&lt;");
24862
- r = r.replace(/>/g, "&gt;");
24863
- r = r.replace(RE_DQUOTE, "&quot;");
24864
- r = r.replace(RE_SQUOTE, "&#39;");
24865
- return r;
25011
+ return gvXmlEscape(s, FLAGS_BASE);
24866
25012
  }
24867
25013
  function escapeXmlTitle(s) {
24868
- let r = escapeXml(s);
24869
- r = r.replace(/-/g, "&#45;");
24870
- r = r.replace(/ {2,}/g, (m) => " " + "&#160;".repeat(m.length - 1));
24871
- return r;
25014
+ return gvXmlEscape(s, FLAGS_TITLE);
24872
25015
  }
24873
25016
  function escapeXmlText(s) {
24874
- let r = escapeXml(s);
24875
- r = r.replace(/-/g, "&#45;");
24876
- r = r.replace(/\n/g, "&#10;");
24877
- r = r.replace(/\r/g, "&#13;");
24878
- r = r.replace(/ {2,}/g, (m) => " " + "&#160;".repeat(m.length - 1));
24879
- return r;
25017
+ return gvXmlEscape(s, FLAGS_TEXT);
24880
25018
  }
24881
25019
 
24882
25020
  // src/gvc/device-cluster.ts
@@ -25057,7 +25195,7 @@ function anchorAttr(n, key) {
25057
25195
  return v !== void 0 && v !== "" ? v : void 0;
25058
25196
  }
25059
25197
  function resolveTooltip(n, tooltip) {
25060
- if (tooltip !== void 0) return substObjAnchor(interpretCRNL(tooltip), n);
25198
+ if (tooltip !== void 0) return substObjAnchor(preprocessTooltip(tooltip), n);
25061
25199
  const label = n.info.label;
25062
25200
  return label?.text ?? "";
25063
25201
  }
@@ -25121,7 +25259,7 @@ function applyPenState(obj, styleAttr, colorAttr, penwidthAttr) {
25121
25259
  obj.penColor = resolveRenderColor(resolvePenColor(colorAttr));
25122
25260
  obj.pen = resolvePenType(flags);
25123
25261
  obj.penWidth = resolvePenWidth(flags, penwidthAttr);
25124
- obj.rawStyle = styleTokens(styleAttr);
25262
+ obj.rawStyle = styleTokens2(styleAttr);
25125
25263
  }
25126
25264
  function applyNodeStyle(obj, n, job) {
25127
25265
  const styleAttr = nodeAttr(n, n.root, "style");
@@ -25344,7 +25482,7 @@ function initFontEdgeAttr(e) {
25344
25482
  return {
25345
25483
  fontsize: lateDouble(e.attrs.get("fontsize"), DEFAULT_FONTSIZE2, MIN_FONTSIZE),
25346
25484
  fontname: e.attrs.get("fontname") || DEFAULT_FONTNAME,
25347
- fontcolor: e.attrs.get("fontcolor") || DEFAULT_COLOR2
25485
+ fontcolor: e.attrs.get("fontcolor") || DEFAULT_COLOR
25348
25486
  };
25349
25487
  }
25350
25488
  function initFontLabelEdgeAttr(e, fi) {
@@ -27574,7 +27712,7 @@ function subst(s, obj) {
27574
27712
  return substObjAnchor(s, obj);
27575
27713
  }
27576
27714
  function tip(s, obj) {
27577
- return substObjAnchor(interpretCRNL(s), obj);
27715
+ return substObjAnchor(preprocessTooltip(s), obj);
27578
27716
  }
27579
27717
  function textOf(lp) {
27580
27718
  return lp?.text ?? null;
@@ -28153,7 +28291,7 @@ function setEdgePen(e, job) {
28153
28291
  );
28154
28292
  obj.pen = resolvePenType(flags);
28155
28293
  obj.penWidth = resolvePenWidth(flags, e.attrs.get("penwidth"));
28156
- obj.rawStyle = styleTokens(e.attrs.get("style"));
28294
+ obj.rawStyle = styleTokens2(e.attrs.get("style"));
28157
28295
  }
28158
28296
  function renderEdge(e, renderer, job) {
28159
28297
  emitEdgeComments(e, renderer, job);