Skip to content

Commit

Permalink
Merge pull request #5771 from hodbauer/rtl-labels-support
Browse files Browse the repository at this point in the history
Rtl labels support
  • Loading branch information
mramato authored Oct 25, 2017
2 parents 3f018d7 + 6bca30d commit 9933716
Show file tree
Hide file tree
Showing 6 changed files with 318 additions and 4 deletions.
17 changes: 17 additions & 0 deletions Apps/Sandcastle/gallery/Labels.html
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,17 @@
});
}

function setRightToLeft() {
Sandcastle.declare(setRightToLeft);
Cesium.Label.enableRightToLeftDetection = true; //Only needs to be set once at the beginning of the application.
viewer.entities.add({
position : Cesium.Cartesian3.fromDegrees(-75.1641667, 39.9522222),
label : {
text : 'Master (אדון): Hello\nתלמיד (student): שלום'
}
});
}

Sandcastle.addToolbarMenu([{
text : 'Add label',
onselect : function() {
Expand Down Expand Up @@ -157,6 +168,12 @@
scaleByDistance();
Sandcastle.highlight(scaleByDistance);
}
}, {
text : 'Set label with right-to-left language',
onselect : function() {
setRightToLeft();
Sandcastle.highlight(setRightToLeft);
}
}]);

