@@ -823,6 +823,7 @@ function split(chars, reflowRTL) {
823823 let MAX_LINE_SPACING = 5 ;
824824 let MIN_LINE_SPACING = - 2 ;
825825 let MAX_LINE_SPACING_CHANGE = 2 ;
826+ let INDENT_EPS = 10 ;
826827
827828 let isGapValid = ( gap ) => gap >= MIN_LINE_SPACING && gap <= MAX_LINE_SPACING ;
828829
@@ -831,6 +832,7 @@ function split(chars, reflowRTL) {
831832
832833
833834 let paragraphBreaks = [ ] ;
835+ let indentedBreaks = new Set ( ) ;
834836 for ( let i = 1 ; i < lineBreaks . length - 1 ; i ++ ) {
835837 let currentLine = { start : lineBreaks [ i - 1 ] , end : lineBreaks [ i ] - 1 } ;
836838 let nextLine = { start : lineBreaks [ i ] || 0 , end : lineBreaks [ i + 1 ] - 1 } ;
@@ -861,18 +863,61 @@ function split(chars, reflowRTL) {
861863 let currentLineFontName = mostCommonFontName ( chars . slice ( currentLine . start , currentLine . end ) ) ;
862864 let nextLineFontName = mostCommonFontName ( chars . slice ( nextLine . start , nextLine . end ) ) ;
863865
866+ let rotation = chars [ currentLine . start ] . rotation ;
867+ let isIndented = (
868+ rotation === 0 && nextRect [ 0 ] > currentRect [ 0 ] + INDENT_EPS
869+ || rotation === 90 && nextRect [ 1 ] > currentRect [ 1 ] + INDENT_EPS
870+ || rotation === 180 && currentRect [ 2 ] > nextRect [ 2 ] + INDENT_EPS
871+ || rotation === 270 && currentRect [ 3 ] > nextRect [ 3 ] + INDENT_EPS
872+ ) ;
873+
864874 if (
865875 // The lines shouldn't be in the same row
866876 ! allowGap
867877 || ! ( currentRect [ 1 ] > nextRect [ 3 ] )
868878 || currentLineFontName !== nextLineFontName && currentRect [ 2 ] < nextRect [ 2 ] - 10
869- || Math . abs ( currentLineHeight - nextLineHeight ) > 2 ) {
879+ || Math . abs ( currentLineHeight - nextLineHeight ) > 2
880+ // Next line is indented relative to the current line
881+ || isIndented ) {
870882 paragraphBreaks . push ( lineBreaks [ i ] ) ;
883+ if ( isIndented ) {
884+ indentedBreaks . add ( lineBreaks [ i ] ) ;
885+ }
871886 }
872887 }
873888
874889 paragraphBreaks . push ( chars . length ) ;
875890
891+ // Merge single-line paragraphs back into the previous paragraph
892+ // when they share the same font, to prevent over-splitting.
893+ // Skip breaks triggered by indentation, as those are intentional.
894+ let paragraphStarts = [ 0 ] ;
895+ for ( let pb of paragraphBreaks ) {
896+ if ( pb < chars . length ) {
897+ paragraphStarts . push ( pb ) ;
898+ }
899+ }
900+ let removedBreaks = new Set ( ) ;
901+ for ( let p = 1 ; p < paragraphStarts . length ; p ++ ) {
902+ let currentStart = paragraphStarts [ p ] ;
903+ if ( indentedBreaks . has ( currentStart ) ) {
904+ continue ;
905+ }
906+ let currentEnd = ( p + 1 < paragraphStarts . length ) ? paragraphStarts [ p + 1 ] : chars . length ;
907+ // Count lines in this paragraph
908+ let lineCount = 0 ;
909+ for ( let lb of lineBreaks ) {
910+ if ( lb >= currentStart && lb < currentEnd ) {
911+ lineCount ++ ;
912+ }
913+ }
914+ if ( lineCount === 1
915+ && chars [ currentStart ] . fontName === chars [ paragraphStarts [ p - 1 ] ] . fontName ) {
916+ removedBreaks . add ( currentStart ) ;
917+ }
918+ }
919+ paragraphBreaks = paragraphBreaks . filter ( pb => ! removedBreaks . has ( pb ) ) ;
920+
876921 for ( let paragraphBreak of paragraphBreaks ) {
877922 chars [ paragraphBreak - 1 ] . paragraphBreakAfter = true ;
878923 }
0 commit comments