domotion-svg 0.7.0 → 0.8.0

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.
@@ -159,8 +159,19 @@ export const createPseudoContentHandler = ({ vp, normColor, measureFontMetrics,
159
159
  probe.style.marginRight = pcs.marginRight;
160
160
  probe.style.marginBottom = pcs.marginBottom;
161
161
  probe.style.marginLeft = pcs.marginLeft;
162
- probe.style.transform = pcs.transform && pcs.transform !== 'none' ? pcs.transform : '';
163
- probe.style.transformOrigin = pcs.transformOrigin || '';
162
+ // DM-928: deliberately DO NOT apply the pseudo's `transform` to the
163
+ // probe. CSS transforms only affect paint, not layout; the probe's
164
+ // `getBoundingClientRect()` returns the AXIS-ALIGNED bounding box of
165
+ // whatever box it currently paints. With a 45° rotation, that AABB is
166
+ // ~√2 × larger than the actual border-box and its top-left sits
167
+ // ~(diagonal-extra)/2 to the upper-left of the true border-box origin
168
+ // — re-rotating that AABB later via the `<g transform>` wrapper
169
+ // places the painted strokes at the wrong position (pricing-table
170
+ // checkmarks drift ~4 px left / 1 px up). Strip the transform here
171
+ // and let the unrotated probe report the actual border-box rect; the
172
+ // transform is re-applied at render time inside `flushPbTransformWrap`.
173
+ probe.style.transform = '';
174
+ probe.style.transformOrigin = '';
164
175
  // Pseudo lives logically inside the host; an absolute child of the host
165
176
  // inherits the same containing-block lookup.
166
177
  if (pseudo === '::before')
@@ -414,39 +414,70 @@ export const createTextSegmentsHandler = ({ vp, measureFontMetrics, needsRaster
414
414
  let rasterLeft = cRec.left - vp.x;
415
415
  let rasterWidth = cRec.right - cRec.left;
416
416
  if (isFirstLetter) {
417
- const ilRaw = flStyle.initialLetter || flStyle.webkitInitialLetter || '';
418
- const ilN = parseFloat(ilRaw);
419
- const parentLineHeight = parseFloat(cs.lineHeight);
420
- if (Number.isFinite(ilN) && ilN > 1 && Number.isFinite(parentLineHeight) && parentLineHeight > 0) {
421
- const expectedHeight = ilN * parentLineHeight;
422
- if (expectedHeight > rasterHeight) {
423
- // Extend downward only — Chrome aligns the cap-top at the
424
- // first line's cap-height position; extra space the
425
- // expansion captures past the actual ink is empty (will
426
- // raster as transparent, with `omitBackground: true`) and
427
- // doesn't paint anything visible.
428
- rasterHeight = expectedHeight;
429
- }
430
- }
431
- // DM-931: decorative ::first-letter drop caps frequently
432
- // declare PADDING + BACKGROUND-IMAGE on the pseudo (e.g.
433
- // `linear-gradient` behind the cap). The base char rect
434
- // measures the GLYPH only — the padding-extended background
435
- // box extends beyond on every side. Without expansion the
436
- // rasterized PNG captures just the glyph and the gradient
437
- // box renders truncated. Expand by the ::first-letter
438
- // padding (top / right / bottom / left). Same omitBackground
439
- // semantics: extra captured area outside the painted box is
440
- // transparent and inert.
417
+ const flFloat = flStyle.float || flStyle.cssFloat || '';
441
418
  const padT = parseFloat(flStyle.paddingTop) || 0;
442
419
  const padR = parseFloat(flStyle.paddingRight) || 0;
443
420
  const padB = parseFloat(flStyle.paddingBottom) || 0;
444
421
  const padL = parseFloat(flStyle.paddingLeft) || 0;
445
- if (padT > 0 || padR > 0 || padB > 0 || padL > 0) {
446
- rasterTop -= padT;
447
- rasterLeft -= padL;
448
- rasterWidth += padL + padR;
449
- rasterHeight += padT + padB;
422
+ if (flFloat === 'left' || flFloat === 'right') {
423
+ // DM-931: floated ::first-letter (drop cap) is positioned
424
+ // relative to the PARAGRAPH's content area, not the first
425
+ // character's line-box position. `Range.getBoundingClientRect`
426
+ // on the first character returns the GLYPH bounds at its
427
+ // line-1 position — which doesn't match where Chrome paints
428
+ // the float when `initial-letter` is set (the cap-top aligns
429
+ // to line-1's cap-top, shifting the painted box DOWN from
430
+ // the Range top by roughly the cap-height-vs-ascender delta).
431
+ // Compute the raster rect from the pseudo's computed
432
+ // padding-box (width/height + padding) + the paragraph's
433
+ // border-box origin + paragraph padding/border + pseudo
434
+ // margins.
435
+ const pBox = el.getBoundingClientRect();
436
+ const pPadT = parseFloat(cs.paddingTop) || 0;
437
+ const pPadL = parseFloat(cs.paddingLeft) || 0;
438
+ const pPadR = parseFloat(cs.paddingRight) || 0;
439
+ const pBorT = parseFloat(cs.borderTopWidth) || 0;
440
+ const pBorL = parseFloat(cs.borderLeftWidth) || 0;
441
+ const pBorR = parseFloat(cs.borderRightWidth) || 0;
442
+ const flMarT = parseFloat(flStyle.marginTop) || 0;
443
+ const flMarL = parseFloat(flStyle.marginLeft) || 0;
444
+ const flMarR = parseFloat(flStyle.marginRight) || 0;
445
+ const w = parseFloat(flStyle.width);
446
+ const h = parseFloat(flStyle.height);
447
+ const padW = (Number.isFinite(w) && w > 0 ? w : rasterWidth) + padL + padR;
448
+ const padH = (Number.isFinite(h) && h > 0 ? h : rasterHeight) + padT + padB;
449
+ rasterTop = pBox.y + pBorT + pPadT + flMarT - vp.y;
450
+ if (flFloat === 'left') {
451
+ rasterLeft = pBox.x + pBorL + pPadL + flMarL - vp.x;
452
+ }
453
+ else {
454
+ rasterLeft = pBox.x + pBox.width - pBorR - pPadR - flMarR - padW - vp.x;
455
+ }
456
+ rasterWidth = padW;
457
+ rasterHeight = padH;
458
+ }
459
+ else {
460
+ // Non-floated ::first-letter (raised cap via font-size only,
461
+ // or `display: inline`). The Range rect tracks the painted
462
+ // glyph correctly; just expand the rect by the pseudo's
463
+ // padding so a `background-color` / gradient behind the
464
+ // glyph isn't truncated. Apply the older `initial-letter`
465
+ // height fallback for safety against under-tall Range
466
+ // measurements on float-less drop caps.
467
+ const ilRaw = flStyle.initialLetter || flStyle.webkitInitialLetter || '';
468
+ const ilN = parseFloat(ilRaw);
469
+ const parentLineHeight = parseFloat(cs.lineHeight);
470
+ if (Number.isFinite(ilN) && ilN > 1 && Number.isFinite(parentLineHeight) && parentLineHeight > 0) {
471
+ const expectedHeight = ilN * parentLineHeight;
472
+ if (expectedHeight > rasterHeight)
473
+ rasterHeight = expectedHeight;
474
+ }
475
+ if (padT > 0 || padR > 0 || padB > 0 || padL > 0) {
476
+ rasterTop -= padT;
477
+ rasterLeft -= padL;
478
+ rasterWidth += padL + padR;
479
+ rasterHeight += padT + padB;
480
+ }
450
481
  }
451
482
  }
452
483
  rasterGlyphs.push({