Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
329d951
Display Right to Left text correctly (Issue #1158).
Aufricer Oct 17, 2021
797ee94
Just a suggestion to set the Right to Left text parameter with the fo…
Aufricer Oct 18, 2021
8f4f5f4
Display Right to Left text correctly (Issue #1158).
Aufricer Oct 18, 2021
2c8751f
Edited out the reverse function of the textinput in BitmapText.
Aufricer Oct 18, 2021
d6e9f69
Changed the behaviour of RtL text without a textblock
Aufricer Oct 24, 2021
9b72b45
Updating the linewidth dor correct calculation if unbounded.
Aufricer Oct 24, 2021
b8659cc
Changed the behaviour of how xoffset is treated while calculating x0 …
Aufricer Oct 27, 2021
c0e256d
Edited out the reverse function of the textinput in BitmapText.
Aufricer Oct 27, 2021
2994d80
validateSize() which is used in BitMapText.getLineWidth was changed t…
Aufricer Nov 19, 2021
a11d329
Merge branch 'jMonkeyEngine:master' into master
Aufricer Nov 27, 2021
aae5c0a
Merged with the adaptions and changes of Ali-RS. BitmapText.getLineWi…
Aufricer Nov 27, 2021
b17e584
Removed ToDos no longer needed due to the last changes
Aufricer Nov 27, 2021
4f5b941
Detected small (incorrect) changes while comparing, fixed
Aufricer Nov 28, 2021
ef404d5
Delete local.properties
Aufricer Nov 28, 2021
0b7f1d1
Get back local.properties
Aufricer Dec 7, 2021
0fc9bd7
Merge remote-tracking branch 'origin/master'
Aufricer Dec 7, 2021
f2f12eb
Fix javadoc & formatting issues.
Ali-RS Dec 9, 2021
489db71
Added license to GlyphParser.
Ali-RS Dec 9, 2021
75635a1
BitmapFont: serialize "rightToLeft" boolean field.
Ali-RS Dec 9, 2021
1797ee8
Made GlyphParser savable.
Ali-RS Dec 10, 2021
f2f39b3
Deprecate BitmapText(BitmapFont font, boolean rightToLeft). The "righ…
Ali-RS Dec 11, 2021
21134ac
Fixed alignment issue where Align.Right was not working when vertical…
Ali-RS Dec 11, 2021
e12275d
GlyphParser:added link to an example implementation for Persian text.
Ali-RS Dec 12, 2021
fba37a6
Merge branch 'master' into master
stephengold Dec 15, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 79 additions & 26 deletions jme3-core/src/main/java/com/jme3/font/BitmapFont.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@

import com.jme3.export.*;
import com.jme3.material.Material;

import java.io.IOException;

/**
Expand Down Expand Up @@ -88,6 +89,24 @@ public enum VAlign {

private BitmapCharacterSet charSet;
private Material[] pages;
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.
*/
public boolean isRightToLeft() {
return rightToLeft;
}

/**
* Specify if this is a right-to-left font. By default it is set to false.
* This can be "overwritten" in the BitmapText constructor.
*/
public void setRightToLeft(boolean rightToLeft) {
this.rightToLeft = rightToLeft;
}

public BitmapFont() {
}
Expand Down Expand Up @@ -123,7 +142,23 @@ public int getPageSize() {
public BitmapCharacterSet getCharSet() {
return charSet;
}


/**
* For cursive fonts a GlyphParser needs to be specified which is used
* to determine glyph shape by the adjacent glyphs. If nothing is set,
* all glyphs will be rendered isolated.
*/
public void setGlyphParser(GlyphParser glyphParser) {
this.glyphParser = glyphParser;
}

/**
* @return The GlyphParser set on the font, or null if it has no glyph parser.
*/
public GlyphParser getGlyphParser() {
return glyphParser;
}

/**
* Gets the line height of a StringBlock.
*
Expand Down Expand Up @@ -156,6 +191,8 @@ 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
Expand All @@ -165,6 +202,8 @@ public void read(JmeImporter im) throws IOException {
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);
}

public float getLineWidth(CharSequence text){
Expand Down Expand Up @@ -208,47 +247,61 @@ public float getLineWidth(CharSequence text){
boolean firstCharOfLine = true;
// float sizeScale = (float) block.getSize() / charSet.getRenderedSize();
float sizeScale = 1f;
for (int i = 0; i < text.length(); i++){
char theChar = text.charAt(i);
CharSequence characters = glyphParser != null ? glyphParser.parse(text) : text;

for (int i = 0; i < characters.length(); i++){
char theChar = characters.charAt(i);
if (theChar == '\n'){
maxLineWidth = Math.max(maxLineWidth, lineWidth);
lineWidth = 0f;
firstCharOfLine = true;
continue;
}
BitmapCharacter c = charSet.getCharacter(theChar);
if (c != null){
if (theChar == '\\' && i<text.length()-1 && text.charAt(i+1)=='#'){
if (i+5<text.length() && text.charAt(i+5)=='#'){
i+=5;
if (c != null) {
if (theChar == '\\' && i < characters.length() - 1 && characters.charAt(i + 1) == '#') {
if (i + 5 < characters.length() && characters.charAt(i + 5) == '#') {
i += 5;
continue;
}else if (i+8<text.length() && text.charAt(i+8)=='#'){
i+=8;
} else if (i + 8 < characters.length() && characters.charAt(i + 8) == '#') {
i += 8;
continue;
}
}
if (!firstCharOfLine){
lineWidth += findKerningAmount(lastChar, theChar) * sizeScale;
if (!firstCharOfLine) {
lineWidth += findKerningAmount(lastChar, theChar) * sizeScale;
} else {
// The first character needs to add in its xOffset, but it
// is the only one... and negative offsets = positive width
// because we're trying to account for the part that hangs
// over the left. So we subtract.
lineWidth -= c.getXOffset() * sizeScale;
if (rightToLeft) {
// Ignore offset, so it will be compatible with BitmapText.getLineWidth().
} else {
// The first character needs to add in its xOffset but it
// is the only one... and negative offsets = positive width
// because we're trying to account for the part that hangs
// over the left. So we subtract.
lineWidth -= c.getXOffset() * sizeScale;
}
firstCharOfLine = false;
}
float xAdvance = c.getXAdvance() * sizeScale;
// If this is the last character, then we really should have
// only added its width. The advance may include extra spacing

// 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 == text.length() - 1) {
lineWidth += c.getWidth() * sizeScale;

// Since the width includes the xOffset then we need
// to take it out again by adding it, ie: offset the width
// we just added by the appropriate amount.
lineWidth += c.getXOffset() * sizeScale;
if (i == characters.length() - 1 || characters.charAt(i + 1) == '\n') {
if (rightToLeft) {
// In RTL text we move the letter x0 by it's xAdvance so
// we should add it to lineWidth
lineWidth += xAdvance;
// Then we move letter by it's xOffset.
// Negative offsets = positive width.
lineWidth -= c.getXOffset() * sizeScale;
} else {
lineWidth += c.getWidth() * sizeScale;
// Since the width includes the xOffset then we need
// to take it out again by adding it, ie: offset the width
// we just added by the appropriate amount.
lineWidth += c.getXOffset() * sizeScale;
}
} else {
lineWidth += xAdvance;
}
Expand Down
9 changes: 7 additions & 2 deletions jme3-core/src/main/java/com/jme3/font/BitmapText.java
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,14 @@ public class BitmapText extends Node {
private Letters letters;

public BitmapText(BitmapFont font) {
this(font, false, false);
this(font, font.isRightToLeft(), false);
}

/**
* @deprecated The "rightToLeft" flag should be specified in the font.
* Use {@link BitmapText#BitmapText(com.jme3.font.BitmapFont)}
*/
@Deprecated
public BitmapText(BitmapFont font, boolean rightToLeft) {
this(font, rightToLeft, false);
}
Expand Down Expand Up @@ -138,7 +143,6 @@ public void setText(CharSequence text) {
*/
public void setText(String text) {
text = text == null ? "" : text;

if (text == block.getText() || block.getText().equals(text)) {
return;
}
Expand Down Expand Up @@ -268,6 +272,7 @@ public float getLineWidth() {
if (textBox != null) {
return Math.max(letters.getTotalWidth(), textBox.width);
}
// Please note that BitMaptext.getLineWidth() might differ from Font.getLineWidth() --> scale it with Font.getPreferredSize()/BitMaptext.getSize()
return letters.getTotalWidth();
}

Expand Down
50 changes: 50 additions & 0 deletions jme3-core/src/main/java/com/jme3/font/GlyphParser.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright (c) 2009-2021 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.jme3.font;

import com.jme3.export.Savable;

/**
* Used for selecting character shape in cursive bitmap text. In cursive scripts,
* the appearance of a letter changes depending on its position:
* isolated, initial (joined on the left), medial (joined on both sides)
* and final (joined on the right) of a word.
*
* For an example implementation see: https://github.com/Ali-RS/JME-PersianGlyphParser
*
* @author Ali-RS
*/
public interface GlyphParser extends Savable {

public CharSequence parse(CharSequence text);

}
96 changes: 65 additions & 31 deletions jme3-core/src/main/java/com/jme3/font/LetterQuad.java
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,12 @@ boolean isInvalid(StringBlock block, float gap) {
if (bound == null) {
return false;
}
return x0 > 0 && bound.x+bound.width-gap < getX1();
if (isRightToLeft()) {
return x0 <0 && x0<bound.x;
// ToDo check for ellipsis, not sure if it is on both sides of a character and need to be deducted as well
} else {
return x0 > 0 && bound.x+bound.width-gap < getX1();
}
}

void clip(StringBlock block) {
Expand All @@ -182,6 +187,7 @@ void clip(StringBlock block) {
// to the string block
float x1 = Math.min(bound.x + bound.width, x0 + width);
float newWidth = x1 - x0;
if (isRightToLeft()) newWidth = x1; // only the available space to the left
if( newWidth == width )
return;

Expand All @@ -195,13 +201,12 @@ float getX0() {
}

float getX1() {
return x0+width;
return x0 + width;
}

float getNextX() {
return x0+xAdvance;
return rightToLeft ? x0 - xAdvance : x0 + xAdvance;
}

float getNextLine() {
return lineY+LINE_DIR*font.getCharSet().getLineHeight() * sizeScale;
}
Expand Down Expand Up @@ -314,6 +319,9 @@ void update(StringBlock block) {

if (isHead()) {
x0 = getBound(block).x;
if (isRightToLeft() && getBound(block) != UNBOUNDED) {
x0 += getBound(block).width;
}
y0 = lineY;
width = 0;
height = 0;
Expand All @@ -333,6 +341,7 @@ void update(StringBlock block) {
xAdvance = width;
} else if (bitmapChar == null) {
x0 = getPrevious().getX1();
if (rightToLeft) x0 = getPrevious().getX0();
y0 = lineY;
width = 0;
height = 0;
Expand All @@ -347,30 +356,56 @@ void update(StringBlock block) {
float kernAmount = 0f;

if (previous.isHead() || previous.eol) {
x0 = bound.x;

// The first letter quad will be drawn right at the first
// position... but it does not offset by the characters offset
// amount. This means that we've potentially accumulated extra
// pixels, and the next letter won't get drawn far enough unless
// we add this offset back into xAdvance, by subtracting it.
// This is the same thing that's done below because we've
// technically baked the offset in just like below. It doesn't
// look like it at first glance, so I'm keeping it separate with
// this comment.
xAdvance -= xOffset * incrScale;

if (rightToLeft) {
// In RTL text we advance toward left by the letter xAdvance. (subtract xAdvance)
// Note, positive offset will move the letter quad toward right and negative offset
// will move it toward left.
if (previous.isHead()) {
x0 = previous.getNextX() - xAdvance - xOffset * incrScale;
} else if (previous.eol) {
// For bounded bitmap text the first letter of a line is always
// on the right end of the textbox and for unbounded bitmap text
// we start from the x=0 and advance toward left.
x0 = getBound(block).x + (getBound(block) != UNBOUNDED ? getBound(block).width : 0) - xAdvance - xOffset * incrScale;
}
// Since x0 has xAdvance baked into it, we need to zero out xAdvance.
// Since x0 will have offset baked into it, we need to counteract that
// in xAdvance. The next x position will be (x0 - xAdvance).
xAdvance = -xOffset * incrScale;
} else {
x0 = bound.x;

// The first letter quad will be drawn right at the first
// position, but it does not offset by the character's offset
// amount. This means that we've potentially accumulated extra
// pixels, and the next letter won't get drawn far enough unless
// we add this offset back into xAdvance, by subtracting it.
// This is the same thing that's done below, because we've
// technically baked the offset in just like below. It doesn't
// look like it at first glance, so I'm keeping it separate with
// this comment.
xAdvance -= xOffset * incrScale;
}
} else {
x0 = previous.getNextX() + xOffset * incrScale;

// Since x0 will have offset baked into it then we
// need to counteract that in xAdvance. This is better
// than removing it in getNextX() because we also need
// to take kerning into account below... which will also
// get baked in.
// Without this, getNextX() will return values too far to
// the left, for example.
xAdvance -= xOffset * incrScale;
if (isRightToLeft()) {
// For RTL text the xAdvance of the current letter is deducted,
// while for LTR text the advance of the letter before is added.
x0 = previous.getNextX() - xAdvance - xOffset * incrScale;
// Since x0 has xAdvance baked into it, we need to zero out xAdvance.
// Since x0 will have offset baked into it we need to counteract that
// in xAdvance. The next x position will be (x0 - xAdvance)
xAdvance = - xOffset * incrScale;
} else {
x0 = previous.getNextX() + xOffset * incrScale;
// Since x0 will have offset baked into it, we
// need to counteract that in xAdvance. This is better
// than removing it in getNextX() because we also need
// to take kerning into account below, which will also
// get baked in.
// Without this, getNextX() will return values too far to
// the left, for example.
xAdvance -= xOffset * incrScale;
}
}
y0 = lineY + LINE_DIR*yOffset;

Expand All @@ -379,12 +414,11 @@ void update(StringBlock block) {
if (lastChar != null && block.isKerning()) {
kernAmount = lastChar.getKerning(c) * sizeScale;
x0 += kernAmount * incrScale;

// Need to unbake the kerning from xAdvance since it
// Need to unbake the kerning from xAdvance since it
// is baked into x0... see above.
//xAdvance -= kernAmount * incrScale;
// No, kerning is an inter-character spacing and _does_ affect
// all subsequent cursor positions.
// all subsequent cursor positions.
}
}
if (isEndOfLine()) {
Expand Down
Loading