fix-CJK-Latin-spacing-at-the-end-of-line.patch (6,137 bytes)   
commit e1bcddf70f55f06e89e2c47903e9ad2ee6d328a9
Author: Fuminobu TAKEYAMA <ftake@geeko.jp>
Date:   Wed May 6 18:08:33 2020 +0900
    #16102: Automatic spaces between CJK and Latin is inserted at the end of line
    
    To add spaces between a CJK and Latin letter according to the position
    in a line, this commit moves part of code adjusting those spaces to
    pageitem_textframe.cpp. Now the apace is inserted before a ltter, in
    a similar way to tracking.
    
    It also introduces ScLayout_CJKLatinSpace, representing places
    where that spaces are insterted.
diff --git a/scribus/pageitem_textframe.cpp b/scribus/pageitem_textframe.cpp
index 077688b12..6d4eecd1e 100644
--- a/scribus/pageitem_textframe.cpp
+++ b/scribus/pageitem_textframe.cpp
@@ -1643,6 +1643,14 @@ void PageItem_TextFrame::layout()
 
 //			glyphs->yadvance = 0;
 
+			// adjust space between CJK and Latin letter
+			if (!current.glyphs[currentIndex].hasFlag(ScLayout_StartOfLine) && current.glyphs[currentIndex].hasFlag(ScLayout_CJKLatinSpace))
+			{
+				double quaterEM = charStyle.fontSize() / 10 / 4;
+				current.glyphs[currentIndex].extraWidth += quaterEM;
+				current.glyphs[currentIndex].xoffset += quaterEM;
+			}
+
 			if (i == current.lineData.firstCluster && current.glyphs[currentIndex].hasFlag(ScLayout_CJKFence))
 			{
 				current.glyphs[currentIndex].extraWidth -= (charStyle.fontSize() / 10 / 2);
diff --git a/scribus/sctextstruct.h b/scribus/sctextstruct.h
index a3fae20ec..20cc1c258 100644
--- a/scribus/sctextstruct.h
+++ b/scribus/sctextstruct.h
@@ -56,7 +56,8 @@ enum LayoutFlags {
 	ScLayout_CJKFence         = 1 << 19,     // marks CJK fence glyph that needs spacing adjustment at start of line
 	ScLayout_NoBreakAfter     = 1 << 20,     // marks glyphs after which a line break cannot occur
 	ScLayout_NoBreakBefore    = 1 << 21,     // marks glyphs before which a line break cannot occur
-	ScLayout_JustificationTracking = 1 << 22 // marks place of tracking in justification (e.g. for Thai)
+	ScLayout_JustificationTracking = 1 << 22, // marks place of tracking in justification (e.g. for Thai)
+	ScLayout_CJKLatinSpace    = 1 << 23      // marks place of space between CJK and latin letter
 };
 
 
diff --git a/scribus/text/textshaper.cpp b/scribus/text/textshaper.cpp
index 3129e1644..81c561fd1 100644
--- a/scribus/text/textshaper.cpp
+++ b/scribus/text/textshaper.cpp
@@ -456,6 +456,7 @@ ShapedText TextShaper::shape(int fromPos, int toPos)
 
 			int firstStat = SpecialChars::getCJKAttr(m_story.text(firstChar));
 			int currStat  = (firstChar != lastChar) ? SpecialChars::getCJKAttr(m_story.text(lastChar)) : firstStat;
+			int prevStat = SpecialChars::getCJKAttr(m_story.text(firstChar - 1));
 
 			if (firstStat & SpecialChars::CJK_NOBREAK_BEFORE)
 				run.setFlag(ScLayout_NoBreakBefore);
@@ -465,7 +466,6 @@ ShapedText TextShaper::shape(int fromPos, int toPos)
 
 			if ((firstChar > 0) && (firstStat != 0) && ((firstStat & SpecialChars::CJK_NOBREAK_BEFORE) == 0))
 			{
-				int prevStat = SpecialChars::getCJKAttr(m_story.text(firstChar - 1));
 				if (prevStat != 0 && ((prevStat & SpecialChars::CJK_NOBREAK_AFTER) == 0))
 					run.setFlag(ScLayout_LineBoundary);
 			}
@@ -560,49 +560,49 @@ ShapedText TextShaper::shape(int fromPos, int toPos)
 			}
 
 			// Apply CJK spacing according to JIS X4051
-			if (lastChar + 1 < m_story.length())
-			{
-				double halfEM = run.style().fontSize() / 10 / 2;
-				double quarterEM = run.style().fontSize() / 10 / 4;
-
-				int nextStat = SpecialChars::getCJKAttr(m_story.text(lastChar + 1));
+			// https://www.w3.org/TR/jlreq/
 
-				// 1. add 1/4 aki (space) between a CJK letter and
-				//    - a latin letter
-				//    - an ASCII Digits
-				if (currStat != 0)
+			// 1. add 1/4 aki (space) between a CJK letter and
+			//    - a latin letter
+			//    - an ASCII digit
+			if (firstChar > 0) {
+				if (prevStat == 0)
 				{
-					// current char is CJK
-					if (SpecialChars::isLetterRequiringSpaceAroundCJK(m_story.text(lastChar + 1).unicode()))
-					{
+					// <Latin> <<CJK>>
+					if (SpecialChars::isLetterRequiringSpaceAroundCJK(m_story.text(firstChar - 1).unicode())) {
 						switch (currStat & SpecialChars::CJK_CHAR_MASK)
 						{
 							case SpecialChars::CJK_KANJI:
 							case SpecialChars::CJK_KANA:
 							case SpecialChars::CJK_NOTOP:
-								run.extraWidth += quarterEM;
+								run.setFlag(ScLayout_CJKLatinSpace);
 						}
 					}
 				}
 				else
 				{
-					// current char is not CJK
-					if (SpecialChars::isLetterRequiringSpaceAroundCJK(m_story.text(lastChar).unicode()))
-					{
-						switch (nextStat & SpecialChars::CJK_CHAR_MASK)
+					// <CJK> <<Latin>>
+					if (SpecialChars::isLetterRequiringSpaceAroundCJK(m_story.text(firstChar).unicode())) {
+						switch (prevStat & SpecialChars::CJK_CHAR_MASK)
 						{
 							case SpecialChars::CJK_KANJI:
 							case SpecialChars::CJK_KANA:
 							case SpecialChars::CJK_NOTOP:
-								// use the size of the current char instead of the next one
-								run.extraWidth += quarterEM;
+								// use the size of the current Latin char
+								// instead of the previous CJK char
+								run.setFlag(ScLayout_CJKLatinSpace);
+						}
 						}
 					}
 				}
 
-				// 2. remove spaces from glyphs with the following CJK attributes
+			// 2. remove spaces from glyphs with the following CJK attributes
+			if (lastChar + 1 < m_story.length())
+			{
 				if (currStat != 0)
 				{	// current char is CJK
+					double halfEM = run.style().fontSize() / 10 / 2;
+					int nextStat = SpecialChars::getCJKAttr(m_story.text(lastChar + 1));
 					switch (currStat & SpecialChars::CJK_CHAR_MASK)
 					{
 						case SpecialChars::CJK_FENCE_END:
@@ -636,7 +636,6 @@ ShapedText TextShaper::shape(int fromPos, int toPos)
 							break;
 
 						case SpecialChars::CJK_FENCE_BEGIN:
-							int prevStat = SpecialChars::getCJKAttr(m_story.text(lastChar - 1));
 							if ((prevStat & SpecialChars::CJK_CHAR_MASK) == SpecialChars::CJK_FENCE_BEGIN)
 							{
 								run.extraWidth -= halfEM;