Skip to content

Commit 53df0d6

Browse files
committed
fix(pdf): rowspans and row island headers
1 parent 995a4b8 commit 53df0d6

File tree

1 file changed

+52
-27
lines changed

1 file changed

+52
-27
lines changed

projects/igniteui-angular/src/lib/services/pdf/pdf-exporter.ts

Lines changed: 52 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -342,45 +342,70 @@ export class IgxPdfExporterService extends IgxBaseExporter {
342342

343343
const rowDimensionOffset = rowDimensionHeaders.length * baseColumnWidth;
344344

345+
const totalHeaderLevels = maxLevel + 1;
346+
347+
// Map layout positions based on actual leaf order so headers align with child data columns
348+
const headerLayoutMap = new Map<any, number>();
349+
const leafHeaders = columnHeaders
350+
.filter(col => col.headerType === ExportHeaderType.ColumnHeader && col.columnSpan > 0)
351+
.sort((a, b) => (a.startIndex ?? 0) - (b.startIndex ?? 0));
352+
353+
leafHeaders.forEach((col, idx) => headerLayoutMap.set(col, idx));
354+
355+
const resolveLayoutStartIndex = (col: any): number => {
356+
if (headerLayoutMap.has(col)) {
357+
return headerLayoutMap.get(col)!;
358+
}
359+
360+
if (col.headerType === ExportHeaderType.MultiColumnHeader) {
361+
const childColumns = columnHeaders.filter(child =>
362+
child.columnGroupParent === col.columnGroup && child.columnSpan > 0);
363+
const childIndices = childColumns.map(child => resolveLayoutStartIndex(child));
364+
365+
if (childIndices.length > 0) {
366+
const minIndex = Math.min(...childIndices);
367+
headerLayoutMap.set(col, minIndex);
368+
return minIndex;
369+
}
370+
}
371+
372+
headerLayoutMap.set(col, 0);
373+
return 0;
374+
};
375+
345376
// Draw column headers level by level (from top/parent to bottom/children)
346377
for (let level = 0; level <= maxLevel; level++) {
347378
// Get headers for this level
348-
const headersForLevel = columnHeaders.filter(col => {
349-
// Include multi-column headers at this level
350-
if (col.level === level && col.headerType === ExportHeaderType.MultiColumnHeader) {
351-
return true;
352-
}
353-
// Include leaf column headers at this level
354-
if (col.level === level && col.headerType === ExportHeaderType.ColumnHeader) {
355-
return true;
356-
}
357-
// For levels > 0, include leaf columns from earlier levels that need to span down
358-
if (level > 0 && col.level < level && col.headerType === ExportHeaderType.ColumnHeader) {
359-
return true;
360-
}
361-
return false;
362-
}).filter(col => col.columnSpan > 0);
363-
364-
if (headersForLevel.length === 0) continue;
379+
const headersForLevel = columnHeaders
380+
.filter(col =>
381+
col.level === level &&
382+
(col.headerType === ExportHeaderType.MultiColumnHeader || col.headerType === ExportHeaderType.ColumnHeader)
383+
)
384+
.filter(col => col.columnSpan > 0);
385+
386+
if (headersForLevel.length === 0) {
387+
yPosition += headerHeight;
388+
continue;
389+
}
365390

366391
// Sort by startIndex to maintain order
367392
headersForLevel.sort((a, b) => a.startIndex - b.startIndex);
368393

369-
// Draw background
370-
pdf.setFillColor(240, 240, 240);
371-
if (options.showTableBorders) {
372-
pdf.rect(xStart + rowDimensionOffset, yPosition, tableWidth - rowDimensionOffset, headerHeight, 'F');
373-
}
374-
375394
// Draw each header in this level
376395
headersForLevel.forEach((col, idx) => {
377396
const colSpan = col.columnSpan || 1;
378397
const width = baseColumnWidth * colSpan;
379-
const startIndex = col.startIndex !== -1 ? col.startIndex : idx;
380-
const xPosition = xStart + rowDimensionOffset + (startIndex * baseColumnWidth);
398+
const normalizedStartIndex = resolveLayoutStartIndex(col);
399+
const xPosition = xStart + rowDimensionOffset + (normalizedStartIndex * baseColumnWidth);
400+
const rowSpan = col.headerType === ExportHeaderType.ColumnHeader ?
401+
Math.max(1, (totalHeaderLevels - (col.level ?? 0))) :
402+
1;
403+
const height = headerHeight * rowSpan;
381404

382405
if (options.showTableBorders) {
383-
pdf.rect(xPosition, yPosition, width, headerHeight);
406+
pdf.setFillColor(240, 240, 240);
407+
pdf.rect(xPosition, yPosition, width, height, 'F');
408+
pdf.rect(xPosition, yPosition, width, height);
384409
}
385410

386411
// Center text in cell with truncation if needed
@@ -397,7 +422,7 @@ export class IgxPdfExporterService extends IgxBaseExporter {
397422

398423
const textWidth = pdf.getTextWidth(headerText);
399424
const textX = xPosition + (width - textWidth) / 2;
400-
const textY = yPosition + headerHeight / 2 + options.fontSize / 3;
425+
const textY = yPosition + (height / 2) + options.fontSize / 3;
401426

402427
pdf.text(headerText, textX, textY);
403428
});

0 commit comments

Comments
 (0)