undefect. CWE-407 — 63 sites patched across 27 ecosystems

Authors: russell@unturf.com · brackishbert@gmail.com · foxhop.net · TimeHexOn.com

Patches, unit tests, benchmarks, whitepaper, and outreach briefs.
Public domain — no copyright claimed. Use freely.
This commit is contained in:
russell@unturf.com 2026-03-26 17:11:57 -04:00
commit 0a580b313d
70422 changed files with 17213626 additions and 0 deletions

View file

@ -0,0 +1,103 @@
/*
* Copyright (c) 2001, 2024, 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.
*
* 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.
*/
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.font.FontRenderContext;
import java.awt.font.TextLayout;
import javax.swing.JPanel;
/*
* @test
* @bug 4427483
* @summary Arabic text followed by newline should have no missing glyphs
* @library /java/awt/regtesthelpers
* @build PassFailJFrame
* @run main/manual ArabicBox
*/
public final class ArabicBox {
private static final String TEXT =
"\u0627\u0644\u0639\u0631\u0628\u064A\u0629\n";
private static final String FONT_NAME = Font.DIALOG;
private static final String INSTRUCTIONS = """
In the below panel, you should see the following text:
"""
+ TEXT + """
(It's \u2018Arabic\u2019 in Arabic.)
If there are no 'box glyphs' for missing glyphs,
press Pass; otherwise, press Fail.""";
public static void main(String[] args) throws Exception {
final Font font = new Font(FONT_NAME, Font.PLAIN, 24);
System.out.println("asked for " + FONT_NAME + " and got: " + font.getFontName());
PassFailJFrame.builder()
.title("Arabic Box")
.instructions(INSTRUCTIONS)
.rows(7)
.columns(40)
.splitUIBottom(() -> createPanel(font))
.build()
.awaitAndCheck();
}
private static JPanel createPanel(Font font) {
return new TextPanel(font);
}
private static final class TextPanel extends JPanel {
private TextLayout layout;
private TextPanel(Font font) {
setForeground(Color.black);
setBackground(Color.white);
setFont(font);
setPreferredSize(new Dimension(300, 150));
}
@Override
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2d = (Graphics2D)g;
if (layout == null) {
Font font = g2d.getFont();
FontRenderContext frc = g2d.getFontRenderContext();
layout = new TextLayout(TEXT, font, frc);
System.out.println(layout.getBounds());
}
layout.draw(g2d, 10, 50);
g2d.drawString(TEXT, 10, 100);
}
}
}

View file

@ -0,0 +1,95 @@
/*
* Copyright (c) 2016, 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.
*
* 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.
*/
/* @test
* @summary verify Arab Diacritic Positioning
* @bug 8168759 8248352
*/
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.Rectangle;
import java.awt.font.FontRenderContext;
import java.awt.font.TextLayout;
import java.util.Locale;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
public class ArabicDiacriticTest {
static final String SAMPLE =
"\u0627\u0644\u0639\u064e\u0631\u064e\u0628\u0650\u064a\u064e\u0651\u0629";
static final String STR1 = "\u0644\u0639\u064e\u0629";
static final String STR2 = "\u0644\u0639\u0629";
static final String FONT = "DejaVu Sans";
public static void main(String[] args) throws Exception {
if ((args.length > 0) && (args[0].equals("-show"))) {
showText(); // for a human
}
measureText(); // for the test harness
}
static void showText() {
SwingUtilities.invokeLater(() -> {
JFrame frame = new JFrame();
JLabel label = new JLabel(SAMPLE);
Font font = new Font(FONT, Font.PLAIN, 36);
label.setFont(font);
frame.setLayout(new GridLayout(3,1));
frame.add(label);
label = new JLabel(STR1);
label.setFont(font);
frame.add(label);
label = new JLabel(STR2);
label.setFont(font);
frame.add(label);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}
static void measureText() {
Font font = new Font(FONT, Font.PLAIN, 36);
if (!font.getFamily(Locale.ENGLISH).equals(FONT)) {
return;
}
FontRenderContext frc = new FontRenderContext(null, false, false);
TextLayout tl1 = new TextLayout(STR1, font, frc);
TextLayout tl2 = new TextLayout(STR2, font, frc);
Rectangle r1 = tl1.getPixelBounds(frc, 0f, 0f);
Rectangle r2 = tl2.getPixelBounds(frc, 0f, 0f);
if (r1.height > r2.height) {
System.out.println(font);
System.out.println(r1);
System.out.println(r2);
throw new RuntimeException("BAD BOUNDS");
}
}
}

View file

@ -0,0 +1,50 @@
/*
* Copyright (c) 2005, 2007, 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.
*
* 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.
*/
/* @test
* @summary verify cast in AttributeValues fails silently - regression
* @bug 6603975
*/
import java.awt.*;
import java.awt.font.*;
import java.awt.geom.*;
import java.text.*;
import java.util.*;
public class AttributeValuesCastTest
{
public static void main (String [] args)
{
System.out.println ("java.version = " + System.getProperty ("java.version"));
try {
Map attributes = new HashMap();
attributes.put("Something","Somethign Else");
Font f = new Font(attributes);
System.out.println("PASS: was able to create font. ");
} catch(Throwable t) {
throw new RuntimeException("FAIL: caught "+t.toString());
}
}
}

View file

@ -0,0 +1,98 @@
/*
* Copyright (c) 2008, 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.
*
* 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.
*/
/* @test
* @summary verify bounds enclose rendering of decorations.
* @bug 6751621
*/
import java.awt.*;
import java.awt.font.*;
import java.awt.geom.*;
import java.awt.image.*;
import java.util.*;
public class DecorationBoundsTest {
public static void main(String[] args) {
BufferedImage bi =
new BufferedImage(600, 300, BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = bi.createGraphics();
g2d.setColor(Color.white);
g2d.fillRect(0, 0, 600, 300);
float x = 10;
float y = 90;
Map map = new HashMap();
map.put(TextAttribute.STRIKETHROUGH,
TextAttribute.STRIKETHROUGH_ON);
map.put(TextAttribute.SIZE, new Float(80));
FontRenderContext frc = g2d.getFontRenderContext();
String text = "Welcome to ";
TextLayout tl = new TextLayout(text, map, frc);
g2d.translate(x, y);
g2d.setColor(Color.RED);
tl.draw(g2d, 0, 0);
g2d.setColor(Color.GREEN);
Rectangle2D bds = tl.getBounds();
/* Since due to pixelisation the glyphs may touch above
* or below the theoretical outline bounds, pad in the
* y direction to avoid spurious failures.
*/
bds.setRect(bds.getX(), bds.getY()-1,
bds.getWidth(), bds.getHeight()+2);
g2d.fill(bds);
map = new HashMap();
map.put(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON);
map.put(TextAttribute.SIZE, new Float(80));
tl = new TextLayout(text, map, frc);
g2d.translate(0, 100);
g2d.setColor(Color.RED);
tl.draw(g2d, 0, 0);
g2d.setColor(Color.GREEN);
bds = tl.getBounds();
bds.setRect(bds.getX(), bds.getY()-1,
bds.getWidth(), bds.getHeight()+2);
g2d.fill(bds);
checkBI(bi, Color.RED);
}
static void checkBI(BufferedImage bi, Color badColor) {
int badrgb = badColor.getRGB();
int w = bi.getWidth(null);
int h = bi.getHeight(null);
for (int x=0; x<w; x++) {
for (int y=0; y<h; y++) {
int col = bi.getRGB(x, y);
if (col == badrgb) {
throw new RuntimeException("Got " + col);
}
}
}
}
}

View file

@ -0,0 +1,74 @@
/*
* Copyright 2017 JetBrains s.r.o.
* 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.
*
* 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.
*/
/* @test
* @bug 8170552
* @summary verify enabling text layout for complex text on macOS
* @requires os.family == "mac"
*/
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
public class DiacriticsDrawingTest {
private static final Font FONT = new Font("Menlo", Font.PLAIN, 12);
private static final int IMAGE_WIDTH = 20;
private static final int IMAGE_HEIGHT = 20;
private static final int TEXT_X = 5;
private static final int TEXT_Y = 15;
public static void main(String[] args) {
BufferedImage composed = drawString("\u00e1"); // latin small letter a with acute
BufferedImage decomposed = drawString("a\u0301"); // same letter in decomposed form
if (!imagesAreEqual(composed, decomposed)) {
throw new RuntimeException("Text rendering is supposed to be the same");
}
}
private static BufferedImage drawString(String text) {
BufferedImage image = new BufferedImage(IMAGE_WIDTH, IMAGE_HEIGHT, BufferedImage.TYPE_INT_RGB);
Graphics g = image.createGraphics();
g.setColor(Color.white);
g.fillRect(0, 0, IMAGE_WIDTH, IMAGE_HEIGHT);
g.setColor(Color.black);
g.setFont(FONT);
g.drawString(text, TEXT_X, TEXT_Y);
g.dispose();
return image;
}
private static boolean imagesAreEqual(BufferedImage i1, BufferedImage i2) {
if (i1.getWidth() != i2.getWidth() || i1.getHeight() != i2.getHeight()) return false;
for (int i = 0; i < i1.getWidth(); i++) {
for (int j = 0; j < i1.getHeight(); j++) {
if (i1.getRGB(i, j) != i2.getRGB(i, j)) {
return false;
}
}
}
return true;
}
}

View file

@ -0,0 +1,89 @@
/*
* Copyright (c) 2018, 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.
*
* 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.
*
*/
/* @test
* @bug 8214002
* @requires (os.family == "windows")
* @summary verify MS Mincho's Plain & Italic style
*/
import java.awt.Font;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
public class FontGlyphCompare {
static BufferedImage getFontImage(Font font, String text) {
int x = 1;
int y = 15;
int w = 10;
int h = 18;
BufferedImage bi = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
Graphics2D g = (Graphics2D)bi.getGraphics();
g.setColor(Color.black);
g.fillRect(0, 0, w, h);
g.setColor(Color.white);
g.setFont(font);
g.drawString(text, x, y);
return bi;
}
public static void main(String[] args) throws Exception {
String osName = System.getProperty("os.name");
System.out.println("OS is " + osName);
osName = osName.toLowerCase();
if (!osName.startsWith("windows")) {
return;
}
Font msMincho = new Font("MS Mincho", Font.PLAIN, 16);
String family = msMincho.getFamily(java.util.Locale.ENGLISH);
if (!family.equalsIgnoreCase("MS Mincho")) {
System.out.println("Japanese fonts not installed");
return;
}
String s = "|";
BufferedImage bi1 = getFontImage(new Font("MS Mincho", Font.PLAIN, 16), s);
int h1 = bi1.getHeight();
int w1 = bi1.getWidth();
BufferedImage bi2 = getFontImage(new Font("MS Mincho", Font.ITALIC, 16), s);
int h2 = bi2.getHeight();
int w2 = bi2.getWidth();
if ((h1 == h2) && (w1 == w2)) {
int cnt = 0;
for(int yy = 0; yy < h1; yy++) {
for(int xx = 0; xx < w1; xx++) {
if (bi1.getRGB(xx, yy) != bi2.getRGB(xx, yy)) {
cnt++;
}
}
}
if (cnt == 0) {
throw new Exception("Test failed");
}
}
}
}

View file

@ -0,0 +1,88 @@
/*
* Copyright (C) 2019 JetBrains s.r.o.
* 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.
*
* 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.
*/
/*
* @test
* @bug 8220231
* @summary Cache HarfBuzz face object for same font's text layout calls
* @comment Test layout operations for the same font performed simultaneously
* from multiple threads
*/
import java.awt.Font;
import java.awt.font.FontRenderContext;
import java.awt.font.GlyphVector;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.atomic.AtomicReference;
public class FontLayoutStressTest {
private static final int NUMBER_OF_THREADS =
Runtime.getRuntime().availableProcessors() * 2;
private static final long TIME_TO_RUN_NS = 1_000_000_000; // 1 second
private static final Font FONT = new Font(Font.SERIF, Font.PLAIN, 12);
private static final FontRenderContext FRC = new FontRenderContext(null,
false, false);
private static final char[] TEXT = "Lorem ipsum dolor sit amet, ..."
.toCharArray();
private static double doLayout() {
GlyphVector gv = FONT.layoutGlyphVector(FRC, TEXT, 0, TEXT.length,
Font.LAYOUT_LEFT_TO_RIGHT);
return gv.getGlyphPosition(gv.getNumGlyphs()).getX();
}
public static void main(String[] args) throws Throwable {
double expectedWidth = doLayout();
AtomicReference<Throwable> throwableRef = new AtomicReference<>();
CyclicBarrier barrier = new CyclicBarrier(NUMBER_OF_THREADS);
List<Thread> threads = new ArrayList<>();
for (int i = 0; i < NUMBER_OF_THREADS; i++) {
Thread thread = new Thread(() -> {
try {
barrier.await();
long timeToStop = System.nanoTime() + TIME_TO_RUN_NS;
while (System.nanoTime() < timeToStop) {
double width = doLayout();
if (width != expectedWidth) {
throw new RuntimeException(
"Unexpected layout result");
}
}
} catch (Throwable e) {
throwableRef.set(e);
}
});
threads.add(thread);
thread.start();
}
for (Thread thread : threads) {
thread.join();
}
Throwable throwable = throwableRef.get();
if (throwable != null) {
throw throwable;
}
}
}

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,49 @@
/*
* Copyright (c) 2017, 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.
*
* 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.
*/
/* @test
* @summary verify Hit index with supplementary characters.
* @bug 8173028
*/
import java.awt.Font;
import java.awt.font.FontRenderContext;
import java.awt.font.TextHitInfo;
import java.awt.font.TextLayout;
public class HitTest {
public static void main(String args[]) {
String s = new String(new int[]{0x1d400, 0x61}, 0, 2);
Font font = new Font("Dialog", Font.PLAIN, 12);
FontRenderContext frc = new FontRenderContext(null, false, false);
TextLayout tl = new TextLayout(s, font, frc);
TextHitInfo currHit = TextHitInfo.beforeOffset(3);
TextHitInfo prevHit = tl.getNextLeftHit(currHit);
System.out.println("index=" + prevHit.getCharIndex()+
" leading edge=" + prevHit.isLeadingEdge());
if (prevHit.getCharIndex() != 2) {
throw new RuntimeException("Expected 2 for hit index");
}
}
}

View file

@ -0,0 +1,67 @@
/*
* Copyright (c) 2011, 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.
*
* 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.
*/
import java.io.*;
import java.awt.*;
import java.awt.font.*;
import java.util.*;
/**
* Shows (top) with kerning, (middle) without, (bottom) also without.
*
* @bug 7017324
*/
public class KernCrash extends Frame {
private static Font font0;
private static Font font1;
private static Font font2;
public static void main(String[] args) throws Exception {
HashMap attrs = new HashMap();
font0 = Font.createFont(Font.TRUETYPE_FONT, new File("Vera.ttf"));
System.out.println("using " + font0);
attrs.put(TextAttribute.SIZE, new Float(58f));
font1 = font0.deriveFont(attrs);
attrs.put(TextAttribute.KERNING, TextAttribute.KERNING_ON);
font2 = font0.deriveFont(attrs);
KernCrash f = new KernCrash();
f.setTitle("Kerning Crash");
f.setSize(600, 300);
f.setForeground(Color.black);
f.show();
}
public void paint(Graphics g) {
Graphics2D g2 = (Graphics2D)g;
FontRenderContext frc = g2.getFontRenderContext();
TextLayout layout = new TextLayout("text", font2, frc);
layout.draw(g2, 10, 150);
String s = "WAVATastic";
TextLayout layout2 = new TextLayout(s, font1, frc);
layout2.draw(g2, 10, 200);
TextLayout layout3 = new TextLayout(s, font2, frc);
layout3.draw(g2, 10, 100);
}
}

View file

@ -0,0 +1,66 @@
/*
* Copyright (c) 2013, 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.
*
* 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.
*/
/**
* @bug 8015334
* @summary Memory leak with kerning.
*/
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.font.TextAttribute;
import java.util.HashMap;
import java.util.Map;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
public class KerningLeak {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
leak();
}
});
}
private static void leak() {
Map<TextAttribute, Object> textAttributes = new HashMap<>();
textAttributes.put(TextAttribute.FAMILY, "Sans Serif");
textAttributes.put(TextAttribute.SIZE, 12);
textAttributes.put(TextAttribute.KERNING, TextAttribute.KERNING_ON);
Font font = Font.getFont(textAttributes);
JLabel label = new JLabel();
int dummy = 0;
for (int i = 0; i < 500; i++) {
if (i % 10 == 0) System.out.println("Starting iter " + (i+1));
for (int j = 0; j <1000; j++) {
FontMetrics fm = label.getFontMetrics(font);
dummy += SwingUtilities.computeStringWidth(fm, Integer.toString(j));
}
}
System.out.println("done " + dummy);
}
}

