@@ -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