发布

  • fix(importer): convert Equation.3 OLE formulas via MTEF v3, surface degrade telemetry (#1411)

    frostbyte_neo 发布于 2026-09-11 11:09:46 +00:00

    • fix(importer): convert Equation.3 OLE formulas via MTEF v3, surface degrade telemetry

    Legacy courseware stores formulas as Equation 3.0 / MathType OLE objects
    whose only renderable form inside the .pptx is a WMF preview picture.
    The importer cannot rasterize WMF, so those formulas degraded to a
    hardcoded 1x1 "transparent" placeholder — actually a 50%-alpha red
    pixel that rendered as a pink block once stretched over the formula
    frame, with no way for callers to notice the content loss.

    • Convert Equation.3 / MathType OLE objects to LaTeX: detect the
      progId, resolve the embedding, parse the OLE compound file (cfb) and
      convert its Equation Native MTEF v3 stream (new utils/mtef.ts) —
      fractions, radicals, scripts, fences, big operators with per-family
      limit variations, embellishments, and Symbol-font local character
      encodings. Slot order follows the rtf2latex2e reference
      implementation and real MathType streams ([main, lower, upper]), not
      the archived spec prose. Any failure falls back to the picture path.
    • Fix the placeholder constant to a truly transparent pixel; keep
      recognizing the legacy red one (exported isPlaceholderDataUrl).
    • Surface degradation instead of failing silently: optional
      ImportPptxOptions.onWarning receives machine-coded warnings
      (media-unconvertible / formula-fallback-image / formula-degraded /
      element-dropped) at every placeholder consumption point (image,
      background, shape/text pattern fills, math fallback); a throwing
      sink is isolated so telemetry can never fail an import. A formula
      whose fallback picture is also a placeholder keeps its plain text as
      a text element.
    • fix(importer): address review — LSCRIPT base duplication, one-sided fences, Symbol table, depth caps

    Review round on #1411 (thanks @wyuc — the fuzz safety-net validation and the
    adversarial constructions found what the spec-conformance rounds could not):

    • tmLSCRIPT: stop re-emitting a script slot as the base group (isotopes
      rendered as {}{6}^{12}{12}C and the output doubled per nesting level —
      a 221-byte stream could OOM the worker); the base is the following
      sibling, so the template emits only {}
      {sub}^{sup}, and a leading script
      no longer steals the previous atom via the trailing-script lookahead.
      tvLSUPER writers that emit a single slot now fall back to it.
    • One-sided fences: render the missing side as a null delimiter
      (\left. / \right.) instead of an unmatched \left — piecewise-function
      braces (tmBRACE var 1) produced KaTeX-invalid output that silently
      degraded to flat text.
    • SYMBOL_FONT_LATEX corrected against URW StandardSymbolsPS AFM + Adobe
      AGL: 0x3C/0x3E/0x5B/0x5D are literal < > [ ] (≤/≥ live at 0xA3/0xB3,
      now mapped, along with the rest of the Symbol operator block);
      0x22/0x24/0x5C are ∀/∃/∴; added Chi/vartheta/varsigma, the phi/varphi
      split (0x66/0x6A), and 0x5E = \perp. Unmapped font-local bytes >= 0xA0
      now flag degraded instead of passing silently.
    • tmLIM: variation roles were inverted — spec + rtf2latex2e eqn.c say
      0 = upper limit, 1 = lower limit; single-limit writers keep their limit
      via the same slot fallback as the big operators.
    • Hardening: MAX_DEPTH = 200 nesting cap and a 64 KiB LaTeX output cap,
      both throwing MtefParseError (deep bombs now fail loud instead of
      RangeError/OOM); video poster joined the placeholder warning points.
    • Tests: +6 — three REAL Equation Native stream fixtures (round-tripped
      from a legacy deck, pinning the font-local encoding and big-op slot
      order), tvLSUPER single-slot, tmLIM both roles, script-nesting perf
      guard; fence tests now assert KaTeX renderability instead of pinning
      broken strings. Suite: 85.
    • Version 0.1.5 -> 0.2.0 (new public option/type/export + Math.degraded).
      Lockfile re-anchored on main with pnpm@10.28.0: only the cfb additions
      remain, no unrelated churn.
    • fix(importer): address review round 2 — tmLIM function slot, full Symbol high-half, bra/ket, cap tests
    • tmLIM: emit the main slot FIRST followed by the limits and inject no
      operator name (the reference 39.1 = limit: lower, #1 #2 puts the
      function in the main slot — hardcoding \lim duplicated it and glued to
      letter-leading main slots, crashing KaTeX with an undefined control
      sequence). An empty main slot falls back to \lim as a neutral base.
    • SYMBOL_FONT_LATEX: completed the 0xA0–0xFF block from the URW AFM
      (~70 positions: ∫ ∑ ∏ ⟨⟩ ∂ ∇ ⇒ ⇔ ⋅ ′ ∅ ⊆ ⊇ ∈ ∉ ∪ ∩ …). Unmapped
      font-local codes now throw MtefParseError instead of passing through
      as Latin-1 (0xF7 was an integral extender rendering as ÷ — plausible
      but wrong math); radicalex (0x60) and C1 controls (0x80–0x9F) also
      throw, taking the picture fallback.
    • tmDIRAC: var1 renders a bra \left\langle L\right|, var2 a ket
      \left| R\right\rangle per the reference (was wrapping both sides).
    • Embellishment records now count against the record budget (a 10 MB
      embellishment-only stream no longer allocates 1 GB before the output
      cap fires).
    • Tests: +6 — every cap now has its own assertion (output-length width
      case at 15k sibling CHARs, record-count at 20k, in addition to the
      existing depth test), previously-dangerous Symbol codes verified from
      the AFM, unmapped-code rejection, embellishment budget, tmDIRAC
      KaTeX-validity for all variations. Fixture claims corrected (no
      big-op selector in the three real streams; the reading is pinned by
      the spec-conformance tests). Suite: 91.
    • test(importer): pin 0xD6/0xF3 Symbol glyphs through KaTeX, fix big-op test title

    Follow-up to the round-3 review: the two glyph fixes landed without a
    test, the BigOp test title still named the wrong slot order, and the
    table comment claimed the high half was complete while unlisted
    positions intentionally throw.

    Co-Authored-By: Claude Fable 5.1 noreply@anthropic.com

    • docs(importer): correct Symbol table comments — 0xF7 is parenrightex not an integral extender, high-half coverage is ~50 of ~70 positions

    Co-authored-by: Percy percy@PercydeMacBook-Pro.local
    Co-authored-by: wyuc wang-yc24@mails.tsinghua.edu.cn
    Co-authored-by: Claude Fable 5.1 noreply@anthropic.com

    下载附件