View Issue Details

IDProjectCategoryView StatusLast Update
0016102ScribusTypographypublic2020-08-11 20:56
Reporterftake Assigned Tojghali  
PrioritynormalSeveritytextReproducibilityalways
Status closedResolutionfixed 
Product Version1.5.6.svn 
Fixed in Version1.5.6.svn 
Summary0016102: Automatic space between CJK and Latin is inserted at the end of line
DescriptionAutomatic spaces between CJK and Latin should not be inserted between the two letters when one of them is placed at the end of line and the other is at the start of line. Otherwise, texts are not well justified.

I will attach a patch soon.
TagsNo tags attached.
PatchNo

Activities

ftake

2020-05-06 09:09

reporter   ~0047594

A patch to fix this
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;

jghali

2020-05-06 11:40

administrator   ~0047596

Applied, thanks!

Issue History

Date Modified Username Field Change
2020-05-06 08:16 ftake New Issue
2020-05-06 09:09 ftake File Added: fix-CJK-Latin-spacing-at-the-end-of-line.patch
2020-05-06 09:09 ftake Note Added: 0047594
2020-05-06 11:40 jghali Note Added: 0047596
2020-05-06 11:40 jghali Summary Automatic spaces between CJK and Latin is inserted at the end of line => Automatic space between CJK and Latin is inserted at the end of line
2020-05-06 11:40 jghali Assigned To => jghali
2020-05-06 11:40 jghali Status new => resolved
2020-05-06 11:40 jghali Resolution open => fixed
2020-05-06 11:40 jghali Fixed in Version => 1.5.6.svn
2020-08-11 20:56 cbradney Status resolved => closed