Skip to content

Commit

Permalink
Use mapbox-icu-js to do arabic text shaping and bidirectional layout.
Browse files Browse the repository at this point in the history
  • Loading branch information
ChrisLoer committed Dec 9, 2016
1 parent 764f80a commit ac20a97
Show file tree
Hide file tree
Showing 3 changed files with 16 additions and 38 deletions.
3 changes: 2 additions & 1 deletion js/data/bucket/symbol_bucket.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use strict';

const Point = require('point-geometry');
const icu = require('mapbox-icu-js');
const ArrayGroup = require('../array_group');
const BufferGroup = require('../buffer_group');
const createVertexArrayType = require('../vertex_array_type');
Expand Down Expand Up @@ -142,7 +143,7 @@ class SymbolBucket {

let text;
if (hasText) {
text = resolveText(feature, layout);
text = icu.applyArabicShaping(resolveText(feature, layout));
}

let icon;
Expand Down
48 changes: 12 additions & 36 deletions js/symbol/shaping.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
'use strict';

const icu = require('mapbox-icu-js');
const scriptDetection = require('../util/script_detection');
const verticalizePunctuation = require('../util/verticalize_punctuation');

Expand Down Expand Up @@ -36,32 +37,14 @@ function Shaping(positionedGlyphs, text, top, bottom, left, right, writingMode)
this.writingMode = writingMode;
}

const newLine = 0x0a;

function breakLines(text, lineBreakPoints) {
const lines = [];
let start = 0;
for (const lineBreak of lineBreakPoints) {
lines.push(text.substring(start, lineBreak));
start = lineBreak;
}

if (start < text.length) {
lines.push(text.substring(start, text.length));
}
return lines;
}

function shapeText(text, glyphs, maxWidth, lineHeight, horizontalAlign, verticalAlign, justify, spacing, translate, verticalHeight, writingMode) {
text = text.trim();
if (writingMode === WritingMode.vertical) text = verticalizePunctuation(text);
let logicalInput = text.trim();
if (writingMode === WritingMode.vertical) logicalInput = verticalizePunctuation(logicalInput);

const positionedGlyphs = [];
const shaping = new Shaping(positionedGlyphs, text, translate[1], translate[1], translate[0], translate[0], writingMode);
const shaping = new Shaping(positionedGlyphs, logicalInput, translate[1], translate[1], translate[0], translate[0], writingMode);

const lines = (writingMode === WritingMode.horizontal && maxWidth) ?
breakLines(text, determineLineBreaks(text, spacing, maxWidth, glyphs)) :
[text];
const lines = icu.processBidirectionalText(logicalInput, determineLineBreaks(logicalInput, spacing, maxWidth, glyphs));

shapeLines(shaping, glyphs, lines, lineHeight, horizontalAlign, verticalAlign, justify, translate, writingMode, spacing, verticalHeight);

Expand All @@ -71,12 +54,8 @@ function shapeText(text, glyphs, maxWidth, lineHeight, horizontalAlign, vertical
return shaping;
}

const invisible = {
0x20: true, // space
0x200b: true // zero-width space
};

const breakable = {
0x0a: true, // newline
0x20: true, // space
0x26: true, // ampersand
0x2b: true, // plus sign
Expand All @@ -89,8 +68,6 @@ const breakable = {
0x2013: true // en dash
};

invisible[newLine] = breakable[newLine] = true;

function determineIdeographicLineWidth(logicalInput, spacing, maxWidth, glyphs) {
let totalWidth = 0;

Expand All @@ -108,6 +85,8 @@ function determineIdeographicLineWidth(logicalInput, spacing, maxWidth, glyphs)
return totalWidth / lineCount;
}

// Identify good line breaks based on maxWidth
// Forced line breaks from newlines will be applied by ICU in processBidirectionalText
function determineLineBreaks(logicalInput, spacing, maxWidth, glyphs) {
if (!maxWidth)
return [];
Expand All @@ -127,8 +106,7 @@ function determineLineBreaks(logicalInput, spacing, maxWidth, glyphs) {
const codePoint = logicalInput.charCodeAt(i);
const glyph = glyphs[codePoint];

// newlines treatment slightly different from gl-native. See: https://github.com/mapbox/mapbox-gl-native/issues/7253
if (!glyph && codePoint !== newLine)
if (!glyph)
continue;

// Ideographic characters, spaces, and word-breaking punctuation that often appear without
Expand All @@ -139,16 +117,14 @@ function determineLineBreaks(logicalInput, spacing, maxWidth, glyphs) {
lastSafeBreakX = currentX;
}

// Break at the last safe break if we're over maxWidth. Always break on newlines.
if ((currentX > maxWidth && lastSafeBreakIndex > 0) ||
codePoint === newLine) {
// Break at the last safe break if we're over maxWidth.
if (currentX > maxWidth && lastSafeBreakIndex > 0) {
lineBreakPoints.push(lastSafeBreakIndex);
currentX -= lastSafeBreakX;
lastSafeBreakX = 0;
}

if (glyph)
currentX += glyph.advance + spacing;
currentX += glyph.advance + spacing;
}

return lineBreakPoints;
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"mapbox-gl-function": "mapbox/mapbox-gl-function#41c6724e2bbd7bd1eb5991451bbf118b7d02b525",
"mapbox-gl-shaders": "mapbox/mapbox-gl-shaders#94518afb141b376e7f86e492c1b022c0159ca4a3",
"mapbox-gl-style-spec": "mapbox/mapbox-gl-style-spec#e85407a377510acb647161de6be6357ab4f606dd",
"mapbox-icu-js": "mapbox/mapbox-icu-js#b1e4ff7dd133a998fc8a5b525b73b22a1d9a8223",
"mapbox-gl-supported": "^1.2.0",
"package-json-versionify": "^1.0.2",
"pbf": "^1.3.2",
Expand Down Expand Up @@ -62,7 +63,7 @@
"in-publish": "^2.0.0",
"jsdom": "^9.4.2",
"lodash.template": "^4.4.0",
"mapbox-gl-test-suite": "mapbox/mapbox-gl-test-suite#f9a1131df12227208812802b8108304a10380509",
"mapbox-gl-test-suite": "mapbox/mapbox-gl-test-suite#d536208f72e68be3634419f2f66abea08a960b43",
"minifyify": "^7.0.1",
"npm-run-all": "^3.0.0",
"nyc": "^8.3.0",
Expand Down

0 comments on commit ac20a97

Please sign in to comment.