View Issue Details

IDProjectCategoryView StatusLast Update
0014913ScribusText Frames / Story Editorpublic2026-08-27 11:21
Reporterlongli Assigned To 
PriorityhighSeverityminorReproducibilityalways
Status newResolutionopen 
Summary0014913: the bug about Automatic Linespacing
Descriptionas title
Steps To Reproduce1.New TextFrame, input text.
2.Set TextFrame property to Automatic Linespacing.
3.Set the font size of TextFrame to 24pt, first paragraph to 12pt, second paragraph to 18pt, third paragraph to 24pt.

The line spacing of all paragraphs is displayed by the font size of the 24pt(24pt is TextFrame font size).
Tags#please_test
Attached Files
PatchNo

Activities

qirat

2026-08-27 11:21

reporter   ~0054341

Automatic leading should follow the paragraph’s effective base typography, not an inherited style value that may no longer match the rendered text. This patch fixed it in my testing.

About the patch:

#Cause
`pageitem_textframe.cpp` calculates Automatic leading from `ParagraphStyle::charStyle()` while text layout itself uses the resolved character formatting from `StoryText`. The wrap/rebreak path also had a related mismatch where font and font size could come from different character positions.

#Patch behaviour
- Uses the effective character style at the actual paragraph start as the basis for Automatic leading.
- Keeps that basis when the paragraph continues into another linked frame.
- Uses the same calculation in normal layout and wrap/rebreak paths.
- Keeps mixed-size runs inside one paragraph from arbitrarily changing its base leading.
- Fixed Line Spacing and Baseline Grid are unchanged.

#File touched
- scribus/pageitem_textframe.cpp
automatic-line-spacing-effective-font-v1.0.patch (6,177 bytes)   
Index: scribus/pageitem_textframe.cpp
===================================================================
--- scribus/pageitem_textframe.cpp	(revision 27785)
+++ scribus/pageitem_textframe.cpp	(working copy)
@@ -1071,19 +1071,31 @@
 	return res;
 }
 
-//cezaryece: I remove static statement as this function is used also by PageItem_NoteFrame
-double calculateLineSpacing (const ParagraphStyle &style, PageItem *item)
+static int findParagraphStart(const StoryText& story, int pos)
+{
+	while (pos > 0 && !story.isBlockStart(pos))
+		--pos;
+	return pos;
+}
+
+static double calculateLineSpacing(const ParagraphStyle& style, const CharStyle& charStyle, PageItem* item)
 {
 	if (style.lineSpacingMode() == ParagraphStyle::AutomaticLineSpacing)
 	{
-		double autoLS = static_cast<double>(item->doc()->typographicPrefs().autoLineSpacing) / 100.0;
-		return (style.charStyle().font().height(style.charStyle().fontSize() / 10.0) * autoLS);
+		const double autoLS = static_cast<double>(item->doc()->typographicPrefs().autoLineSpacing) / 100.0;
+		return charStyle.font().height(charStyle.fontSize() / 10.0) * autoLS;
 	}
 	if (style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing)
 		return item->doc()->guidesPrefs().valueBaselineGrid;
 	return style.lineSpacing();
 }
 
+//cezaryece: I remove static statement as this function is used also by PageItem_NoteFrame
+double calculateLineSpacing (const ParagraphStyle &style, PageItem *item)
+{
+	return calculateLineSpacing(style, style.charStyle(), item);
+}
+
 
 // This assumes that layout() ran on the previous page and set the incomplete* vars
 // It also clears the incomplete* vars, and changes the starting position for this frame
@@ -1346,21 +1358,23 @@
 
 		lastLineY = m_textDistanceMargins.top();
 
-		//automatic line spacing factor (calculated once)
-		double autoLS = static_cast<double>(context->typographicPrefs().autoLineSpacing) / 100.0;
+		int paragraphStart = 0;
+		CharStyle paragraphCharStyle = itemText.defaultStyle().charStyle();
 
 		// find start of first line
 		if (firstInFrame() < itLen)
 		{
 			const CharStyle& cstyle = itemText.charStyle(firstInFrame());
+			paragraphStart = findParagraphStart(itemText, firstInFrame());
+			paragraphCharStyle = itemText.charStyle(paragraphStart);
 			style = itemText.paragraphStyle(firstInFrame());
-			style.setLineSpacing (calculateLineSpacing (style, this));
+			style.setLineSpacing(calculateLineSpacing(style, paragraphCharStyle, this));
 
 //			qDebug() << QString("style @0: %1 -- %2, %4/%5 char: %3").arg(style.leftMargin()).arg(style.rightMargin())
 //				   .arg(style.charStyle().asString()).arg(style.name()).arg(style.parent()?style.parent()->name():"");
 			if (style.hasDropCap())
 			{
-				chs = calculateLineSpacing (style, this) * style.dropCapLines() * 10;
+				chs = style.lineSpacing() * style.dropCapLines() * 10;
 			}
 			else
 				chs = cstyle.fontSize();
@@ -1432,7 +1446,8 @@
 			}
 
 			BulNumMode = false;
