@react-x11/components 0.14.1 → 0.16.1

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 (96) hide show
  1. package/dist/code-editor/node.d.ts +13 -0
  2. package/dist/code-editor/node.d.ts.map +1 -1
  3. package/dist/code-editor/node.js +23 -5
  4. package/dist/code-editor/node.js.map +1 -1
  5. package/dist/html/css/cascade.d.ts +171 -26
  6. package/dist/html/css/cascade.d.ts.map +1 -1
  7. package/dist/html/css/cascade.js +616 -216
  8. package/dist/html/css/cascade.js.map +1 -1
  9. package/dist/html/css/color.d.ts +25 -0
  10. package/dist/html/css/color.d.ts.map +1 -1
  11. package/dist/html/css/color.js +307 -19
  12. package/dist/html/css/color.js.map +1 -1
  13. package/dist/html/css/content.d.ts +11 -6
  14. package/dist/html/css/content.d.ts.map +1 -1
  15. package/dist/html/css/content.js +171 -12
  16. package/dist/html/css/content.js.map +1 -1
  17. package/dist/html/css/parse.d.ts.map +1 -1
  18. package/dist/html/css/parse.js +62 -9
  19. package/dist/html/css/parse.js.map +1 -1
  20. package/dist/html/css/shapes.d.ts +18 -0
  21. package/dist/html/css/shapes.d.ts.map +1 -1
  22. package/dist/html/css/shapes.js +21 -1
  23. package/dist/html/css/shapes.js.map +1 -1
  24. package/dist/html/css/style.d.ts +42 -5
  25. package/dist/html/css/style.d.ts.map +1 -1
  26. package/dist/html/css/style.js +74 -12
  27. package/dist/html/css/style.js.map +1 -1
  28. package/dist/html/css/ua.js +27 -8
  29. package/dist/html/css/ua.js.map +1 -1
  30. package/dist/html/css/values.d.ts +5 -0
  31. package/dist/html/css/values.d.ts.map +1 -1
  32. package/dist/html/css/values.js +18 -9
  33. package/dist/html/css/values.js.map +1 -1
  34. package/dist/html/index.d.ts.map +1 -1
  35. package/dist/html/index.js +7 -0
  36. package/dist/html/index.js.map +1 -1
  37. package/dist/html/layout/block.d.ts +29 -9
  38. package/dist/html/layout/block.d.ts.map +1 -1
  39. package/dist/html/layout/block.js +141 -52
  40. package/dist/html/layout/block.js.map +1 -1
  41. package/dist/html/layout/boxes.d.ts +29 -2
  42. package/dist/html/layout/boxes.d.ts.map +1 -1
  43. package/dist/html/layout/boxes.js +172 -23
  44. package/dist/html/layout/boxes.js.map +1 -1
  45. package/dist/html/layout/css-grid.d.ts.map +1 -1
  46. package/dist/html/layout/css-grid.js +11 -9
  47. package/dist/html/layout/css-grid.js.map +1 -1
  48. package/dist/html/layout/flex.d.ts.map +1 -1
  49. package/dist/html/layout/flex.js +1759 -238
  50. package/dist/html/layout/flex.js.map +1 -1
  51. package/dist/html/layout/inline.d.ts +15 -3
  52. package/dist/html/layout/inline.d.ts.map +1 -1
  53. package/dist/html/layout/inline.js +64 -19
  54. package/dist/html/layout/inline.js.map +1 -1
  55. package/dist/html/layout/table.d.ts +27 -1
  56. package/dist/html/layout/table.d.ts.map +1 -1
  57. package/dist/html/layout/table.js +886 -324
  58. package/dist/html/layout/table.js.map +1 -1
  59. package/dist/html/node.d.ts +35 -4
  60. package/dist/html/node.d.ts.map +1 -1
  61. package/dist/html/node.js +122 -26
  62. package/dist/html/node.js.map +1 -1
  63. package/dist/html/paint.d.ts.map +1 -1
  64. package/dist/html/paint.js +558 -178
  65. package/dist/html/paint.js.map +1 -1
  66. package/dist/html/resources.js +14 -1
  67. package/dist/html/resources.js.map +1 -1
  68. package/dist/html/svg.d.ts +23 -7
  69. package/dist/html/svg.d.ts.map +1 -1
  70. package/dist/html/svg.js +264 -59
  71. package/dist/html/svg.js.map +1 -1
  72. package/dist/html/widgets.d.ts.map +1 -1
  73. package/dist/html/widgets.js +57 -4
  74. package/dist/html/widgets.js.map +1 -1
  75. package/package.json +3 -3
  76. package/src/code-editor/node.ts +24 -5
  77. package/src/html/css/cascade.ts +697 -221
  78. package/src/html/css/color.ts +345 -18
  79. package/src/html/css/content.ts +173 -15
  80. package/src/html/css/parse.ts +60 -8
  81. package/src/html/css/shapes.ts +35 -1
  82. package/src/html/css/style.ts +122 -17
  83. package/src/html/css/ua.ts +27 -8
  84. package/src/html/css/values.ts +23 -9
  85. package/src/html/index.ts +7 -0
  86. package/src/html/layout/block.ts +149 -58
  87. package/src/html/layout/boxes.ts +211 -27
  88. package/src/html/layout/css-grid.ts +12 -9
  89. package/src/html/layout/flex.ts +1975 -231
  90. package/src/html/layout/inline.ts +70 -16
  91. package/src/html/layout/table.ts +961 -332
  92. package/src/html/node.ts +155 -27
  93. package/src/html/paint.ts +692 -234
  94. package/src/html/resources.ts +13 -1
  95. package/src/html/svg.ts +299 -56
  96. package/src/html/widgets.ts +61 -4
