From fe4a847f764ec1cda4ef11e9e49633390c85b75b Mon Sep 17 00:00:00 2001 From: Wyatt Gillette Date: Tue, 1 Jul 2025 15:33:29 +0200 Subject: [PATCH 1/2] Update BitmapText.java --- .../main/java/com/jme3/font/BitmapText.java | 277 +++++++++++------- 1 file changed, 179 insertions(+), 98 deletions(-) diff --git a/jme3-core/src/main/java/com/jme3/font/BitmapText.java b/jme3-core/src/main/java/com/jme3/font/BitmapText.java index eee39bf482..6825ad77b0 100644 --- a/jme3-core/src/main/java/com/jme3/font/BitmapText.java +++ b/jme3-core/src/main/java/com/jme3/font/BitmapText.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2021 jMonkeyEngine + * Copyright (c) 2009-2025 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -38,20 +38,38 @@ import com.jme3.renderer.RenderManager; import com.jme3.scene.Node; import com.jme3.util.clone.Cloner; + import java.util.regex.Matcher; import java.util.regex.Pattern; /** + * `BitmapText` is a spatial node that displays text using a {@link BitmapFont}. + * It handles text layout, alignment, wrapping, coloring, and styling based on + * the properties set via its methods. The text is rendered as a series of + * quads (rectangles) with character textures from the font's pages. + * * @author YongHoon */ public class BitmapText extends Node { + // The font used to render this text. private BitmapFont font; + // Stores the text content and its layout properties (size, box, alignment, etc.). private StringBlock block; + // A flag indicating whether the text needs to be re-assembled private boolean needRefresh = true; + // An array of `BitmapTextPage` instances, each corresponding to a font page. private BitmapTextPage[] textPages; + // Manages the individual letter quads, their positions, colors, and styles. private Letters letters; + /** + * Creates a new `BitmapText` instance using the specified font. + * The text will be rendered left-to-right by default, unless the font itself + * is configured for right-to-left rendering. + * + * @param font The {@link BitmapFont} to use for rendering the text (not null). + */ public BitmapText(BitmapFont font) { this(font, font.isRightToLeft(), false); } @@ -69,6 +87,15 @@ public BitmapText(BitmapFont font, boolean rightToLeft) { this(font, rightToLeft, false); } + /** + * Creates a new `BitmapText` instance with the specified font, text direction, + * and a flag for array-based rendering. + * + * @param font The {@link BitmapFont} to use for rendering the text (not null). + * @param rightToLeft true for right-to-left text rendering, false for left-to-right. + * @param arrayBased If true, the internal text pages will use array-based buffers for rendering. + * This might affect performance or compatibility depending on the renderer. + */ public BitmapText(BitmapFont font, boolean rightToLeft, boolean arrayBased) { textPages = new BitmapTextPage[font.getPageSize()]; for (int page = 0; page < textPages.length; page++) { @@ -84,7 +111,7 @@ public BitmapText(BitmapFont font, boolean rightToLeft, boolean arrayBased) { @Override public BitmapText clone() { - return (BitmapText)super.clone(false); + return (BitmapText) super.clone(false); } /** @@ -114,13 +141,19 @@ public void cloneFields(Cloner cloner, Object original) { // so I guess cloning doesn't come up that often. } + /** + * Returns the {@link BitmapFont} currently used by this `BitmapText` instance. + * + * @return The {@link BitmapFont} object. + */ public BitmapFont getFont() { return font; } /** - * Changes text size - * @param size text size + * Sets the size of the text. This value scales the font's base character sizes. + * + * @param size The desired text size (e.g., in world units or pixels). */ public void setSize(float size) { block.setSize(size); @@ -128,13 +161,20 @@ public void setSize(float size) { letters.invalidate(); } + /** + * Returns the current size of the text. + * + * @return The text size. + */ public float getSize() { return block.getSize(); } /** + * Sets the text content to be displayed. * - * @param text charsequence to change text to + * @param text The `CharSequence` (e.g., `String` or `StringBuilder`) to display. + * If null, the text will be set to an empty string. */ public void setText(CharSequence text) { // note: text.toString() is free if text is already a java.lang.String. @@ -142,72 +182,50 @@ public void setText(CharSequence text) { } /** + * Sets the text content to be displayed. + * If the new text is the same as the current text, no update occurs. + * Otherwise, the internal `StringBlock` and `Letters` objects are updated, + * and a refresh is flagged to re-layout the text. * - * @param text String to change text to + * @param text The `String` to display. If null, the text will be set to an empty string. */ public void setText(String text) { text = text == null ? "" : text; - if (text == block.getText() || block.getText().equals(text)) { + if (block.getText().equals(text)) { return; } - /* - The problem with the below block is that StringBlock carries - pretty much all of the text-related state of the BitmapText such - as size, text box, alignment, etc. - - I'm not sure why this change was needed and the commit message was - not entirely helpful because it purports to fix a problem that I've - never encountered. - - If block.setText("") doesn't do the right thing then that's where - the fix should go because StringBlock carries too much information to - be blown away every time. -pspeed - - Change was made: - http://code.google.com/p/jmonkeyengine/source/detail?spec=svn9389&r=9389 - Diff: - http://code.google.com/p/jmonkeyengine/source/diff?path=/trunk/engine/src/core/com/jme3/font/BitmapText.java&format=side&r=9389&old_path=/trunk/engine/src/core/com/jme3/font/BitmapText.java&old=8843 - - // If the text is empty, reset - if (text.isEmpty()) { - detachAllChildren(); - - for (int page = 0; page < textPages.length; page++) { - textPages[page] = new BitmapTextPage(font, true, page); - attachChild(textPages[page]); - } - - block = new StringBlock(); - letters = new Letters(font, block, letters.getQuad().isRightToLeft()); - } - */ - // Update the text content block.setText(text); letters.setText(text); - - // Flag for refresh needRefresh = true; } /** - * @return returns text + * Returns the current text content displayed by this `BitmapText` instance. + * + * @return The text content as a `String`. */ public String getText() { return block.getText(); } /** - * @return color of the text + * Returns the base color applied to the entire text. + * Note: Substring colors set via `setColor(int, int, ColorRGBA)` or + * `setColor(String, ColorRGBA)` will override this base color for their respective ranges. + * + * @return The base {@link ColorRGBA} of the text. */ public ColorRGBA getColor() { return letters.getBaseColor(); } /** - * changes text color. all substring colors are deleted. - * @param color new color of text + * Sets the base color for the entire text. + * This operation will clear any previously set substring colors. + * + * @param color The new base {@link ColorRGBA} for the text. */ public void setColor(ColorRGBA color) { letters.setColor(color); @@ -216,26 +234,34 @@ public void setColor(ColorRGBA color) { } /** - * Sets an overall alpha that will be applied to all - * letters. If the alpha passed is -1 then alpha reverts - * to default... which will be 1 for anything unspecified - * and color tags will be reset to 1 or their encoded - * alpha. + * Sets an overall alpha (transparency) value that will be applied to all + * letters in the text. + * If the alpha passed is -1, the alpha reverts to its default behavior: + * 1.0 for unspecified parts, and the encoded alpha from any color tags. * - * @param alpha the desired alpha, or -1 to revert to the default + * @param alpha The desired alpha value (0.0 for fully transparent, 1.0 for fully opaque), + * or -1 to revert to default alpha behavior. */ public void setAlpha(float alpha) { letters.setBaseAlpha(alpha); needRefresh = true; } + /** + * Returns the current base alpha value applied to the text. + * + * @return The base alpha value, or -1 if default alpha behavior is active. + */ public float getAlpha() { return letters.getBaseAlpha(); } /** - * Define the area where the BitmapText will be rendered. - * @param rect position and size box where text is rendered + * Defines a rectangular bounding box within which the text will be rendered. + * This box is used for text wrapping and alignment. + * + * @param rect The {@link Rectangle} defining the position (x, y) and size (width, height) + * of the text rendering area. */ public void setBox(Rectangle rect) { block.setTextBox(rect); @@ -244,14 +270,19 @@ public void setBox(Rectangle rect) { } /** - * @return height of the line + * Returns the height of a single line of text, scaled by the current text size. + * + * @return The calculated line height. */ public float getLineHeight() { return font.getLineHeight(block); } /** - * @return height of whole text block + * Calculates and returns the total height of the entire text block, + * considering all lines and the defined text box (if any). + * + * @return The total height of the text block. */ public float getHeight() { if (needRefresh) { @@ -266,7 +297,9 @@ public float getHeight() { } /** - * @return width of line + * Calculates and returns the maximum width of any line in the text block. + * + * @return The maximum line width of the text. */ public float getLineWidth() { if (needRefresh) { @@ -282,7 +315,9 @@ public float getLineWidth() { } /** - * @return line count + * Returns the number of lines the text currently occupies. + * + * @return The total number of lines. */ public int getLineCount() { if (needRefresh) { @@ -291,14 +326,21 @@ public int getLineCount() { return block.getLineCount(); } + /** + * Returns the current line wrapping mode set for this text. + * + * @return The {@link LineWrapMode} enum value. + */ public LineWrapMode getLineWrapMode() { return block.getLineWrapMode(); } /** - * Set horizontal alignment. Applicable only when text bound is set. + * Sets the horizontal alignment for the text within its bounding box. + * This is only applicable if a text bounding box has been set using {@link #setBox(Rectangle)}. * - * @param align the desired alignment (such as Align.Left) + * @param align The desired horizontal alignment (e.g., {@link Align#Left}, {@link Align#Center}, {@link Align#Right}). + * @throws RuntimeException If a bounding box is not set and `align` is not `Align.Left`. */ public void setAlignment(BitmapFont.Align align) { if (block.getTextBox() == null && align != Align.Left) { @@ -310,9 +352,11 @@ public void setAlignment(BitmapFont.Align align) { } /** - * Set vertical alignment. Applicable only when text bound is set. + * Sets the vertical alignment for the text within its bounding box. + * This is only applicable if a text bounding box has been set using {@link #setBox(Rectangle)}. * - * @param align the desired alignment (such as Align.Top) + * @param align The desired vertical alignment (e.g., {@link VAlign#Top}, {@link VAlign#Center}, {@link VAlign#Bottom}). + * @throws RuntimeException If a bounding box is not set and `align` is not `VAlign.Top`. */ public void setVerticalAlignment(BitmapFont.VAlign align) { if (block.getTextBox() == null && align != VAlign.Top) { @@ -323,28 +367,42 @@ public void setVerticalAlignment(BitmapFont.VAlign align) { needRefresh = true; } + /** + * Returns the current horizontal alignment set for the text. + * + * @return The current {@link Align} value. + */ public BitmapFont.Align getAlignment() { return block.getAlignment(); } + /** + * Returns the current vertical alignment set for the text. + * + * @return The current {@link VAlign} value. + */ public BitmapFont.VAlign getVerticalAlignment() { return block.getVerticalAlignment(); } /** - * Set the font style of substring. If font doesn't contain style, default style is used - * @param start start index to set style. inclusive. - * @param end end index to set style. EXCLUSIVE. - * @param style the style to apply + * Sets the font style for a specific substring of the text. + * If the font does not contain the specified style, the default style will be used. + * + * @param start The starting index of the substring (inclusive). + * @param end The ending index of the substring (exclusive). + * @param style The integer style identifier to apply. */ public void setStyle(int start, int end, int style) { letters.setStyle(start, end, style); } /** - * Set the font style of substring. If font doesn't contain style, default style is applied - * @param regexp regular expression - * @param style the style to apply + * Sets the font style for all substrings matching a given regular expression. + * If the font does not contain the specified style, the default style will be used. + * + * @param regexp The regular expression string to match against the text. + * @param style The integer style identifier to apply. */ public void setStyle(String regexp, int style) { Pattern p = Pattern.compile(regexp); @@ -355,10 +413,11 @@ public void setStyle(String regexp, int style) { } /** - * Set the color of substring. - * @param start start index to set style. inclusive. - * @param end end index to set style. EXCLUSIVE. - * @param color the desired color + * Sets the color for a specific substring of the text. + * + * @param start The starting index of the substring (inclusive). + * @param end The ending index of the substring (exclusive). + * @param color The desired {@link ColorRGBA} to apply to the substring. */ public void setColor(int start, int end, ColorRGBA color) { letters.setColor(start, end, color); @@ -367,9 +426,10 @@ public void setColor(int start, int end, ColorRGBA color) { } /** - * Set the color of substring. - * @param regexp regular expression - * @param color the desired color + * Sets the color for all substrings matching a given regular expression. + * + * @param regexp The regular expression string to match against the text. + * @param color The desired {@link ColorRGBA} to apply. */ public void setColor(String regexp, ColorRGBA color) { Pattern p = Pattern.compile(regexp); @@ -382,7 +442,10 @@ public void setColor(String regexp, ColorRGBA color) { } /** - * @param tabs tab positions + * Sets custom tab stop positions for the text. + * Tab characters (`\t`) will align to these specified positions. + * + * @param tabs An array of float values representing the horizontal tab stop positions. */ public void setTabPosition(float... tabs) { block.setTabPosition(tabs); @@ -391,8 +454,10 @@ public void setTabPosition(float... tabs) { } /** - * used for the tabs over the last tab position. - * @param width tab size + * Sets the default width for tabs that extend beyond the last defined tab position. + * This value is used if a tab character is encountered after all custom tab stops have been passed. + * + * @param width The default width for tabs in font units. */ public void setTabWidth(float width) { block.setTabWidth(width); @@ -401,10 +466,10 @@ public void setTabWidth(float width) { } /** - * for setLineWrapType(LineWrapType.NoWrap), - * set the last character when the text exceeds the bound. + * When {@link LineWrapMode#NoWrap} is used and the text exceeds the bounding box, + * this character will be appended to indicate truncation (e.g., '...'). * - * @param c the character to indicate truncated text + * @param c The character to use as the ellipsis. */ public void setEllipsisChar(char c) { block.setEllipsisChar(c); @@ -413,12 +478,18 @@ public void setEllipsisChar(char c) { } /** - * Available only when bounding is set. setBox() method call is needed in advance. - * true when - * @param wrap NoWrap : Letters over the text bound is not shown. the last character is set to '...'(0x2026) - * Character: Character is split at the end of the line. - * Word : Word is split at the end of the line. - * Clip : The text is hard-clipped at the border including showing only a partial letter if it goes beyond the text bound. + * Sets the line wrapping mode for the text. This is only applicable when + * a text bounding box has been set using {@link #setBox(Rectangle)}. + * + * @param wrap The desired {@link LineWrapMode}: + * */ public void setLineWrapMode(LineWrapMode wrap) { if (block.getLineWrapMode() != wrap) { @@ -436,28 +507,38 @@ public void updateLogicalState(float tpf) { } } + /** + * Assembles the text by generating the quad list (character positions and sizes) + * and then populating the vertex buffers of each `BitmapTextPage`. + * This method is called internally when `needRefresh` is true. + */ private void assemble() { - // first generate quad list + // First, generate or update the list of letter quads + // based on current text and layout properties. letters.update(); - for (int i = 0; i < textPages.length; i++) { - textPages[i].assemble(letters); + // Then, for each font page, assemble its mesh data from the generated quads. + for (BitmapTextPage textPage : textPages) { + textPage.assemble(letters); } needRefresh = false; } + /** + * Renders the `BitmapText` spatial. This method iterates through each + * `BitmapTextPage`, sets its texture, and renders it using the provided + * `RenderManager`. + * + * @param rm The `RenderManager` responsible for drawing. + * @param color The base color to apply during rendering. Note that colors + * set per-substring will override this for those parts. + */ public void render(RenderManager rm, ColorRGBA color) { for (BitmapTextPage page : textPages) { Material mat = page.getMaterial(); mat.setTexture("ColorMap", page.getTexture()); - //ColorRGBA original = getColor(mat, "Color"); - //mat.setColor("Color", color); + // mat.setColor("Color", color); // If the material supports a "Color" parameter mat.render(page, rm); - - //if( original == null ) { - // mat.clearParam("Color"); - //} else { - // mat.setColor("Color", original); - //} } } + } From f7f3dc6fe1b6fa97890660948eba44a792b69bb5 Mon Sep 17 00:00:00 2001 From: Wyatt Gillette Date: Tue, 1 Jul 2025 15:34:21 +0200 Subject: [PATCH 2/2] Update BitmapFont.java --- .../main/java/com/jme3/font/BitmapFont.java | 197 +++++++++++++----- 1 file changed, 140 insertions(+), 57 deletions(-) diff --git a/jme3-core/src/main/java/com/jme3/font/BitmapFont.java b/jme3-core/src/main/java/com/jme3/font/BitmapFont.java index 0fec21364e..867206c2a7 100644 --- a/jme3-core/src/main/java/com/jme3/font/BitmapFont.java +++ b/jme3-core/src/main/java/com/jme3/font/BitmapFont.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2021 jMonkeyEngine + * Copyright (c) 2009-2025 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -31,14 +31,22 @@ */ package com.jme3.font; -import com.jme3.export.*; +import com.jme3.export.InputCapsule; +import com.jme3.export.JmeExporter; +import com.jme3.export.JmeImporter; +import com.jme3.export.OutputCapsule; +import com.jme3.export.Savable; import com.jme3.material.Material; import java.io.IOException; /** - * Represents a font within jME that is generated with the AngelCode Bitmap Font Generator + * Represents a font loaded from a bitmap font definition + * (e.g., generated by AngelCode Bitmap Font Generator). + * It manages character sets, font pages (textures), and provides utilities for text measurement and rendering. + * * @author dhdd + * @author Yonghoon */ public class BitmapFont implements Savable { @@ -87,33 +95,30 @@ public enum VAlign { Bottom } + // The character set containing definitions for each character (glyph) in the font. private BitmapCharacterSet charSet; + // An array of materials, where each material corresponds to a font page (texture). private Material[] pages; + // Indicates whether this font is designed for right-to-left (RTL) text rendering. private boolean rightToLeft = false; // For cursive bitmap fonts in which letter shape is determined by the adjacent glyphs. private GlyphParser glyphParser; /** - * @return true, if this is a right-to-left font, otherwise it will return false. + * Creates a new instance of `BitmapFont`. + * This constructor is primarily used for deserialization. */ - public boolean isRightToLeft() { - return rightToLeft; + public BitmapFont() { } /** - * Specify if this is a right-to-left font. By default it is set to false. - * This can be "overwritten" in the BitmapText constructor. + * Creates a new {@link BitmapText} instance initialized with this font. + * The label's size will be set to the font's rendered size, and its text content + * will be set to the provided string. * - * @param rightToLeft true → right-to-left, false → left-to-right - * (default=false) + * @param content The initial text content for the label. + * @return A new {@link BitmapText} instance. */ - public void setRightToLeft(boolean rightToLeft) { - this.rightToLeft = rightToLeft; - } - - public BitmapFont() { - } - public BitmapText createLabel(String content) { BitmapText label = new BitmapText(this); label.setSize(getCharSet().getRenderedSize()); @@ -121,27 +126,81 @@ public BitmapText createLabel(String content) { return label; } + /** + * Checks if this font is configured for right-to-left (RTL) text rendering. + * + * @return true if this is a right-to-left font, otherwise false (default is left-to-right). + */ + public boolean isRightToLeft() { + return rightToLeft; + } + + /** + * Specifies whether this font should be rendered as right-to-left (RTL). + * By default, it is set to false (left-to-right). + * + * @param rightToLeft true to enable right-to-left rendering; false for left-to-right. + */ + public void setRightToLeft(boolean rightToLeft) { + this.rightToLeft = rightToLeft; + } + + /** + * Returns the preferred size of the font, which is typically its rendered size. + * + * @return The preferred size of the font in font units. + */ public float getPreferredSize() { return getCharSet().getRenderedSize(); } + /** + * Sets the character set for this font. The character set contains + * information about individual glyphs, their positions, and kerning data. + * + * @param charSet The {@link BitmapCharacterSet} to associate with this font. + */ public void setCharSet(BitmapCharacterSet charSet) { this.charSet = charSet; } + /** + * Sets the array of materials (font pages) for this font. Each material + * corresponds to a texture page containing character bitmaps. + * The character set's page size is also updated based on the number of pages. + * + * @param pages An array of {@link Material} objects representing the font pages. + */ public void setPages(Material[] pages) { this.pages = pages; charSet.setPageSize(pages.length); } + /** + * Retrieves a specific font page material by its index. + * + * @param index The index of the font page to retrieve. + * @return The {@link Material} for the specified font page. + * @throws IndexOutOfBoundsException if the index is out of bounds. + */ public Material getPage(int index) { return pages[index]; } + /** + * Returns the total number of font pages (materials) associated with this font. + * + * @return The number of font pages. + */ public int getPageSize() { return pages.length; } + /** + * Retrieves the character set associated with this font. + * + * @return The {@link BitmapCharacterSet} of this font. + */ public BitmapCharacterSet getCharSet() { return charSet; } @@ -192,26 +251,19 @@ private int findKerningAmount(int newLineLastChar, int nextChar) { return c.getKerning(nextChar); } - @Override - public void write(JmeExporter ex) throws IOException { - OutputCapsule oc = ex.getCapsule(this); - oc.write(charSet, "charSet", null); - oc.write(pages, "pages", null); - oc.write(rightToLeft, "rightToLeft", false); - oc.write(glyphParser, "glyphParser", null); - } - - @Override - public void read(JmeImporter im) throws IOException { - InputCapsule ic = im.getCapsule(this); - charSet = (BitmapCharacterSet) ic.readSavable("charSet", null); - Savable[] pagesSavable = ic.readSavableArray("pages", null); - pages = new Material[pagesSavable.length]; - System.arraycopy(pagesSavable, 0, pages, 0, pages.length); - rightToLeft = ic.readBoolean("rightToLeft", false); - glyphParser = (GlyphParser) ic.readSavable("glyphParser", null); - } - + /** + * Calculates the width of the given text in font units. + * This method accounts for character advances, kerning, and line breaks. + * It also attempts to skip custom color tags (e.g., "\#RRGGBB#" or "\#RRGGBBAA#") + * based on a specific format. + *

+ * Note: This method calculates width in "font units" where the font's + * {@link BitmapCharacterSet#getRenderedSize() rendered size} is the base. + * Actual pixel scaling for display is typically handled by {@link BitmapText}. + * + * @param text The text to measure. + * @return The maximum line width of the text in font units. + */ public float getLineWidth(CharSequence text) { // This method will probably always be a bit of a maintenance // nightmare since it bases its calculation on a different @@ -252,29 +304,36 @@ public float getLineWidth(CharSequence text) { boolean firstCharOfLine = true; // float sizeScale = (float) block.getSize() / charSet.getRenderedSize(); float sizeScale = 1f; - CharSequence characters = glyphParser != null ? glyphParser.parse(text) : text; - for (int i = 0; i < characters.length(); i++) { - char theChar = characters.charAt(i); - if (theChar == '\n') { + // Use GlyphParser if available for complex script shaping (e.g., cursive fonts). + CharSequence processedText = glyphParser != null ? glyphParser.parse(text) : text; + + for (int i = 0; i < processedText.length(); i++) { + char currChar = processedText.charAt(i); + if (currChar == '\n') { maxLineWidth = Math.max(maxLineWidth, lineWidth); lineWidth = 0f; firstCharOfLine = true; continue; } - BitmapCharacter c = charSet.getCharacter(theChar); + BitmapCharacter c = charSet.getCharacter(currChar); if (c != null) { - if (theChar == '\\' && i < characters.length() - 1 && characters.charAt(i + 1) == '#') { - if (i + 5 < characters.length() && characters.charAt(i + 5) == '#') { + // Custom color tag skipping logic: + // Assumes tags are of the form `\#RRGGBB#` (9 chars total) or `\#RRGGBBAA#` (12 chars total). + if (currChar == '\\' && i < processedText.length() - 1 && processedText.charAt(i + 1) == '#') { + // Check for `\#XXXXX#` (6 chars after '\', including final '#') + if (i + 5 < processedText.length() && processedText.charAt(i + 5) == '#') { i += 5; continue; - } else if (i + 8 < characters.length() && characters.charAt(i + 8) == '#') { + } + // Check for `\#XXXXXXXX#` (9 chars after '\', including final '#') + else if (i + 8 < processedText.length() && processedText.charAt(i + 8) == '#') { i += 8; continue; } } if (!firstCharOfLine) { - lineWidth += findKerningAmount(lastChar, theChar) * sizeScale; + lineWidth += findKerningAmount(lastChar, currChar) * sizeScale; } else { if (rightToLeft) { // Ignore offset, so it will be compatible with BitmapText.getLineWidth(). @@ -292,7 +351,7 @@ public float getLineWidth(CharSequence text) { // If this is the last character of a line, then we really should // have only added its width. The advance may include extra spacing // that we don't care about. - if (i == characters.length() - 1 || characters.charAt(i + 1) == '\n') { + if (i == processedText.length() - 1 || processedText.charAt(i + 1) == '\n') { if (rightToLeft) { // In RTL text we move the letter x0 by its xAdvance, so // we should add it to lineWidth. @@ -315,30 +374,54 @@ public float getLineWidth(CharSequence text) { return Math.max(maxLineWidth, lineWidth); } - /** - * Merge two fonts. - * If two font have the same style, merge will fail. - * @param newFont Style must be assigned to this. - * author: Yonghoon + * Merges another {@link BitmapFont} into this one. + * This operation combines the character sets and font pages. + * If both fonts contain the same style, the merge will fail and throw a RuntimeException. + * + * @param newFont The {@link BitmapFont} to merge into this one. It must have a style assigned. */ public void merge(BitmapFont newFont) { charSet.merge(newFont.charSet); final int size1 = this.pages.length; final int size2 = newFont.pages.length; - Material[] tmp = new Material[size1+size2]; + Material[] tmp = new Material[size1 + size2]; System.arraycopy(this.pages, 0, tmp, 0, size1); System.arraycopy(newFont.pages, 0, tmp, size1, size2); this.pages = tmp; - -// this.pages = Arrays.copyOf(this.pages, size1+size2); -// System.arraycopy(newFont.pages, 0, this.pages, size1, size2); } + /** + * Sets the style for the font's character set. + * This method is typically used when a font file contains only one style + * but needs to be assigned a specific style identifier for merging + * with other multi-style fonts. + * + * @param style The integer style identifier to set. + */ public void setStyle(int style) { charSet.setStyle(style); } -} \ No newline at end of file + @Override + public void write(JmeExporter ex) throws IOException { + OutputCapsule oc = ex.getCapsule(this); + oc.write(charSet, "charSet", null); + oc.write(pages, "pages", null); + oc.write(rightToLeft, "rightToLeft", false); + oc.write(glyphParser, "glyphParser", null); + } + + @Override + public void read(JmeImporter im) throws IOException { + InputCapsule ic = im.getCapsule(this); + charSet = (BitmapCharacterSet) ic.readSavable("charSet", null); + Savable[] pagesSavable = ic.readSavableArray("pages", null); + pages = new Material[pagesSavable.length]; + System.arraycopy(pagesSavable, 0, pages, 0, pages.length); + rightToLeft = ic.readBoolean("rightToLeft", false); + glyphParser = (GlyphParser) ic.readSavable("glyphParser", null); + } +}