Java код для добавления многострочного текста на изображение

13 февраля 2011 г.

Добрый день. Мало вероятно что раскрываемая ниже проблема является распространенной, но тот факт что с ней столкнулся я наводит на мысль что это случалось и случиться с другими. Речь идет о добавлении многострочного текста на изображение.
Стандартные функции в Java позволяют это сделать только для одной стоки, но что делать если текст должен быть многострочным и ограничен в ширине — колонка (например добавить небольшой текст на фотографию). В первую очередь я сделал то что делают большинство пользователей сети — попросил помощи у гугла. Не скажу что я облазил всю сеть в поисках ответа, но после полу часа без результатных поисков я решил что будет проще написать самому. Решено, сделано.

Необходимый функционал

Прикинув что мне нужно от этого функционала, и может понадобиться в будущем, я включил в список требований следующие пункты:

  • Указание Font-а для текста
  • Указание цвета текста
  • Указание размера текста (size)
  • Указание типа для текста (Bold, Italic)
  • Указание верхней точки начала отрисовки текста (TopLeftX, TopLeftY)
  • Указание ширины колонки
  • Указание типа выравнивания (по левому краю, по правому, по центру и по ширине)
  • Указание прозрачности
  • И конечно сам текст

Полученный код

В результате получились 3 функции которые достаточно не плохо справляются с поставленной задачей. Вкратце процесс состоит из следующих этапов:

  • Разбить имеющийся текст на набор из слов, длинна которого не превышает заданный максимум (Каждая стока отрисовывается ниже предыдующей на N пикселей, где N — это высота строки с заданными параметрами)
  • В зависимости от выбранного типа выравнивания находится отступ для строки в целом (если по левому краю, по правому или по центру), либо для каждого слова в отдельности (если выравнивание по ширине)
  • В конечном итоге применяется стандартная функция для добавления текста (в нужном месте и в нужном виде), что в целом приводит к получению желаемого результата

Пример вызова кода и скрины того что получается в результате

Image img;

String text = "Какой-то длинный текст, который должен продемонстрировать как работает данный функционал на деле. Каждый раз все должно выглядеть по разному.";

img = new Image("d:\\temp\\test.png");
img.addTextToImage(text, 290, 20, 250, 1, LEFT_TEXT_MODE, "Arial", Font.BOLD, 16, Color.BLACK);
img.saveAs("d:\\temp\\test2.png");

img = new Image("d:\\temp\\test.png");
img.addTextToImage(text, 290, 20, 250, 1, CENTER_TEXT_MODE, "Arial", Font.ITALIC, 16, Color.BLUE);
img.saveAs("d:\\temp\\test3.png");

img = new Image("d:\\temp\\test.png");
img.addTextToImage(text, 290, 20, 250, 1, RIGHT_TEXT_MODE, "Arial", Font.PLAIN, 16, Color.GREEN);
img.saveAs("d:\\temp\\test4.png");

img = new Image("d:\\temp\\test.png");
img.addTextToImage(text, 290, 20, 250, 1, WIDTH_TEXT_MODE, "Arial", Font.ITALIC, 16, Color.BLUE);
img.saveAs("d:\\temp\\test5.png");

Пример 1

Пример 2

Пример 3

Пример 4

Пример 5

Полный код класса

package test;

import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javax.imageio.ImageIO;

/**
* @author Grigori Paslari
*/
public class Image {
public static final String LEFT_TEXT_MODE = "left-text-mode";
public static final String RIGHT_TEXT_MODE = "right-text-mode";
public static final String CENTER_TEXT_MODE = "center-text-mode";
public static final String WIDTH_TEXT_MODE = "width-text-mode";

private BufferedImage bufferedImage;
private String fileName;

public Image(File imageFile) {
try {
bufferedImage = ImageIO.read(imageFile);
fileName = imageFile.getAbsolutePath();
} catch (Exception e) {
e.printStackTrace();
bufferedImage = null;
imageFile = null;
}
}

public Image(String imageFilePath) {
this(new File(imageFilePath));
}

public BufferedImage getAsBufferedImage(){
return bufferedImage;
}

public void saveAs(String fileName){
saveImage(new File(fileName));
this.fileName = fileName;
}

public void save(){
saveImage(new File(fileName));
}

private void saveImage(File file) {
try {
ImageIO.write(bufferedImage, getFileType(file), file);
} catch (IOException e) {
e.printStackTrace();
}
}

private String getFileType(File file) {
String fileName = file.getName();
int idx = fileName.lastIndexOf(".");
if(idx == -1){
throw new RuntimeException("Invalid file name");
}

return fileName.substring(idx+1);
}

public void addTextToImage(String text,
int topX, int topY,
int zoneW, float alpha,
String mode, String font,
int type, int size, Color color){
Graphics2D g = bufferedImage.createGraphics();
g.setColor(Color.BLACK);
g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
g.setFont(new Font(font, type, size));

final FontMetrics fontMetrics = g.getFontMetrics();
g.dispose();

int lineHeight = fontMetrics.getHeight();

String[] words = text.split(" ");
String line = "";
List lines = new ArrayList();

for (int i = 0; i < words.length; i++) {
if (fontMetrics.stringWidth(line + words[i]) > zoneW) {
lines.add(line);
line = "";
}

line += words[i] + " ";
}

lines.add(line);

for (int i = 0; i < lines.size(); i++) {
addTextLineToImage(lines.get(i),
topX, lineHeight + topY + i * lineHeight,
zoneW, i == (lines.size() - 1),
alpha, mode, font, type, size, color);
}
}

private void addTextLineToImage(String text,
int topX, int topY,
int zoneW, boolean isLastLine,
float alpha, String mode,
String font, int type,
int size, Color color){
String[] words = text.trim().split(" ");

if (words.length == 0) {
return;
} else if (words.length == 1) {
addTextToImage(text, topX, topY, alpha, font, type, size, color);
} else {
Graphics2D g = bufferedImage.createGraphics();
g.setColor(Color.BLACK);
g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
g.setFont(new Font(font, type, size));

final FontMetrics fontMetrics = g.getFontMetrics();
g.dispose();

if (mode == LEFT_TEXT_MODE) {
addTextToImage(text, topX, topY, alpha, font, type, size, color);
} else if (mode == CENTER_TEXT_MODE) {
topX += (int)(zoneW - fontMetrics.stringWidth(text)) / 2;
addTextToImage(text, topX, topY, alpha, font, type, size, color);
} else if (mode == RIGHT_TEXT_MODE) {
topX += zoneW - fontMetrics.stringWidth(text);
addTextToImage(text, topX, topY, alpha, font, type, size, color);
} else {
int totalWordsWidth = 0;
for (int i = 0; i < words.length; i++) {
totalWordsWidth += fontMetrics.stringWidth(words[i]);
}

int delta = Math.round((zoneW - totalWordsWidth) / (words.length - 1));
int offset = 0;

if (isLastLine) {
delta = Math.min(delta, 10);
}

for (int i = 0; i < words.length; i++) {
addTextToImage(words[i], topX + offset, topY, alpha, font, type, size, color);

offset += fontMetrics.stringWidth(words[i]) + delta;
}
}
}
}

private void addTextToImage(String text,
int topX, int topY,
float alpha,
String font, int type,
int size, Color color){
Graphics2D g = bufferedImage.createGraphics();
g.setColor(color);
g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha)); // 0.5 = 50% transparency
g.setFont(new Font(font, type, size));

g.drawString(text, topX, topY);
g.dispose();

}

}
Теги: рубрика Интернет