pptx-angular-viewer 1.14.1 → 1.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.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,8 @@ All notable changes to this project are documented here.
4
4
  This file is generated from [Conventional Commits](https://www.conventionalcommits.org)
5
5
  by [git-cliff](https://git-cliff.org); do not edit it by hand.
6
6
 
7
+ ## [1.14.1](https://github.com/ChristopherVR/pptx-viewer/releases/tag/pptx-angular-viewer@1.14.1) - 2026-07-11
8
+
7
9
  ## [1.14.0](https://github.com/ChristopherVR/pptx-viewer/releases/tag/pptx-angular-viewer@1.14.0) - 2026-07-11
8
10
 
9
11
  ### Other
@@ -20766,6 +20766,51 @@ function defaultTextAdvancedState() {
20766
20766
  rtl: false,
20767
20767
  };
20768
20768
  }
20769
+ // ==========================================================================
20770
+ // Text wrap + autofit mode (standalone; not part of TextAdvancedState so
20771
+ // existing consumers of that interface are unaffected by this addition).
20772
+ // ==========================================================================
20773
+ /** Available text-wrap options (`a:bodyPr/@wrap`). */
20774
+ const TEXT_WRAP_OPTIONS = [
20775
+ ['square', 'Wrap text in shape'],
20776
+ ['none', "Don't wrap text"],
20777
+ ];
20778
+ /**
20779
+ * Available autofit-mode options. Per this codebase's {@link TextStyle.autoFitMode}
20780
+ * mapping: `'shrink'` is `a:spAutoFit` (resize the shape to fit the text) and
20781
+ * `'normal'` is `a:normAutofit` (shrink the text on overflow).
20782
+ */
20783
+ const AUTOFIT_MODE_OPTIONS = [
20784
+ ['none', 'Do not autofit'],
20785
+ ['normal', 'Shrink text on overflow'],
20786
+ ['shrink', 'Resize shape to fit text'],
20787
+ ];
20788
+ /** Read the effective text-wrap mode, defaulting to `'square'` (wrap on). */
20789
+ function textWrapOf(el) {
20790
+ if (!hasTextProperties(el)) {
20791
+ return 'square';
20792
+ }
20793
+ return el.textStyle?.textWrap ?? 'square';
20794
+ }
20795
+ /** Read the effective autofit mode, defaulting to `'none'`. */
20796
+ function autoFitModeOf(el) {
20797
+ if (!hasTextProperties(el)) {
20798
+ return 'none';
20799
+ }
20800
+ return el.textStyle?.autoFitMode ?? 'none';
20801
+ }
20802
+ /** Patch to update the text-wrap mode, preserving the rest of `textStyle`. */
20803
+ function textWrapPatch(el, wrap) {
20804
+ const base = hasTextProperties(el) ? (el.textStyle ?? {}) : {};
20805
+ return { textStyle: { ...base, textWrap: wrap } };
20806
+ }
20807
+ /** Patch to update the autofit mode, preserving the rest of `textStyle`. */
20808
+ function autoFitModePatch(el, mode) {
20809
+ const base = hasTextProperties(el) ? (el.textStyle ?? {}) : {};
20810
+ return {
20811
+ textStyle: { ...base, autoFitMode: mode, autoFit: mode !== 'none' },
20812
+ };
20813
+ }
20769
20814
 
20770
20815
  /**
20771
20816
  * PPTX document-theme font + colour resolution helpers.
@@ -31881,6 +31926,155 @@ function sanitizeStops(raw) {
31881
31926
  return sortStops(mapped);
31882
31927
  }
31883
31928
 
31929
+ /**
31930
+ * Pure (framework-agnostic) helpers for the image adjustments + crop inspector
31931
+ * panel. Readers extract current values from a PptxElement (falling back to
31932
+ * sensible defaults); patch-builders produce shallow-merge-ready
31933
+ * `Partial<PptxElement>` objects safe to pass to each binding's element-update
31934
+ * action. Mirrors the pattern used by `gradient-picker.ts` / `text-advanced.ts`.
31935
+ *
31936
+ * Scope: brightness/contrast/saturation (`PptxImageEffects`) and the four crop
31937
+ * insets (`PptxImageProperties.cropLeft/Top/Right/Bottom`). Other image effects
31938
+ * (duotone, artistic filters, alpha primitives) are out of scope for the
31939
+ * inspector's "highest-traffic" surface; see `image-effects.ts` for the full
31940
+ * read-side effect model.
31941
+ */
31942
+ /** Read the current brightness/contrast/saturation off an element (0 when unset). */
31943
+ function imageAdjustmentsStateOf(el) {
31944
+ const fx = isImageLikeElement(el) ? el.imageEffects : undefined;
31945
+ return {
31946
+ brightness: fx?.brightness ?? 0,
31947
+ contrast: fx?.contrast ?? 0,
31948
+ saturation: fx?.saturation ?? 0,
31949
+ };
31950
+ }
31951
+ /**
31952
+ * Build a Partial<PptxElement> that merges the given adjustment changes into
31953
+ * the element's existing `imageEffects` without dropping other effect fields.
31954
+ * No-op (returns `{}`) for non-image elements.
31955
+ */
31956
+ function imageAdjustmentsPatch(el, changes) {
31957
+ if (!isImageLikeElement(el)) {
31958
+ return {};
31959
+ }
31960
+ return {
31961
+ imageEffects: {
31962
+ ...el.imageEffects,
31963
+ ...changes,
31964
+ },
31965
+ };
31966
+ }
31967
+ /** Read the current crop insets off an element (0 on every edge when unset). */
31968
+ function imageCropStateOf(el) {
31969
+ if (!isImageLikeElement(el)) {
31970
+ return { cropLeft: 0, cropTop: 0, cropRight: 0, cropBottom: 0 };
31971
+ }
31972
+ return {
31973
+ cropLeft: el.cropLeft ?? 0,
31974
+ cropTop: el.cropTop ?? 0,
31975
+ cropRight: el.cropRight ?? 0,
31976
+ cropBottom: el.cropBottom ?? 0,
31977
+ };
31978
+ }
31979
+ /** Clamp a crop fraction to the sane `[0, 0.9]` range (never crop past 90% of an edge). */
31980
+ function clampCropFraction(value) {
31981
+ if (!Number.isFinite(value)) {
31982
+ return 0;
31983
+ }
31984
+ return Math.min(0.9, Math.max(0, value));
31985
+ }
31986
+ /**
31987
+ * Build a Partial<PptxElement> that merges the given crop-inset changes onto
31988
+ * the element. No-op (returns `{}`) for non-image elements.
31989
+ */
31990
+ function imageCropPatch(el, changes) {
31991
+ if (!isImageLikeElement(el)) {
31992
+ return {};
31993
+ }
31994
+ const patch = {};
31995
+ for (const [key, value] of Object.entries(changes)) {
31996
+ if (value !== undefined) {
31997
+ patch[key] = clampCropFraction(value);
31998
+ }
31999
+ }
32000
+ return patch;
32001
+ }
32002
+
32003
+ /**
32004
+ * Pure (framework-agnostic) helpers for the table-level inspector panel
32005
+ * (header row / banded rows toggles + a uniform default cell padding). Mirrors
32006
+ * the reader + patch-builder pattern used by `gradient-picker.ts` /
32007
+ * `text-advanced.ts` / `image-adjustments.ts`.
32008
+ *
32009
+ * Scope note: this binding has no per-cell selection model, so cell-level
32010
+ * formatting (background/border for one specific cell) is out of scope here;
32011
+ * `applyUniformCellPaddingPatch` instead writes the same margin onto every
32012
+ * cell uniformly, which is the closest table-level equivalent.
32013
+ */
32014
+ /** Read table-level flags off a table element (all false/0 for non-table elements). */
32015
+ function tableInspectorStateOf(el) {
32016
+ if (el.type !== 'table' || !el.tableData) {
32017
+ return { firstRowHeader: false, bandedRows: false, bandedColumns: false, cellPadding: 0 };
32018
+ }
32019
+ const data = el.tableData;
32020
+ return {
32021
+ firstRowHeader: data.firstRowHeader ?? false,
32022
+ bandedRows: data.bandedRows ?? false,
32023
+ bandedColumns: data.bandedColumns ?? false,
32024
+ cellPadding: firstCellPadding(data),
32025
+ };
32026
+ }
32027
+ function firstCellPadding(data) {
32028
+ const firstCell = data.rows[0]?.cells[0];
32029
+ return firstCell?.style?.marginLeft ?? 0;
32030
+ }
32031
+ /**
32032
+ * Build a Partial<PptxElement> that merges the given flag changes into the
32033
+ * element's existing `tableData`. No-op (returns `{}`) for non-table elements
32034
+ * or a table with no parsed data.
32035
+ */
32036
+ function tableInspectorPatch(el, changes) {
32037
+ if (el.type !== 'table' || !el.tableData) {
32038
+ return {};
32039
+ }
32040
+ return {
32041
+ tableData: {
32042
+ ...el.tableData,
32043
+ ...changes,
32044
+ },
32045
+ };
32046
+ }
32047
+ /**
32048
+ * Build a Partial<PptxElement> that sets the same left/right/top/bottom margin
32049
+ * (in px) on every cell of every row, so the table gets uniform default
32050
+ * padding. No-op (returns `{}`) for non-table elements or a table with no rows.
32051
+ */
32052
+ function applyUniformCellPaddingPatch(el, padding) {
32053
+ if (el.type !== 'table' || !el.tableData) {
32054
+ return {};
32055
+ }
32056
+ const clamped = Math.max(0, Math.round(padding));
32057
+ const rows = el.tableData.rows.map((row) => ({
32058
+ ...row,
32059
+ cells: row.cells.map((cell) => ({
32060
+ ...cell,
32061
+ style: {
32062
+ ...cell.style,
32063
+ marginLeft: clamped,
32064
+ marginRight: clamped,
32065
+ marginTop: clamped,
32066
+ marginBottom: clamped,
32067
+ },
32068
+ })),
32069
+ }));
32070
+ return {
32071
+ tableData: {
32072
+ ...el.tableData,
32073
+ rows,
32074
+ },
32075
+ };
32076
+ }
32077
+
31884
32078
  /**
31885
32079
  * Pure, framework-agnostic comment-array transforms, shared by every binding.
31886
32080
  *
@@ -40044,6 +40238,7 @@ const translationsEn = {
40044
40238
  'pptx.table.bandRowCycle': 'Row band size',
40045
40239
  'pptx.table.cell': 'Cell R{{row}} C{{col}}',
40046
40240
  'pptx.table.cellBorders': 'Cell borders',
40241
+ 'pptx.table.cellPadding': 'Cell padding',
40047
40242
  'pptx.table.color': 'Text',
40048
40243
  'pptx.table.columnWidths': 'Column widths',
40049
40244
  'pptx.table.even': 'Even',
@@ -40585,6 +40780,9 @@ const translationsEn = {
40585
40780
  'pptx.ribbon.strokeWidth': 'Stroke width',
40586
40781
  'pptx.ribbon.browseThemes': 'Browse Themes',
40587
40782
  'pptx.ribbon.browseThemesTitle': 'Browse and apply built-in themes',
40783
+ 'pptx.ribbon.theme.default': 'Default',
40784
+ 'pptx.ribbon.theme.light': 'Light (Vermilion)',
40785
+ 'pptx.ribbon.theme.dark': 'Dark (Vermilion)',
40588
40786
  'pptx.ribbon.editTheme': 'Edit Theme',
40589
40787
  'pptx.ribbon.editThemeTitle': 'Edit theme - theme editor not yet ported',
40590
40788
  'pptx.ribbon.slideSize': 'Slide Size',
@@ -41337,6 +41535,10 @@ const translationsEn = {
41337
41535
  'pptx.text.characterSpacingTight': 'Tight',
41338
41536
  'pptx.text.characterSpacingVeryLoose': 'Very Loose',
41339
41537
  'pptx.text.characterSpacingVeryTight': 'Very Tight',
41538
+ 'pptx.textAdvanced.autoFit': 'AutoFit',
41539
+ 'pptx.textAdvanced.autoFitNone': 'Do not autofit',
41540
+ 'pptx.textAdvanced.autoFitResize': 'Resize shape to fit text',
41541
+ 'pptx.textAdvanced.autoFitShrink': 'Shrink text on overflow',
41340
41542
  'pptx.textAdvanced.autoPlaceholder': 'auto',
41341
41543
  'pptx.textAdvanced.indent': 'Indent',
41342
41544
  'pptx.textAdvanced.indentMargin': 'Indent & Margin',
@@ -41350,6 +41552,7 @@ const translationsEn = {
41350
41552
  'pptx.textAdvanced.spaceBefore': 'Space Before',
41351
41553
  'pptx.textAdvanced.spacing': 'Spacing',
41352
41554
  'pptx.textAdvanced.vAlign': 'V-Align',
41555
+ 'pptx.textAdvanced.wrapText': 'Wrap text in shape',
41353
41556
  'pptx.textFormatting.bulleted': 'Bulleted',
41354
41557
  'pptx.textFormatting.numbered': 'Numbered',
41355
41558
  'pptx.textFormatting.paragraphAfter': 'Paragraph After',