diff --git a/modules/javafx.graphics/src/main/java/com/sun/javafx/font/freetype/OSPango.java b/modules/javafx.graphics/src/main/java/com/sun/javafx/font/freetype/OSPango.java index cf888509fbd..6c3d68315aa 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/javafx/font/freetype/OSPango.java +++ b/modules/javafx.graphics/src/main/java/com/sun/javafx/font/freetype/OSPango.java @@ -83,6 +83,7 @@ class OSPango { /* Miscellaneous (glib, fontconfig) */ static final native long g_utf8_offset_to_pointer(long str, long offset); static final native long g_utf8_pointer_to_offset(long str, long pos); + static final native long g_utf8_strlen(long str, long max); static final native long g_utf16_to_utf8(char[] str); static final native void g_free(long ptr); static final native int g_list_length(long list); diff --git a/modules/javafx.graphics/src/main/java/com/sun/javafx/font/freetype/PangoGlyphLayout.java b/modules/javafx.graphics/src/main/java/com/sun/javafx/font/freetype/PangoGlyphLayout.java index 77ed8af2c01..e722b7ad760 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/javafx/font/freetype/PangoGlyphLayout.java +++ b/modules/javafx.graphics/src/main/java/com/sun/javafx/font/freetype/PangoGlyphLayout.java @@ -34,6 +34,10 @@ import com.sun.javafx.text.GlyphLayout; import com.sun.javafx.text.TextRun; +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.Map; + class PangoGlyphLayout extends GlyphLayout { private static final long fontmap; @@ -80,9 +84,8 @@ private boolean check(long checkValue, String message, long context, long desc, return true; } - private long str = 0L; + private Map runUtf8 = new LinkedHashMap<>(); public void layout(TextRun run, PGFont font, FontStrike strike, char[] text) { - /* Create the pango font and attribute list */ FontResource fr = font.getFontResource(); boolean composite = fr instanceof CompositeFontResource; @@ -126,17 +129,20 @@ public void layout(TextRun run, PGFont font, FontStrike strike, char[] text) { OSPango.pango_attr_list_insert(attrList, attr); } - if (str == 0L) { - str = OSPango.g_utf16_to_utf8(text); + Long str = runUtf8.get(run); + if (str == null) { + char[] rtext = Arrays.copyOfRange(text, run.getStart(), run.getEnd()); + str = OSPango.g_utf16_to_utf8(rtext); if (check(str, "Failed allocating UTF-8 buffer.", context, desc, attrList)) { return; } + runUtf8.put(run, str); } /* Itemize */ - long start = OSPango.g_utf8_offset_to_pointer(str, run.getStart()); - long end = OSPango.g_utf8_offset_to_pointer(str, run.getEnd()); - long runs = OSPango.pango_itemize(context, str, (int)(start - str), (int)(end - start), attrList, 0); + long utflen = OSPango.g_utf8_strlen(str,-1); + long end = OSPango.g_utf8_offset_to_pointer(str, utflen); + long runs = OSPango.pango_itemize(context, str, 0, (int)(end - str), attrList, 0); if (runs != 0) { /* Shape all PangoItem into PangoGlyphString */ @@ -199,9 +205,9 @@ public void layout(TextRun run, PGFont font, FontStrike strike, char[] text) { @Override public void dispose() { super.dispose(); - if (str != 0L) { + for (Long str: runUtf8.values()) { OSPango.g_free(str); - str = 0L; } + runUtf8.clear(); } } diff --git a/modules/javafx.graphics/src/main/native-font/pango.c b/modules/javafx.graphics/src/main/native-font/pango.c index bc82807d2f8..e29e3a0cfea 100644 --- a/modules/javafx.graphics/src/main/native-font/pango.c +++ b/modules/javafx.graphics/src/main/native-font/pango.c @@ -398,6 +398,13 @@ JNIEXPORT jlong JNICALL OS_NATIVE(g_1utf8_1pointer_1to_1offset) return (jlong)g_utf8_pointer_to_offset((const gchar *)str, (const gchar *)pos); } +JNIEXPORT jlong JNICALL OS_NATIVE(g_1utf8_1strlen) + (JNIEnv *env, jclass that, jlong str, jlong pos) +{ + if (!str) return 0; + return (jlong)g_utf8_strlen((const gchar *)str, (const gchar *)pos); +} + JNIEXPORT jlong JNICALL OS_NATIVE(g_1utf16_1to_1utf8) (JNIEnv *env, jclass that, jcharArray str) { diff --git a/tests/system/src/test/java/test/com/sun/javafx/font/freetype/PangoTest.java b/tests/system/src/test/java/test/com/sun/javafx/font/freetype/PangoTest.java new file mode 100644 index 00000000000..6900ac1c2c2 --- /dev/null +++ b/tests/system/src/test/java/test/com/sun/javafx/font/freetype/PangoTest.java @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package test.com.sun.javafx.font.freetype; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import javafx.application.Application; +import javafx.application.Platform; +import javafx.scene.Scene; +import javafx.scene.layout.Pane; +import javafx.scene.layout.VBox; +import javafx.scene.text.Text; +import javafx.stage.Stage; +import javafx.stage.WindowEvent; + +import org.junit.Test; +import org.junit.AfterClass; +import org.junit.BeforeClass; + +import junit.framework.AssertionFailedError; +import static test.util.Util.TIMEOUT; + +import static org.junit.Assert.*; + +/** + * Test program for UTF16 to UTF8 conversion and Pango + */ +public class PangoTest { + + static CountDownLatch launchLatch = new CountDownLatch(1); + + static MyApp myApp; + static Pane pane; + + public static class MyApp extends Application { + + Stage stage = null; + + public MyApp() { + super(); + } + + @Override + public void init() { + myApp = this; + } + + @Override + public void start(Stage primaryStage) throws Exception { + this.stage = primaryStage; + pane = new VBox(10); + Scene scene = new Scene(pane, 400, 200); + stage.setScene(scene); + stage.addEventHandler(WindowEvent.WINDOW_SHOWN, e -> Platform.runLater(launchLatch::countDown)); + stage.show(); + } + } + + @BeforeClass + public static void setupOnce() throws Exception { + // Start the Application + new Thread(() -> Application.launch(MyApp.class, (String[]) null)).start(); + assertTrue("Timeout waiting for Application to launch", + launchLatch.await(TIMEOUT, TimeUnit.MILLISECONDS)); + assertEquals(0, launchLatch.getCount()); + } + + @AfterClass + public static void teardownOnce() { + Platform.exit(); + } + + private void addTextToPane(Text text) throws Exception { + final CountDownLatch rDone = new CountDownLatch(1); + Platform.runLater(() -> { + text.layoutYProperty().addListener(inv -> { + rDone.countDown(); + }); + pane.getChildren().add(text); + }); + assertTrue("Timeout waiting for runLater", rDone.await(TIMEOUT, TimeUnit.MILLISECONDS)); + } + + @Test + public void testZeroChar() throws Exception { + String FULL_UNICODE_SET; + StringBuilder builder = new StringBuilder(); + for (int character = 0; character < 10000; character++) { + char[] chars = Character.toChars(character); + builder.append(chars); + } + FULL_UNICODE_SET = builder.toString(); + Text text = new Text(FULL_UNICODE_SET); + addTextToPane(text); + } + + @Test + public void testSurrogatePair() throws Exception { + StringBuilder builder = new StringBuilder(); + builder.append(Character.toChars(55358)); + builder.append(Character.toChars(56605)); + builder.append(Character.toChars(8205)); + + Text text = new Text(builder.toString()); + addTextToPane(text); + } +}