package/dist/html/svg.js CHANGED
@@ -18,6 +18,7 @@ import * as ntk from 'react-x11/ntk';
18
18
  import { isSvgRoot, rawTextOf } from './dom.js';
19
19
  import { Cascade } from './css/cascade.js';
20
20
  import { parseMediaQuery, parseStylesheet } from './css/parse.js';
21
+ import { useHref } from './css/shapes.js';
21
22
  import { inkColor, isTransparent, parseColor } from './css/values.js';
22
23
  export { isSvgRoot };
23
24
  function svgAttr(el, name) {
@@ -102,10 +103,93 @@ export function svgIntrinsics(el, fontSize = 16) {
102
103
  }
103
104
  return { width, height, ratio };
104
105
  }
106
+ /**
107
+ * An image's size, in device pixels, where nothing sets it: CSS Images 3's
108
+ * default sizing algorithm (5.3.3), against a default object size of
109
+ * `width` by `height` device pixels — a background's positioning area, a
110
+ * list marker's square. Its own size where it has one; the dimension it
111
+ * lacks from its ratio, or else from the default; and one with a ratio
112
+ * alone as large as fits in the default. An SVG may be any of these, and a
113
+ * raster image is the first. `scale` is device pixels per CSS pixel, which
114
+ * the image's own size is in.
115
+ */
116
+ export function concreteSize(size, width, height, scale) {
117
+ const { ratio } = size;
118
+ const w = size.width === null ? null : size.width * scale;
119
+ const h = size.height === null ? null : size.height * scale;
120
+ if (w !== null && h !== null)
121
+ return [w, h];
122
+ if (w !== null)
123
+ return [w, ratio > 0 ? w / ratio : height];
124
+ if (h !== null)
125
+ return [ratio > 0 ? h * ratio : width, h];
126
+ if (ratio > 0) {
127
+ return width / height > ratio
128
+ ? [height * ratio, height]
129
+ : [width, width / ratio];
130
+ }
131
+ return [width, height];
132
+ }
105
133
  function svgViewClass() {
106
134
  const ctor = ntk.SvgView;
107
135
  return typeof ctor === 'function' ? ctor : null;
108
136
  }
