刘光辉
11 小时以前 0dfe84494048ce27ba8449831782128412d3eb13
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
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);
  }
}