Tk Source Code

Artifact [f6117714]
Login

Artifact f61177140c8fcbedf0cf5fcb9f178f9ae813d25c86f3a2baa34f2c7245c0faa5:

Attachment "tkmacosx.patch" to ticket [00a27923] added by chw 2018-01-02 10:31:19.
Index: macosx/tkMacOSXClipboard.c
==================================================================
--- macosx/tkMacOSXClipboard.c
+++ macosx/tkMacOSXClipboard.c
@@ -26,26 +26,38 @@
 {
     NSMutableString *string = [NSMutableString new];
 
     if (dispPtr && dispPtr->clipboardActive &&
 	    [type isEqualToString:NSStringPboardType]) {
+	Tcl_Encoding utf8 = Tcl_GetEncoding(NULL, "utf-8");
+	Tcl_DString ds;
+
+	Tcl_DStringInit(&ds);
 	for (TkClipboardTarget *targetPtr = dispPtr->clipTargetPtr; targetPtr;
 		targetPtr = targetPtr->nextPtr) {
 	    if (targetPtr->type == XA_STRING ||
 		    targetPtr->type == dispPtr->utf8Atom) {
 		for (TkClipboardBuffer *cbPtr = targetPtr->firstBufferPtr;
 			cbPtr; cbPtr = cbPtr->nextPtr) {
-		    NSString *s = [[NSString alloc] initWithBytesNoCopy:
-			    cbPtr->buffer length:cbPtr->length
+		    char *p = Tcl_UtfToExternalDString(utf8, cbPtr->buffer,
+			    cbPtr->length, &ds);
+		    int len = Tcl_DStringLength(&ds);
+		    NSString *s = [[NSString alloc] initWithBytesNoCopy:p
+			    length:len
 			    encoding:NSUTF8StringEncoding freeWhenDone:NO];
 
-		    [string appendString:s];
-		    [s release];
+		    if (s) {
+			[string appendString:s];
+			[s release];
+		    }
+		    Tcl_DStringSetLength(&ds, 0);
 		}
 		break;
 	    }
 	}
+	Tcl_DStringFree(&ds);
+	Tcl_FreeEncoding(utf8);
     }
     [sender setString:string forType:type];
     [string release];
 }
 
@@ -129,15 +141,24 @@
 	    || target == dispPtr->utf8Atom)) {
 	NSString *string = nil;
 	NSPasteboard *pb = [NSPasteboard generalPasteboard];
 	NSString *type = [pb availableTypeFromArray:[NSArray arrayWithObject:
 		NSStringPboardType]];
+	Tcl_DString ds;
 
 	if (type) {
 	    string = [pb stringForType:type];
 	}
-	result = proc(clientData, interp, string ? [string UTF8String] : "");
+	Tcl_DStringInit(&ds);
+	if (string) {
+	    Tcl_Encoding utf8 = Tcl_GetEncoding(NULL, "utf-8");
+
+	    Tcl_ExternalToUtfDString(utf8, [string UTF8String], -1, &ds);
+	    Tcl_FreeEncoding(utf8);
+	}
+	result = proc(clientData, interp, Tcl_DStringValue(&ds));
+	Tcl_DStringFree(&ds);
     } else {
 	Tcl_SetObjResult(interp, Tcl_ObjPrintf(
 		"%s selection doesn't exist or form \"%s\" not defined",
 		Tk_GetAtomName(tkwin, selection),
 		Tk_GetAtomName(tkwin, target)));

Index: macosx/tkMacOSXFont.c
==================================================================
--- macosx/tkMacOSXFont.c
+++ macosx/tkMacOSXFont.c
@@ -117,10 +117,206 @@
 	((faPtr)->slant == TK_FS_ITALIC ? NSItalicFontMask : NSUnitalicFontMask)
 
 /*
  *---------------------------------------------------------------------------
  *
+ * NumUTF16Chars --
+ *
+ *	Like Tcl_NumUtfChars() but result is count of UTF16Chars,
+ *	i.e. a surrogate pair counts as two UTF16Chars and not
+ *	as a single entity.
+ *
+ * Results:
+ *	As above.
+ *
+ * Side effects:
+ *	None.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+#if TCL_UTF_MAX == 3
+
+/* No special code for BMP needed. */
+#define NumUTF16Chars Tcl_NumUtfChars
+
+#else
+
+static int
+NumUTF16Chars(
+    const char *src,		/* The UTF-8 string to measure. */
+    int length)			/* The length of the string in bytes, or -1
+				 * for strlen(string). */
+{
+    Tcl_UniChar ch = 0;
+    int i = 0;
+
+    if (length < 0) {
+	while (*src != '\0') {
+	    src += Tcl_UtfToUniChar(src, &ch);
+#if TCL_UTF_MAX > 4
+	    if (ch > 0xFFFF) {
+		/* A surrogate pair in UTF16Char representation. */
+		i++;
+	    }
+#endif
+            i++;
+        }
+	if (i < 0) {
+	    i = INT_MAX;
+	}
+    } else {
+	const char *endPtr = src + length - TCL_UTF_MAX;
+
+	while (src < endPtr) {
+	    src += Tcl_UtfToUniChar(src, &ch);
+#if TCL_UTF_MAX > 4
+	    if (ch > 0xFFFF) {
+		/* A surrogate pair in UTF16Char representation. */
+		i++;
+	    }
+#endif
+	    i++;
+	}
+	endPtr += TCL_UTF_MAX;
+	while ((src < endPtr) && Tcl_UtfCharComplete(src, endPtr - src)) {
+	    src += Tcl_UtfToUniChar(src, &ch);
+#if TCL_UTF_MAX > 4
+	    if (ch > 0xFFFF) {
+		/* A surrogate pair in UTF16Char representation. */
+		i++;
+	    }
+#endif
+	    i++;
+	}
+	if (src < endPtr) {
+	    i += endPtr - src;
+	}
+    }
+    return i;
+}
+
+#endif
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * UTF16CharAtIndex
+ *
+ *      Like Tcl_UtfAtIndex() but counting in UTF16Char entities.
+ *	Returns a pointer to the specified character position in
+ *	the given UTF-8 string.
+ *
+ * Results:
+ *      As above.
+ *
+ * Side effects:
+ *      None.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+#if TCL_UTF_MAX == 3
+
+/* No special code for BMP needed. */
+#define UTF16CharAtIndex Tcl_UtfAtIndex
+
+#else
+
+static const char *
+UTF16CharAtIndex(
+    const char *src,		/* The UTF-8 string. */
+    int index)			/* The position of the desired character. */
+{
+    Tcl_UniChar ch = 0;
+
+    while (index > 0) {
+	--index;
+	src += Tcl_UtfToUniChar(src, &ch);
+#if TCL_UTF_MAX > 4
+	if (ch > 0xFFFF) {
+	    /* A surrogate pair in UTF16Char representation. */
+	    --index;
+	}
+#endif
+    }
+    return src;
+}
+
+#endif
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * UtfToUTF16DString --
+ *
+ *	Convert an UTF-8 string to an UTF16Char string using the provided
+ *	Tcl_DString as result buffer. Invalid data is silently replaced
+ *	with "\uFFFD". The Tcl_DString is initialized by this function
+ *	and must be free'd by the caller.
+ *
+ * Results:
+ *	Pointer to UTF16Char string.
+ *
+ * Side effects:
+ *	None.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+static UTF16Char *
+UtfToUTF16DString(
+    const char *src,		/* Input UTF-8 string to be converted.
+				 * Need not be '\0' terminated. */
+    int numBytes,		/* Maximum number of bytes to consider from
+				 * source string in all. */
+    Tcl_DString *dsPtr,		/* Tcl_DString receiving the result. */
+    int *lengthPtr)		/* Number of UTF16Chars in result buffer. */
+{
+    Tcl_UniChar ch = 0;
+    UTF16Char utf16;
+    const char *end;
+
+    Tcl_DStringInit(dsPtr);
+    if (numBytes > 0) {
+	Tcl_DStringSetLength(dsPtr, numBytes * sizeof(utf16));
+	Tcl_DStringSetLength(dsPtr, 0);
+    }
+    end = src + numBytes;
+    while (src < end) {
+	int len = Tcl_UtfToUniChar(src, &ch);
+
+	utf16 = ch;
+#if TCL_UTF_MAX < 4
+	if (ch >= 0xD800 && ch <= 0xDFFF) {
+	    utf16 = 0xFFFD;
+	}
+#elif TCL_UTF_MAX == 4
+	if (ch >= 0xD800 && ch <= 0xDFFF) {
+	    if ((*src & 0xF8) != 0xF0) {
+		utf16 = 0xFFFD;
+	    }
+	}
+#else
+	if (ch >= 0xD800 && ch <= 0xDFFF) {
+	    utf16 = 0xFFFD;
+	} else if (ch > 0xFFFF) {
+	    utf16 = (((ch - 0x10000) >> 10) & 0x3FF) | 0xD800;
+	    Tcl_DStringAppend(dsPtr, (char *) &utf16, sizeof(utf16));
+	    utf16 = ((ch - 0x10000) & 0x3FF) | 0xDC00;
+	}
+#endif
+	Tcl_DStringAppend(dsPtr, (char *) &utf16, sizeof(utf16));
+	src += len;
+    }
+    *lengthPtr = Tcl_DStringLength(dsPtr) / sizeof(utf16);
+    return (UTF16Char *) Tcl_DStringValue(dsPtr);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
  * GetTkFontAttributesForNSFont --
  *
  *	Fill in TkFontAttributes for given NSFont.
  *
  * Results:
@@ -257,11 +453,11 @@
     } else {
 	TkInitFontAttributes(faPtr);
     }
     fontPtr->nsFont = nsFont;
     // some don't like antialiasing on fixed-width even if bigger than limit
-//    dontAA = [nsFont isFixedPitch] && fontPtr->font.fa.size <= 10;
+    //dontAA = [nsFont isFixedPitch] && fontPtr->font.fa.size <= 10;
     if (antialiasedTextEnabled >= 0/* || dontAA*/) {
 	renderingMode = (antialiasedTextEnabled == 0/* || dontAA*/) ?
 		NSFontIntegerAdvancementsRenderingMode :
 		NSFontAntialiasedRenderingMode;
     }
@@ -677,14 +873,22 @@
 {
     MacFont *fontPtr = (MacFont *) tkfont;
     NSFont *nsFont = fontPtr->nsFont;
     *faPtr = fontPtr->font.fa;
     if (nsFont && ![[nsFont coveredCharacterSet] characterIsMember:c]) {
-	UTF16Char ch = (UTF16Char) c;
+	UTF16Char ch[2];
 
-	nsFont = [nsFont bestMatchingFontForCharacters:&ch
-		length:1 attributes:nil actualCoveredLength:NULL];
+	if (c > 0xFFFF) {
+	    ch[0] = (((c - 0x10000) >> 10) & 0x3FF) | 0xD800;
+	    ch[1] = ((c - 0x10000) & 0x3FF) | 0xDC00;
+	    nsFont = [nsFont bestMatchingFontForCharacters:ch
+		    length:2 attributes:nil actualCoveredLength:NULL];
+	} else {
+	    ch[0] = (UTF16Char) c;
+	    nsFont = [nsFont bestMatchingFontForCharacters:ch
+		    length:1 attributes:nil actualCoveredLength:NULL];
+	}
 	if (nsFont) {
 	    GetTkFontAttributesForNSFont(nsFont, faPtr);
 	}
     }
 }
@@ -809,43 +1013,37 @@
     CFRange range = {0, 0};
     CTLineRef line;
     CGFloat offset = 0;
     CFIndex index;
     double width;
-    int length, fit;
+    int length, fit, utf16Len;
+    UTF16Char *utf16String;
+    Tcl_DString ds;
 
     if (rangeStart < 0 || rangeLength <= 0 ||
 	    rangeStart + rangeLength > numBytes ||
 	    (maxLength == 0 && !(flags & TK_AT_LEAST_ONE))) {
 	*lengthPtr = 0;
 	return 0;
     }
-#if 0
-    /* Back-compatibility with ATSUI renderer, appears not to be needed */
-    if (rangeStart == 0 && maxLength == 1 && (flags & TK_ISOLATE_END) &&
-	    !(flags & TK_AT_LEAST_ONE)) {
-	length = 0;
-	fit = 0;
-	goto done;
-    }
-#endif
     if (maxLength > 32767) {
 	maxLength = 32767;
     }
-    string = [[NSString alloc] initWithBytesNoCopy:(void*)source
-		length:numBytes encoding:NSUTF8StringEncoding freeWhenDone:NO];
+    utf16String = UtfToUTF16DString(source, numBytes, &ds, &utf16Len);
+    string = [[NSString alloc] initWithCharactersNoCopy:(void*)utf16String
+		length:utf16Len freeWhenDone:NO];
     if (!string) {
 	length = 0;
 	fit = rangeLength;
 	goto done;
     }
     attributedString = [[NSAttributedString alloc] initWithString:string
 	    attributes:fontPtr->nsAttributes];
     typesetter = CTTypesetterCreateWithAttributedString(
 	    (CFAttributedStringRef)attributedString);
-    start = Tcl_NumUtfChars(source, rangeStart);
-    len = Tcl_NumUtfChars(source + rangeStart, rangeLength);
+    start = NumUTF16Chars(source, rangeStart);
+    len = NumUTF16Chars(source, rangeStart + rangeLength);
     if (start > 0) {
 	range.length = start;
 	line = CTTypesetterCreateLine(typesetter, range);
 	offset = CTLineGetTypographicBounds(line, NULL, NULL, NULL);
 	CFRelease(line);
@@ -908,22 +1106,22 @@
     }
     CFRelease(typesetter);
     [attributedString release];
     [string release];
     length = ceil(width - offset);
-    fit = (Tcl_UtfAtIndex(source, index) - source) - rangeStart;
+    fit = (UTF16CharAtIndex(source, index) - source) - rangeStart;
 done:
+    Tcl_DStringFree(&ds);
 #ifdef TK_MAC_DEBUG_FONTS
     TkMacOSXDbgMsg("measure: source=\"%s\" range=\"%.*s\" maxLength=%d "
 	    "flags='%s%s%s%s' -> width=%d bytesFit=%d\n", source, rangeLength,
 	    source+rangeStart, maxLength,
 	    flags & TK_PARTIAL_OK   ? "partialOk "  : "",
 	    flags & TK_WHOLE_WORDS  ? "wholeWords " : "",
 	    flags & TK_AT_LEAST_ONE ? "atLeastOne " : "",
 	    flags & TK_ISOLATE_END  ? "isolateEnd " : "",
 	    length, fit);
-//if (!(rangeLength==1 && rangeStart == 0)) fprintf(stderr, "   measure len=%d (max=%d, w=%.0f) from %d (nb=%d): source=\"%s\": index=%d return %d\n",rangeLength,maxLength,width,rangeStart,numBytes, source+rangeStart, index, fit);
 #endif
     *lengthPtr = length;
     return fit;
 }
 
