diff --git a/src/java.desktop/macosx/classes/sun/font/CStrike.java b/src/java.desktop/macosx/classes/sun/font/CStrike.java index eb049c3d449..b0131711ca5 100644 --- a/src/java.desktop/macosx/classes/sun/font/CStrike.java +++ b/src/java.desktop/macosx/classes/sun/font/CStrike.java @@ -199,7 +199,7 @@ void getGlyphImageBounds(int glyphCode, Point2D.Float pt, Rectangle result) { getGlyphImageBounds(glyphCode, pt.x, pt.y, floatRect); if (floatRect.width == 0 && floatRect.height == 0) { - result.setRect(0, 0, -1, -1); + result.setRect(0, 0, 0, 0); return; } diff --git a/src/java.desktop/macosx/native/libawt_lwawt/font/CGGlyphImages.m b/src/java.desktop/macosx/native/libawt_lwawt/font/CGGlyphImages.m index f6e8fe6af55..ecdd6e4cdf8 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/font/CGGlyphImages.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/font/CGGlyphImages.m @@ -1020,4 +1020,20 @@ @implementation CGGI_GlyphCanvas CTFontGetAdvancesForGlyphs(font, kCTFontDefaultOrientation, glyphs, advances, count); } } -} \ No newline at end of file + int MAX_SIZE = 1 << 30; + if (bboxes) { + for (int i = 0; i < count; i++) { + if (bboxes[i].origin.x > (double)MAX_SIZE) bboxes[i].origin.x = 0; + if (bboxes[i].origin.y > (double)MAX_SIZE) bboxes[i].origin.y = 0; + if (bboxes[i].size.width > (double)MAX_SIZE) bboxes[i].size.width = 0; + if (bboxes[i].size.height > (double)MAX_SIZE) bboxes[i].size.height = 0; + } + } + if (advances) { + for (int i = 0; i < count; i++) { + if (advances[i].width > (double)MAX_SIZE) advances[i].width = 0; + if (advances[i].height > (double)MAX_SIZE) advances[i].height = 0; + } + } +} + diff --git a/src/java.desktop/share/classes/sun/font/FileFontStrike.java b/src/java.desktop/share/classes/sun/font/FileFontStrike.java index ea2a1608f2d..bf98b8ca578 100644 --- a/src/java.desktop/share/classes/sun/font/FileFontStrike.java +++ b/src/java.desktop/share/classes/sun/font/FileFontStrike.java @@ -37,6 +37,7 @@ import java.awt.geom.Rectangle2D; import java.util.concurrent.ConcurrentHashMap; import static sun.awt.SunHints.*; +import sun.java2d.pipe.OutlineTextRenderer; public class FileFontStrike extends PhysicalStrike { @@ -107,6 +108,7 @@ public class FileFontStrike extends PhysicalStrike { boolean useNatives; NativeStrike[] nativeStrikes; + static final int MAX_IMAGE_SIZE = OutlineTextRenderer.THRESHHOLD; /* Used only for communication to native layer */ private int intPtSize; @@ -697,6 +699,20 @@ float getCodePointAdvance(int cp) { void getGlyphImageBounds(int glyphCode, Point2D.Float pt, Rectangle result) { + if (intPtSize > MAX_IMAGE_SIZE) { + Rectangle.Float obds = getGlyphOutlineBounds(glyphCode); + if (obds.isEmpty()) { + Rectangle bds = getGlyphOutline(glyphCode, pt.x, pt.y).getBounds(); + result.setBounds(bds); + } else { + result.x = (int)Math.floor(pt.x + obds.getX() + 0.5f); + result.y = (int)Math.floor(pt.y + obds.getY() + 0.5f); + result.width = (int)Math.floor(obds.getWidth() + 0.5f); + result.height = (int)Math.floor(obds.getHeight() + 0.5f); + } + return; + } + long ptr = getGlyphImagePtr(glyphCode); float topLeftX, topLeftY; diff --git a/src/java.desktop/share/native/libfontmanager/freetypeScaler.c b/src/java.desktop/share/native/libfontmanager/freetypeScaler.c index 21ac280f0fb..f9ebacad66b 100644 --- a/src/java.desktop/share/native/libfontmanager/freetypeScaler.c +++ b/src/java.desktop/share/native/libfontmanager/freetypeScaler.c @@ -504,6 +504,8 @@ static double euclidianDistance(double a, double b) { return sqrt(a*a+b*b); } +#define TOO_LARGE(a, b) (abs((int)(a / b)) > 32766) + JNIEXPORT jlong JNICALL Java_sun_font_FreetypeFontScaler_createScalerContextNative( JNIEnv *env, jobject scaler, jlong pScaler, jdoubleArray matrix, @@ -515,6 +517,7 @@ Java_sun_font_FreetypeFontScaler_createScalerContextNative( (FTScalerInfo*) jlong_to_ptr(pScaler); if (context == NULL) { + free(context); invalidateJavaScaler(env, scaler, NULL); return (jlong) 0; } @@ -524,7 +527,18 @@ Java_sun_font_FreetypeFontScaler_createScalerContextNative( //text can not be smaller than 1 point ptsz = 1.0; } + if (ptsz > 16384) { + ptsz = 16384; // far enough from 32767 + fm = TEXT_FM_ON; // avoids calculations which might overflow + } context->ptsz = (int)(ptsz * 64); + if (TOO_LARGE(dmat[0], ptsz) || TOO_LARGE(dmat[1], ptsz) || + TOO_LARGE(dmat[2], ptsz) || TOO_LARGE(dmat[3], ptsz)) + { + free(context); + return (jlong)0; + } + context->transform.xx = FloatToFTFixed((float)(dmat[0]/ptsz)); context->transform.yx = -FloatToFTFixed((float)(dmat[1]/ptsz)); context->transform.xy = -FloatToFTFixed((float)(dmat[2]/ptsz)); diff --git a/test/jdk/java/awt/FontMetrics/ExtremeFontSizeTest.java b/test/jdk/java/awt/FontMetrics/ExtremeFontSizeTest.java new file mode 100644 index 00000000000..caa365a3f21 --- /dev/null +++ b/test/jdk/java/awt/FontMetrics/ExtremeFontSizeTest.java @@ -0,0 +1,118 @@ +/* + * Copyright (c) 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.Font; +import java.awt.FontMetrics; +import java.awt.Graphics2D; +import java.awt.Rectangle; +import java.awt.font.FontRenderContext; +import java.awt.font.GlyphVector; +import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; + +/* + * @test + * @bug 8328896 + * @summary test that using very large font sizes used don't break later uses + */ + +public class ExtremeFontSizeTest { + + static BufferedImage bi = new BufferedImage(1,1,1); + static Graphics2D g2d = bi.createGraphics(); + static String testString = "M"; + static Font font = new Font("SansSerif", Font.PLAIN, 12); + static int fontSize = 0; + static boolean failed = false; + static int[] fontSizes = { 10, 12, 1000, 2000, 20000, 100000, 8 }; + static double[] scales = { 1.0, 900.0}; + static boolean[] fms = { false, true }; + + public static void main(String[] args) { + + /* run tests validating bounds etc are non-zero + * then run with extreme scales for which zero is allowed - but not required + * then run the first tests again to be sure they are still reasonable. + */ + runTests(); + test(5_000_000, 10_000, false, testString, false); + test(5_000_000, 10_000, true, testString, false); + test(0, 0.00000001, false, testString, false); + runTests(); + + if (failed) { + throw new RuntimeException("Test failed. Check stdout log."); + } + } + + static void runTests() { + for (int fontSize : fontSizes) { + for (double scale : scales) { + for (boolean fm : fms) { + test(fontSize, scale, fm, testString, true); + } + } + } + } + + static void test(int size, double scale, boolean fm, String str, boolean checkAll) { + + AffineTransform at = AffineTransform.getScaleInstance(scale, scale); + FontRenderContext frc = new FontRenderContext(at, false, fm); + font = font.deriveFont((float)size); + g2d.setTransform(at); + g2d.setFont(font); + FontMetrics metrics = g2d.getFontMetrics(); + int height = metrics.getHeight(); + double width = font.getStringBounds(str, frc).getWidth(); + + GlyphVector gv = font.createGlyphVector(frc, str.toCharArray()); + Rectangle pixelBounds = gv.getPixelBounds(frc, 0, 0); + Rectangle2D visualBounds = gv.getVisualBounds(); + + System.out.println("Test parameters: size="+size+" scale="+scale+" fm="+fm+" str="+str); + System.out.println("font height="+metrics.getHeight()); + System.out.println("string bounds width="+width); + System.out.println("GlyphVector Pixel Bounds="+ pixelBounds); + System.out.println("GlyphVector Visual Bounds="+ visualBounds); + + + if (height < 0 || width < 0 || pixelBounds.getWidth() < 0 || visualBounds.getWidth() < 0) { + failed = true; + System.out.println(" *** Unexpected negative size reported *** "); + } + if (!checkAll) { + System.out.println(); + return; + } + + if (height == 0 || width == 0 || (pixelBounds.isEmpty()) || visualBounds.isEmpty() ) { + failed = true; + System.out.println("Pixel bounds empty="+pixelBounds.isEmpty()); + System.out.println("Visual bounds empty="+visualBounds.isEmpty()); + System.out.println(" *** RESULTS NOT AS EXPECTED *** "); + } + System.out.println(); + } +}