View file

@ -0,0 +1,190 @@
/*
* Copyright (c) 1998, 2016, 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.
*
* 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.
*/
/**
* @test
* @bug 4178145 8144015
*/
/*
* Copyright 1998 IBM Corp. All Rights Reserved.
*/
import java.awt.Color;
import java.awt.Font;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsEnvironment;
import java.awt.font.TextAttribute;
import java.awt.font.TextLayout;
import java.awt.font.TextHitInfo;
import java.awt.font.FontRenderContext;
import java.util.Hashtable;
/**
* This test ensures that TextLayout will not place a caret within
* an Arabic lam-alef ligature, and will correctly caret through
* bidirectional text with numbers.
*/
public class LigatureCaretTest {
public static void main(String[] args) {
testBidiWithNumbers();
testLamAlef();
System.out.println("LigatureCaretTest PASSED");
}
private static final FontRenderContext frc =
new FontRenderContext(null, false, false);
private static Font getFontForText(String s) {
GraphicsEnvironment ge =
GraphicsEnvironment.getLocalGraphicsEnvironment();
Font[] fonts = ge.getAllFonts();
for (Font f : fonts) {
if (f.canDisplayUpTo(s) == -1) {
return f.deriveFont(Font.PLAIN, 24);
}
}
return null;
}
/**
* Caret through text mixed-direction text and check the results.
* If the test fails an Error is thrown.
* @exception an Error is thrown if the test fails
*/
public static void testBidiWithNumbers() {
String bidiWithNumbers = "abc\u05D0\u05D1\u05D2123abc";
Font font = getFontForText(bidiWithNumbers);
if (font == null) {
return;
}
Hashtable map = new Hashtable();
map.put(TextAttribute.FONT, font);
// visual order for the text:
// abc123<gimel><bet><aleph>abc
int[] carets = { 0, 1, 2, 3, 7, 8, 6, 5, 4, 9, 10, 11, 12 };
TextLayout layout = new TextLayout(bidiWithNumbers, map, frc);
// Caret through TextLayout in both directions and check results.
for (int i=0; i < carets.length-1; i++) {
TextHitInfo hit = layout.getNextRightHit(carets[i]);
if (hit.getInsertionIndex() != carets[i+1]) {
throw new Error("right hit failed within layout");
}
}
if (layout.getNextRightHit(carets[carets.length-1]) != null) {
throw new Error("right hit failed at end of layout");
}
for (int i=carets.length-1; i > 0; i--) {
TextHitInfo hit = layout.getNextLeftHit(carets[i]);
if (hit.getInsertionIndex() != carets[i-1]) {
throw new Error("left hit failed within layout");
}
}
if (layout.getNextLeftHit(carets[0]) != null) {
throw new Error("left hit failed at end of layout");
}
}
/**
* Ensure proper careting and hit-testing behavior with
* a lam-alef ligature.
* If the test fails, an Error is thrown.
* @exception an Error is thrown if the test fails
*/
public static void testLamAlef() {
// lam-alef form a mandantory ligature.
final String lamAlef = "\u0644\u0627";
final String ltrText = "abcd";
Font font = getFontForText(lamAlef+ltrText);
if (font == null) {
return;
}
Hashtable map = new Hashtable();
map.put(TextAttribute.FONT, font);
// Create a TextLayout with just a lam-alef sequence. There
// should only be two valid caret positions: one at
// insertion offset 0 and the other at insertion offset 2.
TextLayout layout = new TextLayout(lamAlef, map, frc);
TextHitInfo hit;
hit = layout.getNextLeftHit(0);
if (hit.getInsertionIndex() != 2) {
throw new Error("Left hit failed. Hit:" + hit);
}
hit = layout.getNextRightHit(2);
if (hit.getInsertionIndex() != 0) {
throw new Error("Right hit failed. Hit:" + hit);
}
hit = layout.hitTestChar(layout.getAdvance()/2, 0);
if (hit.getInsertionIndex() != 0 && hit.getInsertionIndex() != 2) {
throw new Error("Hit-test allowed incorrect caret. Hit:" + hit);
}
// Create a TextLayout with some left-to-right text
// before the lam-alef sequence. There should not be
// a caret position between the lam and alef.
layout = new TextLayout(ltrText+lamAlef, map, frc);
final int ltrLen = ltrText.length();
final int layoutLen = layout.getCharacterCount();
for (int i=0; i < ltrLen; i++) {
hit = layout.getNextRightHit(i);
if (hit.getInsertionIndex() != i+1) {
throw new Error("Right hit failed in ltr text.");
}
}
hit = layout.getNextRightHit(ltrLen);
if (layoutLen != hit.getInsertionIndex()) {
throw new Error("Right hit failed at direction boundary.");
}
hit = layout.getNextLeftHit(layoutLen);
if (hit.getInsertionIndex() != ltrLen) {
throw new Error("Left hit failed at end of text.");
}
}
}

View file

@ -0,0 +1,41 @@
/*
* Copyright (c) 2017, 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.
*
* 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.
*/
/* @test
* @summary Verify no exception for unsupported code point.
* @bug 8172967
*/
import java.awt.Font;
import java.awt.font.FontRenderContext;
import java.awt.font.TextLayout;
public class MissingCodePointLayoutTest {
public static void main(String[] args) {
Font font = new Font("Tahoma", Font.PLAIN, 12);
String text = "\ude00";
FontRenderContext frc = new FontRenderContext(null, false, false);
TextLayout layout = new TextLayout(text, font, frc);
layout.getCaretShapes(0);
}
}

View file

