pptx-angular-viewer 3.14.0 → 3.15.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.
@@ -7793,6 +7793,33 @@ function glyphEnvelopeMatrix(x0, x1, edge0, edge1, nomTop, nomBottom) {
7793
7793
  * file, e.g. a signature font on the reader's OS with no webfont match),
7794
7794
  * `text-warp-envelope-layout.ts` falls back to the existing per-glyph affine
7795
7795
  * / piecewise-affine-slice transform, unchanged.
7796
+ *
7797
+ * COM-verified 2026-09-11 (an 8-shape Arimo Bold fixture, `textCanUp`/
7798
+ * `textCanDown`/`textInflate`/`textDeflate` at default and extreme `adj`): an
7799
+ * outline-vs-PowerPoint ink-scan comparison found a large interior-column
7800
+ * mismatch (~30-40% of box height, max 58-80%) that traced NOT to this
7801
+ * module's point-mapping (verified correct: the affine fallback, driven by
7802
+ * the identical inputs, showed the same error to within measurement noise),
7803
+ * but to `text-warp-envelope-layout.ts`'s `nomTop`/`nomBottom` - the
7804
+ * "undeformed" reference band both this module and the affine path map a
7805
+ * glyph's points FROM - being a fixed fraction of box height regardless of
7806
+ * the actual text's real (font-metric) size. See
7807
+ * `measureLineAscent`'s doc comment there for the fix and the re-measured
7808
+ * numbers. A separate, larger, NOT-yet-fixed gap the same investigation
7809
+ * found: real PowerPoint spaces envelope-warped glyphs to fill the box's own
7810
+ * width edge-to-edge (`textCanUp`/`textCanDown` additionally non-uniformly,
7811
+ * cylinder-projection-like) rather than centring the text at its natural
7812
+ * advance width the way `measureGlyphAdvances`/`startX` do today - out of
7813
+ * scope for that fix, left as an open, separately-scoped issue.
7814
+ *
7815
+ * Open question, not root-caused: the same investigation's COM fixture had
7816
+ * to be rendered from a deck that embeds no font at all (`warp-outline-
7817
+ * noembed-clean.pptx`, Arimo installed as a Windows user font instead) -
7818
+ * PowerPoint refused to open an earlier variant of the SAME fixture that
7819
+ * embedded Arimo Bold as a `ppt/fonts/{guid}.fntdata` part (obfuscated per
7820
+ * ECMA-376 14.2.1, wired via `p:embeddedFontLst`/`embedTrueTypeFonts="1"`)
7821
+ * with error `0x808D1001`. Left as an open note for whoever next touches
7822
+ * embedded-font packaging or generates a COM fixture that needs one.
7796
7823
  */
