import type { IFreeze, Workbook } from '@univerjs/core';
|
|
import { Disposable, Inject, IUniverInstanceService, UniverInstanceType } from '@univerjs/core';
|
import { IRenderManagerService, SpreadsheetSkeleton } from '@univerjs/engine-render';
|
import { SheetsSelectionsService } from '@univerjs/sheets';
|
import { SheetPrintInterceptorService, SheetSkeletonManagerService } from '@univerjs/sheets-ui';
|
|
import { isEmptyObject, isNullOrUndefined } from '../utils';
|
import {
|
DefaultPrintConfig,
|
JnpfPaperPaddingForType,
|
JnpfPrintAreaEnum,
|
JnpfPrintCustomFooterEnum,
|
JnpfPrintCustomHeaderEnum,
|
JnpfPrintDirectionEnum,
|
JnpfPrintFreezeEnum,
|
JnpfPrintOtherParamsEnum,
|
JnpfPrintPaperSizeForType,
|
JnpfPrintScaleEnum,
|
} from '../utils/define';
|
import { buildUUID } from '../utils/uuid';
|
|
export class JnpfSheetsPrintService extends Disposable {
|
private _configParams: Record<string, any> = {};
|
private _layoutHeaderFooterParams: any[] = [];
|
|
constructor(
|
@IUniverInstanceService protected readonly _univerInstanceService: IUniverInstanceService,
|
@IRenderManagerService private readonly _renderManagerService: IRenderManagerService,
|
@Inject(SheetsSelectionsService) private readonly _selectionManagerService: SheetsSelectionsService,
|
@Inject(SheetPrintInterceptorService) private readonly _sheetPrintInterceptorService: SheetPrintInterceptorService,
|
) {
|
super();
|
}
|
|
/**
|
* 根据配置获取打印布局。
|
* @param {any} printConfig - 打印配置对象,默认为 `DefaultPrintConfig`。
|
*/
|
getLayouts(printConfig: any = DefaultPrintConfig) {
|
// 分析布局的页眉和页脚配置,并将结果存储在 `_layoutHeaderFooterParams` 中
|
this._layoutHeaderFooterParams = this._analyseLayoutHeaderFooterConfig(printConfig);
|
|
// 获取参数出来判断是不是存在冻结
|
const subUnitId = this._getWorkbook()!.getActiveSheet()?.getSheetId();
|
if (!subUnitId) {
|
return;
|
}
|
|
const snapshot = this._getWorkbook()!.save();
|
|
let freeze = [];
|
|
switch (printConfig.printArea) {
|
case JnpfPrintAreaEnum.currentSheet: {
|
const { xSplit = 0, ySplit = 0 } = snapshot?.sheets?.[subUnitId]?.freeze ?? {};
|
|
// 正确的冻结状态判断
|
const colHasFreeze = xSplit > 0; // 列冻结(xSplit 对应列冻结)
|
const rowHasFreeze = ySplit > 0; // 行冻结(ySplit 对应行冻结)
|
|
// 判断冻结状态【判断是单体还是全部,全部的话判断有开就有,没开就没有】
|
freeze = [
|
rowHasFreeze && printConfig?.yFreeze && JnpfPrintFreezeEnum.row, // 行冻结
|
colHasFreeze && printConfig?.xFreeze && JnpfPrintFreezeEnum.column, // 列冻结
|
].filter(Boolean); // 过滤掉 false/null/undefined
|
|
break;
|
}
|
case JnpfPrintAreaEnum.partSheets: {
|
const { sheets } = snapshot;
|
const { partSheets = [] } = printConfig;
|
|
const colHasFreeze =
|
Object.values(sheets).some((sheet: any) => {
|
return partSheets.includes(sheet.id) ? sheet?.freeze?.xSplit > 0 : false;
|
}) && printConfig?.xFreeze;
|
const rowHasFreeze =
|
Object.values(sheets).some((sheet: any) => {
|
return partSheets.includes(sheet.id) ? sheet?.freeze?.ySplit > 0 : false;
|
}) && printConfig?.yFreeze;
|
|
if (colHasFreeze) {
|
freeze.push('column');
|
}
|
if (rowHasFreeze) {
|
freeze.push('row');
|
}
|
|
break;
|
}
|
case JnpfPrintAreaEnum.workbook: {
|
const { sheets } = snapshot;
|
const colHasFreeze = Object.values(sheets).some((sheet: any) => sheet?.freeze?.xSplit > 0) && printConfig?.xFreeze;
|
const rowHasFreeze = Object.values(sheets).some((sheet: any) => sheet?.freeze?.ySplit > 0) && printConfig?.yFreeze;
|
|
if (colHasFreeze) {
|
freeze.push('column');
|
}
|
if (rowHasFreeze) {
|
freeze.push('row');
|
}
|
|
break;
|
}
|
// No default
|
}
|
|
// 提取剩余配置
|
const { xFreeze, yFreeze, ...restPrintConfig } = printConfig ?? {};
|
|
// 将配置参数存储在 `_configParams` 中
|
this._configParams = {
|
...restPrintConfig,
|
freeze,
|
};
|
|
// 获取所有需要打印的范围
|
const printRanges = this._computePrintRanges();
|
// 初始化空数组,用于存储计算出的布局
|
const layouts: any[] = [];
|
|
// 遍历每个打印范围,计算对应的页面布局
|
printRanges?.forEach(range => {
|
// 如果当前范围存在,继续处理
|
if (range) {
|
// 计算页面布局
|
const pageLayout = this._computePageLayout(range);
|
|
// 如果页面布局存在,将其添加到 layouts 数组中
|
if (pageLayout) {
|
layouts.push(pageLayout);
|
}
|
}
|
});
|
|
// 获取纸张大小
|
const paperSize = this._getPaperSize();
|
// 获取纸张边距
|
const paperPadding = this._getPaperPadding();
|
|
// 整个需要打印的页码数量
|
const bookTotalPage = layouts.reduce((total, layout) => total + layout.pages.length, 0);
|
|
let bookPageNumber = 1;
|
const pagesLayout = layouts.flatMap(({ pages: pageRanges, ...info }) =>
|
pageRanges.map((range: any, rangeIndex: any) => ({
|
...info,
|
bookPageNumber: bookPageNumber++,
|
bookTotalPage,
|
browserPreviewScale: 1,
|
layoutId: `layout_${buildUUID()}`,
|
paperPadding,
|
paperSize,
|
range,
|
sheetPageNumber: rangeIndex + 1, // layout 内部页码从 1 开始
|
sheetTotalPage: pageRanges?.length,
|
})),
|
);
|
|
return {
|
pagesLayout,
|
paperSize,
|
printConfig: {
|
...this._configParams,
|
currentDateTime: new Date(Date.now() + 8 * 3600 * 1000).toJSON().slice(0, 19).replace('T', ' '),
|
headerFooterParams: this._layoutHeaderFooterParams,
|
},
|
};
|
}
|
|
/**
|
* 解析页面布局中的页眉和页脚配置。
|
* @param {any} printConfig - 页眉页脚的配置对象,包含多个布尔值属性来决定是否显示某些信息。
|
* @returns 返回需要在页眉或页脚中包含的参数数组。
|
*/
|
private _analyseLayoutHeaderFooterConfig(printConfig: any) {
|
// 定义一个数组,用于存储需要包含的页眉和页脚参数
|
const layoutHeaderFooterParams = [];
|
|
if (printConfig.headerFooterType === 'default') {
|
if (printConfig?.workbookTitle) {
|
layoutHeaderFooterParams.push(JnpfPrintOtherParamsEnum.workbookTitle);
|
}
|
if (printConfig?.worksheetTitle) {
|
layoutHeaderFooterParams.push(JnpfPrintOtherParamsEnum.worksheetTitle);
|
}
|
if (printConfig?.printDate) {
|
layoutHeaderFooterParams.push(JnpfPrintOtherParamsEnum.printDate);
|
}
|
if (printConfig?.printTime) {
|
layoutHeaderFooterParams.push(JnpfPrintOtherParamsEnum.printTime);
|
}
|
if (printConfig?.pageNumber) {
|
layoutHeaderFooterParams.push(JnpfPrintOtherParamsEnum.pageNumber);
|
}
|
} else {
|
if (printConfig.customHeaderFooterValue?.leftTop) {
|
layoutHeaderFooterParams.push(JnpfPrintCustomHeaderEnum.leftTop);
|
}
|
if (printConfig.customHeaderFooterValue?.centerTop) {
|
layoutHeaderFooterParams.push(JnpfPrintCustomHeaderEnum.centerTop);
|
}
|
if (printConfig.customHeaderFooterValue?.rightTop) {
|
layoutHeaderFooterParams.push(JnpfPrintCustomHeaderEnum.rightTop);
|
}
|
if (printConfig.customHeaderFooterValue?.leftBottom) {
|
layoutHeaderFooterParams.push(JnpfPrintCustomFooterEnum.leftBottom);
|
}
|
if (printConfig.customHeaderFooterValue?.centerBottom) {
|
layoutHeaderFooterParams.push(JnpfPrintCustomFooterEnum.centerBottom);
|
}
|
if (printConfig.customHeaderFooterValue?.rightBottom) {
|
layoutHeaderFooterParams.push(JnpfPrintCustomFooterEnum.rightBottom);
|
}
|
}
|
|
// 返回最终解析出来的页眉页脚参数数组
|
return layoutHeaderFooterParams;
|
}
|
|
/**
|
* 计算页面布局,根据给定的打印范围和工作表的骨架信息,计算每一页的布局。
|
* @param printRange {any} 打印范围,包含工作簿信息、工作表信息、打印范围、冻结信息、缩放比例及内容尺寸
|
* @returns 返回计算出的页面布局信息
|
*/
|
private _computePageLayout(printRange: any) {
|
// 解构打印范围信息
|
const { contentSize, freeze, printScale, range, subUnitId, unitId } = printRange ?? {};
|
|
// 获取工作表的骨架信息
|
const skeleton = this._getUnitSkeleton(unitId, subUnitId);
|
if (!skeleton) {
|
return;
|
}
|
|
// 解构工作表的行高和列宽累计值
|
const { columnWidthAccumulation, rowHeightAccumulation } = skeleton;
|
|
// 解构打印范围的行列信息
|
const { endColumn, endRow, startColumn, startRow } = range;
|
|
const calcRow = (start: any) => {
|
let end = start;
|
const offset = start === 0 ? 0 : rowHeightAccumulation[start - 1];
|
for (; end < endRow; end++) {
|
const current = (rowHeightAccumulation[end] - offset) * printScale;
|
const next = (rowHeightAccumulation[end + 1] - offset) * printScale;
|
if (current > contentSize.h || (current < contentSize.h && next > contentSize.h)) return { endRow: end - 1, startRow: start };
|
}
|
return { endRow: end, startRow: start };
|
};
|
|
const calcColumn = (start: any) => {
|
let end = start;
|
const offset = start === 0 ? 0 : columnWidthAccumulation[start - 1];
|
for (; end < endColumn; end++) {
|
const current = (columnWidthAccumulation[end] - offset) * printScale;
|
const next = (columnWidthAccumulation[end + 1] - offset) * printScale;
|
|
if (current >= contentSize.w || (current < contentSize.w && next > contentSize.w)) {
|
return { endColumn: end, startColumn: start };
|
}
|
}
|
return { endColumn: end, startColumn: start };
|
};
|
|
// 计算所有行的分页范围
|
const rows = [];
|
for (let start = startRow; start <= endRow; ) {
|
const row = calcRow(start);
|
rows.push(row);
|
start = row.endRow + 1; // 移动到下一个分页起始行
|
}
|
|
// 计算所有列的分页范围
|
const columns: any[] = [];
|
for (let start = startColumn; start <= endColumn; ) {
|
const column = calcColumn(start);
|
columns.push(column);
|
start = column.endColumn + 1; // 移动到下一个分页起始列
|
}
|
|
// 将行列的分页信息组合成每一页的布局
|
const pages: any[] = [];
|
rows.forEach(row => {
|
columns.forEach(column => {
|
pages.push({ ...row, ...column });
|
});
|
});
|
|
// 返回计算的页面布局信息,包括单位ID、子单位ID、页面信息、冻结信息和缩放比例
|
return { freeze, pages, printScale, subUnitId, unitId };
|
}
|
|
/**
|
* 计算并返回打印页面的范围,包括内容区域、冻结行列以及打印比例。
|
* @param {string} subUnitId - 工作表的唯一标识符。
|
* @param {any} sheetRange - 需要打印的工作表范围,包含起始和结束的行列。
|
* @returns 返回打印页面的范围、冻结信息、内容区域和打印比例。
|
*/
|
private _computePrintPageRange(subUnitId: string, sheetRange: any) {
|
// 获取工作簿实例
|
const workbook = this._getWorkbook();
|
|
// 如果没有获取到工作簿,返回 undefined
|
if (!workbook) {
|
return;
|
}
|
|
// 获取工作簿的唯一标识符
|
const unitId = workbook!.getUnitId();
|
|
// 获取纸张大小
|
const paperSize = this._getPaperSize();
|
if (!paperSize) {
|
return;
|
}
|
|
// 获取纸张的边距
|
const paperPadding = this._getPaperPadding();
|
if (!paperPadding) {
|
return;
|
}
|
|
// 计算内容区域的大小,减去左右、上下的边距
|
const contentSize = {
|
h: paperSize.h - (paperPadding.top + paperPadding.bottom),
|
w: paperSize.w - (paperPadding.left + paperPadding.right),
|
};
|
|
// 如果起始行列小于等于 -1,返回默认的打印范围,表示无效的打印范围
|
if (sheetRange?.startRow <= -1 || sheetRange?.startColumn <= -1) {
|
return {
|
contentSize,
|
freeze: { startColumn: -1, startRow: -1, xSplit: 0, ySplit: 0 },
|
printScale: 1,
|
range: { endColumn: 0, endRow: 0, startColumn: 0, startRow: 0 },
|
subUnitId,
|
unitId,
|
};
|
}
|
|
// 获取工作表实例
|
const worksheet = workbook!.getSheetBySheetId(subUnitId);
|
if (!worksheet) {
|
return;
|
}
|
|
// 获取工作表的冻结信息
|
const freeze = worksheet?.getFreeze();
|
if (!freeze) {
|
return;
|
}
|
|
// 获取工作表的骨架信息(如列宽、行高等)
|
const skeleton = this._getUnitSkeleton(unitId, subUnitId);
|
if (!skeleton) {
|
return;
|
}
|
|
// 计算打印比例
|
const printScale = this._computePrintScale(skeleton, sheetRange, freeze);
|
|
// 初始化打印范围和冻结信息的默认值
|
let startColumn = -1;
|
let startRow = -1;
|
let xSplit = 0;
|
let ySplit = 0;
|
|
// 获取骨架中的列宽和行高累积数据
|
const { columnWidthAccumulation, rowHeightAccumulation } = skeleton ?? {};
|
let { endColumn: sheetRangeEndColumn, endRow: sheetRangeEndRow, startColumn: sheetRangeStartColumn, startRow: sheetRangeStartRow } = sheetRange ?? {};
|
const { startColumn: freezeStartColumn, startRow: freezeStartRow, xSplit: freezeXSplit, ySplit: freezeYSplit } = freeze ?? {};
|
|
// 处理冻结行(如果配置了冻结行)
|
if (freezeYSplit && this._configParams?.freeze.includes(JnpfPrintFreezeEnum?.row)) {
|
// 如果打印范围的结束行大于等于冻结行,则计算冻结区域的高度并调整打印区域的高度
|
if (sheetRangeEndRow >= freezeStartRow) {
|
const offsetHeight = rowHeightAccumulation[freezeStartRow] - rowHeightAccumulation[freezeStartRow - freezeYSplit];
|
|
if (offsetHeight < contentSize?.h) {
|
startRow = freezeStartRow;
|
ySplit = freezeYSplit;
|
contentSize.h -= offsetHeight;
|
}
|
} else {
|
// 如果打印范围的结束行小于冻结行,则计算冻结区域的高度并调整打印区域的高度
|
startRow = sheetRangeEndRow + 1;
|
ySplit = sheetRangeEndRow + 1 - (freezeStartRow - freezeYSplit);
|
contentSize.h = contentSize?.h - rowHeightAccumulation[freezeStartRow] - rowHeightAccumulation[freezeStartRow - freezeYSplit];
|
}
|
}
|
|
// 更新起始行,确保不小于冻结区域的起始行
|
sheetRangeStartRow = Math.max(startRow, sheetRangeStartRow);
|
|
// 处理冻结列(如果配置了冻结列)
|
if (freezeXSplit && this._configParams?.freeze.includes(JnpfPrintFreezeEnum?.column)) {
|
// 如果打印范围的结束列大于等于冻结列,则计算冻结区域的宽度并调整打印区域的宽度
|
if (sheetRangeEndColumn >= freezeStartColumn) {
|
const offsetWidth =
|
printScale * (columnWidthAccumulation[freezeStartColumn - 1] - (columnWidthAccumulation[freezeStartColumn - freezeXSplit - 1] || 0));
|
|
if (offsetWidth < contentSize?.w) {
|
startColumn = freezeStartColumn;
|
xSplit = freezeXSplit;
|
contentSize.w -= offsetWidth;
|
}
|
} else {
|
// 如果打印范围的结束列小于冻结列,则计算冻结区域的宽度并调整打印区域的宽度
|
const offsetWidth =
|
printScale * (columnWidthAccumulation[freezeStartColumn - 1] - (columnWidthAccumulation[freezeStartColumn - freezeXSplit] - 1 || 0));
|
|
if (offsetWidth < contentSize?.w) {
|
const startColumnCache = sheetRangeEndColumn + 1;
|
const xSplitCache = sheetRangeEndColumn + 1 - (freezeStartColumn - freezeXSplit);
|
|
startColumn = startColumnCache;
|
xSplit = xSplitCache;
|
contentSize.w -= offsetWidth;
|
}
|
}
|
}
|
|
// 更新起始列,确保不小于冻结区域的起始列
|
sheetRangeStartColumn = Math.max(startColumn, sheetRangeStartColumn);
|
|
// 返回计算出的打印页面范围,包含打印区域、冻结信息、内容大小和打印比例
|
return {
|
contentSize,
|
freeze: { startColumn, startRow, xSplit, ySplit },
|
printScale,
|
range: { endColumn: sheetRangeEndColumn, endRow: sheetRangeEndRow, startColumn: sheetRangeStartColumn, startRow: sheetRangeStartRow },
|
subUnitId,
|
unitId,
|
};
|
}
|
|
/**
|
* 计算打印范围,根据配置的区域选择合适的打印范围。
|
* @returns 返回包含一个或多个打印范围的数组。
|
*/
|
private _computePrintRanges() {
|
// 初始化空数组 result,用于存储计算的打印范围
|
let result: any[] = [];
|
|
// 根据配置的打印区域 执行不同的处理
|
switch (this._configParams.printArea) {
|
// 如果区域是当前选择的区域
|
case JnpfPrintAreaEnum.currentSelection: {
|
// 计算当前选择区域的打印范围,并返回该范围的数组
|
result = [this._computePrintSelectionRange()];
|
break;
|
}
|
|
// 如果区域是当前工作表
|
case JnpfPrintAreaEnum.currentSheet: {
|
// 获取当前活动工作表的 ID
|
const subUnitId = this._getWorkbook()!.getActiveSheet()?.getSheetId();
|
|
// 如果工作表 ID 不存在,则跳出
|
if (!subUnitId) {
|
break;
|
}
|
|
// 计算当前工作表的打印范围
|
const range = this._computePrintSheetRange(subUnitId);
|
// 如果计算结果有效,返回一个包含该范围的数组
|
result = range ? [range] : [];
|
break;
|
}
|
|
// 如果区域是部分工作簿
|
case JnpfPrintAreaEnum.partSheets: {
|
const partSheets = new Set(this._configParams.partSheets ?? []);
|
// 获取工作簿中的所有工作表
|
const sheets = this._getWorkbook()!.getSheets();
|
|
const targetSheets = sheets.filter(sheet => partSheets.has(sheet?.getSheetId()));
|
|
// 对每个工作表计算打印范围,并过滤掉无效的范围
|
result = targetSheets
|
?.map(sheet => {
|
const subUnitId = sheet?.getSheetId();
|
// 对每个工作表计算并返回其打印范围
|
return this._computePrintSheetRange(subUnitId);
|
})
|
.filter(Boolean); // 过滤掉无效值(如 null 或 undefined)
|
break;
|
}
|
|
// 如果区域是整个工作簿
|
case JnpfPrintAreaEnum.workbook: {
|
// 获取工作簿中的所有工作表
|
const sheets = this._getWorkbook()!.getSheets();
|
|
// 对每个工作表计算打印范围,并过滤掉无效的范围
|
result = sheets
|
?.map(sheet => {
|
const subUnitId = sheet?.getSheetId();
|
// 对每个工作表计算并返回其打印范围
|
return this._computePrintSheetRange(subUnitId);
|
})
|
.filter(Boolean); // 过滤掉无效值(如 null 或 undefined)
|
break;
|
}
|
|
// 如果区域类型不匹配,返回空数组
|
default: {
|
break;
|
}
|
}
|
|
// 返回计算出的打印范围数组
|
return result;
|
}
|
|
/**
|
* 计算打印比例。
|
* @param {SpreadsheetSkeleton} skeleton - 工作表的骨架信息,包括行高、列宽累积数据。
|
* @param {any} sheetRange - 打印的单元格范围,包括起始行、起始列、结束行、结束列。
|
* @param {IFreeze} freeze - 冻结范围信息,包括冻结的起始行列和冻结的分割范围。
|
* @returns 返回适合的打印缩放比例,范围在 [0, 1] 之间。
|
*/
|
private _computePrintScale(skeleton: SpreadsheetSkeleton, sheetRange: any, freeze: IFreeze) {
|
// 解构骨架中的列宽和行高累积信息
|
const { columnWidthAccumulation, rowHeightAccumulation } = skeleton ?? {};
|
|
// 骨架信息为空时,返回默认比例 1
|
if (isNullOrUndefined(columnWidthAccumulation) || isNullOrUndefined(rowHeightAccumulation)) {
|
return 1;
|
}
|
|
// 解构打印范围的起始和结束行列
|
const { endColumn: sheetRangeEndColumn, endRow: sheetRangeEndRow, startColumn: sheetRangeStartColumn, startRow: sheetRangeStartRow } = sheetRange ?? {};
|
|
// 打印范围信息为空时,返回默认比例 1
|
if (
|
isNullOrUndefined(sheetRangeStartRow) ||
|
isNullOrUndefined(sheetRangeStartColumn) ||
|
isNullOrUndefined(sheetRangeEndRow) ||
|
isNullOrUndefined(sheetRangeEndColumn)
|
) {
|
return 1;
|
}
|
|
// 解构冻结范围的起始行列和分割范围
|
const { startColumn: freezeStartColumn, startRow: freezeStartRow, xSplit: freezeXSplit, ySplit: freezeYSplit } = freeze ?? {};
|
|
// 冻结范围信息为空时,返回默认比例 1
|
if (isNullOrUndefined(freezeXSplit) || isNullOrUndefined(freezeYSplit) || isNullOrUndefined(freezeStartRow) || isNullOrUndefined(freezeStartColumn)) {
|
return 1;
|
}
|
|
// 确定打印范围的起始行:对冻结范围与打印范围取交集
|
const startRow = sheetRangeEndRow < freezeStartRow ? sheetRangeStartRow : Math.max(freezeStartRow, sheetRangeStartRow);
|
|
// 确定打印范围的起始列:对冻结范围与打印范围取交集
|
const startColumn = sheetRangeEndColumn < freezeStartColumn ? sheetRangeStartColumn : Math.max(freezeStartColumn, sheetRangeStartColumn);
|
|
// 获取纸张尺寸
|
const paperSize = this._getPaperSize();
|
if (!paperSize) {
|
return 1;
|
}
|
|
// 获取纸张边距
|
const paperPadding = this._getPaperPadding();
|
if (!paperPadding) {
|
return 1;
|
}
|
|
// 计算冻结区域的宽度(列)和高度(行)
|
const xSplit = freezeXSplit > 0 ? columnWidthAccumulation[freezeStartColumn - 1] - (columnWidthAccumulation[freezeStartColumn - freezeXSplit - 1] || 0) : 0;
|
const ySplit = freezeYSplit > 0 ? rowHeightAccumulation[freezeStartRow - 1] - (rowHeightAccumulation[freezeStartRow - freezeYSplit - 1] || 0) : 0;
|
|
// 计算打印范围的高度和宽度
|
const rowHeight = rowHeightAccumulation[sheetRangeEndRow] - (rowHeightAccumulation[startRow - 1] || 0);
|
const columnWidth = columnWidthAccumulation[sheetRangeEndColumn] - (columnWidthAccumulation[startColumn - 1] || 0);
|
|
// 加上冻结区域的尺寸,得到总的打印高度和宽度
|
const xScale = columnWidth + xSplit;
|
const yScale = rowHeight + ySplit;
|
|
let result = 1;
|
|
// 根据配置参数计算打印比例
|
switch (this._configParams.printScale) {
|
case JnpfPrintScaleEnum.fitHeight: {
|
// 高度适应比例
|
result = Math.min(1, (paperSize.h - paperPadding.top - paperPadding.bottom) / yScale);
|
break;
|
}
|
case JnpfPrintScaleEnum.fitPage: {
|
// 页面完全适应比例(宽度和高度都适应)
|
result = Math.min(1, (paperSize.w - paperPadding.left - paperPadding.right) / xScale, (paperSize.h - paperPadding.top - paperPadding.bottom) / yScale);
|
break;
|
}
|
case JnpfPrintScaleEnum.fitWidth: {
|
// 宽度适应比例
|
result = Math.min(1, (paperSize.w - paperPadding.left - paperPadding.right) / xScale);
|
break;
|
}
|
default: {
|
break;
|
}
|
}
|
|
// 返回比例值,保留两位小数
|
return Math.floor(result * 100) / 100;
|
}
|
|
/**
|
* 计算并返回打印选择范围的详细信息。
|
* @returns 返回选区的打印范围信息。
|
*/
|
private _computePrintSelectionRange() {
|
// 获取当前工作簿实例
|
const workbook = this._getWorkbook();
|
|
// 如果没有获取到工作簿,返回 undefined
|
if (!workbook) {
|
return;
|
}
|
|
// 获取工作簿的唯一标识符
|
const unitId = workbook!.getUnitId();
|
|
// 获取当前活动工作表
|
const worksheet = workbook!.getActiveSheet();
|
|
// 如果没有获取到工作表,返回 undefined
|
if (!worksheet) {
|
return;
|
}
|
|
// 获取当前工作表的唯一标识符(subUnitId)
|
const subUnitId = worksheet?.getSheetId();
|
|
// 获取当前工作簿的最后选择区域
|
const lastSelection = this._selectionManagerService?.getWorkbookSelections(unitId)?.getCurrentLastSelection();
|
|
// 如果没有获取到选择区域,返回 undefined
|
if (!lastSelection) {
|
return;
|
}
|
|
// 根据最后选择的范围计算打印的页面范围
|
return this._computePrintPageRange(subUnitId, lastSelection?.range);
|
}
|
|
/**
|
* 计算工作表的打印范围。
|
* @param {string} subUnitId - 工作表的唯一标识符。
|
* @returns 返回计算出的打印范围,包含页面范围、冻结信息等。
|
*/
|
private _computePrintSheetRange(subUnitId: string) {
|
// 获取工作簿实例
|
const workbook = this._getWorkbook();
|
|
// 如果没有获取到工作簿,返回 undefined
|
if (!workbook) {
|
return;
|
}
|
|
// 获取工作簿的唯一标识符
|
const unitId = workbook!.getUnitId();
|
|
// 获取当前工作表
|
const worksheet = workbook!.getSheetBySheetId(subUnitId);
|
|
// 如果工作表不存在,返回 undefined
|
if (!worksheet) {
|
return;
|
}
|
|
// 获取工作表的骨架信息,包含如行列宽度、冻结状态等数据
|
const skeleton = this._getUnitSkeleton(unitId, subUnitId);
|
|
// 如果工作表的骨架信息不存在,返回 undefined
|
if (!skeleton) {
|
return;
|
}
|
|
// 获取工作表的冻结信息,包括冻结行列位置
|
const freeze = worksheet?.getFreeze();
|
|
// 如果冻结信息不存在,返回 undefined
|
if (!freeze) {
|
return;
|
}
|
|
// 获取当前工作表的单元格矩阵打印范围
|
const cellMatrixPrintRange = worksheet?.getCellMatrixPrintRange();
|
|
// 如果没有获取到单元格矩阵打印范围,返回 undefined
|
if (!cellMatrixPrintRange) {
|
return;
|
}
|
|
// 获取当前工作表骨架中的溢出缓存数据
|
const { overflowCache } = skeleton;
|
if (overflowCache) {
|
// 如果有溢出缓存,遍历其值并调整打印范围
|
overflowCache.forValue((_row: any, _col: any, value: any) => {
|
const { endColumn } = value;
|
|
// 如果溢出的单元格列超过当前打印范围的结束列,则调整打印范围
|
if (endColumn > cellMatrixPrintRange?.endColumn) {
|
cellMatrixPrintRange.endColumn = endColumn;
|
}
|
});
|
}
|
|
// 使用拦截器调整打印范围
|
const realCellMatrixPrintRange = this._sheetPrintInterceptorService.interceptor?.fetchThroughInterceptors(
|
this._sheetPrintInterceptorService.interceptor?.getInterceptPoints()?.PRINTING_RANGE,
|
)(cellMatrixPrintRange, { subUnitId, unitId });
|
|
// 返回最终计算出的打印页面范围,包括冻结行列的调整
|
return this._computePrintPageRange(subUnitId, {
|
// 调整起始行列位置,确保不小于零
|
...realCellMatrixPrintRange,
|
startColumn: Math.max(freeze.startColumn - freeze.xSplit, 0),
|
startRow: Math.max(freeze.startRow - freeze.ySplit, 0),
|
});
|
}
|
|
/**
|
* 获取当前配置的纸张边距信息。
|
* @returns 返回包含边距信息的对象,或者返回 `null`。
|
*/
|
private _getPaperPadding() {
|
// 从配置参数中解构获取边距类型(padding)
|
const { padding } = this._configParams ?? {};
|
|
// 根据边距类型从预定义的边距配置映射中查找对应的边距值
|
const paperPadding = JnpfPaperPaddingForType[padding as keyof typeof JnpfPaperPaddingForType] ?? {};
|
|
// 如果找到有效的边距配置,返回边距配置;否则返回 null
|
return isEmptyObject(paperPadding) ? null : paperPadding;
|
}
|
|
/**
|
* 获取当前配置的纸张大小。
|
* @returns 返回包含宽度和高度的纸张尺寸对象,或者返回 `null`。
|
*/
|
private _getPaperSize() {
|
// 从配置参数中解构获取纸张类型(paperType)和方向(direction)
|
const { direction, paperType } = this._configParams ?? {};
|
|
// 根据纸张类型获取对应的尺寸数据
|
const paperSize = JnpfPrintPaperSizeForType[paperType as keyof typeof JnpfPrintPaperSizeForType] ?? {};
|
// 如果纸张尺寸为空,返回 null
|
if (isEmptyObject(paperSize)) {
|
return null;
|
}
|
|
// 根据打印方向返回调整后的纸张尺寸
|
return direction === JnpfPrintDirectionEnum?.portrait
|
? { ...paperSize } // 如果是纵向模式,直接返回原始纸张尺寸
|
: {
|
h: paperSize?.w,
|
w: paperSize?.h, // 如果是横向模式,交换宽度和高度
|
};
|
}
|
|
/**
|
* 获取指定单元的骨架信息。
|
* @param {string} unitId - 单元的唯一标识符。
|
* @param {string} subUnitId - 子单元的唯一标识符。
|
* @returns 返回单元骨架信息对象,如果未找到则返回空对象。
|
*/
|
private _getUnitSkeleton(unitId: string, subUnitId: string) {
|
// 从 `RenderManagerService` 中获取指定单元 ID 的渲染服务
|
return (this._renderManagerService
|
?.getRenderById(unitId) // 根据单元 ID 获取对应的渲染服务
|
?.with(SheetSkeletonManagerService) // 使用骨架管理服务进行扩展
|
?.getUnitSkeleton(unitId, subUnitId)?.skeleton ?? {}) as any; // 获取单元对应的骨架信息 // 如果骨架信息不存在,则返回空对象 // 强制将返回值类型设置为 `any`
|
}
|
|
/**
|
* 获取当前活动的工作簿实例。
|
* @returns 返回当前活动的工作簿实例,如果不存在则返回 `undefined`。
|
*/
|
private _getWorkbook() {
|
// 使用 `UniverInstanceService` 获取当前类型为 `UNIVER_SHEET` 的活动单元实例
|
return this._univerInstanceService.getCurrentUnitForType<Workbook>(UniverInstanceType.UNIVER_SHEET);
|
}
|
}
|