@ -0,0 +1,144 @@
/*
* Copyright (c) 2019, 2025, 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.
*
* 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.
*/
/*
* @test
* @bug 8223558
* @key headful
* @summary Verifies that Myanmar script is rendered correctly:
* two characters combined into one glyph
* @library /test/lib
* @build jtreg.SkippedException
* @run main MyanmarTextTest
*/
import java.awt.Font;
import java.awt.GraphicsEnvironment;
import java.util.Arrays;
import java.util.List;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
import javax.swing.plaf.TextUI;
import javax.swing.text.BadLocationException;
import javax.swing.text.Position;
import jtreg.SkippedException;
public class MyanmarTextTest {
private static final String TEXT = "\u1000\u103C";
private static final List<String> FONT_CANDIDATES =
List.of("Myanmar MN",
"Padauk",
"Myanmar Text",
"Noto Sans Myanmar");
private static final String FONT_NAME = selectFontName();
private final JFrame frame;
private final JTextField myanmarTF;
private static volatile MyanmarTextTest mtt;
public static void main(String[] args) throws Exception {
if (FONT_NAME == null) {
throw new SkippedException("No suitable font found out of the list: "
+ String.join(", ", FONT_CANDIDATES));
}
try {
SwingUtilities.invokeAndWait(MyanmarTextTest::createUI);
SwingUtilities.invokeAndWait(mtt::checkPositions);
} finally {
SwingUtilities.invokeAndWait(mtt::dispose);
}
}
private static void createUI() {
mtt = new MyanmarTextTest();
mtt.show();
}
private MyanmarTextTest() {
frame = new JFrame("Myanmar Text");
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
myanmarTF = new JTextField(TEXT);
myanmarTF.setFont(new Font(FONT_NAME, Font.PLAIN, 40));
JPanel main = new JPanel();
main.setLayout(new BoxLayout(main, BoxLayout.Y_AXIS));
main.add(myanmarTF);
main.setBorder(BorderFactory.createEmptyBorder(7, 7, 7, 7));
frame.getContentPane().add(main);
}
private void show() {
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private void dispose() {
frame.dispose();
}
private void checkPositions() {
final TextUI ui = myanmarTF.getUI();
final Position.Bias[] biasRet = new Position.Bias[1];
try {
if (2 != ui.getNextVisualPositionFrom(myanmarTF, 0,
Position.Bias.Forward, SwingConstants.EAST, biasRet)) {
throw new RuntimeException("For 0, next position should be 2");
}
if (2 != ui.getNextVisualPositionFrom(myanmarTF, 1,
Position.Bias.Forward, SwingConstants.EAST, biasRet)) {
throw new RuntimeException("For 1, next position should be 2");
}
if (0 != ui.getNextVisualPositionFrom(myanmarTF, 2,
Position.Bias.Forward, SwingConstants.WEST, biasRet)) {
throw new RuntimeException("For 2, prev position should be 0");
}
} catch (BadLocationException e) {
throw new RuntimeException(e);
}
}
private static String selectFontName() {
return Arrays.stream(GraphicsEnvironment
.getLocalGraphicsEnvironment()
.getAvailableFontFamilyNames())
.filter(FONT_CANDIDATES::contains)
.findFirst()
.orElse(null);
}
}

View file

@ -0,0 +1,82 @@
/*
* Copyright (c) 2015, 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.
*
* 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.
*/
/*
* @test
* @bug 7162125
* @summary Test ligatures form on OS X.
*/
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.awt.font.TextAttribute;
import java.util.HashMap;
import java.util.Map;
public class OSXLigatureTest {
public static void main(String[] args) {
if (!System.getProperty("os.name").startsWith("Mac")) {
return;
}
String ligStr = "ffi";
int w = 50, h = 50;
BufferedImage bi1 = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
Graphics2D bi1Graphics = bi1.createGraphics();
bi1Graphics.setColor(Color.white);
bi1Graphics.fillRect(0, 0, w, h);
bi1Graphics.setColor(Color.black);
Font noLigFont = new Font("Gill Sans", Font.PLAIN, 30);
bi1Graphics.setFont(noLigFont);
bi1Graphics.drawString(ligStr, 10, 40);
BufferedImage bi2 = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
Graphics2D bi2Graphics = bi2.createGraphics();
bi2Graphics.setColor(Color.white);
bi2Graphics.fillRect(0, 0, w, h);
bi2Graphics.setColor(Color.black);
Map<TextAttribute, Object> attributes = new HashMap<>();
attributes.put(TextAttribute.LIGATURES, TextAttribute.LIGATURES_ON);
Font ligFont = noLigFont.deriveFont(attributes);
bi2Graphics.setFont(ligFont);
bi2Graphics.drawString(ligStr, 10, 40);
boolean same = true;
for (int x = 0; x < w; x++) {
for (int y = 0; y < h; y++) {
int c1 = bi1.getRGB(x, y);
int c2 = bi2.getRGB(x, y);
same &= (c1 == c2);
}
if (!same) {
break;
}
}
if (same) {
throw new RuntimeException("Images do not differ - no ligature");
}
}
}

View file

@ -0,0 +1,257 @@
/*
* Copyright (c) 2002, 2025, 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.
*
* 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.
*/
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.font.FontRenderContext;
import java.awt.font.TextLayout;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Arrays;
import javax.swing.AbstractAction;
import javax.swing.Box;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.UIManager;
import static javax.swing.BorderFactory.createEmptyBorder;
/*
* @test
* @bug 4650997
* @summary rotate a TextLayout and verify that the bounds are correct
* @library /java/awt/regtesthelpers
* @build PassFailJFrame
* @run main/manual RotFontBoundsTest
*/
public final class RotFontBoundsTest {
private static final String TEXT = ".This is a STRINg.";
private static final String INSTRUCTIONS =
"A string \u201C" + TEXT + "\u201D is drawn at eight different "
+ "angles, and eight boxes that surround the bounds of the text "
+ "layouts (give or take a pixel) are drawn in red. The boxes "
+ "are always composed of horizontal and vertical lines \u2014 "
+ "they are not rotated.\n"
+ "\n"
+ "By default, all the rotations are displayed. Select or clear "
+ "a check box with an angle to show or hide a particular "
+ "rotation. Click \"Select All\" or \"Clear All\" to show all "
+ "the rotations or to hide them.\n"
+ "\n"
+ "Click the Pass button if each box encloses its corresponding "
+ "text layout.\n"
+ "Otherwise, click Screenshot to save a screenshot for failure "
+ "analysis and then click Fail.";
private static boolean verbose;
public static void main(String[] args) throws Exception {
verbose = (args.length > 0 && args[0].equalsIgnoreCase("verbose"));
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
PassFailJFrame.builder()
.instructions(INSTRUCTIONS)
.rows(20)
.columns(50)
.testTimeOut(15)
.screenCapture()
.testUI(RotFontBoundsTest::createUI)
.build()
.awaitAndCheck();
}
private static final int ROTATIONS = 8;
private static JComponent createUI() {
final RotatedTextBounds rotatedText = new RotatedTextBounds();
final JPanel checkBoxes = new JPanel(new FlowLayout(FlowLayout.CENTER,
4, 4));
checkBoxes.setBorder(createEmptyBorder(0, 8, 8, 8));
for (int i = 0; i < ROTATIONS; i++) {
checkBoxes.add(new JCheckBox(new SelectRotationAction(i, rotatedText)));
}
JButton selectAll = new JButton("Select All");
selectAll.addActionListener(
e -> selectAllCheckBoxes(checkBoxes.getComponents(), true));
selectAll.setMnemonic('S');
JButton clearAll = new JButton("Clear All");
clearAll.addActionListener(
e -> selectAllCheckBoxes(checkBoxes.getComponents(), false));
clearAll.setMnemonic('C');
Box controls = Box.createHorizontalBox();
controls.add(new JLabel("Visible Rotations:"));
controls.add(Box.createHorizontalGlue());
controls.add(selectAll);
controls.add(Box.createHorizontalStrut(4));
controls.add(clearAll);
controls.setBorder(createEmptyBorder(8, 8, 0, 8));
Box controlPanel = Box.createVerticalBox();
controlPanel.add(controls);
controlPanel.add(checkBoxes);
Box javaVersion = Box.createHorizontalBox();
javaVersion.setBorder(createEmptyBorder(8, 8, 8, 8));
javaVersion.add(new JLabel("Java version: "
+ System.getProperty("java.runtime.version")));
javaVersion.add(Box.createHorizontalGlue());
Box main = Box.createVerticalBox();
main.setName("Rotated TextLayout Test");
main.add(controlPanel);
main.add(rotatedText);
main.add(javaVersion);
return main;
}
private static final class RotatedTextBounds extends JComponent {
private final Font font = new Font(Font.DIALOG, Font.PLAIN, 24);
private final boolean[] rotationVisible = new boolean[ROTATIONS];
private RotatedTextBounds() {
setBackground(Color.WHITE);
setPreferredSize(new Dimension(400, 400));
Arrays.fill(rotationVisible, true);
}
public void setRotationVisible(int rotation, boolean visible) {
rotationVisible[rotation] = visible;
repaint();
}
// Counts the number of paints
private int counter = 0;
@Override
public void paintComponent(Graphics _g) {
Graphics2D g = (Graphics2D) _g;
Dimension d = getSize();
g.setColor(getBackground());
g.fillRect(0, 0, d.width, d.height);
counter++;
int x = d.width / 2;
int y = d.height / 2;
FontRenderContext frc = g.getFontRenderContext();
for (int i = 0; i < ROTATIONS; i++) {
if (!rotationVisible[i]) {
continue;
}
double angle = -Math.PI / 4.0 * i;
AffineTransform flip = AffineTransform.getRotateInstance(angle);
Font flippedFont = font.deriveFont(flip);
TextLayout tl = new TextLayout(TEXT, flippedFont, frc);
Rectangle2D bb = tl.getBounds();
g.setPaint(Color.BLACK);
tl.draw(g, x, y);
g.setPaint(Color.RED);
g.drawRect(x + (int) bb.getX(), y + (int) bb.getY(),
(int) bb.getWidth(), (int) bb.getHeight());
if (verbose) {
if (counter == 1) {
printDetails(angle, tl);
} else if (i == 0) {
System.out.println("Paint, counter=" + counter);
}
}
}
}
private static void printDetails(double angle, TextLayout tl) {
System.out.println("Angle: " + angle);
System.out.println("getAscent: " + tl.getAscent());
System.out.println("getAdvance: " + tl.getAdvance());
System.out.println("getBaseline: " + tl.getBaseline());
System.out.println("getBounds: " + tl.getBounds());
System.out.println("getDescent: " + tl.getDescent());
System.out.println("getLeading: " + tl.getLeading());
System.out.println("getVisibleAdvance: " + tl.getVisibleAdvance());
System.out.println(".");
}
}
private static final class SelectRotationAction
extends AbstractAction
implements PropertyChangeListener {
private final int rotation;
private final RotatedTextBounds rotatedText;
private SelectRotationAction(int rotation,
RotatedTextBounds rotatedText) {
super(rotation * (360 / ROTATIONS) + "\u00B0");
this.rotation = rotation;
this.rotatedText = rotatedText;
putValue(SELECTED_KEY, true);
addPropertyChangeListener(this);
}
private void updateRotationVisible() {
rotatedText.setRotationVisible(rotation,
(Boolean) getValue(SELECTED_KEY));
}
@Override
public void actionPerformed(ActionEvent e) {
updateRotationVisible();
}
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals(SELECTED_KEY)) {
updateRotationVisible();
}
}
}
private static void selectAllCheckBoxes(Component[] checkBoxes,
boolean visible) {
Arrays.stream(checkBoxes)
.forEach(c -> ((JCheckBox) c).setSelected(visible));
}
}

View file