137
+ const LENT = new WeakMap();
138
+ /**
139
+ * The context a drawing is lent: the same context, keeping count of the
140
+ * saves the drawing makes on it, so that whatever it leaves saved can be
141
+ * restored after it, and making none of the restores it makes past them.
142
+ *
143
+ * `SvgView` saves the context as it starts to draw, and around an element
144
+ * with a `transform`, and restores in no `finally`: an element it throws
145
+ * on — a colour it cannot read, `fill="var(--c)"` — leaves those saves
146
+ * open. The restore after the drawing then took the last of them for its
147
+ * own save, and the clip to the drawing's box was left on the window's
148
+ * context: everything a later paint drew, in that frame and every frame
149
+ * after, was cut to a sixteen-pixel icon. nextjs.org's blog drew its
150
+ * heading, the icon beside it, and nothing more.
151
+ */
152
+ function lend(ctx) {
153
+ const known = LENT.get(ctx);
154
+ if (known)
155
+ return known;
156
+ const bound = new WeakMap();
157
+ const save = () => {
158
+ ctx.save();
159
+ lent.open += 1;
160
+ };
161
+ const restore = () => {
162
+ if (lent.open === 0)
163
+ return;
164
+ lent.open -= 1;
165
+ ctx.restore();
166
+ };
167
+ const lent = {
168
+ open: 0,
169
+ ctx: new Proxy(ctx, {
170
+ get(target, key) {
171
+ if (key === 'save')
172
+ return save;
173
+ if (key === 'restore')
174
+ return restore;
175
+ // on the context itself: an accessor, a method and a private field
176
+ // all see the context they belong to
177
+ const value = Reflect.get(target, key);
178
+ if (typeof value !== 'function')
179
+ return value;
180
+ let method = bound.get(value);
181
+ if (!method) {
182
+ method = value.bind(target);
183
+ bound.set(value, method);
184
+ }
185
+ return method;
186
+ },
187
+ set: (target, key, value) => Reflect.set(target, key, value),
188
+ }),
189
+ };
190
+ LENT.set(ctx, lent);
191
+ return lent;
192
+ }
109
193
  const ALIGN = /^x(Min|Mid|Max)Y(Min|Mid|Max)$/;
110
194
  const AT = { Min: 0, Mid: 0.5, Max: 1 };
111
195
  /**
@@ -117,12 +201,14 @@ export class SvgDrawing {
117
201
  intrinsics;
118
202
  _root;
119
203
  _view = null;
120
- /** What the view was last handed the tree for: the root's child count
121
- * and last child — a streamed document grows an inline drawing after its
122
- * first paint — and, for a drawing with percentages in it, the viewport
123
- * they were resolved against. */
204
+ /** What the view was last handed the tree for: the root's child count,
205
+ * and where the root and each element outside it a `<use>` drew ended —
206
+ * a streamed document grows an inline drawing after its first paint,
207
+ * and a sprite's symbol after the first icon drawn from it — and, for a
208
+ * drawing with percentages in it, the viewport they were resolved
209
+ * against. */
124
210
  _seen = -1;
125
- _seenLast = null;
211
+ _seenEnds = [];
126
212
  _seenViewport = '';
127
213
  /** The `fill` and `stroke` the view's tree was last given its root's,
128
214
  * and what the document's rules gave the elements in it. */
