diff options
Diffstat (limited to 'tools/image_generator')
-rw-r--r-- | tools/image_generator/ImageGenerator.java | 107 |
1 files changed, 81 insertions, 26 deletions
diff --git a/tools/image_generator/ImageGenerator.java b/tools/image_generator/ImageGenerator.java index a011f10e4..8730945b5 100644 --- a/tools/image_generator/ImageGenerator.java +++ b/tools/image_generator/ImageGenerator.java @@ -60,8 +60,9 @@ public class ImageGenerator { // This is the canvas we used to draw texts. private BufferedImage mBufferedImage; - // The width in pixels of our image. Once set, its value won't change. - private final int mImageWidth; + // The width in pixels of our image. The value will be adjusted once when we calculate the + // maximum width to fit the wrapped text strings. + private int mImageWidth; // The current height in pixels of our image. We will adjust the value when drawing more texts. private int mImageHeight; @@ -79,6 +80,9 @@ public class ImageGenerator { // The directory that contains all the needed font files (e.g. ttf, otf, ttc files). private final String mFontDirPath; + // Align the text in the center of the image. + private final boolean mCenterAlignment; + // An explicit map from language to the font name to use. // The map is extracted from frameworks/base/data/fonts/fonts.xml. // And the language-subtag-registry is found in: @@ -130,7 +134,9 @@ public class ImageGenerator { }; // Languages that breaks on arbitrary characters. - // TODO(xunchang) switch to icu library if possible. + // TODO(xunchang) switch to icu library if possible. For example, for Thai and Khmer, there is + // no space between words; and word breaking is based on grammatical analysis and on word + // matching in dictionaries. private static final Set<String> LOGOGRAM_LANGUAGE = new HashSet<String>() { { @@ -138,6 +144,7 @@ public class ImageGenerator { add("km"); // Khmer add("ko"); // Korean add("lo"); // Lao + add("th"); // Thai add("zh"); // Chinese } }; @@ -154,8 +161,13 @@ public class ImageGenerator { } /** Initailizes the fields of the image image. */ - public ImageGenerator(int imageWidth, String textName, float fontSize, String fontDirPath) { - mImageWidth = imageWidth; + public ImageGenerator( + int initialImageWidth, + String textName, + float fontSize, + String fontDirPath, + boolean centerAlignment) { + mImageWidth = initialImageWidth; mImageHeight = INITIAL_HEIGHT; mVerticalOffset = 0; @@ -165,6 +177,8 @@ public class ImageGenerator { mTextName = textName; mFontSize = fontSize; mFontDirPath = fontDirPath; + + mCenterAlignment = centerAlignment; } /** @@ -299,8 +313,6 @@ public class ImageGenerator { List<String> wrappedText = new ArrayList<>(); StringTokenizer st = new StringTokenizer(text, " \n"); - // TODO(xunchang). We assume that all words can fit on the screen. Raise an - // IllegalStateException if the word is wider than the image width. StringBuilder line = new StringBuilder(); while (st.hasMoreTokens()) { String token = st.nextToken(); @@ -373,6 +385,43 @@ public class ImageGenerator { return info; } + /** Returns Graphics2D object that uses the given locale. */ + private Graphics2D createGraphics(Locale locale) throws IOException, FontFormatException { + Graphics2D graphics = mBufferedImage.createGraphics(); + graphics.setColor(Color.WHITE); + graphics.setRenderingHint( + RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_GASP); + graphics.setFont(loadFontsByLocale(locale.getLanguage())); + + return graphics; + } + + /** Returns the maximum screen width needed to fit the given text after wrapping. */ + private int measureTextWidth(String text, Locale locale) + throws IOException, FontFormatException { + Graphics2D graphics = createGraphics(locale); + FontMetrics fontMetrics = graphics.getFontMetrics(); + List<String> wrappedText = wrapText(text, fontMetrics, locale.getLanguage()); + + int textWidth = 0; + for (String line : wrappedText) { + textWidth = Math.max(textWidth, fontMetrics.stringWidth(line)); + } + + // This may happen if one single word is larger than the image width. + if (textWidth > mImageWidth) { + throw new IllegalStateException( + "Wrapped text width " + + textWidth + + " is larger than image width " + + mImageWidth + + " for locale: " + + locale); + } + + return textWidth; + } + /** * Draws the text string on the canvas for given locale. * @@ -381,16 +430,11 @@ public class ImageGenerator { * @throws IOException if we cannot find the corresponding font file for the given locale. * @throws FontFormatException if we failed to load the font file for the given locale. */ - private void drawText(String text, Locale locale, String languageTag, boolean centralAlignment) + private void drawText(String text, Locale locale, String languageTag) throws IOException, FontFormatException { - Graphics2D graphics = mBufferedImage.createGraphics(); - graphics.setColor(Color.WHITE); - graphics.setRenderingHint( - RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_GASP); - graphics.setFont(loadFontsByLocale(locale.getLanguage())); - System.out.println("Encoding \"" + locale + "\" as \"" + languageTag + "\": " + text); + Graphics2D graphics = createGraphics(locale); FontMetrics fontMetrics = graphics.getFontMetrics(); List<String> wrappedText = wrapText(text, fontMetrics, locale.getLanguage()); @@ -402,7 +446,7 @@ public class ImageGenerator { int lineHeight = fontMetrics.getHeight(); // Doubles the height of the image if we are short of space. if (mVerticalOffset + lineHeight >= mImageHeight) { - resizeHeight(mImageHeight * 2); + resize(mImageWidth, mImageHeight * 2); } // Draws the text at mVerticalOffset and increments the offset with line space. @@ -410,7 +454,7 @@ public class ImageGenerator { // Draws from right if it's an RTL language. int x = - centralAlignment + mCenterAlignment ? (mImageWidth - fontMetrics.stringWidth(line)) / 2 : RTL_LANGUAGE.contains(languageTag) ? mImageWidth - fontMetrics.stringWidth(line) @@ -431,18 +475,19 @@ public class ImageGenerator { } /** - * Redraws the image with the new height. + * Redraws the image with the new width and new height. * + * @param width the new width of the image in pixels. * @param height the new height of the image in pixels. */ - private void resizeHeight(int height) { - BufferedImage resizedImage = - new BufferedImage(mImageWidth, height, BufferedImage.TYPE_BYTE_GRAY); + private void resize(int width, int height) { + BufferedImage resizedImage = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY); Graphics2D graphic = resizedImage.createGraphics(); graphic.drawImage(mBufferedImage, 0, 0, null); graphic.dispose(); mBufferedImage = resizedImage; + mImageWidth = width; mImageHeight = height; } @@ -458,11 +503,16 @@ public class ImageGenerator { public void generateImage(Map<Locale, String> localizedTextMap, String outputPath) throws FontFormatException, IOException { Map<String, Integer> languageCount = new TreeMap<>(); + int textWidth = 0; for (Locale locale : localizedTextMap.keySet()) { String language = locale.getLanguage(); languageCount.put(language, languageCount.getOrDefault(language, 0) + 1); + textWidth = Math.max(textWidth, measureTextWidth(localizedTextMap.get(locale), locale)); } + // Removes the black margins to reduce the size of the image. + resize(textWidth, mImageHeight); + for (Locale locale : localizedTextMap.keySet()) { Integer count = languageCount.get(locale.getLanguage()); // Recovery expects en-US instead of en_US. @@ -475,12 +525,10 @@ public class ImageGenerator { languageCount.put(locale.getLanguage(), count - 1); } - drawText(localizedTextMap.get(locale), locale, languageTag, false); + drawText(localizedTextMap.get(locale), locale, languageTag); } - // TODO(xunchang) adjust the width to save some space if all texts are smaller than - // imageWidth. - resizeHeight(mVerticalOffset); + resize(mImageWidth, mVerticalOffset); ImageIO.write(mBufferedImage, "png", new File(outputPath)); } @@ -528,11 +576,17 @@ public class ImageGenerator { options.addOption( OptionBuilder.withLongOpt("output_file") - .withDescription("Path to the generated image") + .withDescription("Path to the generated image.") .hasArgs(1) .isRequired() .create()); + options.addOption( + OptionBuilder.withLongOpt("center_alignment") + .withDescription("Align the text in the center of the screen.") + .hasArg(false) + .create()); + return options; } @@ -557,7 +611,8 @@ public class ImageGenerator { imageWidth, cmd.getOptionValue("text_name"), DEFAULT_FONT_SIZE, - cmd.getOptionValue("font_dir")); + cmd.getOptionValue("font_dir"), + cmd.hasOption("center_alignment")); Map<Locale, String> localizedStringMap = imageGenerator.readLocalizedStringFromXmls(cmd.getOptionValue("resource_dir")); |