@ -0,0 +1,118 @@
/*
* Copyright (c) 2016, 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.
*
* 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.
*/
/*
* @test
* @bug 8139176
* @summary Test layout uses correct styled font.
* @run main StyledFontLayoutTest
*/
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.font.FontRenderContext;
import java.awt.font.GlyphVector;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
public class StyledFontLayoutTest extends JPanel {
static final int W=600, H=400;
static boolean interactive;
static BufferedImage im;
public static void main(String[] args) {
interactive = args.length > 0;
runTest();
if (!interactive) {
return;
}
SwingUtilities.invokeLater(() -> {
JFrame frame = new JFrame("Styled Font Layout Test");
frame.add(new StyledFontLayoutTest());
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setSize(W, H);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}
@Override
protected void paintComponent(Graphics g) {
g.drawImage(im, 0, 0, null);
}
private static void runTest() {
im = new BufferedImage(W, H, BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = im.createGraphics();
g2d.setColor(Color.white);
g2d.fillRect(0, 0, W, H);
g2d.setColor(Color.black);
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
char[] chs = "Sample Text.".toCharArray();
int len = chs.length;
int x = 50, y = 100;
FontRenderContext frc = g2d.getFontRenderContext();
Font plain = new Font("Serif", Font.PLAIN, 48);
GlyphVector pgv = plain.layoutGlyphVector(frc, chs, 0, len, 0);
g2d.setFont(plain);
g2d.drawChars(chs, 0, len, x, y); y +=50;
g2d.drawGlyphVector(pgv, x, y); y += 50;
Rectangle2D plainStrBounds = plain.getStringBounds(chs, 0, len, frc);
Rectangle2D plainGVBounds = pgv.getLogicalBounds();
Font bold = new Font("Serif", Font.BOLD, 48);
GlyphVector bgv = bold.layoutGlyphVector(frc, chs, 0, len, 0);
Rectangle2D boldStrBounds = bold.getStringBounds(chs, 0, len, frc);
Rectangle2D boldGVBounds = bgv.getLogicalBounds();
g2d.setFont(bold);
g2d.drawChars(chs, 0, len, x, y); y +=50;
g2d.drawGlyphVector(bgv, x, y);
System.out.println("Plain String Bounds = " + plainStrBounds);
System.out.println("Bold String Bounds = " + boldStrBounds);
System.out.println("Plain GlyphVector Bounds = " + plainGVBounds);
System.out.println("Bold GlyphVector Bounds = " + boldGVBounds);
if (!plainStrBounds.equals(boldStrBounds) &&
plainGVBounds.equals(boldGVBounds))
{
System.out.println("Test failed: Plain GV bounds same as Bold");
if (!interactive) {
throw new RuntimeException("Plain GV bounds same as Bold");
}
}
};
}

View file

@ -0,0 +1,80 @@
/*
* Copyright (c) 2014, 2018, 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.
*
* 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.
*
*/
/* @test
* @bug 8031462 8198406
* @requires (os.family == "mac")
* @summary verify rendering of MORX fonts on OS X.
*/
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import static java.awt.image.BufferedImage.TYPE_INT_ARGB;
public class TestAATMorxFont {
public static void main(String[] args) {
String osName = System.getProperty("os.name");
System.out.println("OS is " + osName);
osName = osName.toLowerCase();
if (!osName.startsWith("mac")) {
return;
}
BufferedImage bi = new BufferedImage(1200, 400, TYPE_INT_ARGB);
Graphics g = bi.getGraphics();
test(g);
g.dispose();
}
private static void test(Graphics g) {
Graphics2D g2d = (Graphics2D)g;
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
int y = 50;
g.setFont(new Font("Gujarati MT", Font.PLAIN, 40));
System.out.println(g.getFont());
g.drawString("\u0A95\u0ACD \u0A95\u0A95\u0A95 \u0A95\u0ACD\u0A95\u0ACD\u0A95", 20, y);
y += 50;
g.setFont(new Font("Tamil Sangam MN", Font.PLAIN, 40));
System.out.println(g.getFont());
g.drawString("\u0b95\u0bCD \u0b95\u0b95\u0b95 \u0b95\u0bCD\u0b95\u0bCD\u0b95", 20, y);
y += 50;
g.setFont(new Font("Telugu Sangam MN", Font.PLAIN, 40));
System.out.println(g.getFont());
g.drawString("\u0c15\u0c4D \u0c15\u0c15\u0c15 \u0c15\u0c4D\u0c15\u0c4D\u0c15", 20, y);
y += 50;
g.setFont(new Font("Devanagari Sangam MN", Font.PLAIN, 40));
System.out.println(g.getFont());
g.drawString("\u0915\u0940 \u0915\u0947 \u0915\u0942", 20, y);
y += 50;
g.drawString("\u0907\u0930\u094D\u0915\u094D\u0915\u094D\u0915\u094D\u0915\u094D\u0915\u094D\u0915\u094D\u0915\u094D\u0915\u094D\u0915\u094D\u0915", 20, y);
y += 50;
g.drawString("\u0930\u093F\u0935\u094D\u092F\u0942 \u0915\u0947 \u092C\u093E\u0926 \u0935\u093F\u0915\u093E\u0938 \u0913\u0932\u0902\u092A\u093F\u0915 \u0938\u0947 \u092C\u093E\u0939\u0930 (\u0926\u0947\u0935\u0928\u093E\u0917\u0930\u0940) (\u0939\u093F\u0928\u094D\u0926\u0940) \u0907\u0930\u094D\u0915\u094D\u0915\u094D\u0915\u094D\u0915\u094D\u0915\u094D\u0915\u094D\u0915\u094D\u0915\u094D\u0915\u094D\u0915", 20, y);
}
}

View file

@ -0,0 +1,113 @@
/*
* Copyright (c) 2006, 2025, 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.
*
* 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.
*/
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsEnvironment;
import java.awt.RenderingHints;
import javax.swing.JPanel;
/*
* @test
* @bug 6320502
* @summary Display laid out text which substitutes invisible glyphs correctly.
* @library /java/awt/regtesthelpers /test/lib
* @build PassFailJFrame jtreg.SkippedException
* @run main/manual TestGASPHint
*/
public class TestGASPHint extends JPanel {
private static final String text = "\u0905\u0901\u0917\u094d\u0930\u0947\u091c\u093c\u0940";
private static final Font font = getPhysicalFontForText(text, Font.PLAIN, 36);
public static void main(String[] args) throws Exception {
if (font == null) {
throw new jtreg.SkippedException("No Devanagari font found. Test Skipped");
}
final String INSTRUCTIONS = """
A short piece of Devanagari text should appear without any
artifacts. In particular there should be no "empty rectangles"
representing the missing glyph.
If the above condition is true, press Pass, else Fail.""";
PassFailJFrame.builder()
.title("TestGASPHint Instruction")
.instructions(INSTRUCTIONS)
.columns(32)
.splitUI(TestGASPHint::new)
.build()
.awaitAndCheck();
}
@Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
@Override
public void paint(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.WHITE);
g2d.fillRect(0, 0, getWidth(), getHeight());
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_GASP);
g2d.setFont(font);
g2d.setColor(Color.BLACK);
g2d.drawString(text, 10, 50);
}
/*
* Searches the available system fonts for a font which can display all the
* glyphs in the input text correctly. Returns null, if not found.
*/
private static Font getPhysicalFontForText(String text, int style, int size) {
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
String[] names = ge.getAvailableFontFamilyNames();
for (String n : names) {
switch (n.toLowerCase()) {
case "dialog":
case "dialoginput":
case "serif":
case "sansserif":
case "monospaced":
break;
default:
Font f = new Font(n, style, size);
if (f.canDisplayUpTo(text) == -1) {
return f;
}
}
}
return null;
}
}

View file

@ -0,0 +1,261 @@
/*
* Copyright (c) 2005, 2015, 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.
*
* 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.
*/
/**
* @test
* @bug 6271221 8145584
* @summary ask a text layout for its pixel bounds, then render it and
* compute the actual pixel bounds when rendering-- the actual pixel bounds
* must be contained within the bounds reported by the text layout, or there
* will be an exception.
*/
/*
* Copyright 2005 IBM Corp. All Rights Reserved.
*/
import java.awt.*;
import java.awt.font.*;
import java.awt.geom.*;
import java.awt.image.*;
import java.text.*;
import java.util.*;
import static java.awt.Font.*;
import static java.awt.font.GraphicAttribute.*;
import static java.awt.font.ShapeGraphicAttribute.*;
import static java.awt.font.TextAttribute.*;
public class TestGetPixelBounds {
static final boolean DEBUG;
static final boolean DUMP;
static {
String dbg = null;
String dmp = null;
try {
dbg = System.getProperty("DEBUG");
dmp = System.getProperty("DUMP");
}
catch (SecurityException e) {
dbg = dmp = null;
}
DEBUG = dbg != null;
DUMP = dmp != null;
}
public static void main(String[] args) {
float x = 0;
float y = 0;
boolean rotate = false;
boolean underline = false;
boolean graphic = false;
String text = "Ping";
for (int i = 0; i < args.length; ++i) {
String arg = args[i];
if (arg.startsWith("-x")) {
x = Float.parseFloat(args[++i]);
} else if (arg.startsWith("-y")) {
y = Float.parseFloat(args[++i]);
} else if (arg.startsWith("-r")) {
rotate = true;
} else if (arg.startsWith("-u")) {
underline = true;
} else if (arg.startsWith("-g")) {
graphic = true;
} else if (arg.startsWith("-t")) {
text = args[++i];
}
}
FontRenderContext frc = new FontRenderContext(null, true, true);
Map<TextAttribute, Object> m = new HashMap<TextAttribute, Object>();
m.put(FAMILY, SANS_SERIF);
m.put(SIZE, 16);
if (underline) {
m.put(UNDERLINE, UNDERLINE_ON);
}
if (rotate) {
m.put(TRANSFORM, AffineTransform.getRotateInstance(Math.PI/4));
}
Font font = Font.getFont(m);
AttributedString as;
if (graphic) {
GraphicAttribute sga = new ShapeGraphicAttribute(
new Ellipse2D.Float(0,-10,10,20), TOP_ALIGNMENT, STROKE);
as = new AttributedString(text + '*' + text, m);
as.addAttribute(CHAR_REPLACEMENT, sga, text.length(), text.length() + 1);
} else {
as = new AttributedString(text, m);
}
TextLayout tl = new TextLayout(as.getIterator(), frc);
System.out.println("tl bounds: " + tl.getBounds());
System.out.println("tl compute: " + computeLayoutBounds(tl, x, y, frc));
System.out.println("tl pixel bounds: " + tl.getPixelBounds(frc, x, y));
System.out.println(" again int off: " + tl.getPixelBounds(frc, x+2, y - 2));
System.out.println(" again frac off: " + tl.getPixelBounds(frc, x+.5f, y + .2f));
System.out.println(" again frc: " + tl.getPixelBounds(
new FontRenderContext(AffineTransform.getScaleInstance(100, 100), true, true), x, y));
System.out.println(" again int off: " + tl.getPixelBounds(frc, x-2, y+2));
GlyphVector gv = font.createGlyphVector(frc, text);
System.out.println("gv bounds: " + gv.getPixelBounds(frc, x, y));
System.out.println("gv compute: " + computeLayoutBounds(gv, x, y, frc));
if (!tl.getPixelBounds(frc, x, y).contains(computeLayoutBounds(tl, x, y, frc))) {
throw new RuntimeException("error, tl.bounds does not contain computed bounds");
}
}
static Rectangle computeLayoutBounds(TextLayout tl, float x, float y, FontRenderContext frc) {
Rectangle bounds = tl.getBounds().getBounds();
BufferedImage im = new BufferedImage(bounds.width + 4, bounds.height + 4,
BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = im.createGraphics();
g2d.setColor(Color.WHITE);
g2d.fillRect(0, 0, im.getWidth(), im.getHeight());
float fx = (float)Math.IEEEremainder(x,1);
float fy = (float)Math.IEEEremainder(y,1);
g2d.setColor(Color.BLACK);
tl.draw(g2d, fx + 2 - bounds.x, fy + 2 - bounds.y);
Rectangle r = computePixelBounds(im);
r.x += (int)Math.floor(x) - 2 + bounds.x;
r.y += (int)Math.floor(y) - 2 + bounds.y;
return r;
}
static Rectangle computeLayoutBounds(GlyphVector gv, float x, float y, FontRenderContext frc) {
Rectangle bounds = gv.getVisualBounds().getBounds();
BufferedImage im = new BufferedImage(bounds.width + 4, bounds.height + 4,
BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = im.createGraphics();
g2d.setColor(Color.WHITE);
g2d.fillRect(0, 0, im.getWidth(), im.getHeight());
float fx = (float)Math.IEEEremainder(x,1);
float fy = (float)Math.IEEEremainder(y,1);
g2d.setColor(Color.BLACK);
g2d.drawGlyphVector(gv, fx + 2 - bounds.x, fy + 2 - bounds.y);
Rectangle r = computePixelBounds(im);
r.x += (int)Math.floor(x) - 2 + bounds.x;
r.y += (int)Math.floor(y) - 2 + bounds.y;
return r;
}
static Rectangle computePixelBounds(BufferedImage im) {
int w = im.getWidth();
int h = im.getHeight();
Formatter fmt = DEBUG ? new Formatter(System.err) : null;
// dump
if (DUMP && DEBUG) {
fmt.format(" ");
for (int j = 0; j < w; ++j) {
fmt.format("%2d", j);
}
for (int i = 0; i < h; ++i) {
fmt.format("\n[%2d] ", i);
for (int j = 0; j < w; ++j) {
fmt.format("%c ", im.getRGB(j, i) == -1 ? ' ' : '*');
}
}
fmt.format("\n");
}
int l = -1, t = -1, r = w, b = h;
{
// get top
int[] buf = new int[w];
loop:
while (++t < h) {
im.getRGB(0, t, buf.length, 1, buf, 0, w); // w ignored
for (int i = 0; i < buf.length; i++) {
if (buf[i] != -1) {
if (DEBUG) fmt.format("top pixel at %d,%d = 0x%08x\n", i, t, buf[i]);
break loop;
}
}
}
if (DEBUG) fmt.format("t: %d\n", t);
}
// get bottom
{
int[] buf = new int[w];
loop:
while (--b > t) {
im.getRGB(0, b, buf.length, 1, buf, 0, w); // w ignored
for (int i = 0; i < buf.length; ++i) {
if (buf[i] != -1) {
if (DEBUG) fmt.format("bottom pixel at %d,%d = 0x%08x\n", i, b, buf[i]);
break loop;
}
}
}
++b;
if (DEBUG) fmt.format("b: %d\n", b);
}
// get left
{
loop:
while (++l < r) {
for (int i = t; i < b; ++i) {
int v = im.getRGB(l, i);
if (v != -1) {
if (DEBUG) fmt.format("left pixel at %d,%d = 0x%08x\n", l, i, v);
break loop;
}
}
}
if (DEBUG) fmt.format("l: %d\n", l);
}
// get right
{
loop:
while (--r > l) {
for (int i = t; i < b; ++i) {
int v = im.getRGB(r, i);
if (v != -1) {
if (DEBUG) fmt.format("right pixel at %d,%d = 0x%08x\n", r, i, v);
break loop;
}
}
}
++r;
if (DEBUG) fmt.format("r: %d\n", r);
}
return new Rectangle(l, t, r-l, b-t);
}
}

View file

@ -0,0 +1,98 @@
/*
* Copyright (c) 2026, 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.
*
* 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.
*/
/*
* @test
* @bug 6562639
* @summary Verify correct getPixelBounds() behavior regardless of text color.
*/
import java.awt.Color;
import java.awt.Font;
import java.awt.Rectangle;
import java.awt.font.FontRenderContext;
import java.awt.font.TextAttribute;
import java.awt.font.TextLayout;
import java.awt.geom.Rectangle2D;
import java.util.HashMap;
import java.util.Map;
public class TestGetPixelBoundsWithColors {
private static final Color TRANSPARENT_BLACK = new Color(0, 0, 0, 0);
private static final Color TRANSPARENT_WHITE = new Color(255, 255, 255, 0);
public static void main(String[] args) throws Exception {
Color[] colors = new Color[] {
Color.WHITE, Color.BLACK, Color.YELLOW, Color.RED, Color.GREEN,
Color.GRAY, Color.LIGHT_GRAY, Color.DARK_GRAY, Color.PINK,
Color.CYAN, Color.MAGENTA, Color.BLUE, null
};
for (Color color : colors) {
test(color);
}
testTransparent(TRANSPARENT_BLACK);
testTransparent(TRANSPARENT_WHITE);
}
private static void test(Color c) {
Map< TextAttribute, Object > underline = new HashMap<>();
underline.put(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON);
Font font1 = new Font(Font.DIALOG, Font.PLAIN, 60).deriveFont(underline);
Map< TextAttribute, Object > foreground = new HashMap<>();
foreground.put(TextAttribute.FOREGROUND, c);
Font font2 = font1.deriveFont(foreground);
FontRenderContext frc = new FontRenderContext(null, true, true);
TextLayout layout1 = new TextLayout("TEST", font1, frc);
TextLayout layout2 = new TextLayout("TEST", font2, frc);
Rectangle r1 = layout1.getPixelBounds(frc, 0, 0);
Rectangle r2 = layout2.getPixelBounds(frc, 0, 0);
if (!r1.equals(r2)) {
throw new RuntimeException("For color " + c + ", " + r1 + " != " + r2);
}
Rectangle2D bounds = layout1.getBounds();
if (Math.abs(bounds.getX() - r1.x) > 3 ||
Math.abs(bounds.getY() - r1.y) > 3 ||
Math.abs(bounds.getWidth() - r1.width) > 6 ||
Math.abs(bounds.getHeight() - r1.height) > 6) {
throw new RuntimeException("For color " + c + ", pixel bounds " +
r1 + " not similar to " + bounds);
}
}
private static void testTransparent(Color c) {
Font font1 = new Font(Font.DIALOG, Font.PLAIN, 60);
Map< TextAttribute, Object > attributes = new HashMap<>();
attributes.put(TextAttribute.FOREGROUND, c);
Font font2 = font1.deriveFont(attributes);
FontRenderContext frc = new FontRenderContext(null, true, true);
TextLayout layout = new TextLayout("TEST", font2, frc);
Rectangle r = layout.getPixelBounds(frc, 0, 0);
if (!r.isEmpty()) {
throw new RuntimeException("Expected empty pixel bounds for " + c + " but got " + r);
}
}
}

View file

@ -0,0 +1,155 @@
/*
* Copyright (c) 2003, 2025, 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.
*
* 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.
*/
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.font.FontRenderContext;
import java.awt.font.GraphicAttribute;
import java.awt.font.ShapeGraphicAttribute;
import java.awt.font.TextAttribute;
import java.awt.font.TextLayout;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;
import java.text.AttributedCharacterIterator;
import java.text.AttributedString;
import javax.swing.JPanel;
/*
* @test
* @bug 4915565 4920820 4920952
* @summary Display graphics (circles) embedded in text, and draw both the outline (top)
* and black box bounds (bottom) of the result. The circles should each display at a
* different height. The outline and frames should approximately (within a pixel
* or two) surround each character.
* @library /java/awt/regtesthelpers
* @build PassFailJFrame
* @run main/manual TestGraphicOutline
*/
public class TestGraphicOutline {
public static void main(String[] args) throws Exception {
final String INSTRUCTIONS = """
Display graphics (circles) embedded in text, and draw both the
outline (top) and black box bounds (bottom) of the result.
The circles should each display at a different height.
The outline and frames should approximately (within a pixel or two)
surround each character.
Pass the test if these conditions hold.
'Black box bounds' is a term that refers to the bounding rectangles
of each glyph, see the TextLayout API getBlackBoxBounds. It does not
mean that the rendered outlines in the test are supposed to be black.
The color of the outlines does not matter and is not part of the test
conditions. Since there is no API for embedded graphics to return an
outline that matches the shape of the graphics, the outlines of the
graphics are their visual bounding boxes, which are rectangles.
This is not an error. These outlines, as stated, should surround each
character's graphic.""";
PassFailJFrame.builder()
.title("TestGraphicOutline Instruction")
.instructions(INSTRUCTIONS)
.columns(40)
.testUI(TestGraphicsPanel::new)
.build()
.awaitAndCheck();
}
private static final class TestGraphicsPanel extends JPanel {
TextLayout tl;
public TestGraphicsPanel() {
setBackground(Color.white);
setPreferredSize(new Dimension(650, 300));
setName("2D Text");
}
@Override
public void paint(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
int w = getSize().width;
int h = getSize().height;
g2.setColor(getBackground());
g2.fillRect(0, 0, w, h);
Font f1 = new Font(Font.SANS_SERIF, Font.BOLD, 60);
Font f2 = new Font(Font.SERIF, Font.ITALIC, 80);
String str = "The Starry Night ok?";
AttributedString ats = new AttributedString(str);
Shape s = new Ellipse2D.Float(0, -10, 12, 12);
GraphicAttribute iga1 = new ShapeGraphicAttribute(s, GraphicAttribute.TOP_ALIGNMENT, false);
GraphicAttribute iga2 = new ShapeGraphicAttribute(s, GraphicAttribute.HANGING_BASELINE, false);
GraphicAttribute iga3 = new ShapeGraphicAttribute(s, GraphicAttribute.CENTER_BASELINE, false);
GraphicAttribute iga4 = new ShapeGraphicAttribute(s, GraphicAttribute.ROMAN_BASELINE, false);
GraphicAttribute iga5 = new ShapeGraphicAttribute(s, GraphicAttribute.BOTTOM_ALIGNMENT, false);
ats.addAttribute(TextAttribute.CHAR_REPLACEMENT, iga1, 1, 2);
ats.addAttribute(TextAttribute.CHAR_REPLACEMENT, iga2, 3, 4);
ats.addAttribute(TextAttribute.CHAR_REPLACEMENT, iga3, 7, 8);
ats.addAttribute(TextAttribute.CHAR_REPLACEMENT, iga4, 10, 11);
ats.addAttribute(TextAttribute.CHAR_REPLACEMENT, iga5, 14, 15);
ats.addAttribute(TextAttribute.FONT, f1, 0, 20);
ats.addAttribute(TextAttribute.FONT, f2, 4, 10);
AttributedCharacterIterator iter = ats.getIterator();
FontRenderContext frc = g2.getFontRenderContext();
tl = new TextLayout(iter, frc);
Rectangle2D bounds = tl.getBounds();
float sw = (float) bounds.getWidth();
float sh = (float) bounds.getHeight();
g2.translate((w - sw) / 2f, h / 2f - sh + tl.getAscent() - 2);
g2.setColor(Color.blue);
tl.draw(g2, 0, 0);
g2.draw(bounds);
g2.setColor(Color.black);
Shape shape = tl.getOutline(null);
g2.draw(shape);
g2.translate(0, sh + 5);
g2.setColor(Color.blue);
tl.draw(g2, 0, 0);
g2.draw(bounds);
g2.setColor(Color.red);
shape = tl.getBlackBoxBounds(0, tl.getCharacterCount());
g2.draw(shape);
}
}
}

View file

@ -0,0 +1,84 @@
/*
* Copyright (c) 2007, 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.
*
* 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.
*/
/* @test
* @summary verify TextLayout handles Hebrew marks correctly
* @bug 6529141
*/
import java.awt.*;
import java.awt.font.*;
import java.awt.geom.*;
public class TestHebrewMark {
public static void main(String args[]) {
FontRenderContext frc = new FontRenderContext(null,false,false);
final String fonts[] = { "Arial", "Arial Hebrew", "Arial Unicode" };
final char ALEF = '\u05D0'; // a letter
final char QAMATS = '\u05B8'; // a combining mark, should show up UNDER the alef (no advance)
final String string1 = "\u05DE\u05B8\u05E9\u05C1\u05B0\u05DB\u05B5\u05E0\u05B4\u05D9\u05D0\u05B7\u05D7\u05B2\u05E8\u05B6\u05D9\u05DA\u05B8\u05E0\u05BC\u05B8\u05E8\u05D5\u05BC\u05E6\u05B8\u05D4\u05D4\u05B1\u05D1\u05B4\u05D9\u05D0\u05B7\u05E0\u05B4\u05D9\u05D4\u05B7\u05DE\u05BC\u05B6\u05DC\u05B6\u05DA\u05B0\u05D7\u05B2\u05D3\u05B8\u05E8\u05B8\u05D9\u05D5\u05E0\u05B8\u05D2\u05B4\u05D9\u05DC\u05B8\u05D4\u05D5\u05B0\u05E0\u05B4\u05E9\u05C2\u05B0\u05DE\u05B0\u05D7\u05B8\u05D4\u0020\u05D1\u05BC\u05B8\u05DA\u05B0\u05E0\u05B7\u05D6\u05B0\u05DB\u05BC\u05B4\u05D9\u05E8\u05B8\u05D4\u05D3\u05B9\u05D3\u05B6\u05D9\u05DA\u05B8\u05DE\u05B4\u05D9\u05BC\u05B7\u05D9\u05B4\u05DF\u05DE\u05B5\u05D9\u05E9\u05C1\u05B8\u05E8\u05B4\u05D9\u05DD\u05D0\u05B2\u05D4\u05B5\u05D1\u05D5\u05BC\u05DA\u05B8";
final String string2 = string1.replaceAll("\u05B8", ""); // remove qamats
int string1len = string1.length();
int string2len = string2.length();
System.out.println("String1 has " + string1len+" chars, and string2 (without the QAMATS) has " + string2.length());
if(string1len == string2len) {
throw new RuntimeException("Hey, string1 and string2 are both " + string1len + " chars long - shouldn't happen.");
}
Font f = null;
// try to find a font that will work
for(String fontname : fonts ) {
System.err.println("trying: " +fontname);
Font afont = new Font(fontname,Font.PLAIN,18);
if(!afont.getFontName().equals(fontname)) {
System.out.println(fontname + ": is actually " + afont.getFontName() + " - skipping this font.");
continue;
}
if(!afont.canDisplay(ALEF) || !afont.canDisplay(QAMATS)) {
System.out.println(fontname + ": can't display ALEF or QAMATS - skipping this font");
continue;
}
f = afont;
System.err.println("Might be OK: " + fontname);
System.out.println("Using font " + f.getFontName());
TextLayout tl = new TextLayout(string1, f, frc);
TextLayout tl2 = new TextLayout(string2, f, frc);
Rectangle2D tlBounds = tl.getBounds();
Rectangle2D tlBounds2 = tl2.getBounds();
System.out.println("tlbounds="+tlBounds);
System.out.println("tl.getAdvance()="+tl.getAdvance());
System.out.println("tl2bounds="+tlBounds2);
System.out.println("tl2.getAdvance()="+tl2.getAdvance());
if(tl.getAdvance() != tl2.getAdvance()) {
throw new RuntimeException("Advance of string with and without QAMATS differs: " + tl.getAdvance() + " vs. " + tl2.getAdvance());
} else {
System.out.println("6529141 OK, widths are same.");
}
}
// print a notice if none of them worked.
if(f == null) {
System.out.println("Could not find a suitable font - skipping this test.");
return;
}
}
}

View file

@ -0,0 +1,261 @@
/*
* Copyright (c) 1999, 2024, 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.
*
* 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.
*/
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.Panel;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.font.FontRenderContext;
import java.awt.font.LineBreakMeasurer;
import java.awt.font.TextAttribute;
import java.awt.font.TextLayout;
import java.awt.geom.Rectangle2D;
import java.text.AttributedCharacterIterator;
import java.text.AttributedString;
/*
* @test
* @bug 4211728 4178140 8145542
* @summary Justify several lines of text and verify that the lines are the same
length and cursor positions are correct.
Bug 4211728: TextLayout.draw() draws characters at wrong position.
Bug 4178140: TextLayout does not justify.
* @library /java/awt/regtesthelpers
* @build PassFailJFrame
* @run main/manual TestJustification
*/
public class TestJustification {
private static final String INSTRUCTIONS = """
Five lines of text should appear, all justified to the same width,
followed by a sixth line containing only roman characters and
no spaces which is not justified, and instead is centered.
Carets should appear between all characters.
PASS the test if this is true, else press FAIL.
""";
public static void main(String[] args) throws Exception {
PassFailJFrame.builder()
.title("Test Instructions")
.instructions(INSTRUCTIONS)
.rows((int) INSTRUCTIONS.lines().count() + 2)
.columns(35)
.testUI(TestJustification::createUI)
.build()
.awaitAndCheck();
}
private static Frame createUI() {
Frame frame= new Frame("Test Text Justification");
JustificationPanel panel = new JustificationPanel("Bitstream Cyberbit");
frame.add(panel);
frame.add("Center", panel);
frame.setSize(500, 450);
return frame;
}
static class JustificationPanel extends Panel {
TextLayout[] layouts;
String fontname;
float height;
float oldfsize;
AttributedCharacterIterator lineText;
TextLayout[] lines;
int linecount;
float oldwidth;
JustificationPanel(String fontname) {
this.fontname = fontname;
}
private static final String[] texts = {
"This is an english Highlighting demo.", "Highlighting",
"This is an arabic \u0627\u0628\u062a\u062c \u062e\u0644\u0627\u062e demo.", "arabic \u0627\u0628\u062a\u062c",
"This is a hebrew \u05d0\u05d1\u05d2 \u05d3\u05d4\u05d5 demo.", "hebrew \u05d0\u05d1\u05d2",
"This is a cjk \u4e00\u4e01\u4e02\uac00\uac01\uc4fa\uf900\uf901\uf902 demo.", "cjk",
"NoSpaceCJK:\u4e00\u4e01\u4e02and\uac00\uac01\uc4faand\uf900\uf901\uf902", "No",
"NoSpaceRoman", "Space"
};
public void paint(Graphics g) {
Graphics2D g2d = (Graphics2D)g;
Dimension d = getSize();
Insets insets = getInsets();
float w = d.width - insets.left - insets.right;
float h = d.height - insets.top - insets.bottom;
int fsize = (int)w/25;
FontRenderContext frc = g2d.getFontRenderContext();
if (layouts == null || fsize != oldfsize) {
oldfsize = fsize;
Font f0 = new Font(fontname, Font.PLAIN, fsize);
Font f1 = new Font(fontname, Font.ITALIC, (int)(fsize * 1.5));
if (layouts == null) {
layouts = new TextLayout[texts.length / 2];
}
height = 0;
for (int i = 0; i < layouts.length; ++i) {
String text = texts[i*2];
String target = texts[i*2+1];
AttributedString astr = new AttributedString(text);
astr.addAttribute(TextAttribute.FONT, f0, 0, text.length());
int start = text.indexOf(target);
int limit = start + target.length();
astr.addAttribute(TextAttribute.FONT, f1, start, limit);
TextLayout layout = new TextLayout(astr.getIterator(), frc);
layout = layout.getJustifiedLayout(w - 20);
layouts[i] = layout;
height += layout.getAscent() + layout.getDescent() + layout.getLeading();
}
}
g2d.setColor(Color.white);
g2d.fill(new Rectangle.Float(insets.left, insets.top, w, h));
float basey = 20;
for (TextLayout layout : layouts) {
float la = layout.getAscent();
float ld = layout.getDescent();
float ll = layout.getLeading();
float lw = layout.getAdvance();
float lh = la + ld + ll;
float lx = (w - lw) / 2f;
float ly = basey + layout.getAscent();
g2d.setColor(Color.black);
g2d.translate(insets.left + lx, insets.top + ly);
Rectangle2D bounds = new Rectangle2D.Float(0, -la, lw, lh);
g2d.draw(bounds);
layout.draw(g2d, 0, 0);
g2d.setColor(Color.red);
for (int j = 0, e = layout.getCharacterCount(); j <= e; ++j) {
Shape[] carets = layout.getCaretShapes(j, bounds);
g2d.draw(carets[0]);
}
g2d.translate(-insets.left - lx, -insets.top - ly);
basey += layout.getAscent() + layout.getDescent() + layout.getLeading();
}
// add LineBreakMeasurer-generated layouts
if (lineText == null) {
String text = "This is a long line of text that should be broken across multiple "
+ "lines and then justified to fit the break width. This test should pass if "
+ "these lines are justified to the same width, and fail otherwise. It should "
+ "also format the hebrew (\u05d0\u05d1\u05d2 \u05d3\u05d4\u05d5) and arabic "
+ "(\u0627\u0628\u062a\u062c \u062e\u0644\u0627\u062e) and CJK "
+ "(\u4e00\u4e01\u4e02\uac00\uac01\uc4fa\u67b1\u67b2\u67b3\u67b4\u67b5\u67b6\u67b7"
+ "\u67b8\u67b9) text correctly.";
Float regular = 16.0F;
Float big = 24.0F;
AttributedString astr = new AttributedString(text);
astr.addAttribute(TextAttribute.SIZE, regular, 0, text.length());
astr.addAttribute(TextAttribute.FAMILY, fontname, 0, text.length());
int ix = text.indexOf("broken");
astr.addAttribute(TextAttribute.SIZE, big, ix, ix + 6);
ix = text.indexOf("hebrew");
astr.addAttribute(TextAttribute.SIZE, big, ix, ix + 6);
ix = text.indexOf("arabic");
astr.addAttribute(TextAttribute.SIZE, big, ix, ix + 6);
ix = text.indexOf("CJK");
astr.addAttribute(TextAttribute.SIZE, big, ix, ix + 3);
lineText = astr.getIterator();
}
float width = w - 20;
if (lines == null || width != oldwidth) {
oldwidth = width;
lines = new TextLayout[10];
linecount = 0;
LineBreakMeasurer measurer = new LineBreakMeasurer(lineText, frc);
for (;;) {
TextLayout layout = measurer.nextLayout(width);
if (layout == null) {
break;
}
// justify all but last line
if (linecount > 0) {
lines[linecount - 1] = lines[linecount - 1].getJustifiedLayout(width);
}
if (linecount == lines.length) {
TextLayout[] nlines = new TextLayout[lines.length * 2];
System.arraycopy(lines, 0, nlines, 0, lines.length);
lines = nlines;
}
lines[linecount++] = layout;
}
}
float basex = insets.left + 10;
basey += 10;
g2d.setColor(Color.black);
for (int i = 0; i < linecount; ++i) {
TextLayout layout = lines[i];
basey += layout.getAscent();
float adv = layout.getAdvance();
float dx = layout.isLeftToRight() ? 0 : width - adv;
layout.draw(g2d, basex + dx, basey);
basey += layout.getDescent() + layout.getLeading();
}
}
}
}

View file

@ -0,0 +1,82 @@
/*
* 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.
*
* 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.
*
*/
/* @test
* @summary Verify Old Hangul display
* @bug 6886358
* @ignore Requires a special font installed.
*/
import javax.swing.*;
import javax.swing.border.LineBorder;
import java.awt.*;
import java.awt.event.ActionEvent;
public class TestOldHangul {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new TestOldHangul().run();
}
});
}
public static boolean AUTOMATIC_TEST=true; // true; run test automatically, else manually at button push
private void run() {
Font ourFont = null;
final String fontName = "UnBatangOdal.ttf"; // download from http://chem.skku.ac.kr/~wkpark/project/font/GSUB/UnbatangOdal/ and place in {user.home}/fonts/
try {
ourFont = Font.createFont(Font.TRUETYPE_FONT, new java.io.File(new java.io.File(System.getProperty("user.home"),"fonts"), fontName));
ourFont = ourFont.deriveFont((float)48.0);
} catch(Throwable t) {
t.printStackTrace();
System.err.println("Fail: " + t);
return;
}
JFrame frame = new JFrame(System.getProperty("java.version"));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new JPanel();
final JTextArea label = new JTextArea("(empty)");
label.setSize(400, 300);
label.setBorder(new LineBorder(Color.black));
label.setFont(ourFont);
final String str = "\u110A\u119E\u11B7\u0020\u1112\u119E\u11AB\uAE00\u0020\u1100\u119E\u11F9\u0020\u112B\u119E\u11BC\n";
if(AUTOMATIC_TEST) { /* run the test automatically (else, manually) */
label.setText(str);
} else {
JButton button = new JButton("Old Hangul");
button.addActionListener(new AbstractAction() {
public void actionPerformed(ActionEvent actionEvent) {
label.setText(str);
}
});
panel.add(button);
}
panel.add(label);
frame.getContentPane().add(panel);
frame.pack();
frame.setVisible(true);
}
}

