@@ -28,8 +28,8 @@ internal sealed class ConversionOptions
2828 /// <summary>Page bottom margin in points (default: 50).</summary>
2929 public float MarginBottom { get ; set ; } = 50 ;
3030
31- /// <summary>Padding between columns in points (default: 20 ).</summary>
32- public float ColumnPadding { get ; set ; } = 20 ;
31+ /// <summary>Padding between columns in points (default: 4 ).</summary>
32+ public float ColumnPadding { get ; set ; } = 4 ;
3333
3434 /// <summary>Line spacing multiplier (default: 1.6).</summary>
3535 public float LineSpacing { get ; set ; } = 1.6f ;
@@ -119,7 +119,7 @@ private static void RenderSheet(PdfDocument doc, ExcelSheet sheet, ConversionOpt
119119 var pageWidth = options . PageWidth ;
120120 var pageHeight = options . PageHeight ;
121121 var usableWidth = pageWidth - options . MarginLeft - options . MarginRight ;
122- var avgCharWidth = options . FontSize * 0.5f ;
122+ var avgCharWidth = options . FontSize * 0.47f ;
123123
124124 // Determine column widths first to decide on layout strategy
125125 var columnPadding = options . ColumnPadding ;
@@ -274,10 +274,27 @@ void EnsurePage()
274274 var cellText = row [ col ] . Text ;
275275 if ( ! string . IsNullOrEmpty ( cellText ) )
276276 {
277- var maxChars = Math . Max ( 1 , ( int ) ( colWidths [ i ] / avgCharWidth ) ) ;
278- var wrapped = WrapCellText ( cellText , maxChars ) ;
279- cellLines [ i ] = wrapped ;
280- if ( wrapped . Length > maxLinesInRow ) maxLinesInRow = wrapped . Length ;
277+ // Check if the next column in this row has content.
278+ // LibreOffice behaviour: clip when right-neighbour is non-empty,
279+ // otherwise let text overflow (word-wrap) into the empty space.
280+ var nextCol = col + 1 ;
281+ var nextHasContent = nextCol < row . Count && ! string . IsNullOrEmpty ( row [ nextCol ] . Text ) ;
282+
283+ if ( nextHasContent )
284+ {
285+ // Clip to column width — adjacent cell would be obscured otherwise.
286+ var maxChars = Math . Max ( 1 , ( int ) ( colWidths [ i ] / avgCharWidth ) ) ;
287+ var clipped = cellText . Length > maxChars ? cellText [ ..maxChars ] : cellText ;
288+ cellLines [ i ] = new [ ] { clipped } ;
289+ }
290+ else
291+ {
292+ // Overflow allowed: render the full text on one line without clipping.
293+ // This matches LibreOffice behaviour where text flows into adjacent empty cells
294+ // (or to the right margin when this is the last column).
295+ cellLines [ i ] = new [ ] { cellText } ;
296+ }
297+ maxLinesInRow = Math . Max ( maxLinesInRow , cellLines [ i ] . Length ) ;
281298 }
282299 else
283300 {
@@ -396,6 +413,9 @@ void EnsurePage()
396413 /// <summary>
397414 /// Wrap a single cell text into multiple lines at word boundaries.
398415 /// </summary>
416+ private static string [ ] WrapCellText ( string text , float widthPts , float avgCharWidth )
417+ => WrapCellText ( text , Math . Max ( 1 , ( int ) ( widthPts / avgCharWidth ) ) ) ;
418+
399419 private static string [ ] WrapCellText ( string text , int maxCharsPerLine )
400420 {
401421 if ( maxCharsPerLine <= 0 ) maxCharsPerLine = 1 ;
@@ -460,7 +480,7 @@ private static bool IsDefaultSheetName(string name)
460480 /// </summary>
461481 private static float [ ] CalculateNaturalColumnWidths ( ExcelSheet sheet , int maxCols , float usableWidth , ConversionOptions options )
462482 {
463- var avgCharWidth = options . FontSize * 0.5f ;
483+ var avgCharWidth = options . FontSize * 0.47f ;
464484 var colMaxLengths = new int [ maxCols ] ;
465485
466486 foreach ( var row in sheet . Rows )
@@ -493,9 +513,18 @@ private static float[] CalculateNaturalColumnWidths(ExcelSheet sheet, int maxCol
493513 // Clamp to reasonable bounds but respect the spreadsheet's intent
494514 widths [ i ] = Math . Clamp ( excelPts , minColWidth , maxColWidth ) ;
495515 }
516+ else if ( maxCols >= 2 )
517+ {
518+ // No explicit column widths — use Excel's default column width (8.43 char units).
519+ // This matches LibreOffice/Excel behaviour where unset multi-column sheets use the
520+ // workbook default, producing text clipping identical to the reference PDF.
521+ var defaultPts = ExcelSheet . CharUnitsToPoints ( 8.43f ) ;
522+ widths [ i ] = Math . Clamp ( defaultPts , minColWidth , maxColWidth ) ;
523+ }
496524 else
497525 {
498- // Fall back to content-based width
526+ // Single-column sheet: use content-based width so the column fills the page
527+ // (LibreOffice expands 1-column sheets to page width).
499528 var natural = ( Math . Max ( colMaxLengths [ i ] , 3 ) + 2 ) * avgCharWidth ;
500529 widths [ i ] = Math . Clamp ( natural , minColWidth , maxColWidth ) ;
501530 }
0 commit comments