/
githubmirror
/
galahad
Обзор
Документация
Войти
/
githubmirror
/
galahad
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
test/jdk/java/awt/font/TextLayout/FormatCharAdvanceTest.java
387 строк
35 KB
Daniel Gredler
8356803: Test TextLayout/TestControls fails on windows & linux: line and paragraph separator show non-zero advance
04 июн 2025, 02:27
04 июн 2025, 02:27
9397535
Код
Авторство
О чём код?
/* * 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 8208377 6562489 8270265 8356803 8356812 4517298 * @summary Confirm that default-ignorable and ignorable-whitespace * glyphs are not rendered or measured. */ import java.awt.Color; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics2D; import java.awt.GraphicsEnvironment; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.font.FontRenderContext; import java.awt.font.GlyphVector; import java.awt.font.LineBreakMeasurer; import java.awt.font.TextAttribute; import java.awt.font.TextLayout; import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; import java.text.AttributedString; import java.util.Base64; import java.util.Map; public class FormatCharAdvanceTest { /** * <p>Font created for this test which contains glyphs for 0-9, a-z, A-Z, * some whitespace characters, and most characters with Unicode "Format" * (Cf) general category; the tests will pass if the font glyphs and * advances for default-ignorable and ignorable-whitespace characters * are ignored during text measurement and text drawing. * * <p>The following FontForge Python script was used to generate this font: * * <pre> * import fontforge * import base64 * * font = fontforge.font() * font.encoding = 'UnicodeFull' * font.design_size = 16 * font.em = 2048 * font.ascent = 1638 * font.descent = 410 * font.familyname = 'Test' * font.fontname = 'Test' * font.fullname = 'Test' * font.copyright = '' * font.autoWidth(0, 0, 2048) * * space = font.createChar(0x20) * space.width = 569 * * a = font.createChar(ord('a')) * pen = a.glyphPen() * pen.moveTo((100, 100)) * pen.lineTo((100, 200)) * pen.lineTo((500, 200)) * pen.lineTo((500, 100)) * pen.closePath() * a.draw(pen) * pen = None * a.width = 600 * * chars = 'bcdefghijklmnopqrstuvwxyz' \ * 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' \ * '1234567890' \ * '\u0009\u000A\u000B\u000C\u000D\u0085\u00AD' \ * '\u0600\u0601\u0602\u0603\u0604\u0605\u061C\u06DD\u070F\u0890\u0891\u08E2\u180E' \ * '\u200B\u200C\u200D\u200E\u200F\u2028\u2029\u202A\u202B\u202C\u202D\u202E\u202F' \ * '\u2060\u2061\u2062\u2063\u2064\u2065\u2066\u2067\u2068\u2069\u206A\u206B\u206C' \ * '\u206D\u206E\u206F\uFEFF\uFFF9\uFFFA\uFFFB' \ * '\U000110BD\U000110CD\U00013430\U00013431\U00013432\U00013433\U00013434\U00013435\U00013436' \ * '\U00013437\U00013438\U00013439\U0001343A\U0001343B\U0001343C\U0001343D\U0001343E\U0001343F' \ * '\U0001BCA0\U0001BCA1\U0001BCA2\U0001BCA3\U0001D173\U0001D174\U0001D175\U0001D176\U0001D177' \ * '\U0001D178\U0001D179\U0001D17A\U000E0001\U000E0020\U000E0021\U000E007E\U000E007F' * * for char in set(chars): * glyph = font.createChar(ord(char)) * glyph.addReference('a') * glyph.useRefsMetrics('a') * * ttf = 'test.ttf' # TrueType * t64 = 'test.ttf.txt' # TrueType Base64 * pfb = 'test.pfb' # PostScript Type1 * p64 = 'test.pfb.txt' # PostScript Type1 Base64 * * font.generate(ttf) * font.generate(pfb) * * with open(ttf, 'rb') as f1: * encoded = base64.b64encode(f1.read()) * with open(t64, 'wb') as f2: * f2.write(encoded) * * with open(pfb, 'rb') as f3: * encoded = base64.b64encode(f3.read()) * with open(p64, 'wb') as f4: * f4.write(encoded) * </pre> */ private static final String TTF_BYTES = "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cAGQZGRkZGT//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAD//wBkAGQB9ADIEgYALAAA//8AZABkAfQAyBIGACwAAP//AGQAZAH0AMgSBgAsAAAAAAAOAK4AAQAAAAAAAAAAAAIAAQAAAAAAAQAEAA0AAQAAAAAAAgAHACIAAQAAAAAAAwAgAGwAAQAAAAAABAAEAJcAAQAAAAAABQAPALwAAQAAAAAABgAEANYAAwABBAkAAAAAAAAAAwABBAkAAQAIAAMAAwABBAkAAgAOABIAAwABBAkAAwBAACoAAwABBAkABAAIAI0AAwABBAkABQAeAJwAAwABBAkABgAIAMwAAAAAVABlAHMAdAAAVGVzdAAAUgBlAGcAdQBsAGEAcgAAUmVndWxhcgAARgBvAG4AdABGAG8AcgBnAGUAIAAyAC4AMAAgADoAIABUAGUAcwB0ACAAOgAgADMAMAAtADUALQAyADAAMgA1AABGb250Rm9yZ2UgMi4wIDogVGVzdCA6IDMwLTUtMjAyNQAAVABlAHMAdAAAVGVzdAAAVgBlAHIAcwBpAG8AbgAgADAAMAAxAC4AMAAwADAAAFZlcnNpb24gMDAxLjAwMAAAVABlAHMAdAAAVGVzdAAAAAAAAgAAAAAAAP9nAGYAAAAAAAAAAAAAAAAAAAAAAAAAAACZAAAAAQECAQMBBAEFAQYAAwATABQAFQAWABcAGAAZABoAGwAcACQAJQAmACcAKAApACoAKwAsAC0ALgAvADAAMQAyADMANAA1ADYANwA4ADkAOgA7ADwAPQBEAEUARgBHAEgASQBKAEsATABNAE4ATwBQAFEAUgBTAFQAVQBWAFcAWABZAFoAWwBcAF0BBwEIAQkBCgELAQwBDQEOAQ8BEAERARIBEwEUARUBFgEXARgBGQEaARsBHAEdAR4BHwEgASEBIgEjASQBJQEmAScBKAEpASoBKwEsAS0BLgEvATABMQEyATMBNAE1ATYBNwE4ATkBOgE7ATwBPQE+AT8BQAFBAUIBQwFEAUUBRgFHAUgBSQFKAUsBTAFNAU4BTwFQAVEBUgFTAVQBVQFWAVcBWAFZB3VuaTAwMEQHdW5pMDAwOQd1bmkwMDBBB3VuaTAwMEIHdW5pMDAwQwd1bmkwMDg1B3VuaTAwQUQHdW5pMDYwMAd1bmkwNjAxB3VuaTA2MDIHdW5pMDYwMwd1bmkwNjA0B3VuaTA2MDUHdW5pMDYxQwd1bmkwNkREB3VuaTA3MEYHdW5pMDg5MAd1bmkwODkxB3VuaTA4RTIHdW5pMTgwRQd1bmkyMDBCB3VuaTIwMEMHdW5pMjAwRAd1bmkyMDBFB3VuaTIwMEYHdW5pMjAyOAd1bmkyMDI5B3VuaTIwMkEHdW5pMjAyQgd1bmkyMDJDB3VuaTIwMkQHdW5pMjAyRQd1bmkyMDJGB3VuaTIwNjAHdW5pMjA2MQd1bmkyMDYyB3VuaTIwNjMHdW5pMjA2NAd1bmkyMDY1B3VuaTIwNjYHdW5pMjA2Nwd1bmkyMDY4B3VuaTIwNjkHdW5pMjA2QQd1bmkyMDZCB3VuaTIwNkMHdW5pMjA2RAd1bmkyMDZFB3VuaTIwNkYHdW5pRkVGRgd1bmlGRkY5B3VuaUZGRkEHdW5pRkZGQgZ1MTEwQkQGdTExMENEBnUxMzQzMAZ1MTM0MzEGdTEzNDMyBnUxMzQzMwZ1MTM0MzQGdTEzNDM1BnUxMzQzNgZ1MTM0MzcGdTEzNDM4BnUxMzQzOQZ1MTM0M0EGdTEzNDNCBnUxMzQzQwZ1MTM0M0QGdTEzNDNFBnUxMzQzRgZ1MUJDQTAGdTFCQ0ExBnUxQkNBMgZ1MUJDQTMGdTFEMTczBnUxRDE3NAZ1MUQxNzUGdTFEMTc2BnUxRDE3NwZ1MUQxNzgGdTFEMTc5BnUxRDE3QQZ1RTAwMDEGdUUwMDIwBnVFMDAyMQZ1RTAwN0UGdUUwMDdGAAAAAAAAAf//AAIAAAABAAAAAOIB6+cAAAAA5F+vtAAAAADkX6+0"; /** * Same font as above, but in PostScript Type1 (PFB) format. */ private static final String TYPE1_BYTES = "gAFSBwAAJSFQUy1BZG9iZUZvbnQtMS4wOiBUZXN0IDAwMS4wMDAKJSVUaXRsZTogVGVzdAolVmVyc2lvbjogMDAxLjAwMAolJUNyZWF0aW9uRGF0ZTogRnJpIE1heSAzMCAyMDo1NTo0OCAyMDI1CiUlQ … [Строка слишком длинная. Вы можете скачать файл] public static void main(String[] args) throws Exception { BufferedImage image = new BufferedImage(600, 600, BufferedImage.TYPE_BYTE_BINARY); Graphics2D g2d = image.createGraphics(); byte[] type1Bytes = Base64.getDecoder().decode(TYPE1_BYTES); ByteArrayInputStream type1Stream = new ByteArrayInputStream(type1Bytes); Font type1Font = Font.createFont(Font.TYPE1_FONT, type1Stream).deriveFont(80f); testChars(image, g2d, type1Font); byte[] ttfBytes = Base64.getDecoder().decode(TTF_BYTES); ByteArrayInputStream ttfStream = new ByteArrayInputStream(ttfBytes); Font ttf = Font.createFont(Font.TRUETYPE_FONT, ttfStream).deriveFont(80f); testChars(image, g2d, ttf); g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); testChars(image, g2d, ttf); g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON); testChars(image, g2d, ttf); Font kerningFont = ttf.deriveFont(Map.of(TextAttribute.KERNING, TextAttribute.KERNING_ON)); testChars(image, g2d, kerningFont); Font dialogFont = new Font(Font.DIALOG, Font.PLAIN, 80); testChars(image, g2d, dialogFont); Font physicalFont = getPhysicalFont(80); if (physicalFont != null) { testChars(image, g2d, physicalFont); } g2d.dispose(); } private static void testChars(BufferedImage image, Graphics2D g2d, Font font) { testChar('\t', image, g2d, font); // horizontal tab (TAB) testChar('\n', image, g2d, font); // line feed (LF) testChar('\u000B', image, g2d, font); // vertical tab (VT) testChar('\u000C', image, g2d, font); // form feed (FF) testChar('\r', image, g2d, font); // carriage return (CR) testChar('\u0085', image, g2d, font); // next line (NEL) testChar('\u00AD', image, g2d, font); // soft hyphen (SHY) testChar('\u200B', image, g2d, font); // zero width space (ZWSP) testChar('\u200C', image, g2d, font); // zero width non-joiner (ZWNJ) testChar('\u200D', image, g2d, font); // zero width joiner (ZWJ) testChar('\u200E', image, g2d, font); // left-to-right mark (LRM) testChar('\u200F', image, g2d, font); // right-to-left mark (RLM) testChar('\u2028', image, g2d, font); // line separator (LS) testChar('\u2029', image, g2d, font); // paragraph separator (PS) testChar('\u202A', image, g2d, font); // left-to-right embedding (LRE) testChar('\u202B', image, g2d, font); // right-to-left embedding (RLE) testChar('\u202C', image, g2d, font); // pop directional formatting (PDF) testChar('\u202D', image, g2d, font); // left-to-right override (LRO) testChar('\u202E', image, g2d, font); // right-to-left override (RLO) testChar('\u2060', image, g2d, font); // word joiner (WJ) testChar('\u2061', image, g2d, font); // function application testChar('\u2062', image, g2d, font); // invisible times testChar('\u2063', image, g2d, font); // invisible separator testChar('\u2066', image, g2d, font); // left-to-right isolate (LRI) testChar('\u2067', image, g2d, font); // right-to-left isolate (RLI) testChar('\u2068', image, g2d, font); // first strong isolate (FSI) testChar('\u2069', image, g2d, font); // pop directional isolate (PDI) testChar('\u206A', image, g2d, font); // inhibit symmetric swapping testChar('\u206B', image, g2d, font); // activate symmetric swapping testChar('\u206C', image, g2d, font); // inhibit arabic form shaping testChar('\u206D', image, g2d, font); // activate arabic form shaping testChar('\u206E', image, g2d, font); // national digit shapes testChar('\u206F', image, g2d, font); // nominal digit shapes testChar('\uFEFF', image, g2d, font); // zero width no-break space (ZWNBSP/BOM) } private static void testChar(char c, BufferedImage image, Graphics2D g2d, Font font) { g2d.setFont(font); int w = image.getWidth(); int h = image.getHeight(); FontRenderContext frc = g2d.getFontRenderContext(); FontMetrics metrics = g2d.getFontMetrics(font); assertEqual(0, metrics.charWidth(c), "charWidth", c, font); String c5 = String.valueOf(c).repeat(5); int ab1 = metrics.stringWidth("AB"); int ab2 = metrics.stringWidth("A" + c5 + "B"); assertEqual(ab1, ab2, "stringWidth", c, font); ab1 = (int) font.getStringBounds("AB", frc).getWidth(); ab2 = (int) font.getStringBounds("A" + c5 + "B", frc).getWidth(); assertEqual(ab1, ab2, "getStringBounds", c, font); GlyphVector gv1 = font.createGlyphVector(frc, "AB"); GlyphVector gv2 = font.createGlyphVector(frc, "A" + c5 + "B"); ab1 = gv1.getPixelBounds(frc, 0, 0).width; ab2 = gv2.getPixelBounds(frc, 0, 0).width; assertEqual(ab1, ab2, "getPixelBounds", c, font); assertEqual(0, gv2.getGlyphPixelBounds(1, frc, 0, 0).width, "getGlyphPixelBounds", c, font); assertEqual(0d, gv2.getGlyphVisualBounds(1).getBounds2D().getWidth(), "getGlyphVisualBounds", c, font); assertEqual(0d, gv2.getGlyphLogicalBounds(1).getBounds2D().getWidth(), "getGlyphLogicalBounds", c, font); assertEqual(0d, gv2.getGlyphOutline(1).getBounds2D().getWidth(), "getGlyphOutline", c, font); assertEqual(0d, gv2.getGlyphMetrics(1).getAdvance(), "getGlyphMetrics", c, font); ab1 = (int) gv1.getLogicalBounds().getWidth(); ab2 = (int) gv2.getLogicalBounds().getWidth(); assertEqual(ab1, ab2, "getLogicalBounds", c, font); ab1 = (int) gv1.getVisualBounds().getWidth(); ab2 = (int) gv2.getVisualBounds().getWidth(); assertEqual(ab1, ab2, "getVisualBounds", c, font); TextLayout layout1 = new TextLayout("AB", font, frc); TextLayout layout2 = new TextLayout("A" + c5 + "B", font, frc); ab1 = (int) layout1.getAdvance(); ab2 = (int) layout2.getAdvance(); assertEqual(ab1, ab2, "getAdvance", c, font); g2d.setColor(Color.WHITE); g2d.fillRect(0, 0, w, h); g2d.setColor(Color.BLACK); layout1.draw(g2d, w / 2, h / 2); ab1 = findTextBoundingBox(image).width; g2d.setColor(Color.WHITE); g2d.fillRect(0, 0, w, h); g2d.setColor(Color.BLACK); layout2.draw(g2d, w / 2, h / 2); ab2 = findTextBoundingBox(image).width; assertEqual(ab1, ab2, "TextLayout.draw", c, font); g2d.setColor(Color.WHITE); g2d.fillRect(0, 0, w, h); g2d.setColor(Color.BLACK); g2d.drawString("AB", w / 2, h / 2); ab1 = findTextBoundingBox(image).width; g2d.setColor(Color.WHITE); g2d.fillRect(0, 0, w, h); g2d.setColor(Color.BLACK); g2d.drawString("A" + c5 + "B", w / 2, h / 2); ab2 = findTextBoundingBox(image).width; assertEqual(ab1, ab2, "drawString (using String)", c, font); g2d.setColor(Color.WHITE); g2d.fillRect(0, 0, w, h); g2d.setColor(Color.BLACK); g2d.drawChars("AB".toCharArray(), 0, 2, w / 2, h / 2); ab1 = findTextBoundingBox(image).width; g2d.setColor(Color.WHITE); g2d.fillRect(0, 0, w, h); g2d.setColor(Color.BLACK); g2d.drawChars(("A" + c5 + "B").toCharArray(), 0, 7, w / 2, h / 2); ab2 = findTextBoundingBox(image).width; assertEqual(ab1, ab2, "drawChars", c, font); g2d.setColor(Color.WHITE); g2d.fillRect(0, 0, w, h); g2d.setColor(Color.BLACK); g2d.drawGlyphVector(gv1, w / 2, h / 2); ab1 = findTextBoundingBox(image).width; g2d.setColor(Color.WHITE); g2d.fillRect(0, 0, w, h); g2d.setColor(Color.BLACK); g2d.drawGlyphVector(gv2, w / 2, h / 2); ab2 = findTextBoundingBox(image).width; assertEqual(ab1, ab2, "drawGlyphVector", c, font); AttributedString as1 = new AttributedString("AB", Map.of(TextAttribute.FONT, font)); AttributedString as2 = new AttributedString("A" + c5 + "B", Map.of(TextAttribute.FONT, font)); g2d.setColor(Color.WHITE); g2d.fillRect(0, 0, w, h); g2d.setColor(Color.BLACK); g2d.drawString(as1.getIterator(), w / 2, h / 2); ab1 = findTextBoundingBox(image).width; g2d.setColor(Color.WHITE); g2d.fillRect(0, 0, w, h); g2d.setColor(Color.BLACK); g2d.drawString(as2.getIterator(), w / 2, h / 2); ab2 = findTextBoundingBox(image).width; assertEqual(ab1, ab2, "drawString (using AttributedCharacterIterator)", c, font); int max = metrics.stringWidth("AB") + 2; // add a little wiggle room to the max width LineBreakMeasurer measurer1 = new LineBreakMeasurer(as1.getIterator(), frc); LineBreakMeasurer measurer2 = new LineBreakMeasurer(as2.getIterator(), frc); assertEqual(2, measurer1.nextOffset(max), "nextOffset 1", c, font); assertEqual(7, measurer2.nextOffset(max), "nextOffset 2", c, font); } private static void assertEqual(int i1, int i2, String scenario, char c, Font font) { if (i1 != i2) { String msg = String.format("%s for char %04x using font %s: %d != %d", scenario, (int) c, font.getName(), i1, i2); throw new RuntimeException(msg); } } private static void assertEqual(double d1, double d2, String scenario, char c, Font font) { if (d1 != d2) { String msg = String.format("%s for char %04x using font %s: %f != %f", scenario, (int) c, font.getName(), d1, d2); throw new RuntimeException(msg); } } private static Font getPhysicalFont(int size) { GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); String[] names = ge.getAvailableFontFamilyNames(); for (String n : names) { switch (n) { case Font.DIALOG: case Font.DIALOG_INPUT: case Font.SERIF: case Font.SANS_SERIF: case Font.MONOSPACED: continue; default: Font f = new Font(n, Font.PLAIN, size); if (f.canDisplayUpTo("AZaz09") == -1) { return f; } } } return null; } private static Rectangle findTextBoundingBox(BufferedImage image) { int minX = Integer.MAX_VALUE; int minY = Integer.MAX_VALUE; int maxX = Integer.MIN_VALUE; int maxY = Integer.MIN_VALUE; int width = image.getWidth(); int height = image.getHeight(); int[] rowPixels = new int[width]; for (int y = 0; y < height; y++) { image.getRGB(0, y, width, 1, rowPixels, 0, width); for (int x = 0; x < width; x++) { boolean white = (rowPixels[x] == -1); if (!white) { if (x < minX) { minX = x; } if (y < minY) { minY = y; } if (x > maxX) { maxX = x; } if (y > maxY) { maxY = y; } } } } if (minX != Integer.MAX_VALUE) { return new Rectangle(minX, minY, maxX - minX, maxY - minY); } else { return null; } } }