View file

@ -0,0 +1,134 @@
/*
* Copyright (c) 1999, 2025, 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.
*
* 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.
*/
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.font.FontRenderContext;
import java.awt.font.TextAttribute;
import java.awt.font.TextLayout;
import java.text.AttributedString;
import javax.swing.JPanel;
/*
* @test
* @bug 4221422
* @summary Display several TextLayouts with various selections.
* All the selections should be between non-italic and italic text,
* and the top and bottom of the selection region should be horizontal.
* @library /java/awt/regtesthelpers
* @build PassFailJFrame
* @run main/manual TestSelection
*/
public final class TestSelection extends JPanel {
private static final float MARGIN = 20;
public static void main(String[] args) throws Exception {
final String INSTRUCTIONS = """
Several TextLayouts are displayed along with selections.
The selection regions should have horizontal top and bottom segments.
If above condition is true, press Pass else Fail.""";
PassFailJFrame.builder()
.title("TestSelection Instruction")
.instructions(INSTRUCTIONS)
.columns(40)
.splitUI(TestSelection::new)
.build()
.awaitAndCheck();
}
@Override
public Dimension getPreferredSize() {
return new Dimension(300, 300);
}
private float drawSelectionAndLayout(Graphics2D g2d,
TextLayout layout,
float y,
int selStart,
int selLimit) {
Color selectionColor = Color.PINK;
Color textColor = Color.BLACK;
y += layout.getAscent();
g2d.translate(MARGIN, y);
Shape hl = layout.getLogicalHighlightShape(selStart, selLimit);
g2d.setColor(selectionColor);
g2d.fill(hl);
g2d.setColor(textColor);
layout.draw(g2d, 0, 0);
g2d.translate(-MARGIN, -y);
y += layout.getDescent() + layout.getLeading() + 10;
return y;
}
@Override
public void paint(Graphics g) {
String text = "Hello world";
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.WHITE);
g2d.fillRect(0, 0, getWidth(), getHeight());
AttributedString attrStr = new AttributedString(text);
FontRenderContext frc = g2d.getFontRenderContext();
final int midPoint = text.indexOf('w');
final int selStart = midPoint / 2;
final int selLimit = text.length() - selStart;
final Font italic = new Font(Font.SANS_SERIF, Font.ITALIC, 24);
float y = MARGIN;
attrStr.addAttribute(TextAttribute.FONT, italic, 0, midPoint);
TextLayout layout = new TextLayout(attrStr.getIterator(), frc);
y = drawSelectionAndLayout(g2d, layout, y, selStart - 1, selLimit);
y = drawSelectionAndLayout(g2d, layout, y, selStart, selLimit);
y = drawSelectionAndLayout(g2d, layout, y, selStart + 1, selLimit);
attrStr = new AttributedString(text);
attrStr.addAttribute(TextAttribute.FONT,
italic, midPoint, text.length());
layout = new TextLayout(attrStr.getIterator(), frc);
y = drawSelectionAndLayout(g2d, layout, y, selStart, selLimit);
attrStr = new AttributedString(text);
attrStr.addAttribute(TextAttribute.FONT, italic, 0, midPoint);
attrStr.addAttribute(TextAttribute.SIZE, 48f, midPoint, text.length());
layout = new TextLayout(attrStr.getIterator(), frc);
y = drawSelectionAndLayout(g2d, layout, y, selStart, selLimit);
}
}

