Я пытаюсь преобразовать текст в изображение с помощью JAVA AWT. Когда я использую шрифт меньше 100 пунктов, все работает как положено. Но когда я использую, например, 240 точек, то некоторые части текста, которые перекрываются, становятся прозрачными, как на этом изображении . В примере изображения я использовал этот шрифт. Я без проблем использовал этот шрифт в текстовом редакторе.
Шрифт загружается с помощью этого кода:
Font font = Font.createFont(Font.TRUETYPE_FONT, new File("src/main/resources/Autumn in November.ttf"));
Font derived = font.deriveFont(240f);
Чтобы отобразить текст на изображении, я использую этот код:
public static void saveTextToImage(String text, Font font) throws IOException {
# Temp image to get the text dimensions
BufferedImage tempImage = new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB);
Graphics2D tempGraphics = tempImage.createGraphics();
tempGraphics.setFont(font);
FontMetrics fontMetrics = tempGraphics.getFontMetrics();
FontRenderContext fontRenderContext = tempGraphics.getFontRenderContext();
GlyphVector gv = font.createGlyphVector(fontRenderContext, text);
Area area = new Area(gv.getOutline());
Rectangle2D rectangle2D = area.getBounds2D();
int width = (int) rectangle2D.getWidth();
int height = (int) rectangle2D.getHeight();
// Create a new image with white background
BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics2D graphics = img.createGraphics();
graphics.setColor(Color.WHITE);
graphics.fillRect(0, 0, width, height);
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// Draw the text on the image
graphics.setColor(Color.BLACK);
graphics.setFont(font);
graphics.drawString(text, 10, fontMetrics.getAscent() + 5);
// Save the image
File outputFile = new File(text + ".jpg");
ImageIO.write(img, "jpg", outputFile);
}
Я пытался изменить тип шрифта на OTF, добавлять и изменять RenderingHints, а также переключать тип изображения на PNG, но безуспешно. Я не знаю, что еще я мог бы попробовать.
Один из вариантов — создать изображение с огромным текстовым шрифтом и использовать его в проекте Java Swing.
@Holger Я пробовал 8, 11 и 17 с тем же результатом. Я согласен с обходом, но с помощью GlyphVector я могу получить более точный размер текста. Подсказки рендеринга не решают проблему с перекрытием.
@GilbertLeBlanc Мне нужно большое количество этих изображений, создавать их вручную невозможно.
Похоже на ошибку в отрисовке глифа, использование неправильного правила намотки ("четно-нечетное" вместо "ненулевое"), см. Википедия: ненулевое правило, особенно рисунок справа.




Это действительно странное поведение. Я не могу сказать вам, почему это происходит, но я могу предложить вам обходной путь; проблема не возникает при рендеринге глифов строки один за другим:
static final boolean USE_WORKAROUND = true;
public static void saveTextToImage(String text, Font font) throws IOException {
// Temp image to get the GlyphVector and text dimensions
BufferedImage tempImage = new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB);
Graphics2D tempGraphics = tempImage.createGraphics();
tempGraphics.setFont(font);
tempGraphics.setRenderingHint(
RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
FontRenderContext fontRenderContext = tempGraphics.getFontRenderContext();
GlyphVector gv = font.createGlyphVector(fontRenderContext, text);
Rectangle2D rectangle2D = gv.getVisualBounds();
int width = (int)(rectangle2D.getWidth());
int height = (int)(rectangle2D.getHeight());
// Create a new image with white background
BufferedImage img
= new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics2D graphics = img.createGraphics();
graphics.setBackground(Color.WHITE);
graphics.clearRect(0, 0, width, height);
graphics.setRenderingHint(
RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
graphics.setColor(Color.BLACK);
// using GlyphVector avoids repeating the work already done above
if (!USE_WORKAROUND) {
graphics.drawGlyphVector(gv,
(float)-rectangle2D.getX(), (float)-rectangle2D.getY());
} else {
graphics.translate(-rectangle2D.getX(), -rectangle2D.getY());
for(int gix = 0, gnum = gv.getNumGlyphs(); gix < gnum; gix++) {
graphics.fill(gv.getGlyphOutline(gix));
}
}
// Save the image
File outputFile = new File(text + ".jpg");
ImageIO.write(img, "jpg", outputFile);
}
В качестве примечания, я бы использовал PNG вместо JPG, так как он лучше подходит для таких изображений, но, возможно, у вас нет такого выбора.
Далее обратите внимание, что рендеринг существующего GlyphVector через drawGlyphVector был задокументирован как «самый быстрый способ рендеринга набора символов на экране», поэтому, если нам все равно нужно выполнить эту подготовительную работу для вычисления границ, почему бы не использовать ее для рендеринг…
Какая версия Явы? И зачем эти ненужные обходы?
gv.getOutline()возвращаетShape, так что вы можете вызватьgetBounds2D()напрямую, не копируя его вArea, но вы даже можете вызватьgv.getVisualBounds()в первую очередь. Ну, на самом деле, вы можете запросить размеры изfontMetrics, не создаваяGlyphVector. Оставляя в стороне обходные пути, вы должны рассчитывать размеры, используя те же подсказки рендеринга, что и при рисовании. И когда вы впоследствии добавляете к координатам такие константы, как10и5, вам нужно соответствующим образом настроить размер изображения.