-			if (itemText.isBlockStart(a))
+			const bool isParagraphStart = itemText.isBlockStart(a);
+			if (isParagraphStart)
 			{
 				if (currentIndex > 0)
 				{
@@ -1464,12 +1479,17 @@
 			}
 			//--<#13490
 			CharStyle charStyle = ((itemText.text(a) != SpecialChars::PARSEP) ? itemText.charStyle(a) : style.charStyle());
+			if (isParagraphStart)
+			{
+				paragraphStart = a;
+				paragraphCharStyle = charStyle;
+				style.setLineSpacing(calculateLineSpacing(style, paragraphCharStyle, this));
+			}
 
 			double hlcsize10 = charStyle.fontSize() / 10.0;
 			double scaleV = charStyle.scaleV() / 1000.0;
 			double scaleH = charStyle.scaleH() / 1000.0;
 			double offset = hlcsize10 * (charStyle.baselineOffset() / 1000.0);
-			style.setLineSpacing (calculateLineSpacing (style, this));
 			FlopBaseline = (current.startOfCol && firstLineOffset() == FLOPBaselineGrid);
 
 			// find out about par gap and dropcap
@@ -1557,7 +1577,7 @@
 			// find charsize factors
 			if (DropCmode)
 			{
-				DropCapDrop = calculateLineSpacing (style, this) * (DropLines - 1);
+				DropCapDrop = style.lineSpacing() * (DropLines - 1);
 
 				//text height, width, ascent and descent should be calculated for whole text provided by ScText in current position
 				//and that may be more than one char (variable text for example)
@@ -1636,7 +1656,7 @@
 						itemHeight = font.height(style.charStyle().fontSize() / 10.0);
 					asce = currentObjectBox.height();
 					wide = currentObjectBox.width();
-					realAsce = calculateLineSpacing (style, this) * DropLines;
+					realAsce = style.lineSpacing() * DropLines;
 					glyphCluster.setScaleH(glyphCluster.scaleH() / glyphCluster.scaleV());
 					glyphCluster.setScaleV(realAsce / itemHeight);
 					glyphCluster.setScaleH(glyphCluster.scaleH() * glyphCluster.scaleV());
@@ -2511,7 +2531,7 @@
 							current.dropCapWidth += overhang + leftBleed; // follow-line reserve matches
 						}
 					}
-					double spacing = calculateLineSpacing (style, this);
+					double spacing = style.lineSpacing();
 					current.yPos -= spacing * (DropLines - 1);
 					if (style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing)
 						current.yPos = adjustToBaselineGrid (current, this, OwnPage);
@@ -2615,12 +2635,14 @@
 						a = glyphClusters.at(i).firstChar();
 						currentIndex = i - current.lineData.firstCluster;
 						style = itemText.paragraphStyle(a);
-						const_cast<ScFace&>(font) = itemText.charStyle(a).font();
-						if (style.lineSpacingMode() == ParagraphStyle::AutomaticLineSpacing)
-							style.setLineSpacing(font.height(hlcsize10) * autoLS);
-						else if (style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing)
-							style.setLineSpacing(m_Doc->guidesPrefs().valueBaselineGrid);
 						charStyle = itemText.charStyle(a);
+						const_cast<ScFace&>(font) = charStyle.font();
+						if (a < paragraphStart || itemText.isBlockStart(a))
+						{
+							paragraphStart = findParagraphStart(itemText, a);
+							paragraphCharStyle = itemText.charStyle(paragraphStart);
+						}
+						style.setLineSpacing(calculateLineSpacing(style, paragraphCharStyle, this));
 					}
 					assert( i >= 0 );
 					assert( i < glyphClusters.length() );

Issue History

Date Modified Username Field Change
2017-07-15 10:29 longli New Issue
2017-07-15 10:29 longli File Added: Automatic Linespacing.sla
2025-04-27 19:16 cbradney Category Story Editor / Text Frames => Text Frames / Story Editor
2026-08-27 11:21 qirat Note Added: 0054341
2026-08-27 11:21 qirat File Added: automatic-line-spacing-effective-font-v1.0.patch
2026-08-27 11:21 qirat Tag Attached: #please_test