@@ -1069,20 +1267,24 @@
     TkMacOSXDrawingContext drawingContext;
     CGContextRef context;
     CGColorRef fg;
     NSFont *nsFont;
     CGAffineTransform t;
-    int h;
+    int h, utf16Len;
+    Tcl_DString ds;
+    UTF16Char *utf16String;
 
     if (rangeStart < 0 || rangeLength <= 0 ||
 	    rangeStart + rangeLength > numBytes ||
 	    !TkMacOSXSetupDrawingContext(drawable, gc, 1, &drawingContext)) {
 	return;
     }
-    string = [[NSString alloc] initWithBytesNoCopy:(void*)source
-		length:numBytes encoding:NSUTF8StringEncoding freeWhenDone:NO];
+    utf16String = UtfToUTF16DString(source, numBytes, &ds, &utf16Len);
+    string = [[NSString alloc] initWithCharactersNoCopy:(void*)utf16String
+		length:utf16Len freeWhenDone:NO];
     if (!string) {
+	Tcl_DStringFree(&ds);
 	return;
     }
     context = drawingContext.context;
     fg = TkMacOSXCreateCGColor(gc, gc->foreground);
     attributes = [fontPtr->nsAttributes mutableCopy];
@@ -1105,12 +1307,12 @@
 	t = CGAffineTransformTranslate(CGAffineTransformRotate(
 		CGAffineTransformTranslate(t, x, y), angle*PI/180.0), -x, -y);
     }
     CGContextConcatCTM(context, t);
     CGContextSetTextPosition(context, x, y);
-    start = Tcl_NumUtfChars(source, rangeStart);
-    len = Tcl_NumUtfChars(source, rangeStart + rangeLength);
+    start = NumUTF16Chars(source, rangeStart);
+    len = NumUTF16Chars(source, rangeStart + rangeLength);
     if (start > 0) {
 	CGRect clipRect = CGRectInfinite, startBounds;
 	line = CTTypesetterCreateLine(typesetter, CFRangeMake(0, start));
 	startBounds = CTLineGetImageBounds(line, context);
 	CFRelease(line);
@@ -1122,10 +1324,11 @@
     CFRelease(line);
     CFRelease(typesetter);
     [attributedString release];
     [string release];
     [attributes release];
+    Tcl_DStringFree(&ds);
     TkMacOSXRestoreDrawingContext(&drawingContext);
 }
 
 #pragma mark -
 #pragma mark Accessors: