@@ -448,6 +448,26 @@ function calculateTypographyMetrics(
448448 } ;
449449}
450450
451+ /**
452+ * Wraps `calculateTypographyMetrics` and applies inline-image height override.
453+ *
454+ * Typography metrics (ascent, descent) stay text-based so the baseline doesn't
455+ * shift. When the line contains an inline image taller than the text line height,
456+ * lineHeight is expanded to the image height — matching Word's behaviour where
457+ * the text baseline stays fixed and the image occupies exactly its own height.
458+ */
459+ function finalizeLineMetrics (
460+ line : { maxFontSize : number ; maxFontInfo ?: FontInfo ; maxImageHeight ?: number } ,
461+ spacing ?: ParagraphSpacing ,
462+ ) : { ascent : number ; descent : number ; lineHeight : number } {
463+ const metrics = calculateTypographyMetrics ( line . maxFontSize , spacing , line . maxFontInfo ) ;
464+ const imageH = line . maxImageHeight ?? 0 ;
465+ if ( imageH > metrics . lineHeight ) {
466+ metrics . lineHeight = imageH ;
467+ }
468+ return metrics ;
469+ }
470+
451471/**
452472 * Calculates typography metrics for empty paragraphs.
453473 *
@@ -1048,6 +1068,8 @@ async function measureParagraphBlock(block: ParagraphBlock, maxWidth: number): P
10481068 maxFontSize : number ;
10491069 /** Font info for the run with maxFontSize, used for accurate typography metrics */
10501070 maxFontInfo ?: FontInfo ;
1071+ /** Tallest inline image on this line (pixels) */
1072+ maxImageHeight ?: number ;
10511073 maxWidth : number ;
10521074 segments : Line [ 'segments' ] ;
10531075 leaders ?: Line [ 'leaders' ] ;
@@ -1275,7 +1297,7 @@ async function measureParagraphBlock(block: ParagraphBlock, maxWidth: number): P
12751297
12761298 if ( ( run as Run ) . kind === 'break' ) {
12771299 if ( currentLine ) {
1278- const metrics = calculateTypographyMetrics ( currentLine . maxFontSize , spacing , currentLine . maxFontInfo ) ;
1300+ const metrics = finalizeLineMetrics ( currentLine , spacing ) ;
12791301 const lineBase = currentLine ;
12801302 const completedLine : Line = { ...lineBase , ...metrics } ;
12811303 addBarTabsToLine ( completedLine ) ;
@@ -1307,7 +1329,7 @@ async function measureParagraphBlock(block: ParagraphBlock, maxWidth: number): P
13071329 // For leading line breaks (before any text), use fallback font info for accurate height calculation
13081330 const lineBreakFontInfo = hasSeenTextRun ? undefined : fallbackFontInfo ;
13091331 if ( currentLine ) {
1310- const metrics = calculateTypographyMetrics ( currentLine . maxFontSize , spacing , currentLine . maxFontInfo ) ;
1332+ const metrics = finalizeLineMetrics ( currentLine , spacing ) ;
13111333 const completedLine : Line = {
13121334 ...currentLine ,
13131335 ...metrics ,
@@ -1489,7 +1511,8 @@ async function measureParagraphBlock(block: ParagraphBlock, maxWidth: number): P
14891511 toRun : runIndex ,
14901512 toChar : 1 , // Images are treated as single atomic units
14911513 width : imageWidth ,
1492- maxFontSize : imageHeight , // Use image height for line height calculation
1514+ maxFontSize : 0 ,
1515+ maxImageHeight : imageHeight ,
14931516 maxWidth : getEffectiveWidth ( lines . length === 0 ? initialAvailableWidth : bodyContentWidth ) ,
14941517 spaceCount : 0 ,
14951518 segments : [
@@ -1518,7 +1541,7 @@ async function measureParagraphBlock(block: ParagraphBlock, maxWidth: number): P
15181541 if ( ! skipFitCheck && currentLine . width + imageWidth > currentLine . maxWidth && currentLine . width > 0 ) {
15191542 // Image doesn't fit - finish current line and start new line with image
15201543 trimTrailingWrapSpaces ( currentLine ) ;
1521- const metrics = calculateTypographyMetrics ( currentLine . maxFontSize , spacing , currentLine . maxFontInfo ) ;
1544+ const metrics = finalizeLineMetrics ( currentLine , spacing ) ;
15221545 const lineBase = currentLine ;
15231546 const completedLine : Line = {
15241547 ...lineBase ,
@@ -1538,7 +1561,8 @@ async function measureParagraphBlock(block: ParagraphBlock, maxWidth: number): P
15381561 toRun : runIndex ,
15391562 toChar : 1 ,
15401563 width : imageWidth ,
1541- maxFontSize : imageHeight ,
1564+ maxFontSize : 0 ,
1565+ maxImageHeight : imageHeight ,
15421566 maxWidth : getEffectiveWidth ( bodyContentWidth ) ,
15431567 spaceCount : 0 ,
15441568 segments : [
@@ -1555,7 +1579,7 @@ async function measureParagraphBlock(block: ParagraphBlock, maxWidth: number): P
15551579 currentLine . toRun = runIndex ;
15561580 currentLine . toChar = 1 ;
15571581 currentLine . width = roundValue ( currentLine . width + imageWidth ) ;
1558- currentLine . maxFontSize = Math . max ( currentLine . maxFontSize , imageHeight ) ;
1582+ currentLine . maxImageHeight = Math . max ( currentLine . maxImageHeight ?? 0 , imageHeight ) ;
15591583 if ( ! currentLine . segments ) currentLine . segments = [ ] ;
15601584 currentLine . segments . push ( {
15611585 runIndex,
@@ -1668,7 +1692,7 @@ async function measureParagraphBlock(block: ParagraphBlock, maxWidth: number): P
16681692 if ( currentLine . width + annotationWidth > currentLine . maxWidth && currentLine . width > 0 ) {
16691693 // Doesn't fit - finish current line and start new one
16701694 trimTrailingWrapSpaces ( currentLine ) ;
1671- const metrics = calculateTypographyMetrics ( currentLine . maxFontSize , spacing , currentLine . maxFontInfo ) ;
1695+ const metrics = finalizeLineMetrics ( currentLine , spacing ) ;
16721696 const lineBase = currentLine ;
16731697 const completedLine : Line = {
16741698 ...lineBase ,
@@ -1772,7 +1796,7 @@ async function measureParagraphBlock(block: ParagraphBlock, maxWidth: number): P
17721796 currentLine . width > 0
17731797 ) {
17741798 trimTrailingWrapSpaces ( currentLine ) ;
1775- const metrics = calculateTypographyMetrics ( currentLine . maxFontSize , spacing , currentLine . maxFontInfo ) ;
1799+ const metrics = finalizeLineMetrics ( currentLine , spacing ) ;
17761800 const lineBase = currentLine ;
17771801 const completedLine : Line = {
17781802 ...lineBase ,
@@ -1886,7 +1910,7 @@ async function measureParagraphBlock(block: ParagraphBlock, maxWidth: number): P
18861910 ) {
18871911 // Space doesn't fit - finish current line and start new one with the space
18881912 trimTrailingWrapSpaces ( currentLine ) ;
1889- const metrics = calculateTypographyMetrics ( currentLine . maxFontSize , spacing , currentLine . maxFontInfo ) ;
1913+ const metrics = finalizeLineMetrics ( currentLine , spacing ) ;
18901914 const lineBase = currentLine ;
18911915 const completedLine : Line = {
18921916 ...lineBase ,
@@ -1969,7 +1993,7 @@ async function measureParagraphBlock(block: ParagraphBlock, maxWidth: number): P
19691993 // long word can use the pending tab alignment.
19701994 if ( currentLine && currentLine . width > 0 && currentLine . segments && currentLine . segments . length > 0 ) {
19711995 trimTrailingWrapSpaces ( currentLine ) ;
1972- const metrics = calculateTypographyMetrics ( currentLine . maxFontSize , spacing , currentLine . maxFontInfo ) ;
1996+ const metrics = finalizeLineMetrics ( currentLine , spacing ) ;
19731997 const lineBase = currentLine ;
19741998 const completedLine : Line = {
19751999 ...lineBase ,
@@ -2036,7 +2060,7 @@ async function measureParagraphBlock(block: ParagraphBlock, maxWidth: number): P
20362060 } else {
20372061 // More chunks to come - finish this line and push it
20382062 trimTrailingWrapSpaces ( currentLine ) ;
2039- const metrics = calculateTypographyMetrics ( currentLine . maxFontSize , spacing , currentLine . maxFontInfo ) ;
2063+ const metrics = finalizeLineMetrics ( currentLine , spacing ) ;
20402064 const lineBase = currentLine ;
20412065 const completedLine : Line = {
20422066 ...lineBase ,
@@ -2182,7 +2206,7 @@ async function measureParagraphBlock(block: ParagraphBlock, maxWidth: number): P
21822206
21832207 if ( shouldBreak ) {
21842208 trimTrailingWrapSpaces ( currentLine ) ;
2185- const metrics = calculateTypographyMetrics ( currentLine . maxFontSize , spacing , currentLine . maxFontInfo ) ;
2209+ const metrics = finalizeLineMetrics ( currentLine , spacing ) ;
21862210 const lineBase = currentLine ;
21872211 const completedLine : Line = {
21882212 ...lineBase ,
@@ -2247,7 +2271,7 @@ async function measureParagraphBlock(block: ParagraphBlock, maxWidth: number): P
22472271 appendSegment ( currentLine . segments , runIndex , wordStartChar , wordEndNoSpace , wordOnlyWidth , explicitXHere ) ;
22482272 // finish current line and start a new one on next iteration
22492273 trimTrailingWrapSpaces ( currentLine ) ;
2250- const metrics = calculateTypographyMetrics ( currentLine . maxFontSize , spacing , currentLine . maxFontInfo ) ;
2274+ const metrics = finalizeLineMetrics ( currentLine , spacing ) ;
22512275 const lineBase = currentLine ;
22522276 const completedLine : Line = { ...lineBase , ...metrics } ;
22532277 addBarTabsToLine ( completedLine ) ;
@@ -2386,7 +2410,7 @@ async function measureParagraphBlock(block: ParagraphBlock, maxWidth: number): P
23862410 }
23872411
23882412 if ( currentLine ) {
2389- const metrics = calculateTypographyMetrics ( currentLine . maxFontSize , spacing , currentLine . maxFontInfo ) ;
2413+ const metrics = finalizeLineMetrics ( currentLine , spacing ) ;
23902414 const lineBase = currentLine ;
23912415 const finalLine : Line = {
23922416 ...lineBase ,
0 commit comments