@aixa-transformation/pptx-viewer 2.0.24 → 2.0.27

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.
@@ -16450,6 +16450,7 @@ function useSerialize(input) {
16450
16450
  tagCollections,
16451
16451
  notesMaster,
16452
16452
  handoutMaster,
16453
+ embeddedFonts,
16453
16454
  handlerRef,
16454
16455
  inlineEditingElementIdRef,
16455
16456
  inlineEditingTextRef,
@@ -16503,7 +16504,8 @@ function useSerialize(input) {
16503
16504
  customProperties: customProperties.length > 0 ? customProperties : void 0,
16504
16505
  tags: tagCollections.length > 0 ? tagCollections : void 0,
16505
16506
  notesMaster,
16506
- handoutMaster
16507
+ handoutMaster,
16508
+ embeddedFonts: embeddedFonts.length > 0 ? embeddedFonts : void 0
16507
16509
  };
16508
16510
  if (password) {
16509
16511
  return handler.saveEncrypted(slidesToSave, password, saveOptions);
@@ -16522,6 +16524,7 @@ function useSerialize(input) {
16522
16524
  tagCollections,
16523
16525
  notesMaster,
16524
16526
  handoutMaster,
16527
+ embeddedFonts,
16525
16528
  guides,
16526
16529
  activeSlideIndex,
16527
16530
  handlerRef,
@@ -17256,6 +17259,7 @@ function useContentLifecycle(input) {
17256
17259
  tagCollections: state.tagCollections,
17257
17260
  notesMaster: state.notesMaster,
17258
17261
  handoutMaster: state.handoutMaster,
17262
+ embeddedFonts: state.embeddedFonts,
17259
17263
  handlerRef,
17260
17264
  inlineEditingElementIdRef: state.inlineEditingElementIdRef,
17261
17265
  inlineEditingTextRef: state.inlineEditingTextRef,
@@ -17742,7 +17746,7 @@ function useViewerIntegration(input) {
17742
17746
  if (onDirtyChange) {
17743
17747
  onDirtyChange(state.isDirty);
17744
17748
  }
17745
- }, [state.isDirty, onDirtyChange]);
17749
+ }, [state.isDirty, state.slides, state.embeddedFonts, onDirtyChange]);
17746
17750
  React4.useEffect(() => {
17747
17751
  if (onActiveSlideChange) {
17748
17752
  onActiveSlideChange(activeSlideIndex);
@@ -16443,6 +16443,7 @@ function useSerialize(input) {
16443
16443
  tagCollections,
16444
16444
  notesMaster,
16445
16445
  handoutMaster,
16446
+ embeddedFonts,
16446
16447
  handlerRef,
16447
16448
  inlineEditingElementIdRef,
16448
16449
  inlineEditingTextRef,
@@ -16496,7 +16497,8 @@ function useSerialize(input) {
16496
16497
  customProperties: customProperties.length > 0 ? customProperties : void 0,
16497
16498
  tags: tagCollections.length > 0 ? tagCollections : void 0,
16498
16499
  notesMaster,
16499
- handoutMaster
16500
+ handoutMaster,
16501
+ embeddedFonts: embeddedFonts.length > 0 ? embeddedFonts : void 0
16500
16502
  };
16501
16503
  if (password) {
16502
16504
  return handler.saveEncrypted(slidesToSave, password, saveOptions);
@@ -16515,6 +16517,7 @@ function useSerialize(input) {
16515
16517
  tagCollections,
16516
16518
  notesMaster,
16517
16519
  handoutMaster,
16520
+ embeddedFonts,
16518
16521
  guides,
16519
16522
  activeSlideIndex,
16520
16523
  handlerRef,
@@ -17249,6 +17252,7 @@ function useContentLifecycle(input) {
17249
17252
  tagCollections: state.tagCollections,
17250
17253
  notesMaster: state.notesMaster,
17251
17254
  handoutMaster: state.handoutMaster,
17255
+ embeddedFonts: state.embeddedFonts,
17252
17256
  handlerRef,
17253
17257
  inlineEditingElementIdRef: state.inlineEditingElementIdRef,
17254
17258
  inlineEditingTextRef: state.inlineEditingTextRef,
@@ -17735,7 +17739,7 @@ function useViewerIntegration(input) {
17735
17739
  if (onDirtyChange) {
17736
17740
  onDirtyChange(state.isDirty);
17737
17741
  }
17738
- }, [state.isDirty, onDirtyChange]);
17742
+ }, [state.isDirty, state.slides, state.embeddedFonts, onDirtyChange]);
17739
17743
  useEffect(() => {
17740
17744
  if (onActiveSlideChange) {
17741
17745
  onActiveSlideChange(activeSlideIndex);
@@ -1,4 +1,4 @@
1
- import { DEFAULT_STROKE_COLOR, getConnectorPathGeometry, buildLineShadowCss, buildLineGlowFilter, renderConnectorMarker, MIN_ELEMENT_SIZE, getShapeVisualStyle, getTextStyleForElement, DEFAULT_TEXT_COLOR, renderVectorShape, isConnectorOrLineElement, build3DExtrusionData, getImageRenderStyle, cn, getTextLayoutStyle, renderTableElement, renderMediaElement, shouldRenderFallbackLabel, SHAPE_PRESETS, ANIMATION_PRESET_OPTIONS, useReducedMotion, useViewerState, useViewerOptions, useIsMobile, useResizablePanels, useDerivedSlideState, useZoomViewport, useEditorHistory, usePresentationSetup, useTouchGestures, useViewerDialogs, useEditorOperations, useViewerIntegration, useLayoutSwitching, useYjsDocumentSync, useFollowMode, useBroadcastFollower, DEFAULT_FILL_COLOR, hasDagDuotoneEffect, renderDagDuotoneSvgFilter, getCropShapeClipPath, resolvePalette, resolveStyle, layoutToCategory, wrapChrome, isImageTiled, getImageTilingStyle, getDuotoneColors, getTextWarpStyle, renderTextSegments, buildReactChartViewModel, renderChartElement, buildTextBody3DSceneStyle, SLIDE_TRANSITION_OPTIONS, SLIDE_NAV_THUMBNAIL_WIDTH, scopeLayoutOptionsToActiveSlide, useToolbarVisibility, useKeyboardInsets, useModalDismissDrag, useCollaborativeState, styleShadow, styleStroke, colour, fitFontSize, smartArtNodeGroupProps, chevronPoints, SmartArtNodeText, contrastingTextColor, renderStepDownProcess, renderAlternatingFlow, renderDescendingProcess, renderPictureAccentList, renderVerticalBlockList, renderGroupedList, renderPyramidList, renderHorizontalPictureList, renderAccentProcess, renderVerticalChevronList, DEFAULT_TEXT_FONT_SIZE, DEFAULT_FONT_FAMILY, TOOLBAR_SECTIONS, useVirtualizedSlides, useSwipeNavigation, useSheetDismissDrag, detectTargetType, parseEmailUrl, parseSlideFromUrl, ACTION_VERB_MAP, SHORTCUT_REFERENCE_ITEMS as SHORTCUT_REFERENCE_ITEMS$1, buildTree, treeWidth, treeDepth, nodeOpacity, truncate, gearPath, resolveSmartArtDataPalette, HYPERLINK_COLOR, GRID_SIZE, startPreviewAnimation, stopPreviewAnimation } from './chunk-RI6OBMEG.mjs';
1
+ import { DEFAULT_STROKE_COLOR, getConnectorPathGeometry, buildLineShadowCss, buildLineGlowFilter, renderConnectorMarker, MIN_ELEMENT_SIZE, getShapeVisualStyle, getTextStyleForElement, DEFAULT_TEXT_COLOR, renderVectorShape, isConnectorOrLineElement, build3DExtrusionData, getImageRenderStyle, cn, getTextLayoutStyle, renderTableElement, renderMediaElement, shouldRenderFallbackLabel, SHAPE_PRESETS, ANIMATION_PRESET_OPTIONS, useReducedMotion, useViewerState, useViewerOptions, useIsMobile, useResizablePanels, useDerivedSlideState, useZoomViewport, useEditorHistory, usePresentationSetup, useTouchGestures, useViewerDialogs, useEditorOperations, useViewerIntegration, useLayoutSwitching, useYjsDocumentSync, useFollowMode, useBroadcastFollower, DEFAULT_FILL_COLOR, hasDagDuotoneEffect, renderDagDuotoneSvgFilter, getCropShapeClipPath, resolvePalette, resolveStyle, layoutToCategory, wrapChrome, isImageTiled, getImageTilingStyle, getDuotoneColors, getTextWarpStyle, renderTextSegments, buildReactChartViewModel, renderChartElement, buildTextBody3DSceneStyle, SLIDE_TRANSITION_OPTIONS, SLIDE_NAV_THUMBNAIL_WIDTH, scopeLayoutOptionsToActiveSlide, useToolbarVisibility, useKeyboardInsets, useModalDismissDrag, useCollaborativeState, styleShadow, styleStroke, colour, fitFontSize, smartArtNodeGroupProps, chevronPoints, SmartArtNodeText, contrastingTextColor, renderStepDownProcess, renderAlternatingFlow, renderDescendingProcess, renderPictureAccentList, renderVerticalBlockList, renderGroupedList, renderPyramidList, renderHorizontalPictureList, renderAccentProcess, renderVerticalChevronList, DEFAULT_TEXT_FONT_SIZE, DEFAULT_FONT_FAMILY, TOOLBAR_SECTIONS, useVirtualizedSlides, useSwipeNavigation, useSheetDismissDrag, detectTargetType, parseEmailUrl, parseSlideFromUrl, ACTION_VERB_MAP, SHORTCUT_REFERENCE_ITEMS as SHORTCUT_REFERENCE_ITEMS$1, buildTree, treeWidth, treeDepth, nodeOpacity, truncate, gearPath, resolveSmartArtDataPalette, HYPERLINK_COLOR, GRID_SIZE, startPreviewAnimation, stopPreviewAnimation } from './chunk-3VKZ7ZNY.mjs';
2
2
  import { normalizeHexColor, colorWithOpacity, normalizeStrokeDashType, getSvgStrokeDasharray, getCompoundLineOffsets, getCompoundLineWidths, svgLineCap, getElementTransform, isEditableTextElement, getAriaRole, getAriaLabel, getAriaRoleDescription, getImageEffectsOpacity, getImageEffectsFilter, getElementTransformWithoutRotation, ACTION_BUTTON_PRESETS, getElementLabel, RULER_THICKNESS, RULER_FONT_SIZE, EQUATION_TEMPLATES, convertLatexToOmml, convertOmmlToMathMl, sanitizeMathMl, DEFAULT_VIEWER_OPTIONS, buildUserFontFaceStyles, readStoredViewerPrefs, resolveThemeCatalogEntry, THEME_CATALOG, writeStoredViewerPrefs, openPptxFile, readBackstageRecentFile, viewerOptionsToPreferences, applyPreferenceToOptions, listAutosaveSnapshots, deleteAutosaveSnapshot, createBackstagePresentation, buildCssGradientFromShapeStyle, getComputedEffectStyle, getSoftEdgeSvgFilter, getGroupChildParentFill, revealedSmartArtNodeCount, buildSmartArtA11y, buildSummaryZoomView, getImageColorWashStyle, getPendingSelectionRestore, restoreSegmentSelection, getTextCompensationTransform, applyChartBuildReveal, formatAxisValue, hasPressureVariation, getInkReplayStyles, INK_REPLAY_KEYFRAMES, resolveInkColor, resolveInkWidth, resolveInkOpacity, pressuresToWidths, getContentPartReplayStyles, resolveOleType, getOleTypeColor, getOleTypeLabel, resolveGroupChildFill, shouldUseSvgWarp, buildPreviewElements, themeToCssVars, DEFAULT_INSERT_CHART_TYPE, hasCopyableFormat, printPropertiesFrameSlides, printPropertiesSlidesPerPage, VIEWER_OPTIONS_TABS, DEFAULT_QUICK_ACCESS_COMMAND_IDS, buildJoinCollaborationConfig, buildCreateCollaborationConfig, generateBroadcastRoomId, DEFAULT_BROADCAST_SERVER_URL, buildBroadcastViewerUrl, resolveTransportForServerUrl, resolveDrawingShapeNodeId, getImageSvgFilters, buildDuotoneCacheKey, getDuotoneCachedResult, applyDuotone, setDuotoneCachedResult, buildCacheKey, DEFAULT_COLOR_CHANGE_TOLERANCE, getCachedResult, applyColorChange, setCachedResult, findChartPartTarget, dragValueForPart, withChartPointValue, dragAnchorViewY, withChartTitle, computeSmartArtLayout, buildSmartArt3DModel, extractPathPoints, generatePressureCircles, isBrowserOpenableMime, formatBytes, getOleBadgeLabel, openUrlInNewTab, getWarpPath, getSlideBackgroundStyle, filterCommands, resolveTitleBarStatusKey, TITLE_BAR_CLASSES, TITLE_BAR_DEFAULT_FILE_KEY, shouldConfirmExternalHyperlink, safeOpenUrl, isPpactionUrl, parsePpactionUrl, formatVersionTimestamp, formatRelativeTime, scanAvailableFontFamilies, PRESETS, CATEGORIES, convertOmmlToLatex, clampPercent, HANDOUT_OPTIONS, TOOLBAR_TABS, SHORTCUT_REFERENCE_ITEMS, resolveViewerAddinRows, availableQuickAccessCommands, addQuickAccessCommand, removeQuickAccessCommand, moveQuickAccessCommand, activateModalFocus, buildCollaborationShareUrl, presentationInkPath, mobileElapsedSince, isFirstSlide, isLastSlide, formatMobileElapsed, mobileSlideCounter, formatElapsed, computeInlineEditorRect, findSmartArtNodeText, rebuildDrawingShapesIfCleared, shouldCommitSmartArtNodeText, groupIntoParagraphs, substituteFieldText, TAB_ROW_ACTION_CLASSES, listBackstageRecentFiles, BACKSTAGE_NAV, INSERT_CHART_TYPES, SLIDE_VIRTUALIZATION_THRESHOLD, getShapeAdjustmentHandleDescriptor, getSlideTransitionAnimations, SLIDE_TRANSITION_KEYFRAMES, formatSlideCounter, isUrlSafe, computeHandoutLayout, getPrintableArea, generateNoteLineCount, computeAllNotesPages, getNotesPrintableArea, QUICK_ACCESS_COMMAND_CATALOG, notesSegmentsToSpans, erasePresentationInkAt, movePresenterPointer, appendPresentationInkPoint, NOTES_FONT_SIZE_DEFAULT, formatTime, NOTES_FONT_SIZE_MIN, clampNotesFontSize, NOTES_FONT_SIZE_STEP, NOTES_FONT_SIZE_MAX, BACKSTAGE_TEMPLATES, formatBackstageDate, formatBackstageSize, DEFAULT_VIEWER_PROFILE, resolveProfileInitial, AVATAR_COLOR_SWATCHES, getConnectionSites, generateTicks, getCommentMarkerPosition, buildThemeColorGrid, THEME_COLOR_LABELS, buildSmartArtPresetData, getLocalStorageUsageSummary, saveViewerProfile, clearAllLocalViewerData, TABLE_STYLE_PRESETS, withFrameSlides, withSlidesPerPage, formatCommentTimestamp, DIRECTIONAL_PRESETS, computeMergeCellRight, computeMergeCellDown, computeSplitCell, DUOTONE_PRESETS, ARTISTIC_EFFECTS, LBL, SEL, FILL_MODE_OPTIONS, SECTION_HEADING, NUM, GRADIENT_TYPE_OPTIONS, PATTERN_OPTIONS } from './chunk-45A3IISI.mjs';
3
3
  import { LOCALE_CATALOG, translationsEn } from './chunk-2X72MOPZ.mjs';
4
4
  import { hasShapeProperties, hasTextProperties, isInkElement, SWITCHABLE_LAYOUT_TYPES, isImageLikeElement, getLinkedTextBoxSegments, getSubstituteFontFamily, themeColorSchemesEqual, setSmartArtNodeStyle, updateSmartArtNodeText, THEME_COLOR_SCHEME_KEYS, chartDataChangeType, chartDataUpdatePoint, chartDataAddCategory, chartDataRemoveCategory, chartDataAddSeries, chartDataRemoveSeries, setChartAxisLogScale, setChartAxisTitleStyle, setChartAxisGridlineStyle, setChartSeriesMarker, setChartSeriesChartType, setChartDataPointFill, setChartDataPointExplosion, setChartDataPointMarker, setChartDataPointLabel, getOleObjectTypeLabel, pptxActionToElementAction, applyThemeOverrideToSlide, COLOR_MAP_ALIAS_KEYS, DEFAULT_COLOR_MAP, addSmartArtNodeAsChild, removeSmartArtNode, elementActionToPptxAction, switchSmartArtLayout, addSmartArtNode, demoteSmartArtNode, promoteSmartArtNode, reorderSmartArtNode } from './chunk-LTBO2ZJC.mjs';
@@ -6,6 +6,7 @@ import React18, { createContext, useMemo, useState, useCallback, forwardRef, use
6
6
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
7
7
  import { useTranslation } from 'react-i18next';
8
8
  import { LuRotateCw, LuMessageSquare, LuEyeOff, LuSettings, LuX, LuCalendarDays, LuHash, LuText, LuCast, LuSave, LuUndo, LuRedo, LuSearch, LuShieldCheck, LuPlus, LuPanelLeftClose, LuStickyNote, LuMonitor, LuColumns2, LuPresentation, LuMinus, LuLayers, LuSettings2, LuClock, LuDownload, LuTrash2, LuCheck, LuLock, LuEye, LuFileText, LuType, LuLoader, LuShieldAlert, LuSignature, LuInfo, LuTriangleAlert, LuPrinter, LuPenTool, LuChevronUp, LuChevronDown, LuWifi, LuWifiOff, LuUsers, LuCopy, LuChevronLeft, LuChevronRight, LuPlay, LuPause, LuMenu, LuSparkles, LuShare2, LuPanelLeft, LuPanelRight, LuArrowLeft, LuFolderOpen, LuPackage, LuFileImage, LuVideo, LuBox, LuClipboardPaste, LuScissors, LuPaintbrush, LuUpload, LuSquare, LuImage, LuDatabase, LuAArrowUp, LuAArrowDown, LuRemoveFormatting, LuHighlighter, LuList, LuListOrdered, LuIndentDecrease, LuIndentIncrease, LuReplace, LuMousePointer2, LuShapes, LuPaintBucket, LuPenLine, LuPalette, LuPencil, LuStar, LuMoveRight, LuMousePointerClick, LuClock3, LuMonitorPlay, LuListVideo, LuCaptions, LuCamera, LuEraser, LuRotateCcw, LuCircleHelp, LuSpellCheck, LuBookOpen, LuLanguages, LuGlobe, LuGitCompare, LuMessageSquarePlus, LuLockKeyhole, LuPanelTop, LuLayoutGrid, LuGrid3X3, LuPipette, LuRuler, LuZoomIn, LuMaximize2, LuCode, LuCaseSensitive, LuTimer, LuGripVertical, LuBold, LuItalic, LuUnderline, LuStrikethrough, LuLink, LuGrid2X2, LuCopyPlus, LuCirclePlay, LuCirclePause, LuZoomOut, LuScan, LuMonitorOff, LuClipboardCopy, LuWand, LuTextCursorInput, LuFile, LuEllipsis, LuUserRound, LuFilePlus2, LuHouse, LuUser, LuHardDrive, LuLogIn, LuFolderPlus, LuAlignLeft, LuAlignCenter, LuAlignRight, LuAlignJustify, LuColumns3, LuSpline, LuAlignHorizontalSpaceAround, LuAlignVerticalSpaceAround, LuMove, LuRadio, LuLockOpen, LuArrowDown, LuArrowUp, LuArrowRight, LuReply, LuBookmark } from 'react-icons/lu';
9
+ import JSZip from 'jszip';
9
10
 
10
11
  var ViewerThemeContext = createContext(void 0);
11
12
  function ViewerThemeProvider({ theme, children }) {
@@ -1334,7 +1335,7 @@ function AccountPage() {
1334
1335
  return void 0;
1335
1336
  });
1336
1337
  };
1337
- const version = "2.0.24" ;
1338
+ const version = "2.0.27" ;
1338
1339
  return /* @__PURE__ */ jsxs("div", { className: "mt-8 max-w-[700px] space-y-6", children: [
1339
1340
  /* @__PURE__ */ jsxs("section", { className: cardClass, children: [
1340
1341
  /* @__PURE__ */ jsxs("h2", { className: "flex items-center gap-2 text-sm font-semibold", children: [
@@ -7029,6 +7030,13 @@ var COMMON_FONTS = [
7029
7030
  "Verdana"
7030
7031
  ];
7031
7032
  var COMMON_SIZES = [8, 9, 10, 11, 12, 14, 16, 18, 20, 24, 28, 32, 36, 40, 44, 48, 54, 60, 72, 96];
7033
+ var CUSTOM_FONT_FILE_EXTENSION = /\.(?:ttf|otf|woff2?)$/iu;
7034
+ var getZipEntryBaseName = (path) => path.replace(/\\/gu, "/").split("/").filter(Boolean).pop() ?? path;
7035
+ var isUsableZipFontEntry = (path) => {
7036
+ const normalized = path.replace(/\\/gu, "/").toLowerCase();
7037
+ if (normalized.includes("__macosx/") || normalized.endsWith("/.ds_store")) return false;
7038
+ return CUSTOM_FONT_FILE_EXTENSION.test(normalized);
7039
+ };
7032
7040
  function HomeSection(p) {
7033
7041
  const { t } = useTranslation();
7034
7042
  const [fontMenuOpen, setFontMenuOpen] = useState(false);
@@ -7061,33 +7069,89 @@ function HomeSection(p) {
7061
7069
  p.onUpdateTextStyle?.({ fontFamily: family });
7062
7070
  setFontMenuOpen(false);
7063
7071
  };
7064
- const registerLocalFont = async (file) => {
7065
- const stem = file.name.replace(/\.(?:ttf|otf|woff2?)$/iu, "");
7072
+ const fontSourceFromFile = async (file) => {
7073
+ const baseName = getZipEntryBaseName(file.name);
7074
+ const stem = baseName.replace(CUSTOM_FONT_FILE_EXTENSION, "");
7066
7075
  const bold = /(?:^|[-_\s])(?:bold|semibold|demibold|black)(?:$|[-_\s])/iu.test(stem);
7067
7076
  const italic = /(?:^|[-_\s])(?:italic|oblique)(?:$|[-_\s])/iu.test(stem);
7068
7077
  const family = stem.replace(/(?:[-_\s])(?:regular|normal|book|medium|bold|semibold|demibold|black|italic|oblique)+$/iu, "").replace(/[-_]+/gu, " ").trim();
7069
- if (!family || typeof FontFace === "undefined") {
7070
- return;
7078
+ if (!family) return null;
7079
+ const normalizedName = baseName.toLowerCase();
7080
+ const format = normalizedName.endsWith(".woff2") ? "woff2" : normalizedName.endsWith(".woff") ? "woff" : normalizedName.endsWith(".otf") ? "opentype" : "truetype";
7081
+ return {
7082
+ family,
7083
+ src: URL.createObjectURL(file),
7084
+ format,
7085
+ weight: bold ? 700 : 400,
7086
+ style: italic ? "italic" : "normal",
7087
+ data: await file.arrayBuffer()
7088
+ };
7089
+ };
7090
+ const registerFontSources = async (sources) => {
7091
+ if (typeof FontFace === "undefined") return;
7092
+ const loadedSources = [];
7093
+ for (const source of sources) {
7094
+ try {
7095
+ const face = new FontFace(source.family, source.data, {
7096
+ weight: String(source.weight ?? 400),
7097
+ style: source.style ?? "normal"
7098
+ });
7099
+ await face.load();
7100
+ document.fonts.add(face);
7101
+ loadedSources.push(source);
7102
+ } catch (error) {
7103
+ console.warn(`[PowerPointViewer] Could not load custom font ${source.family}.`, error);
7104
+ }
7071
7105
  }
7072
- const face = new FontFace(family, await file.arrayBuffer(), {
7073
- weight: bold ? "700" : "400",
7074
- style: italic ? "italic" : "normal"
7075
- });
7076
- await face.load();
7077
- document.fonts.add(face);
7078
- const next = Array.from(/* @__PURE__ */ new Set([...customFontFamilies, family]));
7106
+ if (loadedSources.length === 0) {
7107
+ throw new Error("None of the selected font files could be loaded by this browser.");
7108
+ }
7109
+ const next = Array.from(/* @__PURE__ */ new Set([...customFontFamilies, ...loadedSources.map((source) => source.family)]));
7079
7110
  setCustomFontFamilies(next);
7080
7111
  const fontWindow = window;
7081
7112
  fontWindow.__AIXA_PPTX_CUSTOM_FONT_FAMILIES__ = next;
7082
7113
  window.dispatchEvent(new CustomEvent("aixa:pptx-custom-fonts", { detail: next }));
7083
- applyFont(family);
7114
+ if (loadedSources[0]) applyFont(loadedSources[0].family);
7115
+ };
7116
+ const registerLocalFontPackage = async (file) => {
7117
+ let sources = [];
7118
+ if (/\.zip$/iu.test(file.name)) {
7119
+ const zip = await JSZip.loadAsync(file);
7120
+ const entries = Object.values(zip.files).filter(
7121
+ (entry) => !entry.dir && isUsableZipFontEntry(entry.name)
7122
+ );
7123
+ sources = (await Promise.all(entries.map(async (entry) => {
7124
+ const blob = await entry.async("blob");
7125
+ return fontSourceFromFile(
7126
+ new File([blob], getZipEntryBaseName(entry.name), { type: blob.type })
7127
+ );
7128
+ }))).filter((source) => Boolean(source));
7129
+ } else {
7130
+ const source = await fontSourceFromFile(file);
7131
+ if (source) sources = [source];
7132
+ }
7133
+ if (sources.length === 0) throw new Error("No browser-compatible font files were found.");
7134
+ await p.onUploadCustomFontPackage?.(
7135
+ file,
7136
+ sources.map(({ data: _data, ...source }) => source)
7137
+ );
7138
+ await registerFontSources(sources);
7139
+ p.onEmbedCustomFonts?.(
7140
+ sources.map((source) => ({
7141
+ name: source.family,
7142
+ dataUrl: "",
7143
+ bold: Number(source.weight ?? 400) >= 600,
7144
+ italic: source.style === "italic" || source.style === "oblique",
7145
+ format: source.format,
7146
+ rawFontData: new Uint8Array(source.data.slice(0))
7147
+ }))
7148
+ );
7084
7149
  };
7085
7150
  useEffect(() => {
7086
7151
  const handleCustomFonts = (event) => {
7087
7152
  const families = event.detail;
7088
- setCustomFontFamilies(
7089
- Array.isArray(families) ? families.filter((family) => typeof family === "string" && family.trim().length > 0) : []
7090
- );
7153
+ const incoming = Array.isArray(families) ? families.filter((family) => typeof family === "string" && family.trim().length > 0) : [];
7154
+ setCustomFontFamilies((current) => Array.from(/* @__PURE__ */ new Set([...current, ...incoming])));
7091
7155
  };
7092
7156
  window.addEventListener("aixa:pptx-custom-fonts", handleCustomFonts);
7093
7157
  return () => window.removeEventListener("aixa:pptx-custom-fonts", handleCustomFonts);
@@ -7278,7 +7342,7 @@ function HomeSection(p) {
7278
7342
  onClick: () => fontFileInputRef.current?.click(),
7279
7343
  children: [
7280
7344
  /* @__PURE__ */ jsx(LuUpload, { className: "h-4 w-4" }),
7281
- "Add custom font file"
7345
+ "Add custom font package"
7282
7346
  ]
7283
7347
  }
7284
7348
  ),
@@ -7287,11 +7351,11 @@ function HomeSection(p) {
7287
7351
  {
7288
7352
  ref: fontFileInputRef,
7289
7353
  type: "file",
7290
- accept: ".ttf,.otf,.woff,.woff2,font/ttf,font/otf,font/woff,font/woff2",
7354
+ accept: ".zip,.ttf,.otf,.woff,.woff2,application/zip,font/ttf,font/otf,font/woff,font/woff2",
7291
7355
  className: "hidden",
7292
7356
  onChange: (event) => {
7293
7357
  const file = event.currentTarget.files?.[0];
7294
- if (file) void registerLocalFont(file);
7358
+ if (file) void registerLocalFontPackage(file);
7295
7359
  event.currentTarget.value = "";
7296
7360
  }
7297
7361
  }
@@ -10479,7 +10543,9 @@ function Toolbar(p) {
10479
10543
  selectedElement: p.selectedElement,
10480
10544
  onUpdateTextStyle: p.onUpdateTextStyle,
10481
10545
  themeFonts: p.themeFonts,
10482
- embeddedFontFamilies: p.embeddedFontFamilies
10546
+ embeddedFontFamilies: p.embeddedFontFamilies,
10547
+ onEmbedCustomFonts: p.onEmbedCustomFonts,
10548
+ onUploadCustomFontPackage: p.onUploadCustomFontPackage
10483
10549
  }
10484
10550
  ),
10485
10551
  sIns && /* @__PURE__ */ jsx(
@@ -29974,6 +30040,8 @@ function ViewerToolbarSection(props) {
29974
30040
  onOpenHeaderFooter,
29975
30041
  onOpenShareDialog,
29976
30042
  onOpenFile,
30043
+ onUploadCustomFontPackage,
30044
+ onEmbedCustomFonts,
29977
30045
  onOpenRecentFile,
29978
30046
  onToggleFormatPainter: onToggleFormatPainterProp,
29979
30047
  fileName,
@@ -30308,6 +30376,8 @@ function ViewerToolbarSection(props) {
30308
30376
  body: s.theme?.fontScheme?.minorFont?.latin
30309
30377
  },
30310
30378
  embeddedFontFamilies: Array.from(new Set(s.embeddedFonts.map((font) => font.name))),
30379
+ onUploadCustomFontPackage,
30380
+ onEmbedCustomFonts,
30311
30381
  onInsertSlideFromLayout: slideOps.handleInsertSlideFromLayout,
30312
30382
  onApplyLayout: (path) => void onApplyLayout(path),
30313
30383
  customShows: s.customShows,
@@ -34033,6 +34103,7 @@ var PowerPointViewer = forwardRef(
34033
34103
  content: incomingContent,
34034
34104
  singleSlideOnly = false,
34035
34105
  fonts = [],
34106
+ onUploadCustomFontPackage,
34036
34107
  filePath,
34037
34108
  fileName,
34038
34109
  canEdit = false,
@@ -34566,7 +34637,22 @@ var PowerPointViewer = forwardRef(
34566
34637
  hiddenActions,
34567
34638
  aiEnabled: Boolean(ai),
34568
34639
  isAiPanelOpen: aiPanel.isOpen,
34569
- onToggleAiPanel: aiPanel.toggle
34640
+ onToggleAiPanel: aiPanel.toggle,
34641
+ onUploadCustomFontPackage,
34642
+ onEmbedCustomFonts: (fonts2) => {
34643
+ state.setEmbeddedFonts((current) => {
34644
+ const merged = [...current];
34645
+ for (const font of fonts2) {
34646
+ const index = merged.findIndex(
34647
+ (existing) => existing.name === font.name && Boolean(existing.bold) === Boolean(font.bold) && Boolean(existing.italic) === Boolean(font.italic)
34648
+ );
34649
+ if (index >= 0) merged[index] = font;
34650
+ else merged.push(font);
34651
+ }
34652
+ return merged;
34653
+ });
34654
+ state.setIsDirty(true);
34655
+ }
34570
34656
  }
34571
34657
  ),
34572
34658
  /* @__PURE__ */ jsx(