From 486aa07930e7b6e900f77e64726c8cc4110511d8 Mon Sep 17 00:00:00 2001
From: Michele Calgaro <michele.calgaro@yahoo.it>
Date: Tue, 4 Feb 2025 17:59:57 +0900
Subject: Extend work on supporting surrogate characters done in commit
 e0a38072

Signed-off-by: Michele Calgaro <michele.calgaro@yahoo.it>
---
 src/kernel/tqfont_x11.cpp       |  32 +++++++-----
 src/kernel/tqfontengine_p.h     |   2 +-
 src/kernel/tqfontengine_x11.cpp |  58 +++++++++-------------
 src/kernel/tqfontmetrics.h      |   2 +-
 src/kernel/tqscriptengine.cpp   |  21 ++++++--
 src/kernel/tqtextengine.cpp     | 107 +++++++++++++++++++++-------------------
 6 files changed, 117 insertions(+), 105 deletions(-)

diff --git a/src/kernel/tqfont_x11.cpp b/src/kernel/tqfont_x11.cpp
index 6f4097029..92cd8b113 100644
--- a/src/kernel/tqfont_x11.cpp
+++ b/src/kernel/tqfont_x11.cpp
@@ -675,22 +675,32 @@ int TQFontMetrics::width( TQChar ch ) const
     return advances[0];
 }
 
-
 int TQFontMetrics::charWidth( const TQString &str, int pos ) const
 {
-    if ( pos < 0 || pos > (int)str.length() )
+    if ( pos < 0 || pos >= (int)str.length() )
 	return 0;
 
-    const TQChar &ch = str.unicode()[ pos ];
-    if ( ch.unicode() < TQFontEngineData::widthCacheSize &&
-	 d->engineData && d->engineData->widthCache[ ch.unicode() ] )
-	return d->engineData->widthCache[ ch.unicode() ];
+    uint uc;
+    bool isSurrogate;
+    if (str[pos].isHighSurrogate() && pos < (str.length() - 1) && str[pos + 1].isLowSurrogate())
+    {
+	isSurrogate = true;
+	uc = TQChar::surrogateToUcs4(str[pos], str[pos + 1]);
+    }
+    else
+    {
+	isSurrogate = false;
+	uc = str[pos].unicode();
+    }
+    if ( uc < TQFontEngineData::widthCacheSize &&
+	 d->engineData && d->engineData->widthCache[ uc ] )
+	return d->engineData->widthCache[ uc ];
 
+    const TQChar &ch = str.unicode()[ pos ];
     TQFont::Script script;
     SCRIPT_FOR_CHAR( script, ch );
 
     int width;
-
     if ( script >= TQFont::Arabic && script <= TQFont::Khmer ) {
 	// complex script shaping. Have to do some hard work
 	int from = TQMAX( 0,  pos - 8 );
@@ -700,7 +710,7 @@ int TQFontMetrics::charWidth( const TQString &str, int pos ) const
 	layout.itemize( TQTextEngine::WidthOnly );
 	width = layout.width( pos-from, 1 );
     } else if ( ::category( ch ) == TQChar::Mark_NonSpacing || qIsZeroWidthChar(ch.unicode())) {
-        width = 0;
+	width = 0;
     } else {
 	TQFontEngine *engine = d->engineForScript( script );
 #ifdef QT_CHECK_STATE
@@ -710,10 +720,10 @@ int TQFontMetrics::charWidth( const TQString &str, int pos ) const
 	glyph_t glyphs[8];
 	advance_t advances[8];
 	int nglyphs = 7;
-	engine->stringToCMap( &ch, 1, glyphs, advances, &nglyphs, FALSE );
+	engine->stringToCMap( &ch, isSurrogate ? 2 : 1, glyphs, advances, &nglyphs, FALSE );
 	width = advances[0];
     }
-    if ( ch.unicode() < TQFontEngineData::widthCacheSize && width > 0 && width < 0x100 )
-	d->engineData->widthCache[ ch.unicode() ] = width;
+    if ( uc < TQFontEngineData::widthCacheSize && width > 0 && width < 0x100 )
+	d->engineData->widthCache[ uc ] = width;
     return width;
 }
diff --git a/src/kernel/tqfontengine_p.h b/src/kernel/tqfontengine_p.h
index fe8714ef3..86fdda8f0 100644
--- a/src/kernel/tqfontengine_p.h
+++ b/src/kernel/tqfontengine_p.h
@@ -284,7 +284,7 @@ public:
     FT_Face face() const { return _face; }
     XftFont *font() const { return _font; }
 
