Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rtl labels support #5771

Merged
merged 60 commits into from
Oct 25, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
60da6aa
support rtl characters (currently, only hebrew) at label to show in c…
hodbauer May 4, 2017
fc812bc
add example of supporting rtl characters to sandcastle
hodbauer May 4, 2017
8762607
add unit tests to check rtl (unfortunately, not working yet)
hodbauer May 4, 2017
64fd8d2
RTL labels now work in constructor
YonatanKra Aug 22, 2017
e3c601f
Merge remote-tracking branch 'AnalyticalGraphicsInc/master'
YonatanKra Aug 22, 2017
db98c03
Merge branch 'master' into rtl-labels-support
YonatanKra Aug 22, 2017
5826c7e
Upadated Changes.md
YonatanKra Aug 22, 2017
775e637
Updated contributors
YonatanKra Aug 23, 2017
f09b883
Support for \n in RTL
YonatanKra Aug 23, 2017
70c3ab6
Cache RTL original string
YonatanKra Aug 23, 2017
8d18aa3
Prevent glyphs parser from running for nought
YonatanKra Aug 23, 2017
52dc64f
Documentation examples
YonatanKra Aug 23, 2017
8350668
Updated contributors list
YonatanKra Aug 23, 2017
a0f0c1a
* fix descriptions
hodbauer Sep 3, 2017
41cea83
support rtl characters (currently, only hebrew) at label to show in c…
hodbauer May 4, 2017
84354e4
add example of supporting rtl characters to sandcastle
hodbauer May 4, 2017
c22adcf
add unit tests to check rtl (unfortunately, not working yet)
hodbauer May 4, 2017
f91e96d
Merge branch 'master' into rtl-labels-support
hodbauer Sep 3, 2017
281d4d3
support rtl characters (currently, only hebrew) at label to show in c…
hodbauer May 4, 2017
c1adde1
add example of supporting rtl characters to sandcastle
hodbauer May 4, 2017
6d65918
add unit tests to check rtl (unfortunately, not working yet)
hodbauer May 4, 2017
5f304a8
RTL labels now work in constructor
YonatanKra Aug 22, 2017
304e774
Updated contributors
YonatanKra Aug 23, 2017
f7dfcaa
Support for \n in RTL
YonatanKra Aug 23, 2017
10b05d2
Cache RTL original string
YonatanKra Aug 23, 2017
8878a7b
Prevent glyphs parser from running for nought
YonatanKra Aug 23, 2017
12fe81d
Documentation examples
YonatanKra Aug 23, 2017
15a3c9c
Updated contributors list
YonatanKra Aug 23, 2017
9ab1704
* fix descriptions
hodbauer Sep 3, 2017
04538ba
rename rtl to rightToLeft
hodbauer Sep 11, 2017
eb9e155
Merge remote-tracking branch 'origin/rtl-labels-support' into rtl-lab…
hodbauer Sep 11, 2017
97c5c36
support rtl characters (currently, only hebrew) at label to show in c…
hodbauer May 4, 2017
7245d72
add example of supporting rtl characters to sandcastle
hodbauer May 4, 2017
4a69abf
add unit tests to check rtl (unfortunately, not working yet)
hodbauer May 4, 2017
cfb6419
RTL labels now work in constructor
YonatanKra Aug 22, 2017
ea5ceeb
Updated contributors
YonatanKra Aug 23, 2017
c098454
Support for \n in RTL
YonatanKra Aug 23, 2017
d3df024
Cache RTL original string
YonatanKra Aug 23, 2017
7068841
Prevent glyphs parser from running for nought
YonatanKra Aug 23, 2017
bdb582e
Documentation examples
YonatanKra Aug 23, 2017
99010f2
Updated contributors list
YonatanKra Aug 23, 2017
ab9ac19
* fix descriptions
hodbauer Sep 3, 2017
84e5d95
RTL labels now work in constructor
YonatanKra Aug 22, 2017
eec1e4d
rename rtl to rightToLeft
hodbauer Sep 11, 2017
b3971a9
add link to PR at CHANGES.md
hodbauer Sep 17, 2017
69a49e8
Merge remote-tracking branch 'origin/rtl-labels-support' into rtl-lab…
hodbauer Sep 17, 2017
bcc762c
Merge branch 'master' into rtl-labels-support
hodbauer Sep 28, 2017
7a0c0e3
Merge branch 'master' into rtl-labels-support
hodbauer Oct 2, 2017
3432f03
Merge branch 'master' into rtl-labels-support
hodbauer Oct 15, 2017
df1ddea
* add more tests for better code coverage
hodbauer Oct 16, 2017
80b370b
Merge remote-tracking branch 'origin/rtl-labels-support' into rtl-lab…
hodbauer Oct 16, 2017
0020337
Merge branch 'master' into rtl-labels-support
hodbauer Oct 19, 2017
abc7b9e
Merge branch 'master' into rtl-labels-support
mramato Oct 20, 2017
fac9512
Merge remote-tracking branch 'origin/master' into rtl-labels-support
hodbauer Oct 21, 2017
11b485e
Merge remote-tracking branch 'origin/master' into rtl-labels-support
hodbauer Oct 23, 2017
32c5657
declare textTypes of rightToLeft mechanism as object and not a function
hodbauer Oct 23, 2017
118abe4
manage rightToLeft as a static property of Label
hodbauer Oct 23, 2017
e109e98
fix tests by set text property also in the Label constructor, which c…
hodbauer Oct 24, 2017
c2aa9cc
Tweaks to right-to-left label handling.
mramato Oct 25, 2017
6bca30d
Tweak Sandcastle
mramato Oct 25, 2017
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
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:/AnalyticalGraphicsInc/cesium/pull/5771)
* Added the ability to load Cesium's assets from the local file system if security permissions allow it. [#5830](https:/AnalyticalGraphicsInc/cesium/issues/5830)
* Added function that inserts missing namespace declarations into KML files. [#5860](https:/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:/jbo023)
* [Logilab](https://www.logilab.fr/)
* [Florent Cayré](https:/fcayre/)
* [webiks](https://www.webiks.com)
* [Hod Bauer](https:/hodbauer)
* [Yonatan Kra](https:/yonatankra)
* [Novetta](http://www.novetta.com/)
* [Natanael Rivera](https:/nrivera-Novetta/)
* [Justin Burr](https:/jburr-nc/)
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]/;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So if I'm understanding this code correctly, this regex detects whether the letter is RTL (in this case specifically Hebrew) but otherwise the rest of the code is the same for other languages? So if we update this regex for other known RTL character sets (perhaps thise discussed here and here Is that correct? If so that's awesome and we can easily update this to handle the other languages.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but otherwise the rest of the code is the same for other languages?

yes. it only necessary when writing a RTL language in Cesium.

we can easily update this to handle the other languages.

basically yes, I do not do that, because i cannot read other RTL languages.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool, we can merge this with just Hebrew and then I'll open a second PR with an updated regex to support additional languages.

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