View file

@ -0,0 +1,72 @@
/*
* 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.
*
* 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.
*
*/
/**
* @test
* @key headful
* @summary verify lack of crash on U+0DDD.
* @bug 6795060
*/
import javax.swing.*;
import javax.swing.border.LineBorder;
import java.awt.*;
import java.awt.event.ActionEvent;
public class TestSinhalaChar {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new TestSinhalaChar().run();
}
});
}
public static boolean AUTOMATIC_TEST=true; // true; run test automatically, else manually at button push
private void run() {
JFrame frame = new JFrame("Test Character (no crash = PASS)");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new JPanel();
final JLabel label = new JLabel("(empty)");
label.setSize(400, 100);
label.setBorder(new LineBorder(Color.black));
label.setFont(new Font(Font.DIALOG, Font.PLAIN, 12));
if(AUTOMATIC_TEST) { /* run the test automatically (else, manually) */
label.setText(Character.toString('\u0DDD'));
} else {
JButton button = new JButton("Set Char x0DDD");
button.addActionListener(new AbstractAction() {
public void actionPerformed(ActionEvent actionEvent) {
label.setText(Character.toString('\u0DDD'));
}
});
panel.add(button);
}
panel.add(label);
frame.getContentPane().add(panel);
frame.pack();
frame.setVisible(true);
}
}

View file

@ -0,0 +1,92 @@
/*
* Copyright (c) 2005, 2025, 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.
*
* 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.
*/
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.font.FontRenderContext;
import java.awt.font.TextAttribute;
import java.awt.font.TextLayout;
import java.text.AttributedCharacterIterator;
import java.text.AttributedString;
import javax.swing.JPanel;
/*
* @test
* @bug 6426360
* @summary Display a TextLayout with strikethrough at a number of
* different offsets relative to the pixel grid.
* @library /java/awt/regtesthelpers
* @build PassFailJFrame
* @run main/manual TestStrikethrough
*/
public class TestStrikethrough extends JPanel {
public static void main(String[] args) throws Exception {
final String INSTRUCTIONS = """
Display text with strikethrough at a number of different positions.
Press Fail if any line is missing a strikethrough else press Pass.""";
PassFailJFrame.builder()
.title("TestStrikethrough Instruction")
.instructions(INSTRUCTIONS)
.columns(35)
.splitUI(TestStrikethrough::new)
.build()
.awaitAndCheck();
}
@Override
public Dimension getPreferredSize() {
return new Dimension(200, 120);
}
@Override
public void paint(Graphics aContext) {
Graphics2D g2d = (Graphics2D) aContext;
g2d.setColor(Color.WHITE);
g2d.fillRect(0, 0, getWidth(), getHeight());
Font font = new Font(Font.DIALOG, Font.PLAIN, 9);
FontRenderContext frc = g2d.getFontRenderContext();
String str = "Where is the strikethrough?";
AttributedString as = new AttributedString(str);
as.addAttribute(TextAttribute.FONT, font);
as.addAttribute(TextAttribute.STRIKETHROUGH, TextAttribute.STRIKETHROUGH_ON);
AttributedCharacterIterator aci = as.getIterator();
TextLayout tl = new TextLayout(aci, frc);
float delta = (float) (Math.ceil(tl.getAscent() + tl.getDescent() + tl.getLeading()) + .1);
float y = delta - .1f;
g2d.setColor(Color.BLACK);
for (int i = 0; i < 11; ++i) {
tl.draw(g2d, 10f, y);
y += delta;
}
}
}

View file