-    void recalcAdvances( int len, glyph_t *glyphs, advance_t *advances );
+    void recalcAdvances( int len, glyph_t *glyphs, advance_t *advances ) const;
 
 private:
     friend class TQFontPrivate;
diff --git a/src/kernel/tqfontengine_x11.cpp b/src/kernel/tqfontengine_x11.cpp
index 47078dea9..aa116b93d 100644
--- a/src/kernel/tqfontengine_x11.cpp
+++ b/src/kernel/tqfontengine_x11.cpp
@@ -1531,12 +1531,11 @@ static glyph_t getAdobeCharIndex(XftFont *font, int cmap, uint ucs4)
     return g;
 }
 
-static uint getUnicode(const TQChar *str, int &i, const int len)
+static uint getCodepoint(const TQChar *str, int &i, const int len)
 {
     if (str[i].isHighSurrogate() && i < (len - 1) && str[i + 1].isLowSurrogate())
     {
-	++i; // Don't delete this: it is required for correct
-	     // advancement when handling surrogate pairs
+	++i; // This is required for correct advancement when handling surrogate pairs
 	return TQChar::surrogateToUcs4(str[i - 1], str[i]);
     }
     return str[i].unicode();
@@ -1551,7 +1550,7 @@ TQFontEngine::Error TQFontEngineXft::stringToCMap( const TQChar *str, int len, g
 
     int glyph_pos = 0;
     for ( int i = 0; i < len; ++i ) {
-	uint uc = getUnicode(str, i, len);
+	uint uc = getCodepoint(str, i, len);
 	if ( uc == 0xa0 )
 	    uc = 0x20;
 	if ( mirrored )
@@ -1575,32 +1574,17 @@ TQFontEngine::Error TQFontEngineXft::stringToCMap( const TQChar *str, int len, g
 	++glyph_pos;
     }
 
-    if ( advances ) {
-	for ( int i = 0; i < glyph_pos; i++ ) {
-	    glyph_t glyph = *(glyphs + i);
-	    advances[i] = (glyph < widthCacheSize) ? widthCache[glyph] : 0;
-	    if ( !advances[i] ) {
-		XGlyphInfo gi;
-		XftGlyphExtents( TQPaintDevice::x11AppDisplay(), _font, &glyph, 1, &gi );
-		advances[i] = gi.xOff;
-		if ( glyph < widthCacheSize && gi.xOff > 0 && gi.xOff < 0x100 )
-		    ((TQFontEngineXft *)this)->widthCache[glyph] = gi.xOff;
-	    }
-	}
-	if ( _scale != 1. ) {
-	    for ( int i = 0; i < len; i++ )
-		advances[i] = tqRound(advances[i]*_scale);
-	}
+    if ( advances )
+    {
+	recalcAdvances(glyph_pos, glyphs, advances);
     }
 
     *nglyphs = glyph_pos;
     return NoError;
 }
 
-
-void TQFontEngineXft::recalcAdvances( int len, glyph_t *glyphs, advance_t *advances )
+void TQFontEngineXft::recalcAdvances( int len, glyph_t *glyphs, advance_t *advances ) const
 {
-
     for ( int i = 0; i < len; i++ ) {
 	FT_UInt glyph = *(glyphs + i);
 	advances[i] = (glyph < widthCacheSize) ? widthCache[glyph] : 0;
@@ -1999,20 +1983,22 @@ bool TQFontEngineXft::canRender( const TQChar *string, int len )
     bool allExist = TRUE;
 
     if (_cmap != -1) {
-        for ( int i = 0; i < len; i++ ) {
-            if (!XftCharExists(0, _font, string[i].unicode())
-                && getAdobeCharIndex(_font, _cmap, string[i].unicode()) == 0) {
-                allExist = FALSE;
-                break;
-            }
-        }
+	for ( int i = 0; i < len; i++ ) {
+	    uint uc = getCodepoint(string, i, len);
+	    if (!XftCharExists(0, _font, uc)
+		&& getAdobeCharIndex(_font, _cmap, uc) == 0) {
+		allExist = FALSE;
+		break;
+	    }
+	}
     } else {
-        for ( int i = 0; i < len; i++ ) {
-            if (!XftCharExists(0, _font, string[i].unicode())) {
-                allExist = FALSE;
-                break;
-            }
-        }
+	for ( int i = 0; i < len; i++ ) {
+	    uint uc = getCodepoint(string, i, len);
+	    if (!XftCharExists(0, _font, uc)) {
+		allExist = FALSE;
+		break;
+	    }
+	}
     }
 
     return allExist;
diff --git a/src/kernel/tqfontmetrics.h b/src/kernel/tqfontmetrics.h
index b635a864e..35d09f5e8 100644
--- a/src/kernel/tqfontmetrics.h
+++ b/src/kernel/tqfontmetrics.h
@@ -79,7 +79,7 @@ public:
     int		width( char c ) const { return width( (TQChar) c ); }
 #endif
 
-    int 		charWidth( const TQString &str, int pos ) const;
+    int 	charWidth( const TQString &str, int pos ) const;
     TQRect	boundingRect( const TQString &, int len = -1 ) const;
     TQRect	boundingRect( TQChar ) const;
     TQRect	boundingRect( int x, int y, int w, int h, int flags,
diff --git a/src/kernel/tqscriptengine.cpp b/src/kernel/tqscriptengine.cpp
index 588596716..36df1fa77 100644
--- a/src/kernel/tqscriptengine.cpp
+++ b/src/kernel/tqscriptengine.cpp
@@ -288,10 +288,21 @@ static void heuristicSetGlyphAttributes(TQShaperItem *item, const TQChar *uc, in
     Q_ASSERT(item->num_glyphs <= length);
 
     unsigned short *logClusters = item->log_clusters;
-
-    int i;
-    for (i = 0; i < length; ++i)
-        logClusters[i] = i;
+    int glyph_pos = 0;
+    for (int i = 0; i < length; i++)
+    {
+        if (uc[i].isHighSurrogate() && i < (length - 1) && uc[i+1].isLowSurrogate())
+        {
+            logClusters[i] = glyph_pos;
+            logClusters[++i] = glyph_pos;
+        }
+        else
+        {
+            logClusters[i] = glyph_pos;
+        }
+        ++glyph_pos;
+    }
+    Q_ASSERT(glyph_pos == item->num_glyphs);
 
     // first char in a run is never (treated as) a mark
     int cStart = 0;
@@ -307,7 +318,7 @@ static void heuristicSetGlyphAttributes(TQShaperItem *item, const TQChar *uc, in
     }
 
     int lastCat = ::category(uc[0]);
-    for (i = 1; i < length; ++i) {
+    for (int i = 1; i < length; ++i) {
         int cat = ::category(uc[i]);
         if (qIsZeroWidthChar(uc[i].unicode())) {
 	    item->attributes[i].mark = FALSE;
diff --git a/src/kernel/tqtextengine.cpp b/src/kernel/tqtextengine.cpp
index 05cdbcc13..067a21740 100644
--- a/src/kernel/tqtextengine.cpp
+++ b/src/kernel/tqtextengine.cpp
@@ -800,72 +800,77 @@ static void calcLineBreaks(const TQString &str, TQCharAttributes *charAttributes
 {
     int len = str.length();
     if (!len)
-        return;
+	return;
 
     const TQChar *uc = str.unicode();
     int cls = lineBreakClass(*uc);
     if (cls >= TQUnicodeTables::LineBreak_CM)
-        cls = TQUnicodeTables::LineBreak_ID;
+	cls = TQUnicodeTables::LineBreak_ID;
 
     charAttributes[0].softBreak = FALSE;
     charAttributes[0].whiteSpace = (cls == TQUnicodeTables::LineBreak_SP);
     charAttributes[0].charStop = TRUE;
 
-    for (int i = 1; i < len; ++i) {
-        int ncls = ::lineBreakClass(uc[i]);
-        int category = ::category(uc[i]);
-        if (category == TQChar::Mark_NonSpacing)
-            goto nsm;
-        
-        if (category == TQChar::Other_Surrogate) {
-            // char stop only on first pair
-            if (uc[i].isHighSurrogate() && i < (len - 1) && uc[i + 1].isLowSurrogate())
-                goto nsm;
-            // ### correctly handle second surrogate
-        }
-
-        if (ncls == TQUnicodeTables::LineBreak_SP) {
-            charAttributes[i].softBreak = FALSE;
-            charAttributes[i].whiteSpace = TRUE;
-            charAttributes[i].charStop = TRUE;
-            cls = ncls;
-            continue;
-        }
+    bool prevIsHighSurrogate = uc[0].isHighSurrogate();
+    for (int i = 1; i < len; ++i)
+    {
+	// Don't stop on low surrogate characters of complete valid pairs
+	if (prevIsHighSurrogate && uc[i].isLowSurrogate())
+	{
+	    prevIsHighSurrogate = false;
+	    charAttributes[i].softBreak = FALSE;
+	    charAttributes[i].whiteSpace = FALSE;
+	    charAttributes[i].charStop = FALSE;
+	    continue;
+	}
+	prevIsHighSurrogate = uc[i].isHighSurrogate();
 
+	int ncls = ::lineBreakClass(uc[i]);
+	int category = ::category(uc[i]);
+	if (category == TQChar::Mark_NonSpacing)
+	{
+	    charAttributes[i].softBreak = FALSE;
+	    charAttributes[i].whiteSpace = FALSE;
+	    charAttributes[i].charStop = FALSE;
+	    continue;
+	}
 
-	if (cls == TQUnicodeTables::LineBreak_SA && ncls == TQUnicodeTables::LineBreak_SA) {
-            // two complex chars (thai or lao), thai_attributes might override, but here
-            // we do a best guess
-            charAttributes[i].softBreak = TRUE;
-            charAttributes[i].whiteSpace = FALSE;
-            charAttributes[i].charStop = TRUE;
-            cls = ncls;
-            continue;
-        }
-        {
-	    int tcls = ncls;
-	    if (tcls >= TQUnicodeTables::LineBreak_SA)
-		tcls = TQUnicodeTables::LineBreak_ID;
-	    if (cls >= TQUnicodeTables::LineBreak_SA)
-		cls = TQUnicodeTables::LineBreak_ID;
-
-	    bool softBreak;
-	    int brk = breakTable[cls][tcls];
-	    if (brk == Ibk)
-		softBreak = (cls == TQUnicodeTables::LineBreak_SP);
-	    else
-		softBreak = (brk == Dbk);
-//        tqDebug("char = %c %04x, cls=%d, ncls=%d, brk=%d soft=%d", uc[i].cell(), uc[i].unicode(), cls, ncls, brk, charAttributes[i].softBreak);
-	    charAttributes[i].softBreak = softBreak;
+	if (ncls == TQUnicodeTables::LineBreak_SP) {
+	    charAttributes[i].softBreak = FALSE;
+	    charAttributes[i].whiteSpace = TRUE;
+	    charAttributes[i].charStop = TRUE;
+	    cls = ncls;
+	    continue;
+	}
+
+	if (cls == TQUnicodeTables::LineBreak_SA && ncls == TQUnicodeTables::LineBreak_SA)
+	{
+	    // two complex chars (thai or lao), thai_attributes might override, but here
+	    // we do a best guess
+	    charAttributes[i].softBreak = TRUE;
 	    charAttributes[i].whiteSpace = FALSE;
 	    charAttributes[i].charStop = TRUE;
 	    cls = ncls;
+	    continue;
 	}
-        continue;
-    nsm:
-        charAttributes[i].softBreak = FALSE;
-        charAttributes[i].whiteSpace = FALSE;
-        charAttributes[i].charStop = FALSE;
+
+	int tcls = ncls;
+	if (tcls >= TQUnicodeTables::LineBreak_SA)
+	    tcls = TQUnicodeTables::LineBreak_ID;
+	if (cls >= TQUnicodeTables::LineBreak_SA)
+	    cls = TQUnicodeTables::LineBreak_ID;
+
+	bool softBreak;
+	int brk = breakTable[cls][tcls];
+	if (brk == Ibk)
+	    softBreak = (cls == TQUnicodeTables::LineBreak_SP);
+	else
+	    softBreak = (brk == Dbk);
+	// tqDebug("char = %c %04x, cls=%d, ncls=%d, brk=%d soft=%d", uc[i].cell(), uc[i].unicode(), cls, ncls, brk, charAttributes[i].softBreak);
+	charAttributes[i].softBreak = softBreak;
+	charAttributes[i].whiteSpace = FALSE;
+	charAttributes[i].charStop = TRUE;
+	cls = ncls;
     }
 }
 
-- 
cgit v1.2.3