@@ -150,10 +236,14 @@ export class SvgDrawing {
150
236
  * drawn at, and what each gave it: by the viewport it was asked at where
151
237
  * a sheet reads the viewport, and under one key where none does. */
152
238
  _cascades = new Map();
153
- constructor(root, intrinsics, standalone = false) {
239
+ /** The element an SVG image's URL names by its fragment, its own
240
+ * `:target`: a sprite sheet shows the icon `image.svg#icon` names. */
241
+ _target;
242
+ constructor(root, intrinsics, standalone = false, target = null) {
154
243
  this._root = root;
155
244
  this.intrinsics = intrinsics;
156
245
  this._standalone = standalone;
246
+ this._target = target;
157
247
  }
158
248
  /**
159
249
  * Draw into a rectangle — the box's content box, in device pixels —
@@ -198,21 +288,45 @@ export class SvgDrawing {
198
288
  }
199
289
  const box = viewBoxOf(root);
200
290
  // the viewport in user units, which a percentage is of
291
+ let vw = w / scale;
292
+ let vh = h / scale;
293
+ // An image with no `viewBox` is laid out at its own size, along an
294
+ // axis it has one on, and stretched to the rectangle along it, as a
295
+ // raster image is: Blink gives it a `viewBox` of its own size that
296
+ // `preserveAspectRatio: none` fits. Drawn at its own size, an icon of
297
+ // `width="24" height="24"` shown 48 wide was a quarter of its box.
298
+ let kx = 1;
299
+ let ky = 1;
300
+ if (!box && this._standalone) {
301
+ const own = this.intrinsics;
302
+ if (own.width !== null && own.width > 0) {
303
+ kx = vw / own.width;
304
+ vw = own.width;
305
+ }
306
+ if (own.height !== null && own.height > 0) {
307
+ ky = vh / own.height;
308
+ vh = own.height;
309
+ }
310
+ }
201
311
  const paint = (fill === null ? '' : `fill:${fill};`) +
202
312
  (stroke === null ? '' : `stroke:${stroke};`);
203
313
  const view = box
204
314
  ? this._viewFor(box[2], box[3], paint, shapes)
205
- : this._viewFor(w / scale, h / scale, paint, shapes);
315
+ : this._viewFor(vw, vh, paint, shapes);
206
316
  if (!view)
207
317
  return;
208
318
  ctx.save();
319
+ // the view draws on the context lent, which counts its saves
320
+ const lent = lend(ctx);
321
+ const outer = lent.open;
322
+ lent.open = 0;
209
323
  try {
210
324
  ctx.beginPath();
211
325
  ctx.rect(x, y, w, h);
212
326
  ctx.clip();
213
327
  const opts = color ? { color } : undefined;
214
328
  if (!box) {
215
- view.draw(ctx, x, y, view.naturalWidth * scale, view.naturalHeight * scale, opts);
329
+ view.draw(lent.ctx, x, y, view.naturalWidth * scale * kx, view.naturalHeight * scale * ky, opts);
216
330
  }
217
331
  else {
218
332
  const fit = (svgAttr(root, 'preserveAspectRatio') ?? '')
@@ -221,7 +335,7 @@ export class SvgDrawing {
221
335
  if (fit[0] === 'defer')
222
336
  fit.shift();
223
337
  if (fit[0] === 'none') {
224
- view.draw(ctx, x, y, w, h, opts);
338
+ view.draw(lent.ctx, x, y, w, h, opts);
225
339
  }
226
340
  else {
227
341
  const align = ALIGN.exec(fit[0] ?? '') ?? ['xMidYMid', 'Mid', 'Mid'];
@@ -230,7 +344,7 @@ export class SvgDrawing {
230
344
  const s = fit[1] === 'slice' ? Math.max(sx, sy) : Math.min(sx, sy);
231
345
  const dw = box[2] * s;
232
346
  const dh = box[3] * s;
233
- view.draw(ctx, x + (w - dw) * AT[align[1]], y + (h - dh) * AT[align[2]], dw, dh, opts);
347
+ view.draw(lent.ctx, x + (w - dw) * AT[align[1]], y + (h - dh) * AT[align[2]], dw, dh, opts);
234
348
  }
235
349
  }
236
350
  }
@@ -240,6 +354,12 @@ export class SvgDrawing {
240
354
  this._failed = true;
241
355
  }
242
356
  finally {
357
+ // what the drawing saved and did not restore, before this one's own
358
+ // save — which the restore after it would otherwise have taken, and
359
+ // this one's clip been left on the window's context (`lend`)
360
+ for (; lent.open > 0; lent.open -= 1)
361
+ ctx.restore();
362
+ lent.open = outer;
243
363
  ctx.restore();
244
364
  }
245
365
  }
@@ -268,7 +388,7 @@ export class SvgDrawing {
268
388
  const made = `${scheme}|${scale}`;
269
389
  let entry = this._cascades.get(made);
270
390
  if (!entry) {
271
- const cascade = new Cascade(sheets, imageLook(scheme, scale), w, h, scale, null, null, null, null, this._root);
391
+ const cascade = new Cascade(sheets, imageLook(scheme, scale), w, h, scale, null, null, null, null, this._root, this._target);
272
392
  entry = { cascade, own: new Map() };
273
393
  this._cascades.set(made, entry);
274
394
  }
@@ -313,11 +433,13 @@ export class SvgDrawing {
313
433
  paint = '', shapes = null) {
314
434
  const root = this._root;
315
435
  const count = root.children.length;
316
- const last = root.lastChild;
317
- // more of the document has arrived since a `<use>` found nothing
318
- const arrived = this._missingAt !== undefined &&
319
- lastNode(documentOf(root)) !== this._missingAt;
320
- const grown = count !== this._seen || last !== this._seenLast || arrived;
436
+ // more of the document has arrived since a `<use>` found nothing, or
437
+ // more of what the tree was read from: the drawing, a group in it, or
438
+ // a symbol outside it that a chunk ended halfway through
439
+ const arrived = (this._missingAt !== undefined &&
440
+ lastNode(documentOf(root)) !== this._missingAt) ||
441
+ this._seenEnds.some(moved);
442
+ const grown = count !== this._seen || arrived;
321
443
  if (grown || this._percent === null) {
322
444
  this._percent = hasPercent(root);
323
445
  this._uses = needsExpanding(root);
@@ -338,15 +460,20 @@ export class SvgDrawing {
338
460
  }
339
461
  try {
340
462
  const view = this._view ?? new View(null);
341
- const missing = { any: false };
463
+ const reached = { missing: false, outside: new Set() };
342
464
  const tree = sized || shapes || root.name.includes(':')
343
- ? copyTree(root, sized ? [width, height] : null, missing, shapes)
465
+ ? copyTree(root, sized ? [width, height] : null, reached, shapes)
344
466
  : root;
345
467
  view.setSvgDom(paint ? withPaint(tree, paint) : tree);
346
- this._missingAt = missing.any ? lastNode(documentOf(root)) : undefined;
468
+ this._missingAt = reached.missing
469
+ ? lastNode(documentOf(root))
470
+ : undefined;
471
+ const ends = [endOf(root)];
472
+ for (const el of reached.outside)
473
+ ends.push(endOf(el));
347
474
  this._view = view;
348
475
  this._seen = count;
349
- this._seenLast = last;
476
+ this._seenEnds = ends;
350
477
  this._seenViewport = viewport;
351
478
  this._seenPaint = painted;
352
479
  return view;
@@ -504,12 +631,6 @@ function hasPercent(root) {
504
631
  };
505
632
  return walk(root);
506
633
  }
507
- /** The element a `<use>` refers to by a fragment, its id: one in another
508
- * document is none here, where nothing is fetched for a drawing. */
509
- function useTarget(el) {
510
- const href = el.attribs.href ?? el.attribs['xlink:href'] ?? '';
511
- return href.length > 1 && href.startsWith('#') ? href.slice(1) : null;
512
- }
513
634
  /** The elements under a root by their ids, the first of each. */
514
635
  function idsUnder(root) {
515
636
  const ids = new Map();
@@ -530,10 +651,10 @@ function idsUnder(root) {
530
651
  /**
531
652
  * Whether a `<use>` under the root is one `SvgView` cannot draw as it
532
653
  * stands: it looks a reference up among the root's own elements, and draws
533
- * a `<symbol>` as its children where they are. So one to an element
534
- * outside the root — an icon sprite's, a hidden `<svg>` of symbols at the
535
- * top of the page — and one to a symbol with a `viewBox` of its own are
536
- * drawn from a copy (`copyTree`).
654
+ * a `<symbol>` as its children where they are, with the `<use>`'s paint.
655
+ * So one to an element outside the root — an icon sprite's, a hidden
656
+ * `<svg>` of symbols at the top of the page — and one to a symbol with a
657
+ * `viewBox` or a paint of its own are drawn from a copy (`copyTree`).
537
658
  */
538
659
  function needsExpanding(root) {
539
660
  let ids = null;
@@ -543,13 +664,14 @@ function needsExpanding(root) {
543
664
  continue;
544
665
  const tag = child;
545
666
  if (localName(tag.name) === 'use') {
546
- const id = useTarget(tag);
667
+ const id = useHref(tag);
547
668
  if (id !== null) {
548
669
  ids ??= idsUnder(root);
549
670
  const target = ids.get(id);
550
671
  if (!target)
551
672
  return true;
552
- if (localName(target.name) === 'symbol' && viewBoxOf(target)) {
673
+ if (localName(target.name) === 'symbol' &&
674
+ (viewBoxOf(target) || symbolOwn(target))) {
553
675
  return true;
554
676
  }
555
677
  }
@@ -575,6 +697,17 @@ function lastNode(top) {
575
697
  last = last.lastChild;
576
698
  return last;
577
699
  }
700
+ function endOf(top) {
701
+ const last = lastNode(top);
702
+ return { top, last, text: last?.type === 'text' ? last.data.length : -1 };
703
+ }
704
+ /** Whether more of a part has arrived since its `End` was taken. */
705
+ function moved(end) {
706
+ const last = lastNode(end.top);
707
+ if (last !== end.last)
708
+ return true;
709
+ return last?.type === 'text' && last.data.length !== end.text;
710
+ }
578
711
  /** The first element of a document with an id, in its order. */
579
712
  function elementById(top, id) {
580
713
  const stack = [...top.children].reverse();
@@ -630,6 +763,43 @@ const USE_PLACEMENT = new Set([
630
763
  'width',
631
764
  'height',
632
765
  ]);
766
+ /** A `<symbol>`'s attributes that paint what is in it: its presentation
767
+ * attributes and its `style` (SVG 2, 6.3). Not where it goes, the viewport
768
+ * it makes or a name, which a symbol's copy is no group to have. */
769
+ const SYMBOL_PAINTS = new Set([
770
+ 'style',
771
+ 'fill',
772
+ 'fill-opacity',
773
+ 'fill-rule',
774
+ 'stroke',
775
+ 'stroke-width',
776
+ 'stroke-linecap',
777
+ 'stroke-linejoin',
778
+ 'stroke-miterlimit',
779
+ 'stroke-dasharray',
780
+ 'stroke-dashoffset',
781
+ 'stroke-opacity',
782
+ 'color',
783
+ 'opacity',
784
+ 'visibility',
785
+ 'display',
786
+ 'font-family',
787
+ 'font-size',
788
+ 'font-style',
789
+ 'font-weight',
790
+ 'text-anchor',
791
+ ]);
792
+ /** A `<symbol>`'s own attributes that paint what is in it, or null where
793
+ * it has none: Chrome draws a sprite's `<symbol fill="…">` in that fill,
794
+ * where `SvgView` draws a symbol's children alone. */
795
+ function symbolOwn(symbol) {
796
+ let kept = null;
797
+ for (const name in symbol.attribs) {
798
+ if (SYMBOL_PAINTS.has(name))
799
+ (kept ??= {})[name] = symbol.attribs[name];
800
+ }
801
+ return kept;
802
+ }
633
803
  /**
634
804
  * The tree `SvgView` reads, where the document's own will not do: with
635
805
  * local names — `SvgView` knows `rect`, not `svg:rect`, and a prefix is
@@ -639,8 +809,9 @@ const USE_PLACEMENT = new Set([
639
809
  * group of what it refers to (SVG 2, 5.5): the element, from wherever in
640
810
  * the document it is, or a symbol's children, its `viewBox` fitted to the
641
811
  * viewport the `<use>` gives it — its `width` and `height`, and all of the
642
- * drawing's where it has none. `missing.any` is set where one refers to an
643
- * element the document does not have.
812
+ * drawing's where it has none. `reached` is told where one refers to an
813
+ * element the document does not have, and of each element outside the root
814
+ * that one was drawn from.
644
815
  *
645
816
  * And with what the document's rules give each element (`shapes`), where
646
817
  * `SvgView` reads an element's own: at the end of its `style`, over its
@@ -648,9 +819,12 @@ const USE_PLACEMENT = new Set([
648
819
  * cascade that made them has weighed already. An element a rule gives
649
820
  * `display: none` is left out, with what is in it, and a shape one hides
650
821
  * — `visibility` is inherited, and a shape in a hidden group may be shown
651
- * again — is left out alone.
822
+ * again — is left out alone. What a `<use>` draws has what the rules give
823
+ * its copy (`ShapeStyles.used`), which may not be what they give the
824
+ * element where it stands, so where they give anything, every `<use>` is
825
+ * drawn from a copy here.
652
826
  */
653
- function copyTree(root, viewport, missing, shapes = null) {
827
+ function copyTree(root, viewport, reached, shapes = null) {
654
828
  const colon = root.name.indexOf(':');
655
829
  const prefix = colon < 0 ? null : `${root.name.slice(0, colon)}:`;
656
830
  const strip = (name) => prefix && name.startsWith(prefix) ? name.slice(prefix.length) : name;
@@ -658,29 +832,36 @@ function copyTree(root, viewport, missing, shapes = null) {
658
832
  ? Math.sqrt((viewport[0] ** 2 + viewport[1] ** 2) / 2)
659
833
  : 0;
660
834
  let ids = null;
661
- const expand = (use, resolve, depth, hidden) => {
662
- const id = useTarget(use);
835
+ const expand = (use, resolve, depth, hidden,
836
+ /** What the rules give the `<use>`. */
837
+ own) => {
838
+ const id = useHref(use);
663
839
  if (id === null || depth >= USE_DEPTH)
664
840
  return null;
665
841
  ids ??= idsUnder(root);
666
842
  const inside = ids.get(id);
667
843
  const target = inside ?? elementById(documentOf(root), id);
668
844
  if (!target) {
669
- if (missing)
670
- missing.any = true;
845
+ if (reached)
846
+ reached.missing = true;
671
847
  return null;
672
848
  }
849
+ if (!inside)
850
+ reached?.outside.add(target);
673
851
  const symbol = localName(target.name) === 'symbol';
674
852
  const box = symbol ? viewBoxOf(target) : null;
675
- // one of the root's own with no viewport to fit: `SvgView` draws it
676
- if (inside && !box)
853
+ const kept = symbol ? symbolOwn(target) : null;
854
+ // one of the root's own that `SvgView` draws as it is: with no
855
+ // viewport to fit, no paint of a symbol's own, and no rule's styles
856
+ if (inside && !box && !kept && !shapes)
677
857
  return null;
858
+ // what the rules give the copy, element by element
859
+ const styles = shapes?.used?.get(use) ?? null;
678
860
  const attribs = {};
679
861
  for (const name in use.attribs) {
680
862
  if (!USE_PLACEMENT.has(name))
681
863
  attribs[name] = use.attribs[name];
682
864
  }
683
- const own = shapes?.of.get(use);
684
865
  if (own)
685
866
  restyle(attribs, own);
686
867
  const x = parseFloat(use.attribs.x ?? '') || 0;
@@ -703,29 +884,47 @@ function copyTree(root, viewport, missing, shapes = null) {
703
884
  // a length in a symbol with a `viewBox` is of that, and left as it is
704
885
  const within = resolve && !box;
705
886
  const children = [];
706
- if (symbol)
707
- copyInto(children, target, within, depth + 1, hidden);
708
- else if (shown(target, hidden)) {
709
- children.push(copy(target, within, depth + 1, hidden));
887
+ if (symbol) {
888
+ // The symbol is the top of the copy, and paints what is in it: its
889
+ // attributes, and what the rules give it there. `SvgView` draws a
890
+ // symbol's children alone, so they go in a group that is it. Where
891
+ // it stands a symbol is drawn by nobody, so `display: none` takes
892
+ // nothing from it there (`UNDRAWN`); at the top of a copy it takes
893
+ // the copy, as Chrome has it.
894
+ const mine = styles?.get(target);
895
+ const inner = hiddenIn(mine, hidden);
896
+ if (mine?.display !== 'none') {
897
+ copyInto(children, target, within, depth + 1, inner, styles);
898
+ }
899
+ if (kept || mine) {
900
+ const paints = { ...kept };
901
+ if (mine)
902
+ restyle(paints, mine);
903
+ const group = new Element('g', paints, children.splice(0));
904
+ children.push(group);
905
+ }
906
+ }
907
+ else if (shown(target, hidden, styles)) {
908
+ children.push(copy(target, within, depth + 1, hidden, styles));
710
909
  }
711
910
  return new Element('g', attribs, children);
712
911
  };
713
912
  /** Whether an element is drawn at all, in a group that is hidden or
714
913
  * not: one no rule gives `display: none`, and no shape a rule hides. */
715
- const shown = (el, hidden) => {
716
- if (!shapes)
914
+ const shown = (el, hidden, styles) => {
915
+ if (!styles)
717
916
  return true;
718
- const own = shapes.of.get(el);
917
+ const own = styles.get(el);
719
918
  const name = localName(el.name);
720
919
  if (own?.display === 'none' && !UNDRAWN.has(name))
721
920
  return false;
722
921
  return !(hiddenIn(own, hidden) && HIDEABLE.has(name));
723
922
  };
724
- const copyInto = (children, el, resolve, depth, hidden) => {
923
+ const copyInto = (children, el, resolve, depth, hidden, styles) => {
725
924
  for (const child of el.children) {
726
925
  if (child.type === 'tag') {
727
- if (shown(child, hidden)) {
728
- children.push(copy(child, resolve, depth, hidden));
926
+ if (shown(child, hidden, styles)) {
927
+ children.push(copy(child, resolve, depth, hidden, styles));
729
928
  }
730
929
  }
731
930
  else if (child.type === 'text')
@@ -734,11 +933,14 @@ function copyTree(root, viewport, missing, shapes = null) {
734
933
  };
735
934
  const copy = (el, resolve, depth = 0,
736
935
  /** Whether the element inherits a `visibility` of `hidden`. */
737
- hidden = false) => {
738
- const own = shapes?.of.get(el);
936
+ hidden = false,
937
+ /** What the rules give the elements of the tree it is in: the
938
+ * drawing's, or a `<use>`'s copy's. */
939
+ styles = shapes?.of ?? null) => {
940
+ const own = styles?.get(el);
739
941
  hidden = hiddenIn(own, hidden);
740
942
  if (localName(el.name) === 'use') {
741
- const group = expand(el, resolve, depth, hidden);
943
+ const group = expand(el, resolve, depth, hidden, own);
742
944
  if (group)
743
945
  return group;
744
946
  }
@@ -760,7 +962,7 @@ function copyTree(root, viewport, missing, shapes = null) {
760
962
  }
761
963
  }
762
964
  const children = [];
763
- copyInto(children, el, here, depth, hidden);
965
+ copyInto(children, el, here, depth, hidden, styles);
764
966
  return new Element(strip(el.name), attribs, children);
765
967
  };
766
968
  return copy(root, true);
@@ -839,7 +1041,7 @@ function restyle(attribs, own) {
839
1041
  * which is how a PNG or a JPEG goes on to the image decoder. Nothing names
840
1042
  * the type, so it is sniffed: markup, with an `<svg>` root.
841
1043
  */
842
- export function svgFromBytes(bytes) {
1044
+ export function svgFromBytes(bytes, fragment = '') {
843
1045
  let i = bytes[0] === 0xef && bytes[1] === 0xbb && bytes[2] === 0xbf ? 3 : 0;
844
1046
  while (bytes[i] === 0x20 ||
845
1047
  bytes[i] === 0x09 ||
@@ -855,7 +1057,10 @@ export function svgFromBytes(bytes) {
855
1057
  try {
856
1058
  const doc = parseDocument(text, { xmlMode: true });
857
1059
  const root = findRoot(doc.children);
858
- return root ? new SvgDrawing(root, svgIntrinsics(root), true) : null;
1060
+ if (!root)
1061
+ return null;
1062
+ const target = fragment ? elementById(doc, fragment) : null;
1063
+ return new SvgDrawing(root, svgIntrinsics(root), true, target);
859
1064
  }
860
1065
  catch {
861
1066
  return null;