@ -0,0 +1,86 @@
/*
* 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.
*
* 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.
*
*/
/* @test
* @summary verify tibetan output
* @bug 6886358
* @ignore Requires a special font installed
*/
import javax.swing.*;
import javax.swing.border.LineBorder;
import java.awt.*;
import java.awt.event.ActionEvent;
public class TestTibetan {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new TestTibetan().run();
}
});
}
public static boolean AUTOMATIC_TEST=true; // true; run test automatically, else manually at button push
private void run() {
Font ourFont = null;
try {
//For best results: Font from: http://download.savannah.gnu.org/releases/free-tibetan/jomolhari/
// place in $(user.home)/fonts/
ourFont = Font.createFont(Font.TRUETYPE_FONT, new java.io.File(new java.io.File(System.getProperty("user.home"),"fonts"), "Jomolhari-alpha3c-0605331.ttf"));
//ourFont = new Font("serif",Font.PLAIN, 24);
ourFont = ourFont.deriveFont((float)24.0);
} catch(Throwable t) {
t.printStackTrace();
System.err.println("Fail: " + t);
return;
}
JFrame frame = new JFrame(System.getProperty("java.version"));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new JPanel();
final JTextArea label = new JTextArea("(empty)");
label.setSize(400, 300);
label.setBorder(new LineBorder(Color.black));
label.setFont(ourFont);
final String str = "\u0F04\u0F05\u0F0D\u0F0D\u0020\u0F4F\u0F72\u0F53\u0F0B\u0F4F\u0F72\u0F53\u0F0B\u0F42\u0FB1\u0F72\u0F0B\u0F51\u0F54\u0F60\u0F0B\u0F62\u0FA9\u0F63"; // TinTin.
if(AUTOMATIC_TEST) { /* run the test automatically (else, manually) */
label.setText(str);
} else {
JButton button = new JButton("Set Char x0DDD");
button.addActionListener(new AbstractAction() {
public void actionPerformed(ActionEvent actionEvent) {
label.setText(str);
}
});
panel.add(button);
}
panel.add(label);
frame.getContentPane().add(panel);
frame.pack();
frame.setVisible(true);
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View file

@ -0,0 +1,92 @@
/*
* 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.
*
* 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.
*
*/
/* @test
* @summary Verify Variation Selector matches an expected image
* @bug 8187100
* @ignore Requires a special font installed.
*/
import javax.swing.SwingUtilities;
import javax.swing.border.LineBorder;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JFrame;
import javax.swing.JTextArea;
import javax.swing.ImageIcon;
import java.awt.Font;
import java.awt.Color;
public class TestVS {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new TestVS().run();
}
});
}
private void run() {
Font ourFont = null;
final String fontName = "ipaexm.ttf";
// download from https://ipafont.ipa.go.jp/node26#en
// and place in {user.home}/fonts/
try {
ourFont = Font.createFont(Font.TRUETYPE_FONT,
new java.io.File(new java.io.File(
System.getProperty("user.home"),
"fonts"), fontName));
ourFont = ourFont.deriveFont((float)48.0);
final String actualFontName = ourFont.getFontName();
if (!actualFontName.equals("IPAexMincho")) {
System.err.println("*** Warning: missing font IPAexMincho.");
System.err.println("*** Using font: " + actualFontName);
}
} catch(Throwable t) {
t.printStackTrace();
System.err.println("Fail: " + t);
return;
}
JFrame frame = new JFrame(System.getProperty("java.version"));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new JPanel();
final JTextArea label = new JTextArea("empty");
label.setSize(400, 300);
label.setBorder(new LineBorder(Color.black));
label.setFont(ourFont);
final String str = "\u845b\udb40\udd00\u845b\udb40\udd01\n";
label.setText(str);
panel.add(label);
panel.add(new JLabel(ourFont.getFamily()));
// Show the expected result.
panel.add(new JLabel(new ImageIcon("TestVS-expect.png")));
frame.getContentPane().add(panel);
frame.pack();
frame.setVisible(true);
}
}

View file

@ -0,0 +1,53 @@
/*
* Copyright (c) 2005, 2007, 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.
*
* 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.
*/
/* @test
* @summary verify TextLayout.getBounds() return visual bounds
* @bug 6323611 6761856
*/
import java.awt.*;
import java.awt.font.*;
import java.awt.geom.*;
public class TextLayoutBounds {
public static void main(String args[]) {
FontRenderContext frc = new FontRenderContext(null, false, false);
Font f = new Font("SansSerif",Font.BOLD,32);
String s = new String("JAVA");
TextLayout tl = new TextLayout(s, f, frc);
Rectangle2D tlBounds = tl.getBounds();
GlyphVector gv = f.createGlyphVector(frc, s);
Rectangle2D gvvBounds = gv.getVisualBounds();
Rectangle2D oBounds = tl.getOutline(null).getBounds2D();
System.out.println("tlbounds="+tlBounds);
System.out.println("gvbounds="+gvvBounds);
System.out.println("outlineBounds="+oBounds);
if (!gvvBounds.equals(tlBounds)) {
throw new RuntimeException("Bounds differ [gvv != tl]");
}
if (!tlBounds.equals(oBounds)) {
throw new RuntimeException("Bounds differ [tl != outline]");
}
}
}

View file

@ -0,0 +1,316 @@
/*
* Copyright (c) 2025, 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.
*
* 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.
*/
/*
* @test
* @bug 4138921
* @summary Confirm constructor behavior for various edge cases.
*/
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.font.FontRenderContext;
import java.awt.font.TextAttribute;
import java.awt.font.TextHitInfo;
import java.awt.font.TextLayout;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.text.AttributedCharacterIterator;
import java.text.AttributedString;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
public class TextLayoutConstructorTest {
public static void main(String[] args) throws Exception {
testFontConstructor();
testMapConstructor();
testIteratorConstructor();
}
private static void testFontConstructor() {
// new TextLayout(String, Font, FontRenderContext)
Font font = new Font(Font.DIALOG, Font.PLAIN, 20);
FontRenderContext frc = new FontRenderContext(null, true, true);
assertThrows(() -> new TextLayout(null, font, frc),
IllegalArgumentException.class,
"Null string passed to TextLayout constructor.");
assertThrows(() -> new TextLayout("test", (Font) null, frc),
IllegalArgumentException.class,
"Null font passed to TextLayout constructor.");
assertThrows(() -> new TextLayout("test", font, null),
IllegalArgumentException.class,
"Null font render context passed to TextLayout constructor.");
Function< String, TextLayout > creator = (s) -> new TextLayout(s, font, frc);
assertEmptyTextLayoutBehavior(creator);
}
private static void testMapConstructor() {
// new TextLayout(String, Map, FontRenderContext)
Map< TextAttribute, Object > attributes = Map.of(TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD);
FontRenderContext frc = new FontRenderContext(null, true, true);
assertThrows(() -> new TextLayout(null, attributes, frc),
IllegalArgumentException.class,
"Null string passed to TextLayout constructor.");
assertThrows(() -> new TextLayout("test", (Map) null, frc),
IllegalArgumentException.class,
"Null map passed to TextLayout constructor.");
assertThrows(() -> new TextLayout("test", attributes, null),
IllegalArgumentException.class,
"Null font render context passed to TextLayout constructor.");
Function< String, TextLayout > creator = (s) -> new TextLayout(s, attributes, frc);
assertEmptyTextLayoutBehavior(creator);
}
private static void testIteratorConstructor() {
// new TextLayout(AttributedCharacterIterator, FontRenderContext)
Map< TextAttribute, Object > attributes = Map.of();
FontRenderContext frc = new FontRenderContext(null, true, true);
assertThrows(() -> new TextLayout(null, frc),
IllegalArgumentException.class,
"Null iterator passed to TextLayout constructor.");
AttributedCharacterIterator it1 = new AttributedString("test", attributes).getIterator();
assertThrows(() -> new TextLayout(it1, null),
IllegalArgumentException.class,
"Null font render context passed to TextLayout constructor.");
Function< String, TextLayout > creator = (s) -> {
AttributedCharacterIterator it2 = new AttributedString(s, attributes).getIterator();
return new TextLayout(it2, frc);
};
assertEmptyTextLayoutBehavior(creator);
}
private static void assertEmptyTextLayoutBehavior(Function< String, TextLayout > creator) {
TextLayout tl = creator.apply("");
TextLayout ref = creator.apply(" "); // space
FontRenderContext frc = new FontRenderContext(null, true, true);
Rectangle zero = new Rectangle(0, 0, 0, 0);
Rectangle2D.Float zero2D = new Rectangle2D.Float(0, 0, 0, 0);
Rectangle2D.Float oneTwo = new Rectangle2D.Float(1, 2, 0, 0);
Rectangle2D.Float kilo = new Rectangle2D.Float(0, 0, 1000, 1000);
AffineTransform identity = new AffineTransform();
TextLayout.CaretPolicy policy = new TextLayout.CaretPolicy();
TextHitInfo start = TextHitInfo.trailing(-1);
TextHitInfo end = TextHitInfo.leading(0);
assertEqual(0, tl.getJustifiedLayout(100).getAdvance(), "justified advance");
assertEqual(0, tl.getBaseline(), "baseline");
float[] offsets = tl.getBaselineOffsets();
float[] refOffsets = ref.getBaselineOffsets();
assertEqual(3, offsets.length, "baseline offsets");
assertEqual(refOffsets[0], offsets[0], "baseline offset 1");
assertEqual(refOffsets[1], offsets[1], "baseline offset 2");
assertEqual(refOffsets[2], offsets[2], "baseline offset 3");
assertEqual(0, tl.getAdvance(), "advance");
assertEqual(0, tl.getVisibleAdvance(), "visible advance");
assertEqual(ref.getAscent(), tl.getAscent(), "ascent");
assertEqual(ref.getDescent(), tl.getDescent(), "descent");
assertEqual(ref.getLeading(), tl.getLeading(), "leading");
assertEqual(zero2D, tl.getBounds(), "bounds");
assertEqual(zero2D, tl.getPixelBounds(frc, 0, 0), "pixel bounds 1");
assertEqual(oneTwo, tl.getPixelBounds(frc, 1, 2), "pixel bounds 2");
assertEqual(true, tl.isLeftToRight(), "left to right");
assertEqual(false, tl.isVertical(), "is vertical");
assertEqual(0, tl.getCharacterCount(), "character count");
float[] caretInfo = tl.getCaretInfo(start, kilo);
float[] refCaretInfo = ref.getCaretInfo(start, kilo);
assertEqual(6, caretInfo.length, "caret info length 1");
assertEqual(refCaretInfo[0], caretInfo[0], "first caret info 1");
assertEqual(refCaretInfo[1], caretInfo[1], "second caret info 1");
assertEqual(refCaretInfo[2], caretInfo[2], "third caret info 1");
assertEqual(refCaretInfo[3], caretInfo[3], "fourth caret info 1");
assertEqual(refCaretInfo[4], caretInfo[4], "fifth caret info 1");
assertEqual(refCaretInfo[5], caretInfo[5], "sixth caret info 1");
float[] caretInfo2 = tl.getCaretInfo(start);
float[] refCaretInfo2 = ref.getCaretInfo(start);
assertEqual(6, caretInfo2.length, "caret info length 2");
assertEqual(refCaretInfo2[0], caretInfo2[0], "first caret info 2");
assertEqual(refCaretInfo2[1], caretInfo2[1], "second caret info 2");
assertEqual(refCaretInfo2[2], caretInfo2[2], "third caret info 2");
assertEqual(refCaretInfo2[3], caretInfo2[3], "fourth caret info 2");
assertEqual(refCaretInfo2[4], caretInfo2[4], "fifth caret info 2");
assertEqual(refCaretInfo2[5], caretInfo2[5], "sixth caret info 2");
assertEqual(null, tl.getNextRightHit(start), "next right hit 1");
assertEqual(null, tl.getNextRightHit(end), "next right hit 2");
assertEqual(null, tl.getNextRightHit(0, policy), "next right hit 3");
assertEqual(null, tl.getNextRightHit(0), "next right hit 4");
assertEqual(null, tl.getNextLeftHit(start), "next left hit 1");
assertEqual(null, tl.getNextLeftHit(end), "next left hit 2");
assertEqual(null, tl.getNextLeftHit(0, policy), "next left hit 3");
assertEqual(null, tl.getNextLeftHit(0), "next left hit 4");
assertEqual(end, tl.getVisualOtherHit(start), "visual other hit");
Shape caretShape = tl.getCaretShape(start, kilo);
Shape refCaretShape = ref.getCaretShape(start, kilo);
assertEqual(refCaretShape.getBounds(), caretShape.getBounds(), "caret shape 1");
Shape caretShape2 = tl.getCaretShape(start);
Shape refCaretShape2 = ref.getCaretShape(start);
assertEqual(refCaretShape2.getBounds(), caretShape2.getBounds(), "caret shape 2");
assertEqual(0, tl.getCharacterLevel(0), "character level");
Shape[] caretShapes = tl.getCaretShapes(0, kilo, policy);
Shape[] refCaretShapes = ref.getCaretShapes(0, kilo, policy);
assertEqual(2, caretShapes.length, "caret shapes length 1");
assertEqual(refCaretShapes[0].getBounds(), caretShapes[0].getBounds(), "caret shapes strong 1");
assertEqual(refCaretShapes[1], caretShapes[1], "caret shapes weak 1");
assertEqual(null, caretShapes[1], "caret shapes weak 1");
Shape[] caretShapes2 = tl.getCaretShapes(0, kilo);
Shape[] refCaretShapes2 = ref.getCaretShapes(0, kilo);
assertEqual(2, caretShapes2.length, "caret shapes length 2");
assertEqual(refCaretShapes2[0].getBounds(), caretShapes2[0].getBounds(), "caret shapes strong 2");
assertEqual(refCaretShapes2[1], caretShapes2[1], "caret shapes weak 2");
assertEqual(null, caretShapes2[1], "caret shapes weak 2");
Shape[] caretShapes3 = tl.getCaretShapes(0);
Shape[] refCaretShapes3 = ref.getCaretShapes(0);
assertEqual(2, caretShapes3.length, "caret shapes length 3");
assertEqual(refCaretShapes3[0].getBounds(), caretShapes3[0].getBounds(), "caret shapes strong 3");
assertEqual(refCaretShapes3[1], caretShapes3[1], "caret shapes weak 3");
assertEqual(null, caretShapes3[1], "caret shapes weak 3");
assertEqual(0, tl.getLogicalRangesForVisualSelection(start, start).length, "logical ranges for visual selection");
assertEqual(zero2D, tl.getVisualHighlightShape(start, start, kilo).getBounds(), "visual highlight shape 1");
assertEqual(zero2D, tl.getVisualHighlightShape(start, start).getBounds(), "visual highlight shape 2");
assertEqual(zero, tl.getLogicalHighlightShape(0, 0, kilo).getBounds(), "logical highlight shape 1");
assertEqual(zero, tl.getLogicalHighlightShape(0, 0).getBounds(), "logical highlight shape 2");
assertEqual(zero, tl.getBlackBoxBounds(0, 0).getBounds(), "black box bounds");
TextHitInfo hit = tl.hitTestChar(0, 0);
assertEqual(-1, hit.getCharIndex(), "hit test char index 1");
assertEqual(false, hit.isLeadingEdge(), "hit test leading edge 1");
TextHitInfo hit2 = tl.hitTestChar(0, 0, kilo);
assertEqual(-1, hit2.getCharIndex(), "hit test char index 2");
assertEqual(false, hit2.isLeadingEdge(), "hit test leading edge 2");
assertEqual(false, tl.equals(creator.apply("")), "equals");
assertEqual(false, tl.toString().isEmpty(), "to string");
assertDoesNotDraw(tl);
assertEqual(zero2D, tl.getOutline(identity).getBounds(), "outline");
assertEqual(null, tl.getLayoutPath(), "layout path");
Point2D.Float point = new Point2D.Float(7, 7);
tl.hitToPoint(start, point);
assertEqual(0, point.x, "hit to point x");
assertEqual(0, point.y, "hit to point y");
}
private static void assertEqual(int expected, int actual, String name) {
if (expected != actual) {
throw new RuntimeException("Expected " + name + " = " + expected + ", but got " + actual);
}
}
private static void assertEqual(float expected, float actual, String name) {
if (expected != actual) {
throw new RuntimeException("Expected " + name + " = " + expected + ", but got " + actual);
}
}
private static void assertEqual(boolean expected, boolean actual, String name) {
if (expected != actual) {
throw new RuntimeException("Expected " + name + " = " + expected + ", but got " + actual);
}
}
private static void assertEqual(Object expected, Object actual, String name) {
if (!Objects.equals(expected, actual)) {
throw new RuntimeException("Expected " + name + " = " + expected + ", but got " + actual);
}
}
private static void assertThrows(Runnable r, Class< ? > type, String message) {
Class< ? > actualType;
String actualMessage;
Exception actualException;
try {
r.run();
actualType = null;
actualMessage = null;
actualException = null;
} catch (Exception e) {
actualType = e.getClass();
actualMessage = e.getMessage();
actualException = e;
}
if (!Objects.equals(type, actualType)) {
throw new RuntimeException(type + " != " + actualType, actualException);
}
if (!Objects.equals(message, actualMessage)) {
throw new RuntimeException(message + " != " + actualMessage, actualException);
}
}
private static void assertDoesNotDraw(TextLayout layout) {
int w = 200;
int h = 200;
BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_BYTE_BINARY);
Graphics2D g2d = image.createGraphics();
int expected = image.getRGB(0, 0);
layout.draw(g2d, w / 2f, h / 2f); // should not actually draw anything
int[] rowPixels = new int[w];
for (int y = 0; y < h; y++) {
image.getRGB(0, y, w, 1, rowPixels, 0, w);
for (int x = 0; x < w; x++) {
if (rowPixels[x] != expected) {
throw new RuntimeException(
"pixel (" + x + ", " + y +"): " + expected + " != " + rowPixels[x]);
}
}
}
}
}

