@flowaccount/pdfmake 1.0.7-staging.1 → 1.0.7-staging.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flowaccount/pdfmake",
3
- "version": "1.0.7-staging.1",
3
+ "version": "1.0.7-staging.4",
4
4
  "description": "Client/server side PDF printing in pure JavaScript",
5
5
  "main": "src/printer.js",
6
6
  "browser": "build/pdfmake.js",
@@ -538,13 +538,32 @@ LayoutBuilder.prototype.processNode = function (node) {
538
538
  startY: ctx.y,
539
539
  startPage: ctx.page,
540
540
  availableHeight: ctx.availableHeight,
541
- itemCount: currentPage ? currentPage.items.length : 0
541
+ itemCount: currentPage ? currentPage.items.length : 0,
542
+ x: ctx.x,
543
+ summaryRenderedPage: null
542
544
  };
543
545
  }
544
546
 
545
547
  // Process the stack
546
548
  self.processVerticalContainer(node);
547
549
 
550
+ // Track which page summary was rendered on (summary is first item, unbreakable)
551
+ if (preRenderState && node.stack && node.stack[0]) {
552
+ var summaryNode = node.stack[0];
553
+ if (summaryNode.summary || summaryNode.unbreakable) {
554
+ // Summary is unbreakable, check if it was pushed to a new page
555
+ // by comparing its height with available space at startPage
556
+ var summaryHeight = Math.abs(summaryNode._height || 0);
557
+ if (summaryHeight <= preRenderState.availableHeight) {
558
+ // Summary fit on startPage
559
+ preRenderState.summaryRenderedPage = preRenderState.startPage;
560
+ } else {
561
+ // Summary was pushed to next page
562
+ preRenderState.summaryRenderedPage = preRenderState.startPage + 1;
563
+ }
564
+ }
565
+ }
566
+
548
567
  // Post-render repositioning
