Add QExcell XLSX page setup support

This commit is contained in:
Курнат Андрей
2026-06-04 22:08:58 +03:00
parent 96aff5b76e
commit 5dfe97013d
4 changed files with 237 additions and 5 deletions
+70 -1
View File
@@ -32,7 +32,7 @@ import {
Underline, Underline,
WrapText WrapText
} from "lucide-react"; } from "lucide-react";
import type { CellFormat, SheetChart, SheetChartType, Workbook } from "../../types"; import type { CellFormat, SheetChart, SheetChartType, SheetPageMarginPreset, SheetPageOrientation, SheetPageSetup, SheetPageSizeId, Workbook } from "../../types";
import { cellId, columnLabel, columnLabelsForCount, evaluateCell, formatCellValueForDisplay, mergedCellInfo, parseCellId, summarizeColumn, usedSheetBounds } from "../../utils/spreadsheet"; import { cellId, columnLabel, columnLabelsForCount, evaluateCell, formatCellValueForDisplay, mergedCellInfo, parseCellId, summarizeColumn, usedSheetBounds } from "../../utils/spreadsheet";
import { downloadBlobFile, downloadTextFile } from "../../utils/download"; import { downloadBlobFile, downloadTextFile } from "../../utils/download";
import { chartDataPoints, exportXlsxBlob, importXlsxFile } from "../../io/excellOffice"; import { chartDataPoints, exportXlsxBlob, importXlsxFile } from "../../io/excellOffice";
@@ -95,6 +95,20 @@ const chartTypeOptions: Array<{ value: SheetChartType; label: string }> = [
{ value: "line", label: "Линейная" }, { value: "line", label: "Линейная" },
{ value: "pie", label: "Круговая" } { value: "pie", label: "Круговая" }
]; ];
const sheetPageSizeOptions: Array<{ id: SheetPageSizeId; label: string }> = [
{ id: "a4", label: "A4" },
{ id: "letter", label: "Letter" }
];
const sheetPageOrientationOptions: Array<{ id: SheetPageOrientation; label: string }> = [
{ id: "portrait", label: "Книжная" },
{ id: "landscape", label: "Альбомная" }
];
const sheetPageMarginOptions: Array<{ id: SheetPageMarginPreset; label: string }> = [
{ id: "normal", label: "Обычные" },
{ id: "narrow", label: "Узкие" },
{ id: "wide", label: "Широкие" }
];
const defaultSheetPageSetup: SheetPageSetup = { size: "a4", orientation: "portrait", marginPreset: "normal" };
const horizontalAlignOptions = [ const horizontalAlignOptions = [
{ value: "left", label: "По левому краю", icon: AlignLeft }, { value: "left", label: "По левому краю", icon: AlignLeft },
@@ -159,6 +173,18 @@ function normalizedCellFormat(format: CellFormat): CellFormat | undefined {
return Object.keys(normalized).length > 0 ? normalized : undefined; return Object.keys(normalized).length > 0 ? normalized : undefined;
} }
function normalizedSheetPageSetup(setup?: SheetPageSetup): SheetPageSetup {
const candidateSize = setup?.size;
const candidateOrientation = setup?.orientation;
const candidateMarginPreset = setup?.marginPreset;
const size = candidateSize && sheetPageSizeOptions.some((option) => option.id === candidateSize) ? candidateSize : defaultSheetPageSetup.size;
const orientation =
candidateOrientation && sheetPageOrientationOptions.some((option) => option.id === candidateOrientation) ? candidateOrientation : defaultSheetPageSetup.orientation;
const marginPreset =
candidateMarginPreset && sheetPageMarginOptions.some((option) => option.id === candidateMarginPreset) ? candidateMarginPreset : defaultSheetPageSetup.marginPreset;
return { size, orientation, marginPreset };
}
function cellInputStyle(format?: CellFormat): CSSProperties { function cellInputStyle(format?: CellFormat): CSSProperties {
const textDecoration = [format?.underline ? "underline" : "", format?.strike ? "line-through" : ""].filter(Boolean).join(" "); const textDecoration = [format?.underline ? "underline" : "", format?.strike ? "line-through" : ""].filter(Boolean).join(" ");
return { return {
@@ -276,6 +302,7 @@ export function QExcell({ workbook, onChange }: QExcellProps) {
const activeHiddenColumns = activeSheet.hiddenColumns ?? emptyHiddenItems; const activeHiddenColumns = activeSheet.hiddenColumns ?? emptyHiddenItems;
const activeHiddenRows = activeSheet.hiddenRows ?? emptyHiddenItems; const activeHiddenRows = activeSheet.hiddenRows ?? emptyHiddenItems;
const activeCharts = activeSheet.charts ?? emptyCharts; const activeCharts = activeSheet.charts ?? emptyCharts;
const activeSheetPageSetup = normalizedSheetPageSetup(activeSheet.pageSetup);
const selectedRawValue = activeSheet.cells[selectedCell] ?? ""; const selectedRawValue = activeSheet.cells[selectedCell] ?? "";
const selectedHyperlink = activeHyperlinks[selectedCell] ?? ""; const selectedHyperlink = activeHyperlinks[selectedCell] ?? "";
const selectedComment = activeComments[selectedCell] ?? ""; const selectedComment = activeComments[selectedCell] ?? "";
@@ -418,6 +445,15 @@ export function QExcell({ workbook, onChange }: QExcellProps) {
}); });
} }
function updateActiveSheetPageSetup(pageSetupPatch: Partial<SheetPageSetup>) {
onChange({
...workbook,
sheets: workbook.sheets.map((sheet) =>
sheet.id === activeSheet.id ? { ...sheet, pageSetup: normalizedSheetPageSetup({ ...activeSheetPageSetup, ...pageSetupPatch }) } : sheet
)
});
}
function addSheet() { function addSheet() {
const id = `sheet-${workbook.sheets.length + 1}`; const id = `sheet-${workbook.sheets.length + 1}`;
onChange({ onChange({
@@ -854,6 +890,39 @@ export function QExcell({ workbook, onChange }: QExcellProps) {
onBlur={(event) => updateActiveSheetPrintArea(normalizedPrintAreaInput(event.target.value))} onBlur={(event) => updateActiveSheetPrintArea(normalizedPrintAreaInput(event.target.value))}
/> />
</label> </label>
<label className="office-field">
<span>Размер</span>
<select value={activeSheetPageSetup.size} onChange={(event) => updateActiveSheetPageSetup({ size: event.target.value as SheetPageSizeId })}>
{sheetPageSizeOptions.map((option) => (
<option key={option.id} value={option.id}>
{option.label}
</option>
))}
</select>
</label>
<label className="office-field">
<span>Ориентация</span>
<select
value={activeSheetPageSetup.orientation}
onChange={(event) => updateActiveSheetPageSetup({ orientation: event.target.value as SheetPageOrientation })}
>
{sheetPageOrientationOptions.map((option) => (
<option key={option.id} value={option.id}>
{option.label}
</option>
))}
</select>
</label>
<label className="office-field">
<span>Поля</span>
<select value={activeSheetPageSetup.marginPreset} onChange={(event) => updateActiveSheetPageSetup({ marginPreset: event.target.value as SheetPageMarginPreset })}>
{sheetPageMarginOptions.map((option) => (
<option key={option.id} value={option.id}>
{option.label}
</option>
))}
</select>
</label>
</div> </div>
<div className="office-tool-section"> <div className="office-tool-section">
+42
View File
@@ -8,6 +8,8 @@ import {
buildColumnWidthsXml, buildColumnWidthsXml,
buildHyperlinksXml, buildHyperlinksXml,
buildMergeCellsXml, buildMergeCellsXml,
buildPageMarginsXml,
buildPageSetupXml,
buildSheetPrXml, buildSheetPrXml,
buildSheetViewsXml, buildSheetViewsXml,
createCellStyleRegistry, createCellStyleRegistry,
@@ -32,6 +34,7 @@ import {
worksheetXmlToHyperlinks, worksheetXmlToHyperlinks,
worksheetCommentsXmlToComments, worksheetCommentsXmlToComments,
worksheetXmlToMergedCells, worksheetXmlToMergedCells,
worksheetXmlToPageSetup,
worksheetXmlToRowHeights, worksheetXmlToRowHeights,
worksheetXmlToShowGridLines, worksheetXmlToShowGridLines,
worksheetXmlToTabColor, worksheetXmlToTabColor,
@@ -739,6 +742,45 @@ describe("excellOffice OOXML helpers", () => {
expect(imported.sheets[1].printArea).toBe("B2:C4,E1:E3"); expect(imported.sheets[1].printArea).toBe("B2:C4,E1:E3");
}); });
it("сохраняет и импортирует параметры страницы XLSX через pageMargins и pageSetup", async () => {
expect(
worksheetXmlToPageSetup({
worksheet: {
pageMargins: { left: "0.25", right: "0.25", top: "0.75", bottom: "0.75", header: "0.3", footer: "0.3" },
pageSetup: { paperSize: "1", orientation: "landscape" }
}
})
).toEqual({ size: "letter", orientation: "landscape", marginPreset: "narrow" });
expect(buildPageMarginsXml({ size: "a4", orientation: "portrait", marginPreset: "wide" })).toBe(
'<pageMargins left="1" right="1" top="1" bottom="1" header="0.5" footer="0.5"/>'
);
expect(buildPageSetupXml({ size: "letter", orientation: "landscape", marginPreset: "normal" })).toBe(
'<pageSetup paperSize="1" orientation="landscape"/>'
);
const blob = await exportXlsxBlob({
id: "book-1",
title: "Page setup.xlsx",
updatedAt: "2026-06-01T00:00:00.000Z",
activeSheet: "sheet-1",
sheets: [
{
id: "sheet-1",
name: "Layout",
pageSetup: { size: "letter", orientation: "landscape", marginPreset: "narrow" },
cells: { A1: "Layout" }
}
]
});
const zip = await JSZip.loadAsync(await blob.arrayBuffer());
const sheetXml = (await zip.file("xl/worksheets/sheet1.xml")?.async("string")) ?? "";
const imported = await importXlsxFile(new File([blob], "Page setup.xlsx", { type: blob.type }));
expect(sheetXml).toContain('<pageMargins left="0.25" right="0.25" top="0.75" bottom="0.75" header="0.3" footer="0.3"/>');
expect(sheetXml).toContain('<pageSetup paperSize="1" orientation="landscape"/>');
expect(imported.sheets[0].pageSetup).toEqual({ size: "letter", orientation: "landscape", marginPreset: "narrow" });
});
it("сохраняет и импортирует скрытые листы XLSX через sheet state", async () => { it("сохраняет и импортирует скрытые листы XLSX через sheet state", async () => {
const blob = await exportXlsxBlob({ const blob = await exportXlsxBlob({
id: "book-1", id: "book-1",
+115 -4
View File
@@ -1,4 +1,15 @@
import type { CellFormat, MergedCellRange, SheetChart, SheetChartType, SheetFrozenPane, Workbook } from "../types"; import type {
CellFormat,
MergedCellRange,
SheetChart,
SheetChartType,
SheetFrozenPane,
SheetPageMarginPreset,
SheetPageOrientation,
SheetPageSetup,
SheetPageSizeId,
Workbook
} from "../types";
import { evaluateCell, expandRange } from "../utils/spreadsheet"; import { evaluateCell, expandRange } from "../utils/spreadsheet";
import { officeTitleFromFileName, readOfficeFileAsArrayBuffer, XLSX_MIME_TYPE } from "./fileHelpers"; import { officeTitleFromFileName, readOfficeFileAsArrayBuffer, XLSX_MIME_TYPE } from "./fileHelpers";
@@ -19,6 +30,7 @@ export interface ImportedExcellWorkbook {
showGridLines?: boolean; showGridLines?: boolean;
zoomScale?: number; zoomScale?: number;
printArea?: string; printArea?: string;
pageSetup?: SheetPageSetup;
cells: Record<string, string>; cells: Record<string, string>;
mergedCells?: MergedCellRange[]; mergedCells?: MergedCellRange[];
hyperlinks?: Record<string, string>; hyperlinks?: Record<string, string>;
@@ -77,6 +89,7 @@ interface ParsedSheet {
showGridLines?: boolean; showGridLines?: boolean;
zoomScale?: number; zoomScale?: number;
printArea?: string; printArea?: string;
pageSetup?: SheetPageSetup;
cells: Record<string, string>; cells: Record<string, string>;
mergedCells?: MergedCellRange[]; mergedCells?: MergedCellRange[];
hyperlinks?: Record<string, string>; hyperlinks?: Record<string, string>;
@@ -210,6 +223,16 @@ const builtinNumberFormats: Record<number, string> = {
48: "##0.0E+0", 48: "##0.0E+0",
49: "@" 49: "@"
}; };
const defaultSheetPageSetup: SheetPageSetup = { size: "a4", orientation: "portrait", marginPreset: "normal" };
const xlsxPageSizePaperIds: Record<SheetPageSizeId, number> = {
letter: 1,
a4: 9
};
const xlsxPageMargins: Record<SheetPageMarginPreset, { left: number; right: number; top: number; bottom: number; header: number; footer: number }> = {
normal: { left: 0.7, right: 0.7, top: 0.75, bottom: 0.75, header: 0.3, footer: 0.3 },
narrow: { left: 0.25, right: 0.25, top: 0.75, bottom: 0.75, header: 0.3, footer: 0.3 },
wide: { left: 1, right: 1, top: 1, bottom: 1, header: 0.5, footer: 0.5 }
};
export async function importXlsxFile(file: File): Promise<ImportedExcellWorkbook> { export async function importXlsxFile(file: File): Promise<ImportedExcellWorkbook> {
if (!file.name.toLowerCase().endsWith(".xlsx")) { if (!file.name.toLowerCase().endsWith(".xlsx")) {
@@ -320,7 +343,8 @@ export async function exportXlsxBlob(workbook: Workbook): Promise<Blob> {
sheet.hiddenRows, sheet.hiddenRows,
sheet.showGridLines, sheet.showGridLines,
sheet.zoomScale, sheet.zoomScale,
legacyDrawingRelationshipId legacyDrawingRelationshipId,
sheet.pageSetup
) )
); );
const relationshipsXml = buildWorksheetRelationshipsXml( const relationshipsXml = buildWorksheetRelationshipsXml(
@@ -371,6 +395,7 @@ export function parsedWorksheetToSheet(
const hiddenRows = worksheetXmlToHiddenRows(worksheetXml); const hiddenRows = worksheetXmlToHiddenRows(worksheetXml);
const showGridLines = worksheetXmlToShowGridLines(worksheetXml); const showGridLines = worksheetXmlToShowGridLines(worksheetXml);
const zoomScale = worksheetXmlToZoomScale(worksheetXml); const zoomScale = worksheetXmlToZoomScale(worksheetXml);
const pageSetup = worksheetXmlToPageSetup(worksheetXml);
return { return {
id: sheet.id || `sheet-${index + 1}`, id: sheet.id || `sheet-${index + 1}`,
@@ -385,6 +410,7 @@ export function parsedWorksheetToSheet(
...(Object.keys(hiddenRows).length > 0 ? { hiddenRows } : {}), ...(Object.keys(hiddenRows).length > 0 ? { hiddenRows } : {}),
...(showGridLines === false ? { showGridLines } : {}), ...(showGridLines === false ? { showGridLines } : {}),
...(zoomScale ? { zoomScale } : {}), ...(zoomScale ? { zoomScale } : {}),
...(pageSetup ? { pageSetup } : {}),
cells: worksheetXmlToCells(worksheetXml, sharedStrings, styleTable, date1904), cells: worksheetXmlToCells(worksheetXml, sharedStrings, styleTable, date1904),
mergedCells: worksheetXmlToMergedCells(worksheetXml), mergedCells: worksheetXmlToMergedCells(worksheetXml),
hyperlinks: worksheetXmlToHyperlinks(worksheetXml, relationshipsXml), hyperlinks: worksheetXmlToHyperlinks(worksheetXml, relationshipsXml),
@@ -540,6 +566,18 @@ export function worksheetXmlToZoomScale(worksheetXml: unknown) {
return normalizedSheetZoomScale(sheetView.zoomScale); return normalizedSheetZoomScale(sheetView.zoomScale);
} }
export function worksheetXmlToPageSetup(worksheetXml: unknown): SheetPageSetup | undefined {
const worksheet = childRecord(worksheetXml, "worksheet");
const pageSetup = childRecord(worksheet, "pageSetup");
const pageMargins = childRecord(worksheet, "pageMargins");
const size = sheetPageSizeFromPaperId(pageSetup.paperSize) ?? defaultSheetPageSetup.size;
const orientation = normalizedSheetPageOrientation(pageSetup.orientation) ?? defaultSheetPageSetup.orientation;
const marginPreset = sheetPageMarginPresetFromRecord(pageMargins) ?? defaultSheetPageSetup.marginPreset;
const hasPageSetup = Object.keys(pageSetup).length > 0;
const hasPageMargins = Object.keys(pageMargins).length > 0;
return hasPageSetup || hasPageMargins ? { size, orientation, marginPreset } : undefined;
}
export function worksheetXmlToAutoFilter(worksheetXml: unknown) { export function worksheetXmlToAutoFilter(worksheetXml: unknown) {
const worksheet = childRecord(worksheetXml, "worksheet"); const worksheet = childRecord(worksheetXml, "worksheet");
const autoFilter = childRecord(worksheet, "autoFilter"); const autoFilter = childRecord(worksheet, "autoFilter");
@@ -804,7 +842,8 @@ export function buildWorksheetXml(
hiddenRows: Record<string, boolean> = {}, hiddenRows: Record<string, boolean> = {},
showGridLines?: boolean, showGridLines?: boolean,
zoomScale?: number, zoomScale?: number,
legacyDrawingRelationshipId = "" legacyDrawingRelationshipId = "",
pageSetup?: SheetPageSetup
) { ) {
const normalizedRowHeights = normalizedSheetRowHeights(rowHeights); const normalizedRowHeights = normalizedSheetRowHeights(rowHeights);
const normalizedHiddenRows = normalizedSheetHiddenRows(hiddenRows); const normalizedHiddenRows = normalizedSheetHiddenRows(hiddenRows);
@@ -845,9 +884,11 @@ export function buildWorksheetXml(
const sheetPrXml = buildSheetPrXml(tabColor); const sheetPrXml = buildSheetPrXml(tabColor);
const sheetViewsXml = buildSheetViewsXml(frozenPane, showGridLines, zoomScale); const sheetViewsXml = buildSheetViewsXml(frozenPane, showGridLines, zoomScale);
const colsXml = buildColumnWidthsXml(columnWidths, hiddenColumns); const colsXml = buildColumnWidthsXml(columnWidths, hiddenColumns);
const pageMarginsXml = buildPageMarginsXml(pageSetup);
const pageSetupXml = buildPageSetupXml(pageSetup);
return xmlDeclaration( return xmlDeclaration(
`<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">${sheetPrXml}${sheetViewsXml}${colsXml}<sheetData>${rowXml}</sheetData>${autoFilterXml}${mergeCellsXml}${hyperlinksXml}${drawingXml}${legacyDrawingXml}</worksheet>` `<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">${sheetPrXml}${sheetViewsXml}${colsXml}<sheetData>${rowXml}</sheetData>${autoFilterXml}${mergeCellsXml}${hyperlinksXml}${pageMarginsXml}${pageSetupXml}${drawingXml}${legacyDrawingXml}</worksheet>`
); );
} }
@@ -940,6 +981,24 @@ export function buildSheetPrXml(tabColor?: string) {
return color ? `<sheetPr><tabColor rgb="FF${color}"/></sheetPr>` : ""; return color ? `<sheetPr><tabColor rgb="FF${color}"/></sheetPr>` : "";
} }
export function buildPageMarginsXml(pageSetup?: SheetPageSetup) {
if (!pageSetup) {
return "";
}
const margins = xlsxPageMargins[normalizedSheetPageSetup(pageSetup).marginPreset];
return `<pageMargins left="${formatSheetDimension(margins.left)}" right="${formatSheetDimension(margins.right)}" top="${formatSheetDimension(margins.top)}" bottom="${formatSheetDimension(margins.bottom)}" header="${formatSheetDimension(margins.header)}" footer="${formatSheetDimension(margins.footer)}"/>`;
}
export function buildPageSetupXml(pageSetup?: SheetPageSetup) {
if (!pageSetup) {
return "";
}
const normalized = normalizedSheetPageSetup(pageSetup);
return `<pageSetup paperSize="${xlsxPageSizePaperIds[normalized.size]}" orientation="${normalized.orientation}"/>`;
}
export function buildColumnWidthsXml(columnWidths: Record<string, number> = {}, hiddenColumns: Record<string, boolean> = {}) { export function buildColumnWidthsXml(columnWidths: Record<string, number> = {}, hiddenColumns: Record<string, boolean> = {}) {
const columnsByNumber = new Map<number, { width?: number; hidden?: boolean }>(); const columnsByNumber = new Map<number, { width?: number; hidden?: boolean }>();
normalizedSheetColumnWidths(columnWidths).forEach(({ column, width }) => { normalizedSheetColumnWidths(columnWidths).forEach(({ column, width }) => {
@@ -1914,6 +1973,58 @@ function normalizedSheetZoomScale(value: unknown) {
return zoomScale >= 10 && zoomScale <= 400 && zoomScale !== 100 ? zoomScale : 0; return zoomScale >= 10 && zoomScale <= 400 && zoomScale !== 100 ? zoomScale : 0;
} }
function normalizedSheetPageSetup(pageSetup?: SheetPageSetup): SheetPageSetup {
const size = isSheetPageSizeId(pageSetup?.size) ? pageSetup.size : defaultSheetPageSetup.size;
const orientation = normalizedSheetPageOrientation(pageSetup?.orientation) ?? defaultSheetPageSetup.orientation;
const marginPreset = isSheetPageMarginPreset(pageSetup?.marginPreset) ? pageSetup.marginPreset : defaultSheetPageSetup.marginPreset;
return { size, orientation, marginPreset };
}
function normalizedSheetPageOrientation(value: unknown): SheetPageOrientation | undefined {
return value === "portrait" || value === "landscape" ? value : undefined;
}
function isSheetPageSizeId(value: unknown): value is SheetPageSizeId {
return value === "a4" || value === "letter";
}
function isSheetPageMarginPreset(value: unknown): value is SheetPageMarginPreset {
return value === "normal" || value === "narrow" || value === "wide";
}
function sheetPageSizeFromPaperId(value: unknown): SheetPageSizeId | undefined {
const paperSize = positiveIntegerAttribute(value);
return (Object.keys(xlsxPageSizePaperIds) as SheetPageSizeId[]).find((sizeId) => xlsxPageSizePaperIds[sizeId] === paperSize);
}
function sheetPageMarginPresetFromRecord(record: XmlRecord): SheetPageMarginPreset | undefined {
const left = positiveNumberAttribute(record.left);
const right = positiveNumberAttribute(record.right);
const top = positiveNumberAttribute(record.top);
const bottom = positiveNumberAttribute(record.bottom);
const header = positiveNumberAttribute(record.header);
const footer = positiveNumberAttribute(record.footer);
if (!left || !right || !top || !bottom || !header || !footer) {
return undefined;
}
return (Object.keys(xlsxPageMargins) as SheetPageMarginPreset[]).find((preset) => {
const margins = xlsxPageMargins[preset];
return (
isCloseDimension(left, margins.left) &&
isCloseDimension(right, margins.right) &&
isCloseDimension(top, margins.top) &&
isCloseDimension(bottom, margins.bottom) &&
isCloseDimension(header, margins.header) &&
isCloseDimension(footer, margins.footer)
);
});
}
function isCloseDimension(actual: number, expected: number) {
return Math.abs(actual - expected) <= 0.01;
}
function buildRowAttributes(height?: number, hidden?: boolean) { function buildRowAttributes(height?: number, hidden?: boolean) {
return `${height ? ` ht="${formatSheetDimension(height)}" customHeight="1"` : ""}${hidden ? ' hidden="1"' : ""}`; return `${height ? ` ht="${formatSheetDimension(height)}" customHeight="1"` : ""}${hidden ? ' hidden="1"' : ""}`;
} }
+10
View File
@@ -3,6 +3,9 @@ export type AppId = "word" | "sheets" | "slides" | "mail";
export type WordPageSizeId = "a4" | "letter"; export type WordPageSizeId = "a4" | "letter";
export type WordPageOrientation = "portrait" | "landscape"; export type WordPageOrientation = "portrait" | "landscape";
export type WordPageMarginPreset = "normal" | "narrow" | "wide"; export type WordPageMarginPreset = "normal" | "narrow" | "wide";
export type SheetPageSizeId = "a4" | "letter";
export type SheetPageOrientation = "portrait" | "landscape";
export type SheetPageMarginPreset = "normal" | "narrow" | "wide";
export interface WordPageSetup { export interface WordPageSetup {
size: WordPageSizeId; size: WordPageSizeId;
@@ -10,6 +13,12 @@ export interface WordPageSetup {
marginPreset: WordPageMarginPreset; marginPreset: WordPageMarginPreset;
} }
export interface SheetPageSetup {
size: SheetPageSizeId;
orientation: SheetPageOrientation;
marginPreset: SheetPageMarginPreset;
}
export interface WordDocument { export interface WordDocument {
id: string; id: string;
title: string; title: string;
@@ -40,6 +49,7 @@ export interface Sheet {
showGridLines?: boolean; showGridLines?: boolean;
zoomScale?: number; zoomScale?: number;
printArea?: string; printArea?: string;
pageSetup?: SheetPageSetup;
cells: Record<string, string>; cells: Record<string, string>;
mergedCells?: MergedCellRange[]; mergedCells?: MergedCellRange[];
hyperlinks?: Record<string, string>; hyperlinks?: Record<string, string>;