View file

@ -0,0 +1,55 @@
/*
* Copyright (c) 2016, 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.
*
* 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.
*/
/*
* @test
* @bug 4497648
* @summary Test equals methods on TextLayout
*/
import java.awt.Font;
import java.awt.font.FontRenderContext;
import java.awt.font.TextLayout;
public class TextLayoutEqualsTest {
public static void main(String args[]) {
Font font = new Font(Font.DIALOG, Font.PLAIN, 12);
String text = "hello world";
FontRenderContext frc = new FontRenderContext(null, false, false);
TextLayout tl1 = new TextLayout(text, font, frc);
TextLayout tl2 = new TextLayout(text, font, frc);
if (tl1.equals(tl2) ||
tl2.equals(tl1) ||
tl1.equals((Object)tl2) ||
tl2.equals((Object)tl1))
{
throw new RuntimeException("Equal TextLayouts");
}
if (!tl1.equals(tl1) ||
!tl1.equals((Object)tl1))
{
throw new RuntimeException("Non-Equal TextLayouts");
}
}
}

View file

@ -0,0 +1,88 @@
/*
* Copyright (c) 2008, 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.
*
* 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.
*/
/* @test
* @summary verify outline and stroking of underline match.
* @bug 6751616
*/
import java.awt.*;
import java.awt.font.*;
import java.awt.geom.*;
import java.awt.image.*;
import java.util.*;
public class UnderlinePositionTest {
public static void main(String[] args) {
BufferedImage bi =
new BufferedImage(600, 150, BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = bi.createGraphics();
g2d.setColor(Color.white);
g2d.fillRect(0, 0, 600, 150);
float x = 10;
float y = 90;
Map map = new HashMap();
map.put(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON);
map.put(TextAttribute.SIZE, new Float(80));
FontRenderContext frc = g2d.getFontRenderContext();
// Use all spaces for the text so we know we are dealing
// only with pixels from the underline.
String text = " ";
TextLayout tl = new TextLayout(text, map, frc);
Shape outline = tl.getOutline(null);
Rectangle2D bounds = outline.getBounds();
g2d.translate(x, y);
g2d.setColor(Color.RED);
tl.draw(g2d, 0, 0);
/* By getting the outline, then its bounds, then filling
* according to the same pixelisation rules, this ought to
* match the position of the original underline. If any
* red pixels are left, then the test will fail.
*/
g2d.setColor(Color.BLUE);
g2d.fill(bounds);
g2d.dispose();
checkBI(bi, Color.RED);
}
static void checkBI(BufferedImage bi, Color badColor) {
int badrgb = badColor.getRGB();
int w = bi.getWidth(null);
int h = bi.getHeight(null);
for (int x=0; x<w; x++) {
for (int y=0; y<h; y++) {
int col = bi.getRGB(x, y);
if (col == badrgb) {
throw new RuntimeException("Got " + col);
}
}
}
}
}

View file

@ -0,0 +1,65 @@
/* @test
* @summary Verify two identical 'a's are rendered
* @bug 8187100
* @ignore Requires a special font installed.
*/
import javax.swing.JFrame;
import javax.swing.JComponent;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.font.FontRenderContext;
import java.awt.font.GlyphVector;
public class VariationSelectorTest {
// A font supporting Unicode variation selectors is required
// At least DejaVu 2.20 from 2007
private static final Font FONT = new Font("DejaVu Sans", Font.PLAIN, 12);
public static void main(String[] args) {
final String fontName = FONT.getFontName();
if (!fontName.equals("DejaVuSans")) {
System.err.println("*** Warning: Font DejaVuSans not installed.");
System.err.println("*** Using font: " + fontName);
}
SwingUtilities.invokeLater(() -> {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
frame.add(new MyComponent());
frame.setSize(200, 200);
frame.setVisible(true);
frame.setLocationRelativeTo(null);
});
}
private static class MyComponent extends JComponent {
@Override
protected void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
FontRenderContext frc = g2d.getFontRenderContext();
String text = "a";
GlyphVector gv = FONT.layoutGlyphVector(
frc, text.toCharArray(), 0, text.length(),
Font.LAYOUT_LEFT_TO_RIGHT);
System.out.println("'a'=" + gv.getNumGlyphs());
g2d.drawString("=" + gv.getNumGlyphs() + " ('a')", 100, 50);
g2d.drawGlyphVector(gv, 80, 50);
String text2 = "a\ufe00";
GlyphVector gv2 = FONT.layoutGlyphVector(
frc, text2.toCharArray(), 0, text2.length(),
Font.LAYOUT_LEFT_TO_RIGHT);
g2d.drawGlyphVector(gv2, 80, 100);
System.out.println("'a'+VS=" + gv2.getNumGlyphs());
g2d.drawString("=" + gv2.getNumGlyphs() + " ('a'+VS)", 100, 100);
if ((gv.getNumGlyphs() == 1) && (gv2.getNumGlyphs() == 1)) {
System.out.println("PASS");
g2d.drawString("PASS", 10, 15);
} else {
System.err.println("FAIL");
g2d.drawString("FAIL", 10, 15);
}
}
}
}

View file

@ -0,0 +1,107 @@
/*
* Copyright (c) 2005, 2008, 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.
*
* 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.
*/
import java.awt.*;
import java.awt.font.*;
import java.awt.geom.*;
import java.text.*;
/* @test
* @summary verify TextLine advance
* @bug 6582460 8164818
*/
/*
Sample correct output:
Left-to-right (One style): Advance = 127.30078, Visible advance = 118.30078
Right-to-left (One style): Advance = 127.30078, Visible advance = 118.30078
Left-to-right (Multiple styles): Advance = 127.30078, Visible advance = 118.30078
Right-to-left (Multiple styles): Advance = 127.30078, Visible advance = 118.30078
*/
public class VisibleAdvance
{
public static void main (String [] args)
{
System.out.println ("java.version = " + System.getProperty ("java.version"));
float advances[] = null;
advances = showAndCalculateAdvance ("Left-to-right (One style): ", getString (TextAttribute.RUN_DIRECTION_LTR, false), advances);
advances = showAndCalculateAdvance ("Right-to-left (One style): ", getString (TextAttribute.RUN_DIRECTION_RTL, false), advances);
advances = showAndCalculateAdvance ("Left-to-right (Multiple styles): ", getString (TextAttribute.RUN_DIRECTION_LTR, true), advances);
advances = showAndCalculateAdvance ("Right-to-left (Multiple styles): ", getString (TextAttribute.RUN_DIRECTION_RTL, true), advances);
}
private static final String textA = "Text with trailing ";
private static final String textB = "spaces";
private static final String textC = " ";
private static final String text = textA + textB + textC;
private static final int startOfTextB = textA.length ();
private static final int endOfTextB = startOfTextB + textB.length ();
private static final Font font = new Font ("Serif", Font.PLAIN, 12);
private static AttributedString getString (Boolean direction,
boolean multipleStyles)
{
AttributedString as = new AttributedString (text);
as.addAttribute (TextAttribute.FONT, font);
as.addAttribute (TextAttribute.RUN_DIRECTION, direction);
if (multipleStyles)
as.addAttribute (TextAttribute.FOREGROUND, Color.RED, startOfTextB, endOfTextB);
return as;
}
private static FontRenderContext fontRenderContext =
new FontRenderContext (new AffineTransform (), true, true);
/*
* @param advances on input, null or float[2]. On output: { advance, visibleAdvance }
* @param return new float array
*/
private static float[] showAndCalculateAdvance (String what,
AttributedString as,
float advances[])
{
TextLayout layout = new TextLayout (as.getIterator (), fontRenderContext);
System.out.println (what + "Advance = " + layout.getAdvance () +
", Visible advance = " + layout.getVisibleAdvance ());
float advance = layout.getAdvance();
float visAdvance = layout.getVisibleAdvance();
if(advances == null) {
advances = new float[2];
} else if( Float.compare(advances[0],advance)!=0 || Float.compare(advances[1],visAdvance)!=0) {
throw new RuntimeException("MISMATCH in advance.. " + what + "Advance = " + layout.getAdvance () +
", Visible advance = " + layout.getVisibleAdvance () + ", previous values were: ["+advances[0]+","+advances[1]+"]");
}
advances[0] = advance;
advances[1] = visAdvance;
return advances;
}
}