Sandcastle.reset = function() {
Expand Down
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ Change Log

### 1.39 - 2017-11-01

* Added support for right-to-left languages in labels. [#5771](https://github.com/AnalyticalGraphicsInc/cesium/pull/5771)
* Added the ability to load Cesium's assets from the local file system if security permissions allow it. [#5830](https://github.com/AnalyticalGraphicsInc/cesium/issues/5830)
* Added function that inserts missing namespace declarations into KML files. [#5860](https://github.com/AnalyticalGraphicsInc/cesium/pull/5860)
* Added support for the layer.json `parentUrl` property in `CesiumTerrainProvider` to allow for compositing of tilesets.
Expand Down
3 changes: 3 additions & 0 deletions CONTRIBUTORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for details on how to contribute to Cesiu
* [Jannes Bolling](https://github.com/jbo023)
* [Logilab](https://www.logilab.fr/)
* [Florent Cayré](https://github.com/fcayre/)
* [webiks](https://www.webiks.com)
* [Hod Bauer](https://github.com/hodbauer)
* [Yonatan Kra](https://github.com/yonatankra)
* [Novetta](http://www.novetta.com/)
* [Joshua Bernstein](https://github.com/jbernstein/)
* [Natanael Rivera](https://github.com/nrivera-Novetta/)
Expand Down
214 changes: 212 additions & 2 deletions Source/Scene/Label.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ define([
'../Core/defineProperties',
'../Core/DeveloperError',
'../Core/DistanceDisplayCondition',
'../Core/freezeObject',
'../Core/NearFarScalar',
'./Billboard',
'./HeightReference',
Expand All @@ -24,6 +25,7 @@ define([
defineProperties,
DeveloperError,
DistanceDisplayCondition,
freezeObject,
NearFarScalar,
Billboard,
HeightReference,
Expand All @@ -32,6 +34,13 @@ define([
VerticalOrigin) {
'use strict';

var textTypes = freezeObject({
LTR : 0,
RTL : 1,
WEAK : 2,
BRACKETS : 3
});

function rebindAllGlyphs(label) {
if (!label._rebindAllGlyphs && !label._repositionAllGlyphs) {
// only push label if it's not already been marked dirty
Expand Down Expand Up @@ -110,7 +119,8 @@ define([
distanceDisplayCondition = DistanceDisplayCondition.clone(distanceDisplayCondition);
}

this._text = defaultValue(options.text, '');
this._renderedText = undefined;
this._text = undefined;
this._show = defaultValue(options.show, true);
this._font = defaultValue(options.font, '30px sans-serif');
this._fillColor = Color.clone(defaultValue(options.fillColor, Color.WHITE));
Expand Down Expand Up @@ -147,6 +157,8 @@ define([

this._clusterShow = true;

this.text = defaultValue(options.text, '');

this._updateClamping();
}

Expand Down Expand Up @@ -281,6 +293,7 @@ define([

if (this._text !== value) {
this._text = value;
this._renderedText = Label.enableRightToLeftDetection ? reverseRtl(value) : value;
rebindAllGlyphs(this);
}
}
Expand Down Expand Up @@ -1167,7 +1180,7 @@ define([
this._verticalOrigin === other._verticalOrigin &&
this._horizontalOrigin === other._horizontalOrigin &&
this._heightReference === other._heightReference &&
this._text === other._text &&
this._renderedText === other._renderedText &&
this._font === other._font &&
Cartesian3.equals(this._position, other._position) &&
Color.equals(this._fillColor, other._fillColor) &&
Expand Down Expand Up @@ -1196,5 +1209,202 @@ define([
return false;
};

/**
* Determines whether or not run the algorithm, that match the text of the label to right-to-left languages
* @memberof Label
* @type {Boolean}
* @default false
*
* @example
* // Example 1.
* // Set a label's rightToLeft before init
* Cesium.Label.enableRightToLeftDetection = true;
* var myLabelEntity = viewer.entities.add({
* label: {
* id: 'my label',
* text: 'זה טקסט בעברית \n ועכשיו יורדים שורה',
* }
* });
*
* @example
* // Example 2.
* var myLabelEntity = viewer.entities.add({
* label: {
* id: 'my label',
* text: 'English text'
* }
* });
* // Set a label's rightToLeft after init
* Cesium.Label.enableRightToLeftDetection = true;
* myLabelEntity.text = 'טקסט חדש';
*/
Label.enableRightToLeftDetection = false;

function convertTextToTypes(text, rtlChars) {
var ltrChars = /[a-zA-Z0-9]/;
var bracketsChars = /[()[\]{}<>]/;
var parsedText = [];
var word = '';
var lastType = textTypes.LTR;
var currentType = '';
var textLength = text.length;
for (var textIndex = 0; textIndex < textLength; ++textIndex) {
var character = text.charAt(textIndex);
if (rtlChars.test(character)) {
currentType = textTypes.RTL;
}
else if (ltrChars.test(character)) {
currentType = textTypes.LTR;
}
else if (bracketsChars.test(character)) {
currentType = textTypes.BRACKETS;
}
else {
currentType = textTypes.WEAK;
}

if (textIndex === 0) {
lastType = currentType;
}

if (lastType === currentType && currentType !== textTypes.BRACKETS) {
word += character;
}
else {
if (word !== '') {
parsedText.push({Type : lastType, Word : word});
}
lastType = currentType;
word = character;
}
}
parsedText.push({Type : currentType, Word : word});
return parsedText;
}

function reverseWord(word) {
return word.split('').reverse().join('');
}

function spliceWord(result, pointer, word) {
return result.slice(0, pointer) + word + result.slice(pointer);
}

function reverseBrackets(bracket) {
switch(bracket) {
case '(':
return ')';
case ')':
return '(';
case '[':
return ']';
case ']':
return '[';
case '{':
return '}';
case '}':
return '{';
case '<':
return '>';
case '>':
return '<';
}
}

/**
*
* @param {String} value the text to parse and reorder
* @returns {String} the text as rightToLeft direction
* @private
*/
function reverseRtl(value) {
var rtlChars = /[\u05D0-\u05EA]/;
var texts = value.split('\n');
var result = '';
for (var i = 0; i < texts.length; i++) {
var text = texts[i];
var rtlDir = rtlChars.test(text.charAt(0));
var parsedText = convertTextToTypes(text, rtlChars);

var splicePointer = 0;
var line = '';
for (var wordIndex = 0; wordIndex < parsedText.length; ++wordIndex) {
var subText = parsedText[wordIndex];
var reverse = subText.Type === textTypes.BRACKETS ? reverseBrackets(subText.Word) : subText.Word;
if (rtlDir) {
if (subText.Type === textTypes.RTL) {
line = reverseWord(subText.Word) + line;
splicePointer = 0;
}
else if (subText.Type === textTypes.LTR) {
line = spliceWord(line, splicePointer, subText.Word);
splicePointer += subText.Word.length;
}
else if (subText.Type === textTypes.WEAK || subText.Type === textTypes.BRACKETS) {
if (subText.Type === textTypes.WEAK && parsedText[wordIndex - 1].Type === textTypes.BRACKETS) {
line = reverseWord(subText.Word) + line;
}
else if (parsedText[wordIndex - 1].Type === textTypes.RTL) {
line = reverse + line;
splicePointer = 0;
}
else if (parsedText.length > wordIndex + 1) {
if (parsedText[wordIndex + 1].Type === textTypes.RTL) {
line = reverse + line;
splicePointer = 0;
}
else {
line = spliceWord(line, splicePointer, subText.Word);
splicePointer += subText.Word.length;
}
}
else {
line = spliceWord(line, 0, reverse);
}
}
}
else if (subText.Type === textTypes.RTL) {
line = spliceWord(line, splicePointer, reverseWord(subText.Word));
}
else if (subText.Type === textTypes.LTR) {
line += subText.Word;
splicePointer = line.length;
}
else if (subText.Type === textTypes.WEAK || subText.Type === textTypes.BRACKETS) {
if (wordIndex > 0) {
if (parsedText[wordIndex - 1].Type === textTypes.RTL) {
if (parsedText.length > wordIndex + 1) {
if (parsedText[wordIndex + 1].Type === textTypes.RTL) {
line = spliceWord(line, splicePointer, reverse);
}
else {
line += subText.Word;
splicePointer = line.length;
}
}
else {
line += subText.Word;
}
}
else {
line += subText.Word;
splicePointer = line.length;
}
}
else {
line += subText.Word;
splicePointer = line.length;
}
}
}

result += line;
if (i < texts.length - 1) {
result += '\n';
}
}
return result;
}

return Label;
});
4 changes: 2 additions & 2 deletions Source/Scene/LabelCollection.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ define([
}

function rebindAllGlyphs(labelCollection, label) {
var text = label._text;
var text = label._renderedText;
var textLength = text.length;
var glyphs = label._glyphs;
var glyphsLength = glyphs.length;
Expand Down Expand Up @@ -290,7 +290,7 @@ define([

function repositionAllGlyphs(label, resolutionScale) {
var glyphs = label._glyphs;
var text = label._text;
var text = label._renderedText;
var glyph;
var dimensions;
var lastLineWidth = 0;
Expand Down
Loading

0 comments on commit 9933716

Please sign in to comment.