7797
7824
  /**
7798
7825
  * Map `y` (a point on the glyph's nominal, undeformed `[nomTop, nomBottom]`
@@ -8057,6 +8084,65 @@ function measureGlyphAdvances(text, font) {
8057
8084
  }
8058
8085
  return advances;
8059
8086
  }
8087
+ /**
8088
+ * The real (ink-measured) ascent of `segments`' text at their own font
8089
+ * sizes, as the tallest `actualBoundingBoxAscent` across every segment on
8090
+ * the line (not a per-character average - one tall glyph anywhere on the
8091
+ * line sets the reference the whole line warps against, matching how a
8092
+ * single baseline/cap-height pair governs a real text run).
8093
+ *
8094
+ * `buildGlyphEnvelope` used to map every glyph's nominal band from a FIXED
8095
+ * `NOMINAL_ENVELOPE_BAND` fraction of the box height (0.15..0.85), assuming
8096
+ * a glyph's own cap height fills that whole span. COM-measured (2026-09-11,
8097
+ * `text-warp-glyph-outline.ts`'s doc comment): for an 8-shape WordArt
8098
+ * fixture (Arimo Bold 44pt captions in 100pt-tall boxes, the `textCanUp` /
8099
+ * `textCanDown` / `textInflate` / `textDeflate` presets at both default and
8100
+ * extreme `adj`), real cap height reaches only about `t = 0.57` of that
8101
+ * nominal span, not `t = 0`, so every glyph's mapped top undershot the
8102
+ * curve's own top edge by the same amount - an outline-vs-COM interior-
8103
+ * column ink-scan comparison measured ~30-40% of box height mean error (max
8104
+ * 58-80%) on BOTH the outline path and the affine fallback alike (both use
8105
+ * this same nominal band, so both shared the bug identically: the residual
8106
+ * lived here, not in the outline point-mapping math). Anchoring `nomTop` to
8107
+ * the line's REAL measured ascent instead - clamped to never exceed the
8108
+ * historical fixed band, so a line whose font genuinely fills (or exceeds)
8109
+ * the nominal span keeps the old, already-validated behaviour unchanged -
8110
+ * dropped the `textInflate`/`textDeflate` interior mean error to ~2.6-2.9%
8111
+ * (max ~9-10%), in the range `text-warp-glyph-slicing.ts`'s doc comment
8112
+ * already documents as the residual once this band mismatch is not also
8113
+ * present. The `textCanUp`/`textCanDown` cases still show an elevated
8114
+ * residual (their interior mean measured ~6-20% even after this fix) that
8115
+ * further investigation traced to a SEPARATE, larger issue: real PowerPoint
8116
+ * spaces envelope-warped glyphs to fill the box's own width edge-to-edge
8117
+ * (measured ink spanning ~99.9% of box width) rather than centering the
8118
+ * text at its natural (unstretched) advance width the way `startX`/
8119
+ * `measureGlyphAdvances` do today, with `textCanUp`/`textCanDown` additionally
8120
+ * showing non-uniform (cylinder-projection-like) horizontal spacing this fix
8121
+ * does not address - both are horizontal-layout gaps, out of scope for this
8122
+ * (purely vertical) band fix and left as an open, separately-scoped issue.
8123
+ *
8124
+ * Returns `undefined` with no DOM (SSR, or a test environment without a 2D
8125
+ * canvas context), so a caller falls back to the previous fixed-fraction
8126
+ * band unchanged, exactly like {@link measureGlyphAdvances}'s own fallback.
8127
+ */
8128
+ function measureLineAscent(segments) {
8129
+ const ctx = getMeasureCtx$1();
8130
+ if (!ctx) {
8131
+ return undefined;
8132
+ }
8133
+ let maxAscent = 0;
8134
+ for (const segment of segments) {
8135
+ if (!segment.text) {
8136
+ continue;
8137
+ }
8138
+ ctx.font = toCanvasFont$1(segment.font);
8139
+ const ascent = ctx.measureText(segment.text).actualBoundingBoxAscent;
8140
+ if (Number.isFinite(ascent) && ascent > maxAscent) {
8141
+ maxAscent = ascent;
8142
+ }
8143
+ }
8144
+ return maxAscent > 0 ? maxAscent : undefined;
8145
+ }
8060
8146
  function startX(align, width, lineWidth) {
8061
8147
  if (align === 'right') {
8062
8148
  return width - lineWidth;
@@ -8094,7 +8180,13 @@ function buildGlyphEnvelope(preset, segments, width, height, align, adj, adj2, l
8094
8180
  const safeLineIndex = Math.min(Math.max(0, Math.floor(lineIndex)), safeLineCount - 1);
8095
8181
  const perSegmentAdvances = segments.map((seg) => measureGlyphAdvances(seg.text, seg.font));
8096
8182
  const lineWidth = perSegmentAdvances.reduce((sum, advances) => sum + advances.reduce((s, w) => s + w, 0), 0);
8097
- const { top: nomTop, bottom: nomBottom } = sliceBand(height * NOMINAL_ENVELOPE_BAND.top, height * NOMINAL_ENVELOPE_BAND.bottom, safeLineIndex, safeLineCount);
8183
+ const { top: fixedBandTop, bottom: nomBottom } = sliceBand(height * NOMINAL_ENVELOPE_BAND.top, height * NOMINAL_ENVELOPE_BAND.bottom, safeLineIndex, safeLineCount);
8184
+ // Prefer the line's own real ink ascent over the fixed-fraction band (see
8185
+ // `measureLineAscent`'s doc comment for why): never LOWER than the fixed
8186
+ // band's top, so a line whose font already fills (or exceeds) the nominal
8187
+ // span keeps today's behaviour unchanged.
8188
+ const realAscent = measureLineAscent(segments);
8189
+ const nomTop = realAscent !== undefined ? Math.max(fixedBandTop, nomBottom - realAscent) : fixedBandTop;
8098
8190
  const placements = [];
8099
8191
  let x = startX(align, width, lineWidth);
8100
8192
  segments.forEach((segment, segIdx) => {
@@ -38133,6 +38225,147 @@ function isBevelProfileInverted(bevelType) {
38133
38225
  return bevelType === 'softRound';
38134
38226
  }
38135
38227
 
38228
+ /**
38229
+ * `a:bevelT/@prst` profile -> SVG height-map shape.
38230
+ *
38231
+ * Split out of `visual-3d-bevel-lighting-tables.ts` to keep both files under
38232
+ * the repo's ~300 LOC guideline; see that module's doc comment for the other
38233
+ * two axes (light rig elevation, material response) and the highlight
38234
+ * DIRECTION, which is resolved separately in `visual-3d-bevel-light.ts`.
38235
+ *
38236
+ * @module render/visual-3d-bevel-lighting-profile
38237
+ */
38238
+ /**
38239
+ * `a:bevelT/@prst` (and `bevelB`, which shares the same profile vocabulary)
38240
+ * -> height-map shape. ECMA-376 20.1.10.9 describes each profile's silhouette
38241
+ * (a "circular", "flat sloped", "crossed", "art-deco stepped" etc.
38242
+ * cross-section); these factors were ORIGINALLY grouped into 3
38243
+ * physically-motivated buckets by that description (curved / faceted /
38244
+ * steep-narrow) rather than 12 independent hand-tuned entries, reasoned from
38245
+ * ECMA-376 alone.
38246
+ *
38247
+ * COM-MEASURED 2026-09 (real PowerPoint `Slide.Export`, mid-grey `matte`
38248
+ * square, `threePt` rig / `dir="t"`, `orthographicFront` camera, both a 6pt
38249
+ * and a 24pt `a:bevelT`, a line of 40 brightness samples from the top edge
38250
+ * inward for all 12 profiles): fitting these SAME 3 factors (grid search,
38251
+ * minimum RMSE against the measured curve, both depths jointly) against real
38252
+ * cross-section data overturned the bucket story for 3 profiles.
38253
+ * `circle`/`convex`/`softRound`/`divot` (curved) and `angle`/`cross`/
38254
+ * `coolSlant`/`riblet`/`artDeco` (faceted) fit closely (RMSE 1-7 brightness
38255
+ * units) with factors in the same rough range as the original reasoning, so
38256
+ * their bucket membership held up. `relaxedInset`, `slope` and `hardEdge`
38257
+ * did NOT: all three measured a genuine BRIGHT-BUMP-THEN-DARK-TROUGH double
38258
+ * transition partway through the ramp (e.g. `hardEdge` at 24pt: baseline 133
38259
+ * -> peaks ~139 -> drops to 67 -> recovers), which this filter's single
38260
+ * monotonic blur(+erode) height map (one bell-shaped slope lobe) cannot
38261
+ * reproduce - the fit pushes `surfaceScaleFactor` to the largest tested
38262
+ * value trying to reach the trough depth, landing the LARGEST relief factor
38263
+ * of any profile, the opposite of the pre-2026-09 "slope/hardEdge are
38264
+ * low-relief" assumption (`slope`/`hardEdge` were previously reasoned as
38265
+ * "steep/narrow" with REDUCED relief; `relaxedInset` was previously grouped
38266
+ * as "curved" with full relief and no erode at all). Their factors below are
38267
+ * therefore the closest achievable fit within this 3-parameter chain, not a
38268
+ * claim of a clean match (RMSE 12-19, versus 1-7 for the other 9); a proper
38269
+ * fix needs a genuinely non-monotonic (two-lobe) height-map primitive chain,
38270
+ * out of scope for this pass - see `docs/guide/limitations.md`. The
38271
+ * direction-independence these three still show (`measuredUniform`) is
38272
+ * unaffected: it is a separate, already-COM-confirmed finding (see
38273
+ * `visual-3d-bevel-light.ts`'s module doc comment) about which CARDINAL EDGE
38274
+ * lights up, not about the cross-section ramp shape this campaign measures.
38275
+ * Scripts (scratch, not committed, same convention as `com-acceptance.mjs`):
38276
+ * `scripts/make-bevel-profile-fixture.mjs` (fixture, all 12
38277
+ * profiles x 2 depths), `scripts/measure-bevel-profile-com.ps1`
38278
+ * (COM export + 40-point sampler), `scripts/fit-bevel-profile-com.mjs`
38279
+ * (grid-search fit; a closed-form Gaussian-CDF reimplementation of the
38280
+ * primitive chain, not a headless-browser render - Playwright's Chromium
38281
+ * launch hangs indefinitely via a plain script in this environment, though
38282
+ * `bunx playwright test` itself works fine, used to independently verify the
38283
+ * metal/circle routing conclusion in `visual-3d-bevel-lighting-routing.ts`).
38284
+ * The raw 10-point-per-profile table (24pt depth) is pinned in
38285
+ * `visual-3d-bevel-lighting-tables.test.ts`; the full 40-point x 2-depth
38286
+ * table is in the task report.
38287
+ */
38288
+ const BEVEL_PROFILE_HEIGHT_MAP = {
38289
+ circle: { blurFactor: 0.35, surfaceScaleFactor: 0.65, measuredUniform: false },
38290
+ convex: {
38291
+ blurFactor: 0.18,
38292
+ morphologyFactor: 0.4,
38293
+ surfaceScaleFactor: 0.2,
38294
+ measuredUniform: false,
38295
+ },
38296
+ softRound: {
38297
+ blurFactor: 0.25,
38298
+ morphologyFactor: 0.4,
38299
+ surfaceScaleFactor: 0.65,
38300
+ measuredUniform: false,
38301
+ },
38302
+ relaxedInset: {
38303
+ blurFactor: 0.35,
38304
+ morphologyFactor: 0.5,
38305
+ surfaceScaleFactor: 1.5,
38306
+ measuredUniform: false,
38307
+ },
38308
+ divot: { blurFactor: 0.18, surfaceScaleFactor: 0.5, measuredUniform: false },
38309
+ angle: {
38310
+ blurFactor: 0.35,
38311
+ morphologyFactor: 0.4,
38312
+ surfaceScaleFactor: 0.35,
38313
+ measuredUniform: false,
38314
+ },
38315
+ cross: {
38316
+ blurFactor: 0.12,
38317
+ morphologyFactor: 0.18,
38318
+ surfaceScaleFactor: 0.2,
38319
+ measuredUniform: false,
38320
+ },
38321
+ coolSlant: {
38322
+ blurFactor: 0.25,
38323
+ morphologyFactor: 0.06,
38324
+ surfaceScaleFactor: 0.5,
38325
+ measuredUniform: false,
38326
+ },
38327
+ riblet: {
38328
+ blurFactor: 0.25,
38329
+ surfaceScaleFactor: 0.5,
38330
+ measuredUniform: false,
38331
+ },
38332
+ artDeco: {
38333
+ blurFactor: 0.18,
38334
+ morphologyFactor: 0.32,
38335
+ surfaceScaleFactor: 0.35,
38336
+ measuredUniform: false,
38337
+ },
38338
+ // `relaxedInset`/`slope`/`hardEdge` (see this table's doc comment): COM
38339
+ // measured a genuine BRIGHT-BUMP-THEN-DARK-TROUGH double transition for
38340
+ // all three, which a single monotonic blur(+erode) ramp cannot reproduce
38341
+ // (its height field has one bell-shaped slope lobe, so the diffuse/
38342
+ // specular response can only rise-then-settle, never rise-then-undershoot-
38343
+ // then-settle). These factors are the closest achievable fit within the
38344
+ // existing 3-parameter primitive chain (the grid search pushed
38345
+ // `surfaceScaleFactor` to its upper bound trying to reach the measured
38346
+ // trough depth), not a claim of a clean match; see the doc comment.
38347
+ slope: {
38348
+ blurFactor: 0.55,
38349
+ morphologyFactor: 0.5,
38350
+ surfaceScaleFactor: 1.5,
38351
+ measuredUniform: true,
38352
+ },
38353
+ hardEdge: {
38354
+ blurFactor: 0.55,
38355
+ morphologyFactor: 0.5,
38356
+ surfaceScaleFactor: 1.5,
38357
+ measuredUniform: true,
38358
+ },
38359
+ };
38360
+ const DEFAULT_HEIGHT_MAP = {
38361
+ blurFactor: 0.4,
38362
+ surfaceScaleFactor: 0.8,
38363
+ measuredUniform: false,
38364
+ };
38365
+ function getBevelProfileHeightMap(bevelType) {
38366
+ return BEVEL_PROFILE_HEIGHT_MAP[bevelType] ?? DEFAULT_HEIGHT_MAP;
38367
+ }
38368
+
38136
38369
  /**
38137
38370
  * Material -> `feDiffuseLighting`/`feSpecularLighting` response table.
38138
38371
  *
@@ -38165,12 +38398,26 @@ const white = '#ffffff';
38165
38398
  * .ts`'s module doc table) found this module's `metal` numbers give a mixed
38166
38399
  * result: `matte` improved clearly over the old box-shadow approach (56.3 ->
38167
38400
  * 34.8 mean error) while `metal` did not (61.2 -> 59.7, and `metal`/`circle`
38168
- * specifically got WORSE, 54.9 -> 80.1) - the constants below are therefore
38169
- * flagged as UNVALIDATED for `metal` specifically, not just "less precisely
38170
- * calibrated" than `matte`. The other materials are positioned between/
38171
- * around the `matte`/`metal` anchors by category (glossy plastics near
38172
- * `metal` but softer, matte/powder variants near `matte`), not independently
38173
- * COM-measured at all.
38401
+ * specifically got WORSE, 54.9 -> 80.1) - the constants below were therefore
38402
+ * flagged UNVALIDATED for `metal`, and `metal`/`circle` was ROUTED to the
38403
+ * legacy box-shadow model (`visual-3d-bevel-lighting-routing.ts`). The other
38404
+ * materials are positioned between/around the `matte`/`metal` anchors by
38405
+ * category (glossy plastics near `metal` but softer, matte/powder variants
38406
+ * near `matte`), not independently COM-measured at all.
38407
+ *
38408
+ * RESOLVED for `metal`/`circle` (2026-09, the bevel-profile cross-section
38409
+ * campaign, `visual-3d-bevel-lighting-tables.ts`'s `BEVEL_PROFILE_HEIGHT_MAP`
38410
+ * doc comment): re-fitting `circle`'s profile-table entry against real COM
38411
+ * cross-section data (unrelated to material tuning) changed
38412
+ * `surfaceScaleFactor` from 1 to 0.65, and with these SAME `metal` constants
38413
+ * (unchanged from the paragraph above) that alone brought `metal`/`circle`'s
38414
+ * mean error to 39.5 against the SAME 54.9 baseline - confirmed against
38415
+ * fresh COM ground truth AND an actual headless-Chromium rasterisation of
38416
+ * the real filter chain (not just the closed-form model this module's own
38417
+ * campaigns otherwise used), so `metal`/`circle` no longer routes; see
38418
+ * `visual-3d-bevel-lighting-routing.ts`'s doc comment for the numbers and a
38419
+ * separate, NOT-landed attempt at fixing the flat-interior specular
38420
+ * saturation defect below.
38174
38421
  */
38175
38422
  const MATERIAL_LIGHTING = {
38176
38423
  matte: {
@@ -38247,10 +38494,14 @@ const MATERIAL_LIGHTING = {
38247
38494
  // (`angle`: baseline ~70, now ~19-27; `hardEdge`: baseline ~69.5, now 44;
38248
38495
  // `softRound`: baseline ~50, now 6.5-19). `circle` alone could NOT be
38249
38496
  // brought below baseline with any tested combination of these four
38250
- // parameters (tried down to `surfaceScaleMultiplier` 0.15 and up to 2.5;
38251
- // see `getBevelLightingFilterMarkup`'s `LEGACY_BEVEL_ROUTING` doc comment
38252
- // for why) and is routed to the legacy `box-shadow` model instead of
38253
- // shipping a regression.
38497
+ // MATERIAL parameters (tried down to `surfaceScaleMultiplier` 0.15 and up
38498
+ // to 2.5) against the then-current (ECMA-376-reasoned) profile table, and
38499
+ // was routed to the legacy `box-shadow` model instead of shipping a
38500
+ // regression. SUPERSEDED 2026-09: re-fitting the PROFILE table's own
38501
+ // `circle` entry against real COM data (a change orthogonal to these
38502
+ // material constants) resolved it without touching the numbers below; see
38503
+ // this file's module doc comment and `visual-3d-bevel-lighting-routing
38504
+ // .ts`.
38254
38505
  //
38255
38506
  // A SEPARATE, more severe issue was found (2026-09, while attempting an
38256
38507
  // ambient/wrap-term fix for the `circle` routing above) that this
@@ -38347,9 +38598,12 @@ function getMaterialLighting(material) {
38347
38598
  * Split out of `visual-3d-bevel-lighting.ts` to keep that module under the
38348
38599
  * repo's ~300 LOC guideline. Three independent axes feed the filter this
38349
38600
  * module's data drives: bevel PROFILE shape (`a:bevelT/@prst`, ECMA-376
38350
- * 20.1.10.9 `ST_BevelPresetType`), light rig ELEVATION/specular character
38351
- * (`a:lightRig/@rig`, ECMA-376 20.1.10.36 `ST_LightRigType`), and MATERIAL
38352
- * response (`a:sp3d/@prstMaterial`, ECMA-376 20.1.10.50 `ST_PresetMaterialType`).
38601
+ * 20.1.10.9 `ST_BevelPresetType`, re-exported here but defined in
38602
+ * `visual-3d-bevel-lighting-profile.ts`, itself split out for the same LOC
38603
+ * reason), light rig ELEVATION/specular character (`a:lightRig/@rig`,
38604
+ * ECMA-376 20.1.10.36 `ST_LightRigType`, defined below), and MATERIAL
38605
+ * response (`a:sp3d/@prstMaterial`, ECMA-376 20.1.10.50 `ST_PresetMaterialType`,
38606
+ * re-exported from `visual-3d-bevel-lighting-material.ts`).
38353
38607
  * The highlight/shadow DIRECTION (azimuth) itself is mostly resolved
38354
38608
  * elsewhere: `visual-3d-bevel-light`'s already COM-measured cardinal-snap
38355
38609
  * vector supplies the base azimuth from `a:lightRig/@dir`, and this module's
@@ -38374,80 +38628,6 @@ function getMaterialLighting(material) {
38374
38628
  *
38375
38629
  * @module render/visual-3d-bevel-lighting-tables
38376
38630
  */
38377
- /**
38378
- * `a:bevelT/@prst` (and `bevelB`, which shares the same profile vocabulary)
38379
- * -> height-map shape. ECMA-376 20.1.10.9 describes each profile's silhouette
38380
- * (a "circular", "flat sloped", "crossed", "art-deco stepped" etc. cross-
38381
- * section); this table groups the 12 values by that description into 3
38382
- * physically-motivated buckets rather than 12 independent hand-tuned entries:
38383
- *
38384
- * - **Curved** (`circle`, `convex`, `softRound`, `relaxedInset`, `divot`):
38385
- * a smooth, rounded cross-section -> wide Gaussian-only ramp, full relief.
38386
- * - **Faceted** (`angle`, `cross`, `coolSlant`, `riblet`, `artDeco`): a flat
38387
- * angled facet with a visible crease -> a medium blur PLUS a light erode so
38388
- * the ramp gets a crisper inner edge (the crease), full relief.
38389
- * - **Steep/narrow** (`slope`, `hardEdge`): COM-measured to show no clean
38390
- * directional signal (see {@link BevelProfileHeightMap.measuredUniform});
38391
- * a narrow, heavily-eroded ramp with reduced relief reproduces that
38392
- * physically instead of guessing a highlight side.
38393
- */
38394
- const BEVEL_PROFILE_HEIGHT_MAP = {
38395
- circle: { blurFactor: 0.55, surfaceScaleFactor: 1, measuredUniform: false },
38396
- convex: { blurFactor: 0.6, surfaceScaleFactor: 1.05, measuredUniform: false },
38397
- softRound: { blurFactor: 0.5, surfaceScaleFactor: 0.9, measuredUniform: false },
38398
- relaxedInset: { blurFactor: 0.45, surfaceScaleFactor: 0.85, measuredUniform: false },
38399
- divot: { blurFactor: 0.4, surfaceScaleFactor: 0.8, measuredUniform: false },
38400
- angle: {
38401
- blurFactor: 0.32,
38402
- morphologyFactor: 0.12,
38403
- surfaceScaleFactor: 1,
38404
- measuredUniform: false,
38405
- },
38406
- cross: {
38407
- blurFactor: 0.3,
38408
- morphologyFactor: 0.15,
38409
- surfaceScaleFactor: 0.95,
38410
- measuredUniform: false,
38411
- },
38412
- coolSlant: {
38413
- blurFactor: 0.28,
38414
- morphologyFactor: 0.14,
38415
- surfaceScaleFactor: 0.95,
38416
- measuredUniform: false,
38417
- },
38418
- riblet: {
38419
- blurFactor: 0.26,
38420
- morphologyFactor: 0.18,
38421
- surfaceScaleFactor: 0.9,
38422
- measuredUniform: false,
38423
- },
38424
- artDeco: {
38425
- blurFactor: 0.24,
38426
- morphologyFactor: 0.2,
38427
- surfaceScaleFactor: 1,
38428
- measuredUniform: false,
38429
- },
38430
- slope: {
38431
- blurFactor: 0.12,
38432
- morphologyFactor: 0.35,
38433
- surfaceScaleFactor: 0.4,
38434
- measuredUniform: true,
38435
- },
38436
- hardEdge: {
38437
- blurFactor: 0.1,
38438
- morphologyFactor: 0.4,
38439
- surfaceScaleFactor: 0.35,
38440
- measuredUniform: true,
38441
- },
38442
- };
38443
- const DEFAULT_HEIGHT_MAP = {
38444
- blurFactor: 0.4,
38445
- surfaceScaleFactor: 0.8,
38446
- measuredUniform: false,
38447
- };
38448
- function getBevelProfileHeightMap(bevelType) {
38449
- return BEVEL_PROFILE_HEIGHT_MAP[bevelType] ?? DEFAULT_HEIGHT_MAP;
38450
- }
38451
38631
  /**
38452
38632
  * `a:lightRig/@rig` -> elevation/sharpness/direction. COM-CALIBRATED
38453
38633
  * (2026-09, real PowerPoint `Slide.Export`, mid-grey #808080 1.4in square,
@@ -38638,7 +38818,7 @@ function resolveLayer(index, bevelType, widthEmu, heightEmu, isBottom, scene, ma
38638
38818
 
38639
38819
  /**
38640
38820
  * Legacy `box-shadow` routing for a `material`/profile combination the SVG
38641
- * lighting filter cannot yet beat.
38821
+ * lighting filter cannot beat.
38642
38822
  *
38643
38823
  * Split out of `visual-3d-bevel-lighting.ts` to keep that file under the
38644
38824
  * repo's ~300 LOC guideline.
@@ -38646,26 +38826,45 @@ function resolveLayer(index, bevelType, widthEmu, heightEmu, isBottom, scene, ma
38646
38826
  * @module render/visual-3d-bevel-lighting-routing
38647
38827
  */
38648
38828
  /**
38649
- * `material|profile` pairs that measured WORSE than the legacy `box-shadow`
38650
- * approach even after calibration (see `visual-3d-bevel-lighting-material
38651
- * .ts`'s module doc comment for the numbers) and therefore route to the
38652
- * legacy model instead of shipping a regression. Currently just
38653
- * `metal|circle`: a grid search over `diffuseConstant`/`specularConstant`/
38654
- * `specularExponent`/`surfaceScaleMultiplier` (2026-09, the same real
38655
- * render-vs-COM pipeline used throughout this module) could not find ANY
38656
- * combination bringing `metal`/`circle`'s mean error at or below the
38657
- * box-shadow baseline in any of the 4 `a:lightRig/@dir` values tested:
38658
- * `feDiffuseLighting`'s `N.L<=0` clamp-to-black on the shadow side is
38659
- * structural to this primitive chain (independent of `diffuseConstant`'s
38660
- * magnitude, which only scales the LIT side), and reducing `surfaceScale`
38661
- * enough to lift the clamped shadow side toward COM's measured ~178/255
38662
- * pulls the highlight side down away from its own accurate ~221/255 reading
38663
- * faster than it helps - the two targets cannot both be reached with these
38664
- * four parameters for this specific profile/material pair. A real fix needs
38665
- * an ambient/floor term this primitive chain does not have; out of scope for
38666
- * this pass.
38667
- */
38668
- const LEGACY_BEVEL_ROUTING = new Set(['metal|circle']);
38829
+ * `material|profile` pairs that measure WORSE than the legacy `box-shadow`
38830
+ * approach and therefore route to the legacy model instead of shipping a
38831
+ * regression. Empty as of the 2026-09 bevel-profile-cross-section campaign
38832
+ * (`visual-3d-bevel-lighting-tables.ts`'s `BEVEL_PROFILE_HEIGHT_MAP` doc
38833
+ * comment): `metal|circle` was the one routed pair (a grid search over
38834
+ * `diffuseConstant`/`specularConstant`/`specularExponent`/
38835
+ * `surfaceScaleMultiplier` against the OLD, ECMA-376-reasoned profile table
38836
+ * could not bring it at or below baseline in any direction - see this file's
38837
+ * git history for that campaign's numbers). Re-fitting `circle`'s
38838
+ * `BEVEL_PROFILE_HEIGHT_MAP` entry against real COM cross-section data
38839
+ * (`surfaceScaleFactor` 1 -> 0.65) changed the balance enough that the
38840
+ * UNCHANGED material constants now beat the box-shadow baseline in every
38841
+ * `a:lightRig/@dir`, confirmed two ways: fresh COM ground truth (mid-grey
38842
+ * `circle`/metal square, 24pt bevel, `threePt` rig, all 4 directions,
38843
+ * 0.15in-from-edge highlight+shadow sampling, script
38844
+ * `scripts/make-bevel-material-fixture.mjs` +
38845
+ * `measure-bevel-material-com.ps1`) and an ACTUAL headless-Chromium
38846
+ * rasterisation of the real filter primitive chain (a one-off Playwright
38847
+ * spec, not committed) sampled the same points: mean absolute error 39.5
38848
+ * (baseline was 54.9). The same real-browser check also re-confirmed
38849
+ * `angle`/`hardEdge`/`softRound` still beat their baselines (41.5/62.0/27.5
38850
+ * against baselines ~70/~69.5/~50) with the new profile table, so nothing
38851
+ * newly regressed.
38852
+ *
38853
+ * A candidate fix for the SEPARATE flat-interior specular-saturation defect
38854
+ * (masking `feSpecularLighting`'s contribution to the actual curved bevel
38855
+ * band via a `feComponentTransfer`/`feColorMatrix` triangle-of-height mask,
38856
+ * `1 - |2*height-1|`, zero at the flat cap) was measured against the same
38857
+ * real-browser pipeline for all 4 profiles and made EVERY ONE of them worse,
38858
+ * often drastically (`circle` 39.5 -> 103.5, `angle` 41.5 -> 104.0,
38859
+ * `hardEdge` 62.0 -> 94.0, `softRound` 27.5 -> 89.0): the mask's `feFuncA
38860
+ * type="table" tableValues="0 1 0"` zeroes specular well before COM's real
38861
+ * highlight has decayed at the 0.15in sample offset (the mask, tuned only to
38862
+ * the height VALUE crossing 0.5, does not track where the actual specular
38863
+ * lobe sits for a high `specularExponent`), so this attempt was measured and
38864
+ * NOT landed. See `docs/guide/limitations.md` for the still-open
38865
+ * flat-interior saturation defect this was meant to fix.
38866
+ */
38867
+ const LEGACY_BEVEL_ROUTING = new Set();
38669
38868
  /**
38670
38869
  * Whether a `material`/`profile` combination is routed to the legacy
38671
38870
  * `box-shadow` bevel model instead of the SVG lighting filter.
@@ -38737,12 +38936,12 @@ function isRoutedToLegacyBevelShadow(material, profile) {
38737
38936
  * `metal`/`circle` below its baseline in any direction (the two targets pull
38738
38937
  * in opposite directions as `surfaceScale` changes - see
38739
38938
  * `visual-3d-bevel-lighting-routing.ts`'s `isRoutedToLegacyBevelShadow` doc
38740
- * comment), so `metal`/`circle` ROUTES to the legacy `box-shadow` model. The
38741
- * "before" numbers are themselves large because this campaign scores
38742
- * absolute brightness match, not just highlight/shadow SIGN agreement (which
38743
- * is all `getBevelShadow`'s box-shadow output was previously verified
38744
- * against). All scripts used are scratch tooling (not committed, not wired
38745
- * into CI, same as `com-acceptance.mjs`); full tables are in the task report.
38939
+ * comment), so `metal`/`circle` ROUTED to the legacy `box-shadow` model at
38940
+ * the time (SUPERSEDED 2026-09 below; it no longer routes). The "before"
38941
+ * numbers are large because this campaign scores absolute brightness match,
38942
+ * not just highlight/shadow SIGN agreement (all `getBevelShadow`'s
38943
+ * box-shadow output was previously verified against). Scripts: scratch
38944
+ * tooling, same convention as `com-acceptance.mjs`.
38746
38945
  *
38747
38946
  * ## Re-run against the CURRENT `threePt` elevationDeg (2026-09, post-lightRig-recalibration)
38748
38947
  *
@@ -38800,6 +38999,16 @@ function isRoutedToLegacyBevelShadow(material, profile) {
38800
38999
  * material against this same COM ground truth. Neither was completed in
38801
39000
  * this pass; see `docs/guide/limitations.md`.
38802
39001
  *
39002
+ * ## 2026-09 bevel-profile cross-section + specular-masking follow-up
39003
+ *
39004
+ * Kept in the files they most directly touch (LOC budget): the 12
39005
+ * `a:bevelT/@prst` height-map SHAPES were fit against real COM cross-section
39006
+ * curves for the first time (`visual-3d-bevel-lighting-tables.ts`'s doc +
39007
+ * pinned table in its `.test.ts`), changing `circle`'s `surfaceScaleFactor`
39008
+ * enough that `metal`/`circle` now beats baseline unrouted; a follow-up
39009
+ * specular-band-masking attempt at the saturation defect above was measured
39010
+ * and made things WORSE (`visual-3d-bevel-lighting-routing.ts`'s doc).
39011
+ *
38803
39012
  * @module render/visual-3d-bevel-lighting
38804
39013
  */
38805
39014
  /**
@@ -39236,19 +39445,13 @@ function applyHomography(h, p) {
39236
39445
  *
39237
39446
  * 1. The shape's flat picture plane is a unit square in its own local XY
39238
39447
  * plane (`z=0`), corners at `(-0.5,-0.5) .. (0.5,0.5)`.
39239
- * 2. Each corner is projected by {@link projectCorner}: a PRIMARY per-axis
39240
- * orthographic cosine foreshortening (`lon` shrinks width, `lat` shrinks
39241
- * height), COM-validated for a single-axis rotation (see below), plus a
39242
- * SECONDARY genuine pinhole perspective skew that activates only for a
39243
- * combined (both axes nonzero) pose - see that function's own doc comment
39244
- * for why a naive single pinhole projection is the WRONG primary model
39245
- * here, unlike the preset table's own two-axis families.
39246
- * 3. The pinhole secondary term's focal length is `f = 1/tan(fov/2)` (the
39247
- * same FOV <-> perspective-distance relationship `visual-3d-camera-fov`
39248
- * already uses), so `lat=lon=rev=0` reproduces an EXACT identity
39249
- * homography - the same trivial case `orthographicFront` is COM-measured
39250
- * to produce - by construction (the secondary term is architecturally
39251
- * zero whenever either axis is zero, so this holds regardless of `fov`).
39448
+ * 2. Each corner is projected by {@link projectCorner}: an ORTHOGRAPHIC
39449
+ * (parallel, no perspective divide) rotation-composition transform - see
39450
+ * that function's own doc comment for the exact formula and its
39451
+ * derivation.
39452
+ * 3. `lat=lon=rev=0` reproduces an EXACT identity homography - the same
39453
+ * trivial case `orthographicFront` is COM-measured to produce - by
39454
+ * construction (`cos(0)=1`, every other term vanishes).
39252
39455
  * 4. `rev` (roll about the view axis) commutes with the projection: rolling
39253
39456
  * the camera about its own aim axis is exactly a 2D rotation of the
39254
39457
  * already-projected image, applied here as a post-projection step rather
@@ -39259,88 +39462,96 @@ function applyHomography(h, p) {
39259
39462
  * `visual-3d-camera-homography`'s existing `homographyToMatrix3d`
39260
39463
  * embedding unchanged.
39261
39464
  *
39262
- * ## COM validation (2026-09, real PowerPoint `Slide.Export`, 144px/in)
39465
+ * ## COM validation, round 2: the 27-point lat x lon x rev grid (2026-09)
39466
+ *
39467
+ * The first campaign (single COM measurement per case, see history) found
39468
+ * the primary per-axis cosine scale exact for any single-axis `a:rot`, but a
39469
+ * damped pinhole-perspective "secondary term" (weighted by
39470
+ * `sin(lat)*sin(lon)`, FOV-dependent) under-predicted a genuinely combined
39471
+ * pose (`lat=35.26deg lon=45deg rev=45deg`) by ~25-29% relative corner error,
39472
+ * repeatably across two independent measurements. That secondary term is
39473
+ * REPLACED here, not patched: a fresh 27-point grid (`lat in {0, 25,
39474
+ * 35.26deg}` x `lon in {0, 25, 45deg}` x `rev in {0, 25, 45deg}`, including
39475
+ * both prior points exactly) was rendered via real PowerPoint COM
39476
+ * (`Slide.Export`, 144px/in, flat 2in `prst="orthographicFront"` + `a:rot`
39477
+ * squares) and each cell's 4 corners extracted by convex-hull fit (the
39478
+ * boundary/hull/quad-simplification method `visual-3d-camera-homography.ts`
39479
+ * already validated, NOT the fragile "4 extreme pixels" shortcut the first
39480
+ * campaign used, which silently mis-ordered corners for any near-45deg `rev`
39481
+ * by matching against UNDISTORTED reference positions - a large rotation's
39482
+ * true nearest axis-aligned corner is not its physical origin; fixed by
39483
+ * matching against a cosine-scale-plus-rev PRIOR position instead).
39484
+ *
39485
+ * Fitting the 27 measured cells against every hypothesis in the task brief
39486
+ * (Euler order lon-then-lat vs lat-then-lon vs the old damped-pinhole model;
39487
+ * orthographic vs a true perspective divide at the override's own FOV;
39488
+ * rotation about the shape centre - confirmed by near-zero centroid shift
39489
+ * uncorrelated with a cell's distance from the canvas centre, ruling out a
39490
+ * slide-centre pivot) found an EXACT closed form: keep `x` as the
39491
+ * already-validated pure cosine scale (COM-confirmed independent of `lat`:
39492
+ * the same `lon=45deg` cells produced identical `x` at `lat=25deg` and
39493
+ * `lat=35.26deg`), and add a rotation-composition cross term to `y` ONLY,
39494
+ * with a NEGATIVE sign relative to the naive `Ry(lon).Rx(lat)` composition
39495
+ * this module's first attempt used:
39263
39496
  *
39264
- * Three explicit `a:camera/a:rot` cases (a required `prst="orthographicFront"`
39265
- * plus an overriding `a:rot`, since real PowerPoint rejects a schema-invalid
39266
- * `a:camera` with no `@prst` at all - `CT_Camera`'s `prst` attribute turned
39267
- * out to be REQUIRED, contrary to what this codebase's own writer, which
39268
- * merges onto an already-`@prst`-bearing parsed node, implied was optional),
39269
- * a flat 2in square, corners extracted as the 4 extreme (min/max x/y) grey
39270
- * pixels - reliable here since none of the 3 cases roll far enough to turn
39271
- * the square into a diamond whose extremes are edge midpoints, the situation
39272
- * `visual-3d-camera-homography.ts`'s own campaign had to use a full
39273
- * convex-hull fit for:
39497
+ * ```
39498
+ * x = X * cos(lon)
39499
+ * y = Y * cos(lat) - X * sin(lat) * sin(lon)
39500
+ * ```
39501
+ *
39502
+ * Across all 27 grid cells (script: `gen-fixture.mjs` -> `measure.ps1` ->
39503
+ * `solve-corners.mjs` -> `fit-model.mjs`, scratch/one-off, not committed):
39504
+ * average max-corner error 0.61%, median well under 1%, worst 3 cells (all
39505
+ * `rev=25deg`, an "ugly" non-axis-aligned roll angle that maximises
39506
+ * antialiasing-boundary noise at a 288px-side element, not a systematic
39507
+ * lat/lon pattern) at 2.10% / 1.96% / 1.70% - see the raw per-cell table
39508
+ * below. This lands the combined case in the SAME ~1% band as the
39509
+ * single-axis cases, closing the ~25-29% gap the first campaign left open,
39510
+ * with NO fov/zoom dependency at all: the model is purely orthographic, so
39511
+ * `ParametricCameraParams.fovRad` is now unused by {@link projectCorner}
39512
+ * (kept in the type for API stability; `@fov`/`@zoom` were not
39513
+ * independently varied by this campaign, only held at their
39514
+ * `orthographicFront` default, so this does not claim they have no effect
39515
+ * under some other combination this grid did not cover).
39516
+ *
39517
+ * Raw per-cell max-corner error (fraction of the square's own side, sorted
39518
+ * worst-first; `lat=35.26` is the isometric angle `atan(1/sqrt(2))`, reusing
39519
+ * the first campaign's own combined-case angle set):
39274
39520
  *
39275
39521
  * ```
39276
- * case corner error (px, avg of 4, on a 288px-side element)
39277
- * lat=0 lon=0 rev=0 (sanity: == identity) 1.2
39278
- * lat=0 lon=25deg rev=0 (single-axis yaw) 0.75
39279
- * lat=35.26deg lon=45deg rev=45deg (combined + roll) 82 (29% relative)
39522
+ * lat25_lon0_rev25 2.098% lat25_lon0_rev0 0.390%
39523
+ * lat0_lon45_rev25 1.959% lat25_lon0_rev45 0.362%
39524
+ * lat35.26_lon45_rev25 1.696% lat35.26_lon0_rev45 0.353%
39525
+ * lat0_lon25_rev25 0.776% lat0_lon25_rev45 0.349%
39526
+ * lat25_lon25_rev25 0.756% lat35.26_lon0_rev25 0.347%
39527
+ * lat25_lon25_rev45 0.735% lat35.26_lon45_rev45 0.347%
39528
+ * lat0_lon0_rev45 0.669% lat25_lon45_rev0 0.342%
39529
+ * lat35.26_lon25_rev45 0.654% lat25_lon25_rev0 0.323%
39530
+ * lat35.26_lon25_rev25 0.585% lat35.26_lon45_rev0 0.312%
39531
+ * lat0_lon0_rev0 0.491% lat35.26_lon25_rev0 0.304%
39532
+ * lat0_lon45_rev0 0.450% lat25_lon45_rev25 0.292%
39533
+ * lat25_lon45_rev45 0.420% lat0_lon0_rev25 0.259%
39534
+ * lat0_lon45_rev45 0.404%
39535
+ * lat35.26_lon0_rev0 0.402% (avg 0.610%, max 2.098%)
39280
39536
  * ```
39281
39537
  *
39282
- * The identity and single-axis cases are sub-pixel accurate - well within the
39283
- * preset homography table's own ~0.7%-relative-error tolerance. The combined
39284
- * case is NOT: at this extreme (all three angles large and simultaneous) the
39285
- * primary cosine term plus the damped secondary skew above under-predicts the
39286
- * real distortion by roughly 29%, i.e. this module does NOT claim COM parity
39287
- * for a genuinely combined multi-axis override, only documents the measured
39288
- * gap. This is the same class of difficulty `visual-3d-camera.ts`'s own doc
39289
- * comment records for the PRESET two-axis families ("A centred `perspective`
39290
- * alone cannot fully reproduce the two-axis presets' off-axis camera... a
39291
- * genuine off-axis vanishing point"): PowerPoint's real camera formula for a
39292
- * combined pose is not fully reverse-engineered here either. What IS
39293
- * COM-established, and was previously entirely unverified (the old code used
39294
- * a `rotateX`/`rotateY` + centred CSS `perspective()` approximation for
39295
- * EVERY override, single-axis included): a pure single-axis `a:rot` is a
39296
- * symmetric per-axis scale with NO keystone and NO centre shift, which the
39297
- * old model could not represent either (it always keystones via
39298
- * `perspective()`). `lon`'s sign was independently isolated and COM-checked
39299
- * (a positive `lon` measured a symmetric width shrink, matching this
39300
- * module).
39301
- *
39302
- * `lat`'s sign is NOT independently observable from a single-axis case:
39303
- * `cos` is an even function, so this module's primary term produces the
39304
- * IDENTICAL homography for `lat=+25deg` and `lat=-25deg` in isolation (no
39305
- * `lon`) - proven analytically, and confirmed by a real `lat=25deg only`
39306
- * COM measurement (2026-09, same 2in-square/144px-in methodology) matching
39307
- * this module's prediction to within 1px on every one of the 4 measured
39308
- * corners (predicted top/bottom edge at y=56.7/317.7 vs measured 56/317,
39309
- * width unchanged both sides). Sign only becomes observable jointly with
39310
- * `lon` (the secondary term), which the combined case below already
39311
- * exercises; a single-axis case genuinely cannot add information here.
39312
- *
39313
- * `rev`'s sign WAS independently isolated: a real `rev=45deg only` COM
39314
- * measurement (lat=lon=0) produced a diamond-oriented square whose 4 extreme
39315
- * points matched this module's predicted corner-to-extreme mapping (which
39316
- * original corner becomes the new top/right/bottom/left vertex) for a
39317
- * POSITIVE `rev`, each within about 10 degrees of angle from the shape's own
39318
- * centre (a small, consistent systematic offset in the SAME rotational
39319
- * sense across all 4 points, not a sign flip) - this module's `rev` sign
39320
- * convention is therefore COM-confirmed, not merely architecturally
39321
- * plausible.
39322
- *
39323
- * A second, independent combined-case measurement (a fresh fixture, same
39324
- * lat=35.26/lon=45/rev=45 angles) reproduced the same ~25-29% relative
39325
- * corner error as the original campaign above (70.8px average this time, vs
39326
- * 82px originally, both on a 288px element) - confirming the combined-case
39327
- * residual is a real, repeatable limitation of this module's secondary term,
39328
- * not measurement noise from a single run. The fixture/export/pixel-sampling
39329
- * scripts used for all of this measurement were scratch, one-off tooling
39330
- * (not committed - see the task report for the methodology if reproducing).
39538
+ * `lon`'s sign was independently isolated and COM-checked in the first
39539
+ * campaign (a positive `lon` measured a symmetric width shrink, matching
39540
+ * this module) and is unaffected by the cross-term replacement (`x` is
39541
+ * unchanged). `lat`'s sign is not independently observable from a
39542
+ * single-axis case (`cos` is even) but IS observable jointly with `lon` via
39543
+ * the cross term; the 27-point grid's fit (rather than an isolated
39544
+ * combined-case check) is itself the confirmation this module's `lat` sign
39545
+ * convention is correct across the whole grid, not just one pose. `rev`'s
39546
+ * sign was independently isolated in the first campaign (a real `rev=45deg
39547
+ * only` measurement matched this module's predicted corner-to-extreme
39548
+ * mapping for a positive `rev`) and is reused unchanged here: it is still
39549
+ * applied as a simple post-projection 2D roll, and the fit above already
39550
+ * exercises every `rev` level jointly with every `lat`/`lon` combination
39551
+ * without needing a different composition order.
39331
39552
  *
39332
39553
  * @module render/visual-3d-camera-parametric
39333
39554
  */
39334
- function rotateX(v, angle) {
39335
- const c = Math.cos(angle);
39336
- const s = Math.sin(angle);
39337
- return { x: v.x, y: v.y * c - v.z * s, z: v.y * s + v.z * c };
39338
- }
39339
- function rotateY(v, angle) {
39340
- const c = Math.cos(angle);
39341
- const s = Math.sin(angle);
39342
- return { x: v.x * c + v.z * s, y: v.y, z: -v.x * s + v.z * c };
39343
- }
39344
39555
  function rotate2d(p, angle) {
39345
39556
  if (angle === 0) {
39346
39557
  return p;
@@ -39353,52 +39564,22 @@ function rotate2d(p, angle) {
39353
39564
  * Project one local unit-square corner `(x, y)` (already centred, y-up)
39354
39565
  * through the camera.
39355
39566
  *
39356
- * The PRIMARY term is an orthographic per-axis cosine foreshortening
39357
- * (`x *= cos(lon)`, `y *= cos(lat)`), not a full pinhole perspective divide:
39358
- * COM measurement (see the module doc comment) found a pure single-axis
39359
- * `a:rot` produces a symmetric scale with NO keystone and NO centre shift at
39360
- * all - matching this term to within ~1% - whereas a naive pinhole
39361
- * projection (translate the camera sideways, re-aim, divide by depth)
39362
- * predicts both a shift and a slant that COM does not show. This mirrors
39363
- * `visual-3d-camera-homography.ts`'s own finding #2 for the equivalent
39364
- * single-axis PRESET family (`perspectiveLeft`/`Right`/`Above`/`Below`):
39365
- * "a pure anisotropic scale + small offset", not a keystone.
39366
- *
39367
- * A SECONDARY genuine perspective skew (a real off-axis vanishing point, the
39368
- * pinhole formula's deviation from the cosine term) is blended in only when
39369
- * BOTH `lat` and `lon` are nonzero at once (weighted by `sin(lat)*sin(lon)`,
39370
- * which is exactly 0 for any single-axis rotation, so that COM-validated
39371
- * case is reproduced UNCHANGED). This mirrors the preset table's own
39372
- * two-axis families (`*Facing`/`Contrasting*`/`Heroic*`) genuinely needing a
39373
- * skew a pure scale cannot represent. `fov` modulates this secondary term's
39374
- * strength (a wider FOV -> a nearer, more exaggerated camera -> more
39375
- * foreshortening), the only place `@fov`/`@zoom` affect this model: no COM
39376
- * data varies FOV independently for an override, so treat this coupling as
39377
- * physically-motivated but NOT independently calibrated, unlike the
39378
- * COM-validated primary term.
39567
+ * `x` is a pure per-axis cosine foreshortening (`x = X*cos(lon)`), COM-
39568
+ * confirmed independent of `lat` (see the module doc comment): the 27-point
39569
+ * grid's `lon=45deg` cells produced the identical `x` at both `lat=25deg`
39570
+ * and `lat=35.26deg`. `y` gets the SAME cosine scale on its own axis
39571
+ * (`Y*cos(lat)`) plus a rotation-composition cross term, `-X*sin(lat)*
39572
+ * sin(lon)`, that is exactly 0 whenever EITHER axis is 0 (so both the
39573
+ * identity and every single-axis case reproduce their already-COM-validated
39574
+ * result unchanged) and otherwise fits the 27-point grid to within ~1% on
39575
+ * average (see the module doc comment for the full per-cell table). This is
39576
+ * a purely ORTHOGRAPHIC transform (no perspective divide, no `fov`
39577
+ * dependency): a genuine pinhole projection was one of the hypotheses tested
39578
+ * against the grid and fit measurably worse than this cross term.
39379
39579
  */
39380
39580
  function projectCorner(localX, localY, params) {
39381
- const scaleX = Math.cos(params.lonRad);
39382
- const scaleY = Math.cos(params.latRad);
39383
- let x = localX * scaleX;
39384
- let y = localY * scaleY;
39385
- const twoAxisWeight = Math.sin(params.latRad) * Math.sin(params.lonRad);
39386
- if (twoAxisWeight !== 0) {
39387
- const f = 1 / Math.tan(params.fovRad / 2);
39388
- const local = { x: localX, y: localY, z: 0 };
39389
- // R^T * P, where R = Ry(lon) . Rx(lat): apply Ry(-lon) then Rx(-lat).
39390
- const viewNoTranslate = rotateX(rotateY(local, -params.lonRad), -params.latRad);
39391
- const viewZ = viewNoTranslate.z - f;
39392
- // Guard a degenerate camera-through-the-plane case (should not occur
39393
- // for any realistic lat/lon): skip the secondary term rather than
39394
- // divide by ~0.
39395
- if (Math.abs(viewZ) > 1e-6) {
39396
- const pinholeX = (f * viewNoTranslate.x) / -viewZ;
39397
- const pinholeY = (f * viewNoTranslate.y) / -viewZ;
39398
- x += (pinholeX - localX * scaleX) * Math.abs(twoAxisWeight);
39399
- y += (pinholeY - localY * scaleY) * Math.abs(twoAxisWeight);
39400
- }
39401
- }
39581
+ const x = localX * Math.cos(params.lonRad);
39582
+ const y = localY * Math.cos(params.latRad) - localX * Math.sin(params.latRad) * Math.sin(params.lonRad);
39402
39583
  return rotate2d({ x, y }, params.revRad);
39403
39584
  }
39404
39585
  /**
@@ -103016,7 +103197,7 @@ function createLocalStorageBackend(namespace) {
103016
103197
  /** Try IndexedDB first; fall back to localStorage on any failure. */
103017
103198
  async function resolveBackend(dbName, namespace) {
103018
103199
  try {
103019
- const { openChatDb, createIdbBackend } = await import('./pptx-angular-viewer-chat-history-idb-C08A1rjA.mjs');
103200
+ const { openChatDb, createIdbBackend } = await import('./pptx-angular-viewer-chat-history-idb-DIH9Texq.mjs');
103020
103201
  const db = await openChatDb(dbName);
103021
103202
  return createIdbBackend(db);
103022
103203
  }
@@ -142944,7 +143125,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImpor
142944
143125
  }], propDecorators: { canEdit: [{ type: i0.Input, args: [{ isSignal: true, alias: "canEdit", required: false }] }], slideIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "slideIndex", required: false }] }], selectedElement: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedElement", required: false }] }] } });
142945
143126
 
142946
143127
  // Generated by scripts/inline-shared.mjs from package.json. Do not edit.
142947
- const PPTX_ANGULAR_VIEWER_VERSION = "3.13.0";
143128
+ const PPTX_ANGULAR_VIEWER_VERSION = "3.14.0";
142948
143129
 
142949
143130
  /**
142950
143131
  * account-page.component.ts: File > Account content.
@@ -181406,4 +181587,4 @@ function cn(...values) {
181406
181587
  */
181407
181588
 
181408
181589
  export { CollaborationService as $, AFTER_ANIMATION_VALUES as A, AnimationAuthorPanelComponent as B, AnimationPanelComponent as C, AnimationPlaybackService as D, AutosaveRecoveryDialogComponent as E, AutosaveService as F, BroadcastDialogComponent as G, CHART_EDITOR_STYLES as H, CURSOR_PALETTE as I, CanvasFitService as J, ChartAxisOptionsComponent as K, ChartAxisStyleOptionsComponent as L, ChartComboTypeOptionsComponent as M, ChartDataEditorComponent as N, ChartDataLabelOptionsComponent as O, ChartDatapointMarkerOptionsComponent as P, ChartDatapointOptionsComponent as Q, ChartDisplayOptionsComponent as R, ChartElementViewComponent as S, ChartErrorBarOptionsComponent as T, ChartMarkerOptionsComponent as U, ChartPartSelectionService as V, ChartPrimitivesComponent as W, ChartRendererComponent as X, ChartTrendlineOptionsComponent as Y, ChartTypeSelectorComponent as Z, CollaborationCursorsComponent as _, ALIGN_OPTIONS as a, InsertSmartArtDialogComponent as a$, ColorChangedImageComponent as a0, CommentMarkersOverlayComponent as a1, CommentsPanelComponent as a2, CommentsService as a3, ComparePanelComponent as a4, ConnectorRendererComponent as a5, ConnectorTextOverlayComponent as a6, CustomShowsComponent as a7, DEFAULT_BOUNDS as a8, DEFAULT_BROADCAST_SERVER_URL as a9, EditorToolbarComponent as aA, EffectsPanelComponent as aB, ElementRendererComponent as aC, EmbeddedFontsService as aD, EncryptedFileDialogComponent as aE, EquationEditorDialogComponent as aF, EquationRendererComponent as aG, EquationTemplateGalleryComponent as aH, ExportProgressModalComponent as aI, ExportService as aJ, FieldContextService as aK, FindBarComponent as aL, FindReplaceBarComponent as aM, FollowModeBarComponent as aN, FontEmbeddingListComponent as aO, FontEmbeddingPanelComponent as aP, GALLERY_THEME_PRESETS as aQ, GOOGLE_WEBFONTS_LINK_ID as aR, GRIDLINE_COLOR$1 as aS, GoogleWebfontsService as aT, GradientPickerComponent as aU, HANDOUT_OPTIONS as aV, HeaderFooterDialogComponent as aW, HyperlinkDialogComponent as aX, ImagePropertiesPanelComponent as aY, InkDrawingService as aZ, InkRendererComponent as a_, DEFAULT_CANVAS_HEIGHT as aa, DEFAULT_CANVAS_WIDTH as ab, DEFAULT_COLOR_SCHEME as ac, DEFAULT_FILL_COLOR$1 as ad, DEFAULT_LAYOUT as ae, DEFAULT_PALETTE$1 as af, DEFAULT_PATTERN_FILL_PRESET as ag, DEFAULT_PRINT_SETTINGS as ah, DEFAULT_SLIDE_BACKGROUND as ai, DEFAULT_STROKE_COLOR as aj, DEFAULT_STYLE as ak, DEFAULT_TABLE_ROW_HEIGHT as al, DEFAULT_TEXT_COLOR$2 as am, DEFAULT_VIEWER_PROFILE as an, DIRECTIONAL_PRESETS as ao, DIRECTION_OPTIONS as ap, DocumentPropertiesCardComponent as aq, EFFECT_SOUND_CATALOGUE as ar, EMBEDDED_FONTS_STYLE_ID as as, EMPHASIS_PRESETS as at, ENTRANCE_PRESETS as au, TEMPLATES as av, EXIT_PRESETS as aw, EditorContextMenuComponent as ax, EditorHistory as ay, EditorStateService as az, ANIMATION_PRESET_CATEGORIES as b, RibbonArrangeSectionComponent as b$, InspectorPaneHeaderComponent as b0, InspectorPanelComponent as b1, IsMobileService as b2, KeepAnnotationsDialogComponent as b3, LOCALE_CATALOG as b4, LONG_PRESS_DURATION_MS as b5, LONG_PRESS_MOVE_TOLERANCE_PX as b6, LoadContentService as b7, LocalPresencePublisher as b8, MAX_ZOOM_SCALE as b9, PX_PER_INCH as bA, PasswordProtectionDialogComponent as bB, PasswordStrengthMeterComponent as bC, PowerPointViewerComponent as bD, PresentToolbarAutoHide as bE, PresentationAnnotationOverlayComponent as bF, PresentationAnnotationsService as bG, PresentationOverlayComponent as bH, PresentationPropertiesPanelComponent as bI, PresentationSettingsCardComponent as bJ, PresentationSubtitleBarComponent as bK, PresentationToolbarComponent as bL, PresentationTransitionOverlayComponent as bM, PresenterViewComponent as bN, PresenterWindowService as bO, PrintDialogComponent as bP, PrintService as bQ, PrintSettingsPanelComponent as bR, PropertiesDialogComponent as bS, REPEAT_MODE_OPTIONS as bT, RESIZE_HANDLES as bU, RULER_FONT_SIZE as bV, RULER_THICKNESS as bW, ReadingViewOverlayComponent as bX, RemoteSelectionOverlayComponent as bY, RibbonAnimationGalleryComponent as bZ, RibbonAnimationsSectionComponent as b_, MIN_ZOOM_SCALE as ba, MOTION_PATH_COLUMNS as bb, MediaPreviewComponent as bc, MediaPropertiesPanelComponent as bd, MediaRendererComponent as be, MediaTrimTimelineComponent as bf, MobileBottomBarComponent as bg, MobileMenuSheetComponent as bh, MobilePresenterViewComponent as bi, MobileSheetComponent as bj, MobileSlidesSheetComponent as bk, MobileToolbarComponent as bl, ModalDialogComponent as bm, Model3DRendererComponent as bn, NotesHandoutCardComponent as bo, NotesPanelComponent as bp, NotesToolbarComponent as bq, OleRendererComponent as br, OutlineViewOverlayComponent as bs, POWER_POINT_VIEWER_PROVIDERS as bt, PPTX_OPEN_ACCEPT as bu, PRESENTATION_OPEN_EXTENSIONS as bv, PRESENTER_CHANNEL_NAME as bw, PRESENTER_MSG_ORIGIN as bx, PRESENTER_TIMER_SEGMENT_MS as by, PX_PER_CM as bz, AUDIENCE_HASH as c, TABLE_STRUCTURE_TOGGLES as c$, RibbonColorPopoverComponent as c0, RibbonComponent as c1, RibbonDesignSectionComponent as c2, RibbonDrawSectionComponent as c3, RibbonDrawingGroupComponent as c4, RibbonEditingSectionComponent as c5, RibbonFileSectionComponent as c6, RibbonFontControlsComponent as c7, RibbonHomeSectionComponent as c8, RibbonHyperlinkButtonComponent as c9, SettingsAppearanceTabComponent as cA, SettingsDialogComponent as cB, SettingsLanguageTabComponent as cC, ShareDialogComponent as cD, ShortcutPanelComponent as cE, ShowOptionsFieldsetComponent as cF, ShowSlidesFieldsetComponent as cG, SignatureStrippedDialogComponent as cH, SignaturesPanelComponent as cI, SignaturesService as cJ, SlideBackgroundCardComponent as cK, SlideCanvasComponent as cL, SlideDefaultInspectorComponent as cM, SlideDiffChangesComponent as cN, SlideDiffRowComponent as cO, SlideDiffThumbnailsComponent as cP, SlideSizeCardComponent as cQ, SlideSorterOverlayComponent as cR, SlideThemeOverridePanelComponent as cS, SlideTransitionCardComponent as cT, SlidesPanelComponent as cU, SmartArt3DRendererComponent as cV, SmartArt3DService as cW, SmartArtPreviewComponent as cX, SmartArtPropertiesComponent as cY, SmartArtRendererComponent as cZ, StatusBarComponent as c_, RibbonInsertFieldsComponent as ca, RibbonInsertSectionComponent as cb, RibbonMotionPathGalleryComponent as cc, RibbonParagraphControlsComponent as cd, RibbonPrimaryRowComponent as ce, RibbonReviewSectionComponent as cf, RibbonShapeExtrasComponent as cg, RibbonSlideshowSectionComponent as ch, RibbonTransitionsSectionComponent as ci, RibbonViewSectionComponent as cj, RulerGuidesService as ck, SEQUENCE_OPTIONS as cl, SEVERITY_GROUPS as cm, SEVERITY_LABELS as cn, SHORTCUT_REFERENCE_ITEMS as co, SLIDE_TRANSITION_KEYFRAMES as cp, DEFAULT_PALETTE as cq, PALETTES$1 as cr, SMART_ART_COLOR_SCHEMES as cs, SMART_ART_STYLE_OPTIONS as ct, SUB_ITEM_LABEL as cu, SVG_WARP_PRESETS as cv, SWIPE_MAX_VERTICAL_PX as cw, SWIPE_THRESHOLD_PX as cx, SelectionPaneComponent as cy, SetUpSlideShowDialogComponent as cz, AUDIENCE_NONCE_KEY as d, alignPatch as d$, TEXT_3D_BOTTOM_BEVEL_KEYS as d0, TEXT_3D_TOP_BEVEL_KEYS as d1, TEXT_DIRECTION_OPTIONS$1 as d2, THEME_CATALOG as d3, TIMING_CURVE_OPTIONS as d4, TRIGGER_OPTIONS as d5, TYPE_LABELS as d6, TableCellAdvancedFillComponent as d7, TableCellFormattingComponent as d8, TableDataEditorComponent as d9, ViewerExportService as dA, ViewerExtraDialogsComponent as dB, ViewerFileIOService as dC, ViewerFindReplaceService as dD, ViewerFormatPainterService as dE, ViewerInspectorPanelService as dF, ViewerKeyboardService as dG, ViewerMobileSheetService as dH, ViewerPresentationModeService as dI, ViewerThemeGalleryService as dJ, ViewerTouchGesturesService as dK, ViewerZoomService as dL, WEBM_MIME_CANDIDATES as dM, WriteBackScheduler as dN, ZERO_LINE_COLOR as dO, ZoomNavigationService as dP, ZoomRendererComponent as dQ, ZoomTargetService as dR, addCategory as dS, addCommentToList as dT, addGradientStopPatch as dU, addItem as dV, addSeries as dW, addSubItem as dX, advanceStep as dY, affordanceElements as dZ, aiToggleVisible as d_, TablePropertiesComponent as da, TableRendererComponent as db, TableResizeOverlayComponent as dc, TableSelectionService as dd, TagsCardComponent as de, Text3DBevelSectionComponent as df, Text3DPanelComponent as dg, TextAdvancedPanelComponent as dh, ThemeEditorFieldsComponent as di, ThemeGalleryComponent as dj, ThemeSelectorCardComponent as dk, TitleBarComponent as dl, TitleBarSearchComponent as dm, TransitionDirectionPickerComponent as dn, TransitionPreviewComponent as dp, VALIGN_OPTIONS as dq, VIEWER_THEME as dr, VersionHistoryPanelComponent as ds, ViewerCanvasEditingService as dt, ViewerCollabCursorService as du, ViewerCollaborationSessionService as dv, ViewerCompareService as dw, ViewerCustomShowsService as dx, ViewerDialogsService as dy, ViewerDocumentPropertiesService as dz, AVATAR_COLOR_SWATCHES as e, buildStockViewModel as e$, animationFor as e0, animationPresetLabelKey as e1, annotationMapToInkInserts as e2, applyAcceptedDiff as e3, applyAnimationPreset as e4, applyFindReplacements as e5, applyFormatToElement as e6, applyMove as e7, applyResize as e8, asMediaElement as e9, buildEmbeddedFontStyles as eA, buildEquationElement as eB, buildEquationSegment as eC, buildFallbackViewModel as eD, buildFontFaceRule as eE, buildGradientFillCss as eF, buildGridlinesAndLabels as eG, buildHyperlinkPatch as eH, buildInkContainerStyle as eI, buildInkStrokes as eJ, buildLegend as eK, buildLiveInkStrokeView as eL, buildMarkTooltip as eM, buildModel3DContainerStyle as eN, buildModel3DViewModel as eO, buildOleActionModel as eP, buildOleInfoRows as eQ, buildPatternFillCss as eR, buildPieViewModel as eS, buildPrintHtmlDocument as eT, buildPropertiesPatch as eU, buildRadarViewModel as eV, buildRegionMapViewModel as eW, buildSaveSlides as eX, buildShareUrl as eY, buildSmartArtInsertElement as eZ, buildSmartArtNodes as e_, assignUserColor as ea, attachShowVisibilityPause as eb, attachTouchGestures as ec, axisTickValues as ed, beginNodeEdit as ee, bevelSizePatch as ef, boolFromEvent as eg, bringForward as eh, bringToFront as ei, buildBarActions as ej, buildBroadcastConfig as ek, buildBroadcastViewerUrl as el, buildCategoryLabels as em, buildCellParagraphs as en, buildChartViewModel as eo, buildChatLogExport as ep, buildChatLogMarkdown as eq, buildChromeStyle as er, buildClearHyperlinkPatch as es, buildClickGroups as et, buildColStyles as eu, buildCollaborationConfig as ev, buildComboViewModel as ew, buildCssGradientFromShapeStyle as ex, buildDuotoneFilter as ey, buildDuotoneFilterId as ez, AXIS_LABEL_COLOR as f, computePlotLayout as f$, buildSurfaceViewModel as f0, buildTableViewModel as f1, buildTreemapViewModel as f2, buildTrimFragment as f3, buildWaterfallViewModel as f4, buildZeroLine as f5, buildZoomContainerStyle as f6, buildZoomViewModel as f7, bulletIndentPx as f8, canAddTopLevelNode as f9, collectAccessibilityIssues as fA, collectElementText as fB, collectSlideText as fC, collectStoredChats as fD, collectUsedFontFamilies as fE, columnWidthStyle as fF, commitNodeText as fG, computeAlign as fH, computeAxisTitlePrimitives as fI, computeBarRects as fJ, computeBubbleRadius as fK, computeCornerHandle as fL, computeDistribute as fM, computeDrawingViewBox as fN, computeErrorBarPrimitives as fO, computeFocusTargets as fP, computeGridSpacingPx as fQ, computeHandleBoxes as fR, computeHandoutLayout as fS, computeIsMobile as fT, computeIsTablet as fU, computeLinePoints as fV, computeLinearRegression as fW, computePageCount as fX, computePieLayout as fY, computePieSlicePath as fZ, computePieSlices as f_, canEditSmartArtNodes as fa, canGroupSelection as fb, canRemoveTopLevelNode as fc, canSetStrokeWidth as fd, canStartBroadcast as fe, canStartShare as ff, canUngroupSelection as fg, canUseClipboard as fh, captionDisplayText as fi, cellRunStyle as fj, cellStyleToStyleMap as fk, cellTdStyle as fl, changeCountLabel as fm, changeIcon as fn, characterSpacingPatch as fo, chartPreserveAspectRatio as fp, checkFontAvailable as fq, clampCursorPosition as fr, clampGifDimensions as fs, clampIndex as ft, clampNotesFontSize as fu, clampScale as fv, clampStep as fw, clearAllLocalViewerData as fx, clearAudienceContent as fy, cn as fz, AccessibilityPanelComponent as g, focusTargetChips as g$, computeRSquared as g0, computeRadarPoints as g1, computeResizeHandleBoxes as g2, computeRotateHandleBox as g3, computeScatterDots as g4, computeScatterXDomain as g5, computeSelectionBoxes as g6, computeSingleSelected as g7, computeSlideIndices as g8, computeSnap as g9, disableGlowPatch as gA, disableInnerShadowPatch as gB, disableOuterShadowPatch as gC, disableReflectionPatch as gD, disableSoftEdgePatch as gE, duplicateElementById as gF, durationOf as gG, effectsStateOf as gH, enableGlowPatch as gI, enableInnerShadowPatch as gJ, enableOuterShadowPatch as gK, enableReflectionPatch as gL, enableSoftEdgePatch as gM, encodeGif as gN, endShowMediaCleanup as gO, estimatePageCount as gP, exitPresentationFullscreen as gQ, exportAiChatLogs as gR, extractPathPoints as gS, eyedropperAvailable as gT, fillColorOf$1 as gU, findInSlides as gV, findOwningSlideIndex as gW, findSlideIndexByElementId as gX, firstVisibleIndex as gY, fitPolynomial as gZ, fitZoom as g_, computeStackedBarRects as ga, computeStackedValueRange as gb, computeTrendlinePrimitives as gc, computeValueRange as gd, convertOmmlToMathMl as ge, copyFormatFromElement as gf, countAccessibilityIssues as gg, countAnnotationStrokes as gh, createAngularAiBridge as gi, createCustomShow as gj, createSwipeDismissDrag as gk, createWebrtcBundle as gl, createWebsocketBundle as gm, cssObjectToStyleMap as gn, currentColorScheme as go, currentLayout as gp, currentStyle as gq, defaultCssVars as gr, defaultRadius as gs, defaultThemeColors as gt, deleteElementsByIds as gu, deleteVersion as gv, demoteNode as gw, deriveModel3DBlobUrl as gx, derivePresenceList as gy, describeSmartArtBounds as gz, AccessibilityService as h, insertTableElementRow as h$, fontMimeForFormat as h0, fontSizeOf as h1, forgetSessionDeck as h2, formatAxisValue as h3, formatBytes as h4, formatCursorLabel as h5, formatElapsed as h6, formatFileSize as h7, formatPropertyDate as h8, formatTime as h9, getShapeFillStrokeStyle as hA, getSlideBackgroundStyle as hB, getSlideTransitionAnimations as hC, getSmartArtNodeBounds as hD, getSpeechRecognitionCtor as hE, getTextBlockStyle as hF, getTextWarp as hG, getTouchDistance as hH, getWarpCategory as hI, getWarpPath as hJ, gradientStateFromStyle as hK, gradientStateOf as hL, gradientStatePatch as hM, gradientStopColorCommitPatch as hN, gridColumns as hO, groupIssuesBySeverity as hP, hasAnimation as hQ, hasCopyableFormat as hR, hasExistingLink as hS, hasExitedFullscreen as hT, hasGradientFill as hU, hasPressureVariation as hV, hasVisibleSlideAfter as hW, headerLabel as hX, imageDimensions as hY, inkViewBox as hZ, insertTableElementColumn as h_, fpsToFrameIntervalMs as ha, generateBroadcastRoomId as hb, generateCommentId as hc, generateCustomShowId as hd, generatePressureCircles as he, generateTicks as hf, getClrChangeParams as hg, getContainerStyle as hh, getDuotoneFilterDef as hi, getEffectSoundAsset as hj, getEffectSoundState as hk, getImageSrc as hl, getLocalStorageUsageSummary as hm, getOleAriaLabel as hn, getOleBadgeLabel as ho, getOleDisplayName as hp, getOleDownloadFileName as hq, getOleTypeColor as hr, getOleTypeLabel as hs, getPasswordStrength as ht, getPatternSvg as hu, getPlaceholderStyle as hv, getVersions as hw, getResolvedShapeClipPath as hx, getResolvedShapeClipPathFor as hy, getSessionTabId as hz, AccessibilityTextPanelComponent as i, normalizeSlidesPerPage as i$, interpolateWidth as i0, isAudienceTab as i1, isBold as i2, isBrowserOpenableMime as i3, isChildNode as i4, isElementInteractive as i5, isInjectableUrl as i6, isItalic as i7, isLegacyBinaryPresentation as i8, isPpactionUrl as i9, mergeDown as iA, mergeRight as iB, mergeSelection as iC, mergeTablesDirective as iD, moveElementBy as iE, moveNodeDown as iF, moveNodeUp as iG, msToFrameDelayCs as iH, narrowToCircle as iI, narrowToPolygon as iJ, narrowToRect as iK, newChartElement as iL, newEquationElement as iM, newPresetShapeElement as iN, newShapeElement as iO, newSmartArtElement as iP, newTableElement as iQ, newTextElement as iR, nextVisibleIndex as iS, nodeBold as iT, nodeEditBox as iU, nodeFillColor as iV, nodeFontColor as iW, nodeIdFromKey as iX, nodeItalic as iY, nodeStyle as iZ, normalizeFontFormat as i_, isPresenterMessage as ia, isSigned as ib, isSupportedPresentationFile as ic, isTextElement as id, isTwoTableFocus as ie, isUnderline as ig, isUrlSafe as ih, isValidRoomId as ii, isViewportBackgroundPressTarget as ij, isZoomActivationKey as ik, issueTrackKey as il, issueTypeLabel as im, keyToLabel as io, lastVisibleIndex as ip, latexToMathml as iq, layoutConnectorPaints as ir, layoutNodeLabels as is, linePointsToSvgString as it, lineSpacingPatch as iu, loadAudienceContent as iv, loadSessionDeck as iw, mediaFallbackFor as ix, mediaSurfaceFor as iy, mergeCaptionResults as iz, AccountPageComponent as j, resolveParagraphBullet as j$, normalizeValue as j0, numFromEvent as j1, ommlToMathml as j2, ooxmlDashToCssBorderStyle as j3, openNativeEyeDropper as j4, overallStatus as j5, paletteColor as j6, parseAudienceNonce as j7, parseNodeTextarea as j8, partitionSlides as j9, readAsDataUrl as jA, recordWebm as jB, registerCrossSlideAudio as jC, rememberSessionDeck as jD, removeAnimation as jE, removeCategory as jF, removeTableElementColumn as jG, removeCommentFromList as jH, removeElementAnimation as jI, removeGradientStopPatch as jJ, removeNode as jK, removeTableElementRow as jL, removeSeries as jM, renderToCanvas as jN, reorderAnimationDown as jO, reorderAnimationUp as jP, replaceInSlides as jQ, replaceMatch as jR, requestPresentationFullscreen as jS, resizeElement as jT, resolveCaptionTracks as jU, resolveChartKind as jV, resolveFontVariant as jW, resolveHyperlinkHref as jX, resolveInteractiveElementId as jY, resolveMediaSrc as jZ, resolveOleType as j_, patchChartData as ja, patchChartStyle as jb, patchTableData as jc, patchTextStyle as jd, patternPresetOptions as je, pendingElementStyles as jf, pickColorByClickFallback as jg, pickFile as jh, pickSupportedMimeType as ji, planGifFrames as jj, planVideoSegments as jk, pointFromPointerEvent as jl, pointsToSvgPathD as jm, presenceToCursors as jn, presentationBaseName as jo, presentationStageStyle as jp, presenterTimerProgress as jq, presetByLayout as jr, presetsForCategory as js, pressuresToWidths as jt, prevVisibleIndex as ju, projectDrawingShapes as jv, promoteNode as jw, provideViewerTheme as jx, radarAngle as jy, radarRingPoints as jz, ActionSettingsPanelComponent as k, setSequence as k$, resolvePresenterNotes as k0, resolveProfileInitial as k1, resolveRegionCode as k2, resolveRibbonCanGroup as k3, resolveSlideAutoAdvanceMs as k4, resolvePalette as k5, resolveThemeCatalogEntry as k6, resolveTransitionDuration as k7, restoreSessionDeck as k8, revealedElementStyles as k9, setAnimationEmphasis as kA, setAnimationEntrance as kB, setAnimationExit as kC, setAxis as kD, setAxisLogScale as kE, setAxisTitleStyle as kF, setCategoryLabel as kG, setCellText as kH, setColorScheme as kI, setDataLabels as kJ, setDataPointExplosion as kK, setDataPointFill as kL, setDataPointLabel as kM, setDataPointMarker as kN, setDelay as kO, setDirection as kP, setDuration as kQ, setEffectSound as kR, setEffectStockSound as kS, setElementPosition as kT, setGridlineStyle as kU, setLayout as kV, setLegend as kW, setNodeStyle as kX, setNodeText as kY, setRepeatCount as kZ, setRepeatMode as k_, routeOrthogonalConnector as ka, rowStyle as kb, rulerDragToGuidePosition as kc, rulerHighlight as kd, rulerStripTicks as ke, sampleColorFromSlide as kf, sanitizeColor as kg, sanitizeSlideIndex as kh, sanitizeUserName as ki, saveViewerProfile as kj, savedPresentationFileName as kk, scanAvailableFonts as kl, searchSlides as km, seedBroadcastFields as kn, seedHyperlinkDraft as ko, seedPropertiesDraft as kp, seedShareFields as kq, segmentFrameCount as kr, selectValue$3 as ks, sendBackward as kt, sendToBack as ku, sequentialColorScale as kv, serializeWriteBack as kw, seriesColor as kx, setAfterAnimation as ky, setAfterAnimationColor as kz, AdvancedChartEditorComponent as l, updateReflectionPatch as l$, setSeriesChartType as l0, setSeriesColor as l1, setSeriesErrorBars as l2, setSeriesMarker as l3, setSeriesName as l4, setSeriesTrendline as l5, setSeriesValue as l6, setStyle as l7, setTimingCurve as l8, setTitle as l9, strokeWidthOf as lA, styleShadowFilter as lB, surfaceColor as lC, textAdvancedPatch as lD, textAdvancedStateFromStyle as lE, textAdvancedStateOf as lF, textColorOf as lG, textDirectionPatch as lH, textFontSizePatch as lI, textStyleOf as lJ, textStylePatch as lK, themeStyle as lL, themeToCssVars as lM, thumbnailHeight as lN, thumbnailZoom as lO, toggleCommentResolvedInList as lP, toggleNodeBold as lQ, toggleNodeItalic as lR, toggleSheet as lS, topLevelNodeCount as lT, transformSelectedTextCase as lU, translationsEn as lV, updateElementById as lW, updateGlowPatch as lX, updateGradientStopPatch as lY, updateInnerShadowPatch as lZ, updateOuterShadowPatch as l_, setTrigger as la, setTriggerShapeId as lb, shapeStylePatch$1 as lc, sheetAfterNavigate as ld, shouldBlockClickAdvance as le, shouldUseSvgWarp as lf, showDirectionPicker as lg, showsTemplateAffordance as lh, signatureCountLabel as li, signatureKey as lj, signatureTimestamp as lk, signerName as ll, statusLabel as lm, slideNumberOf as ln, slidesWithReappliedLayout as lo, smartArtNodes as lp, paletteColour as lq, snapToGridStep as lr, splitCursorCell as ls, splitMergedCell as lt, statusKind as lu, statusLabel$1 as lv, storeAudienceContent as lw, stringFromEvent$5 as lx, strokeColorOf as ly, strokeToInkElement as lz, AiChangeOverlayComponent as m, vAlignPatch as m0, validatePassword as m1, validatePrintSettings as m2, validateRoomId as m3, valueToY as m4, vermilionDarkColors as m5, vermilionDarkTheme as m6, vermilionLightColors as m7, vermilionLightTheme as m8, vermilionRadius as m9, waypointsToPathD as ma, withManualLayouts as mb, worstStatus as mc, zoomTargetSlideIndex as md, AiChatPanelComponent as n, AiChatService as o, AiComposerComponent as p, AiFocusBarComponent as q, AiFocusHighlightOverlayComponent as r, AiHistoryMenuComponent as s, toChatSummary as t, AiHistoryService as u, AiMessageListComponent as v, AiPanelStore as w, AiProposalCardComponent as x, AiSettingsSectionComponent as y, AiToolCallCardComponent as z };
181409
- //# sourceMappingURL=pptx-angular-viewer-pptx-angular-viewer-CrlToiB1.mjs.map
181590
+ //# sourceMappingURL=pptx-angular-viewer-pptx-angular-viewer-CVv5SsHZ.mjs.map