549
568
  if (footerGapEnabled && preRenderState) {
550
569
  var ctx = self.writer.context();
@@ -683,6 +702,132 @@ LayoutBuilder.prototype.processNode = function (node) {
683
702
  // Intermediate pages (remark only) should not be repositioned
684
703
  var pageIdx = ctx.page;
685
704
  if (pageIdx > preRenderState.startPage) {
705
+ // Also reposition items on the START page (where summary might be)
706
+ var startPage = pages[preRenderState.startPage];
707
+ if (startPage && startPage.items && startPage.items.length > 0) {
708
+ var startPageHeight = startPage.pageSize.height;
709
+ var startPageBottom = startPageHeight - pageMargins.bottom;
710
+
711
+ // Find content bottom on start page
712
+ var startContentBottom = 0;
713
+ for (var si = 0; si < startPage.items.length; si++) {
714
+ var sItem = startPage.items[si];
715
+ var sItemBottom = 0;
716
+ if (sItem.item) {
717
+ if (typeof sItem.item.y === 'number') {
718
+ var sh = 0;
719
+ // Line items have getHeight() method
720
+ if (sItem.type === 'line' && typeof sItem.item.getHeight === 'function') {
721
+ sh = sItem.item.getHeight();
722
+ } else {
723
+ sh = sItem.item.h || sItem.item.height || 0;
724
+ }
725
+ sItemBottom = sItem.item.y + sh;
726
+ }
727
+ if (typeof sItem.item.y2 === 'number' && sItem.item.y2 > sItemBottom) {
728
+ sItemBottom = sItem.item.y2;
729
+ }
730
+ }
731
+ if (sItemBottom > startContentBottom) {
732
+ startContentBottom = sItemBottom;
733
+ }
734
+ }
735
+
736
+ // Calculate gap and move items on start page
737
+ var startGapHeight = startPageBottom - startContentBottom;
738
+ if (startGapHeight > 0) {
739
+ var startStackStartY = preRenderState.startY;
740
+ for (var si = 0; si < startPage.items.length; si++) {
741
+ var sItem = startPage.items[si];
742
+ var sItemY = null;
743
+ if (sItem.item && typeof sItem.item.y1 === 'number') {
744
+ sItemY = sItem.item.y1;
745
+ } else if (sItem.item && typeof sItem.item.y === 'number') {
746
+ sItemY = sItem.item.y;
747
+ } else if (sItem.item && sItem.item.points && Array.isArray(sItem.item.points) && sItem.item.points.length > 0) {
748
+ // For polylines, use the first point's Y
749
+ sItemY = sItem.item.points[0].y;
750
+ }
751
+
752
+ // Skip items fully above footer start
753
+ if (sItemY === null || sItemY < startStackStartY) continue;
754
+
755
+ // Move item down
756
+ if (sItem.item && typeof sItem.item.y === 'number') {
757
+ sItem.item.y += startGapHeight;
758
+ }
759
+ if (sItem.item && typeof sItem.item.y1 === 'number') {
760
+ sItem.item.y1 += startGapHeight;
761
+ }
762
+ if (sItem.item && typeof sItem.item.y2 === 'number') {
763
+ sItem.item.y2 += startGapHeight;
764
+ }
765
+ // Handle polylines (points array)
766
+ if (sItem.item && sItem.item.points && Array.isArray(sItem.item.points)) {
767
+ for (var spi = 0; spi < sItem.item.points.length; spi++) {
768
+ if (typeof sItem.item.points[spi].y === 'number') {
769
+ sItem.item.points[spi].y += startGapHeight;
770
+ }
771
+ }
772
+ }
773
+ }
774
+
775
+ // MULTI-PAGE: Draw guide lines in the gap area (same as single-page)
776
+ var colSpec = node._footerGapOption && node._footerGapOption.columns ? node._footerGapOption.columns : (self._footerGapOption && self._footerGapOption.columns);
777
+ if (colSpec) {
778
+ var rawWidths = colSpec.content && colSpec.content.vLines ? colSpec.content.vLines : [];
779
+ if (rawWidths && rawWidths.length > 1) {
780
+ var style = (colSpec.style || {});
781
+ var lw = style.lineWidth != null ? style.lineWidth : 0.5;
782
+ var lc = style.color || '#000000';
783
+ var dashCfg = style.dash;
784
+ var includeOuter = colSpec.includeOuter !== false;
785
+ var startIndex = includeOuter ? 0 : 1;
786
+ var endIndex = includeOuter ? rawWidths.length : rawWidths.length - 1;
787
+ var pageX = preRenderState.x || pageMargins.left;
788
+
789
+ for (var ci = startIndex; ci < endIndex; ci++) {
790
+ var xGuide = pageX + rawWidths[ci] - 0.25;
791
+ // Extend first and last vertical lines to content bottom after moving
792
+ var isFirstOrLast = (ci === 0) || (ci === rawWidths.length - 1);
793
+ var vLineY2 = isFirstOrLast ? (startContentBottom + startGapHeight) : (startStackStartY + startGapHeight);
794
+ startPage.items.push({
795
+ type: 'vector',
796
+ item: {
797
+ type: 'line',
798
+ x1: xGuide,
799
+ y1: startStackStartY,
800
+ x2: xGuide,
801
+ y2: vLineY2,
802
+ lineWidth: lw,
803
+ lineColor: lc,
804
+ dash: dashCfg ? {
805
+ length: dashCfg.length,
806
+ space: dashCfg.space != null ? dashCfg.space : dashCfg.gap
807
+ } : undefined,
808
+ _footerGuideLine: true
809
+ }
810
+ });
811
+ }
812
+
813
+ // Remove existing horizontal line at startStackStartY (drawn by tableProcessor)
814
+ // It's at wrong position now - we'll draw new one at bottom of gap
815
+ for (var ri = startPage.items.length - 1; ri >= 0; ri--) {
816
+ var rItem = startPage.items[ri];
817
+ if (rItem.type === 'vector' && rItem.item && rItem.item.type === 'line') {
818
+ // Check if it's a horizontal line at startStackStartY
819
+ if (Math.abs(rItem.item.y1 - startStackStartY) < 1 &&
820
+ Math.abs(rItem.item.y2 - startStackStartY) < 1) {
821
+ startPage.items.splice(ri, 1);
822
+ break;
823
+ }
824
+ }
825
+ }
826
+ }
827
+ }
828
+ }
829
+ }
830
+
686
831
  var page = pages[pageIdx];
687
832
  if (page && page.items && page.items.length > 0) {
688
833
  var pageHeight = page.pageSize.height;
@@ -703,19 +848,35 @@ LayoutBuilder.prototype.processNode = function (node) {
703
848
  if (typeof item.item.y2 === 'number' && item.item.y2 > itemBottom) {
704
849
  itemBottom = item.item.y2;
705
850
  }
851
+ // Handle polylines (points array)
852
+ if (item.item.points && Array.isArray(item.item.points)) {
853
+ for (var pi = 0; pi < item.item.points.length; pi++) {
854
+ if (typeof item.item.points[pi].y === 'number' && item.item.points[pi].y > itemBottom) {
855
+ itemBottom = item.item.points[pi].y;
856
+ }
857
+ }
858
+ }
706
859
  }
707
860
  if (itemBottom > contentBottom) {
708
861
  contentBottom = itemBottom;
709
862
  }
710
863
  }
711
864
 
712
- // Footer starts at contentBottom - footerHeight
713
- var stackStartY = contentBottom - stackHeight;
865
+ // Check if summary is on the last page
866
+ var summaryOnLastPage = (preRenderState.summaryRenderedPage === pageIdx);
714
867
 
715
- // Calculate gap: how much to move footer down
716
- var gapHeight = pageBottom - contentBottom;
717
- if (gapHeight > 0) {
718
- // Only move items that are part of footer (Y >= footerStartY)
868
+ var stackStartY;
869
+ if (summaryOnLastPage) {
870
+ // Summary is on this page - move all: summary + remark + footer together
871
+ // Calculate header threshold for this page
872
+ var repeatableHeaderHeight = 0;
873
+ for (var ri = 0; ri < self.writer.repeatables.length; ri++) {
874
+ repeatableHeaderHeight += self.writer.repeatables[ri].height;
875
+ }
876
+ var headerThreshold = pageMargins.top + repeatableHeaderHeight;
877
+
878
+ // Find minimum Y of all items below header threshold
879
+ stackStartY = contentBottom;
719
880
  for (var i = 0; i < page.items.length; i++) {
720
881
  var item = page.items[i];
721
882
  var itemY = null;
@@ -724,23 +885,92 @@ LayoutBuilder.prototype.processNode = function (node) {
724
885
  } else if (item.item && typeof item.item.y === 'number') {
725
886
  itemY = item.item.y;
726
887
  } else if (item.item && item.item.points && Array.isArray(item.item.points) && item.item.points.length > 0) {
727
- // For polylines, use the first point's Y
728
888
  itemY = item.item.points[0].y;
729
889
  }
890
+ if (itemY !== null && itemY >= headerThreshold && itemY < stackStartY) {
891
+ stackStartY = itemY;
892
+ }
893
+ }
730
894
 
731
- // Skip items above footer
732
- if (itemY === null || itemY < stackStartY) continue;
895
+ // Draw guide lines in the gap area (same as single-page)
896
+ var gapHeightForLines = pageBottom - contentBottom;
897
+ if (gapHeightForLines > 0) {
898
+ var colSpec = node._footerGapOption && node._footerGapOption.columns ? node._footerGapOption.columns : (self._footerGapOption && self._footerGapOption.columns);
899
+ if (colSpec) {
900
+ var rawWidths = colSpec.content && colSpec.content.vLines ? colSpec.content.vLines : [];
901
+ if (rawWidths && rawWidths.length > 1) {
902
+ var style = (colSpec.style || {});
903
+ var lw = style.lineWidth != null ? style.lineWidth : 0.5;
904
+ var lc = style.color || '#000000';
905
+ var dashCfg = style.dash;
906
+ var includeOuter = colSpec.includeOuter !== false;
907
+ var startIndex = includeOuter ? 0 : 1;
908
+ var endIndex = includeOuter ? rawWidths.length : rawWidths.length - 1;
909
+ var pageX = preRenderState.x || pageMargins.left;
910
+
911
+ for (var ci = startIndex; ci < endIndex; ci++) {
912
+ var xGuide = pageX + rawWidths[ci] - 0.25;
913
+ page.items.push({
914
+ type: 'vector',
915
+ item: {
916
+ type: 'line',
917
+ x1: xGuide,
918
+ y1: stackStartY,
919
+ x2: xGuide,
920
+ y2: stackStartY + gapHeightForLines,
921
+ lineWidth: lw,
922
+ lineColor: lc,
923
+ dash: dashCfg ? {
924
+ length: dashCfg.length,
925
+ space: dashCfg.space != null ? dashCfg.space : dashCfg.gap
926
+ } : undefined,
927
+ _footerGuideLine: true
928
+ }
929
+ });
930
+ }
931
+ }
932
+ }
933
+ }
934
+ } else {
935
+ // Summary is NOT on this page - move only footer
936
+ stackStartY = contentBottom - stackHeight;
937
+ }
733
938
 
734
- // Don't move items that are near the top margin (table headers)
735
- // Use actual header height from repeatables instead of hardcoded threshold
939
+ // Calculate gap: how much to move footer stack down
940
+ var gapHeight = pageBottom - contentBottom;
941
+ if (gapHeight > 0) {
942
+ // Calculate header threshold if not already calculated
943
+ if (typeof headerThreshold === 'undefined') {
736
944
  var repeatableHeaderHeight = 0;
737
945
  for (var ri = 0; ri < self.writer.repeatables.length; ri++) {
738
946
  repeatableHeaderHeight += self.writer.repeatables[ri].height;
739
947
  }
740
- var headerThreshold = pageMargins.top + repeatableHeaderHeight;
948
+ headerThreshold = pageMargins.top + repeatableHeaderHeight;
949
+ }
950
+
951
+ // Move all items that are part of footer stack (Y >= stackStartY)
952
+ for (var i = 0; i < page.items.length; i++) {
953
+ var item = page.items[i];
954
+
955
+ // Skip guide lines (they're already positioned correctly)
956
+ if (item.item && item.item._footerGuideLine) continue;
957
+
958
+ var itemY = null;
959
+ if (item.item && typeof item.item.y1 === 'number') {
960
+ itemY = item.item.y1;
961
+ } else if (item.item && typeof item.item.y === 'number') {
962
+ itemY = item.item.y;
963
+ } else if (item.item && item.item.points && Array.isArray(item.item.points) && item.item.points.length > 0) {
964
+ itemY = item.item.points[0].y;
965
+ }
966
+
967
+ // Skip items above footer stack
968
+ if (itemY === null || itemY < stackStartY) continue;
969
+
970
+ // Skip items in header area (repeated headers)
741
971
  if (itemY < headerThreshold) continue;
742
972
 
743
- // Move footer item down
973
+ // Move footer stack item down
744
974
  if (item.item && typeof item.item.y === 'number') {
745
975
  item.item.y += gapHeight;
746
976
  }
@@ -560,11 +560,13 @@ TableProcessor.prototype.endRow = function (rowIndex, writer, pageBreaks, nextRo
560
560
  var ctx = writer.context();
561
561
  var currentPage = ctx.getCurrentPage && ctx.getCurrentPage();
562
562
  var pageHeight = currentPage ? (currentPage.pageSize.height - ctx.pageMargins.bottom) : 0;
563
-
563
+ var footerGapOptBreak = ctx._footerGapOption;
564
+ var hLineY = (footerGapOptBreak && footerGapOptBreak.enabled) ? pageHeight - 0.25 : y2;
564
565
  if (willBreak && this.layout.hLineWhenBroken !== false) {
565
566
  // Check if we're at the true page bottom
566
567
  var isAtTruePageBottom = (pageHeight - y2) <= nearBottomThreshold;
567
- this.drawHorizontalLine(rowIndex + 1, writer, y2, true, null, isAtTruePageBottom);
568
+ // Use pageHeight for footer gap tables, y2 for normal tables
569
+ this.drawHorizontalLine(rowIndex + 1, writer, hLineY, true, null, isAtTruePageBottom);
568
570
  }
569
571
  if (rowBreakWithoutHeader && this.layout.hLineWhenBroken !== false) {
570
572
  // Check if previous segment ended at true page bottom
@@ -573,6 +575,10 @@ TableProcessor.prototype.endRow = function (rowIndex, writer, pageBreaks, nextRo
573
575
  this.drawHorizontalLine(rowIndex, writer, y1, true, null, prevWasAtPageBottom);
574
576
  }
575
577
 
578
+ // vLineY: ONLY use hLineY when BOTH willBreak AND footerGapOptBreak.enabled are true
579
+ // Otherwise use y2 (original behavior) to avoid NaN errors
580
+ var vLineY = (willBreak && footerGapOptBreak && footerGapOptBreak.enabled) ? hLineY : y2;
581
+
576
582
  for (i = 0, l = xs.length; i < l; i++) {
577
583
  var leftCellBorder = false;
578
584
  var rightCellBorder = false;
@@ -599,7 +605,7 @@ TableProcessor.prototype.endRow = function (rowIndex, writer, pageBreaks, nextRo
599
605
  }
600
606
 
601
607
  if (leftCellBorder) {
602
- this.drawVerticalLine(xs[i].x, y1 - hzLineOffset, y2 + this.bottomLineWidth, xs[i].index, writer, rowIndex, xs[i - 1] ? xs[i - 1].index : null);
608
+ this.drawVerticalLine(xs[i].x, y1 - hzLineOffset, vLineY + this.bottomLineWidth, xs[i].index, writer, rowIndex, xs[i - 1] ? xs[i - 1].index : null);
603
609
  }
604
610
 
605
611
  if (i < l - 1) {
package/src/textTools.js CHANGED
@@ -317,7 +317,7 @@ function getStyleProperty(item, styleContextStack, property, defaultValue) {
317
317
  }
318
318
 
319
319
  if(property === 'bold' && (item.text === '' || item.text === '\u200B')) {
320
- return false
320
+ return false;
321
321
  }
322
322
 
323
323
  if (value !== null && value !== undefined) {