@mapwhit/tilerenderer 0.47.2 → 0.48.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/min/package.json +1 -1
- package/package.json +1 -1
- package/src/data/bucket/symbol_bucket.js +25 -11
- package/src/data/program_configuration.js +18 -10
- package/src/geo/transform.js +4 -2
- package/src/index.js +1 -1
- package/src/source/rtl_text_plugin.js +1 -0
- package/src/source/source_cache.js +1 -1
- package/src/source/tile.js +6 -5
- package/src/source/worker.js +1 -10
- package/src/style/style.js +4 -19
- package/src/style/style_layer/symbol_style_layer_properties.js +1 -1
- package/src/style-spec/expression/compound_expression.js +30 -16
- package/src/style-spec/expression/definitions/coercion.js +13 -0
- package/src/style-spec/expression/definitions/comparison.js +193 -0
- package/src/style-spec/expression/definitions/formatted.js +123 -0
- package/src/style-spec/expression/definitions/index.js +10 -60
- package/src/style-spec/expression/definitions/interpolate.js +17 -7
- package/src/style-spec/expression/definitions/literal.js +5 -0
- package/src/style-spec/expression/parsing_context.js +4 -0
- package/src/style-spec/expression/types.js +12 -1
- package/src/style-spec/feature_filter/index.js +1 -1
- package/src/style-spec/reference/v8.json +118 -47
- package/src/symbol/get_anchors.js +11 -22
- package/src/symbol/mergelines.js +4 -1
- package/src/symbol/placement.js +8 -2
- package/src/symbol/quads.js +7 -6
- package/src/symbol/shaping.js +185 -40
- package/src/symbol/symbol_layout.js +9 -6
- package/src/symbol/transform_text.js +12 -1
- package/src/ui/map.js +8 -25
- package/src/style-spec/expression/definitions/equals.js +0 -93
package/src/symbol/shaping.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const { charHasUprightVerticalOrientation, charAllowsIdeographicBreaking } = require('../util/script_detection');
|
|
2
2
|
const verticalizePunctuation = require('../util/verticalize_punctuation');
|
|
3
3
|
const { plugin: rtlTextPlugin } = require('../source/rtl_text_plugin');
|
|
4
|
+
const { Formatted } = require('../style-spec/expression/definitions/formatted');
|
|
4
5
|
|
|
5
6
|
const WritingMode = {
|
|
6
7
|
horizontal: 1,
|
|
@@ -14,20 +15,94 @@ module.exports = {
|
|
|
14
15
|
WritingMode
|
|
15
16
|
};
|
|
16
17
|
|
|
17
|
-
|
|
18
|
+
class TaggedString {
|
|
19
|
+
constructor() {
|
|
20
|
+
this.text = '';
|
|
21
|
+
this.sectionIndex = []; // maps each character in 'text' to its corresponding entry in 'sections'
|
|
22
|
+
this.sections = [];
|
|
23
|
+
}
|
|
18
24
|
|
|
19
|
-
|
|
25
|
+
static fromFeature(text, defaultFontStack) {
|
|
26
|
+
const result = new TaggedString();
|
|
27
|
+
if (text instanceof Formatted) {
|
|
28
|
+
for (let i = 0; i < text.sections.length; i++) {
|
|
29
|
+
const section = text.sections[i];
|
|
30
|
+
result.sections.push({
|
|
31
|
+
scale: section.scale || 1,
|
|
32
|
+
fontStack: section.fontStack || defaultFontStack
|
|
33
|
+
});
|
|
34
|
+
result.text += section.text;
|
|
35
|
+
for (let j = 0; j < section.text.length; j++) {
|
|
36
|
+
result.sectionIndex.push(i);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
} else {
|
|
40
|
+
result.text = text;
|
|
41
|
+
result.sections.push({ scale: 1, fontStack: defaultFontStack });
|
|
42
|
+
for (let i = 0; i < text.length; i++) {
|
|
43
|
+
result.sectionIndex.push(0);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return result;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
length() {
|
|
50
|
+
return this.text.length;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
getSection(index) {
|
|
54
|
+
return this.sections[this.sectionIndex[index]];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
getCharCode(index) {
|
|
58
|
+
return this.text.charCodeAt(index);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
verticalizePunctuation() {
|
|
62
|
+
this.text = verticalizePunctuation(this.text);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
trim() {
|
|
66
|
+
let beginningWhitespace = 0;
|
|
67
|
+
for (let i = 0; i < this.text.length && whitespace[this.text.charCodeAt(i)]; i++) {
|
|
68
|
+
beginningWhitespace++;
|
|
69
|
+
}
|
|
70
|
+
let trailingWhitespace = this.text.length;
|
|
71
|
+
for (let i = this.text.length - 1; i >= 0 && i >= beginningWhitespace && whitespace[this.text.charCodeAt(i)]; i--) {
|
|
72
|
+
trailingWhitespace--;
|
|
73
|
+
}
|
|
74
|
+
this.text = this.text.substring(beginningWhitespace, trailingWhitespace);
|
|
75
|
+
this.sectionIndex = this.sectionIndex.slice(beginningWhitespace, trailingWhitespace);
|
|
76
|
+
}
|
|
20
77
|
|
|
21
|
-
|
|
78
|
+
substring(start, end) {
|
|
79
|
+
const substring = new TaggedString();
|
|
80
|
+
substring.text = this.text.substring(start, end);
|
|
81
|
+
substring.sectionIndex = this.sectionIndex.slice(start, end);
|
|
82
|
+
substring.sections = this.sections;
|
|
83
|
+
return substring;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
toString() {
|
|
87
|
+
return this.text;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
getMaxScale() {
|
|
91
|
+
return this.sectionIndex.reduce((max, index) => Math.max(max, this.sections[index].scale), 0);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function breakLines(input, lineBreakPoints) {
|
|
22
96
|
const lines = [];
|
|
97
|
+
const text = input.text;
|
|
23
98
|
let start = 0;
|
|
24
99
|
for (const lineBreak of lineBreakPoints) {
|
|
25
|
-
lines.push(
|
|
100
|
+
lines.push(input.substring(start, lineBreak));
|
|
26
101
|
start = lineBreak;
|
|
27
102
|
}
|
|
28
103
|
|
|
29
104
|
if (start < text.length) {
|
|
30
|
-
lines.push(
|
|
105
|
+
lines.push(input.substring(start, text.length));
|
|
31
106
|
}
|
|
32
107
|
return lines;
|
|
33
108
|
}
|
|
@@ -35,6 +110,7 @@ function breakLines(text, lineBreakPoints) {
|
|
|
35
110
|
function shapeText(
|
|
36
111
|
text,
|
|
37
112
|
glyphs,
|
|
113
|
+
defaultFontStack,
|
|
38
114
|
maxWidth,
|
|
39
115
|
lineHeight,
|
|
40
116
|
textAnchor,
|
|
@@ -44,9 +120,9 @@ function shapeText(
|
|
|
44
120
|
verticalHeight,
|
|
45
121
|
writingMode
|
|
46
122
|
) {
|
|
47
|
-
|
|
123
|
+
const logicalInput = TaggedString.fromFeature(text, defaultFontStack);
|
|
48
124
|
if (writingMode === WritingMode.vertical) {
|
|
49
|
-
logicalInput
|
|
125
|
+
logicalInput.verticalizePunctuation();
|
|
50
126
|
}
|
|
51
127
|
|
|
52
128
|
const positionedGlyphs = [];
|
|
@@ -62,9 +138,39 @@ function shapeText(
|
|
|
62
138
|
|
|
63
139
|
let lines;
|
|
64
140
|
|
|
65
|
-
const { processBidirectionalText } = rtlTextPlugin;
|
|
66
|
-
if (processBidirectionalText) {
|
|
67
|
-
|
|
141
|
+
const { processBidirectionalText, processStyledBidirectionalText } = rtlTextPlugin;
|
|
142
|
+
if (processBidirectionalText && logicalInput.sections.length === 1) {
|
|
143
|
+
// Bidi doesn't have to be style-aware
|
|
144
|
+
lines = [];
|
|
145
|
+
const untaggedLines = processBidirectionalText(
|
|
146
|
+
logicalInput.toString(),
|
|
147
|
+
determineLineBreaks(logicalInput, spacing, maxWidth, glyphs)
|
|
148
|
+
);
|
|
149
|
+
for (const line of untaggedLines) {
|
|
150
|
+
const taggedLine = new TaggedString();
|
|
151
|
+
taggedLine.text = line;
|
|
152
|
+
taggedLine.sections = logicalInput.sections;
|
|
153
|
+
for (let i = 0; i < line.length; i++) {
|
|
154
|
+
taggedLine.sectionIndex.push(0);
|
|
155
|
+
}
|
|
156
|
+
lines.push(taggedLine);
|
|
157
|
+
}
|
|
158
|
+
} else if (processStyledBidirectionalText) {
|
|
159
|
+
// Need version of mapbox-gl-rtl-text with style support for combining RTL text
|
|
160
|
+
// with formatting
|
|
161
|
+
lines = [];
|
|
162
|
+
const processedLines = processStyledBidirectionalText(
|
|
163
|
+
logicalInput.text,
|
|
164
|
+
logicalInput.sectionIndex,
|
|
165
|
+
determineLineBreaks(logicalInput, spacing, maxWidth, glyphs)
|
|
166
|
+
);
|
|
167
|
+
for (const line of processedLines) {
|
|
168
|
+
const taggedLine = new TaggedString();
|
|
169
|
+
taggedLine.text = line[0];
|
|
170
|
+
taggedLine.sectionIndex = line[1];
|
|
171
|
+
taggedLine.sections = logicalInput.sections;
|
|
172
|
+
lines.push(taggedLine);
|
|
173
|
+
}
|
|
68
174
|
} else {
|
|
69
175
|
lines = breakLines(logicalInput, determineLineBreaks(logicalInput, spacing, maxWidth, glyphs));
|
|
70
176
|
}
|
|
@@ -73,6 +179,7 @@ function shapeText(
|
|
|
73
179
|
|
|
74
180
|
if (!positionedGlyphs.length) return false;
|
|
75
181
|
|
|
182
|
+
shaping.text = shaping.text.toString();
|
|
76
183
|
return shaping;
|
|
77
184
|
}
|
|
78
185
|
|
|
@@ -105,13 +212,15 @@ const breakable = {
|
|
|
105
212
|
// See https://github.com/mapbox/mapbox-gl-js/issues/3658
|
|
106
213
|
};
|
|
107
214
|
|
|
108
|
-
function determineAverageLineWidth(logicalInput, spacing, maxWidth,
|
|
215
|
+
function determineAverageLineWidth(logicalInput, spacing, maxWidth, glyphMap) {
|
|
109
216
|
let totalWidth = 0;
|
|
110
217
|
|
|
111
|
-
for (let index = 0; index < logicalInput.length; index++) {
|
|
112
|
-
const
|
|
218
|
+
for (let index = 0; index < logicalInput.length(); index++) {
|
|
219
|
+
const section = logicalInput.getSection(index);
|
|
220
|
+
const positions = glyphMap[section.fontStack];
|
|
221
|
+
const glyph = positions?.[logicalInput.getCharCode(index)];
|
|
113
222
|
if (!glyph) continue;
|
|
114
|
-
totalWidth += glyph.metrics.advance + spacing;
|
|
223
|
+
totalWidth += glyph.metrics.advance * section.scale + spacing;
|
|
115
224
|
}
|
|
116
225
|
|
|
117
226
|
const lineCount = Math.max(1, Math.ceil(totalWidth / maxWidth));
|
|
@@ -182,39 +291,41 @@ function leastBadBreaks(lastLineBreak) {
|
|
|
182
291
|
return leastBadBreaks(lastLineBreak.priorBreak).concat(lastLineBreak.index);
|
|
183
292
|
}
|
|
184
293
|
|
|
185
|
-
function determineLineBreaks(logicalInput, spacing, maxWidth,
|
|
294
|
+
function determineLineBreaks(logicalInput, spacing, maxWidth, glyphMap) {
|
|
186
295
|
if (!maxWidth) return [];
|
|
187
296
|
|
|
188
297
|
if (!logicalInput) return [];
|
|
189
298
|
|
|
190
299
|
const potentialLineBreaks = [];
|
|
191
|
-
const targetWidth = determineAverageLineWidth(logicalInput, spacing, maxWidth,
|
|
300
|
+
const targetWidth = determineAverageLineWidth(logicalInput, spacing, maxWidth, glyphMap);
|
|
192
301
|
|
|
193
302
|
let currentX = 0;
|
|
194
303
|
|
|
195
|
-
for (let i = 0; i < logicalInput.length; i++) {
|
|
196
|
-
const
|
|
197
|
-
const
|
|
304
|
+
for (let i = 0; i < logicalInput.length(); i++) {
|
|
305
|
+
const section = logicalInput.getSection(i);
|
|
306
|
+
const codePoint = logicalInput.getCharCode(i);
|
|
307
|
+
const positions = glyphMap[section.fontStack];
|
|
308
|
+
const glyph = positions?.[codePoint];
|
|
198
309
|
|
|
199
|
-
if (glyph && !whitespace[codePoint]) currentX += glyph.metrics.advance + spacing;
|
|
310
|
+
if (glyph && !whitespace[codePoint]) currentX += glyph.metrics.advance * section.scale + spacing;
|
|
200
311
|
|
|
201
312
|
// Ideographic characters, spaces, and word-breaking punctuation that often appear without
|
|
202
313
|
// surrounding spaces.
|
|
203
|
-
if (i < logicalInput.length - 1 && (breakable[codePoint] || charAllowsIdeographicBreaking(codePoint))) {
|
|
314
|
+
if (i < logicalInput.length() - 1 && (breakable[codePoint] || charAllowsIdeographicBreaking(codePoint))) {
|
|
204
315
|
potentialLineBreaks.push(
|
|
205
316
|
evaluateBreak(
|
|
206
317
|
i + 1,
|
|
207
318
|
currentX,
|
|
208
319
|
targetWidth,
|
|
209
320
|
potentialLineBreaks,
|
|
210
|
-
calculatePenalty(codePoint, logicalInput.
|
|
321
|
+
calculatePenalty(codePoint, logicalInput.getCharCode(i + 1)),
|
|
211
322
|
false
|
|
212
323
|
)
|
|
213
324
|
);
|
|
214
325
|
}
|
|
215
326
|
}
|
|
216
327
|
|
|
217
|
-
return leastBadBreaks(evaluateBreak(logicalInput.length, currentX, targetWidth, potentialLineBreaks, 0, true));
|
|
328
|
+
return leastBadBreaks(evaluateBreak(logicalInput.length(), currentX, targetWidth, potentialLineBreaks, 0, true));
|
|
218
329
|
}
|
|
219
330
|
|
|
220
331
|
function getAnchorAlignment(anchor) {
|
|
@@ -250,7 +361,17 @@ function getAnchorAlignment(anchor) {
|
|
|
250
361
|
return { horizontalAlign, verticalAlign };
|
|
251
362
|
}
|
|
252
363
|
|
|
253
|
-
function shapeLines(
|
|
364
|
+
function shapeLines(
|
|
365
|
+
shaping,
|
|
366
|
+
glyphMap,
|
|
367
|
+
lines,
|
|
368
|
+
lineHeight,
|
|
369
|
+
textAnchor,
|
|
370
|
+
textJustify,
|
|
371
|
+
writingMode,
|
|
372
|
+
spacing,
|
|
373
|
+
verticalHeight
|
|
374
|
+
) {
|
|
254
375
|
// the y offset *should* be part of the font metadata
|
|
255
376
|
const yOffset = -17;
|
|
256
377
|
|
|
@@ -262,27 +383,49 @@ function shapeLines(shaping, glyphs, lines, lineHeight, textAnchor, textJustify,
|
|
|
262
383
|
|
|
263
384
|
const justify = textJustify === 'right' ? 1 : textJustify === 'left' ? 0 : 0.5;
|
|
264
385
|
|
|
265
|
-
for (
|
|
266
|
-
line
|
|
386
|
+
for (const line of lines) {
|
|
387
|
+
line.trim();
|
|
388
|
+
|
|
389
|
+
const lineMaxScale = line.getMaxScale();
|
|
267
390
|
|
|
268
|
-
if (!line.length) {
|
|
391
|
+
if (!line.length()) {
|
|
269
392
|
y += lineHeight; // Still need a line feed after empty line
|
|
270
393
|
continue;
|
|
271
394
|
}
|
|
272
395
|
|
|
273
396
|
const lineStartIndex = positionedGlyphs.length;
|
|
274
|
-
for (let i = 0; i < line.length; i++) {
|
|
275
|
-
const
|
|
276
|
-
const
|
|
397
|
+
for (let i = 0; i < line.length(); i++) {
|
|
398
|
+
const section = line.getSection(i);
|
|
399
|
+
const codePoint = line.getCharCode(i);
|
|
400
|
+
// We don't know the baseline, but since we're laying out
|
|
401
|
+
// at 24 points, we can calculate how much it will move when
|
|
402
|
+
// we scale up or down.
|
|
403
|
+
const baselineOffset = (lineMaxScale - section.scale) * 24;
|
|
404
|
+
const positions = glyphMap[section.fontStack];
|
|
405
|
+
const glyph = positions?.[codePoint];
|
|
277
406
|
|
|
278
407
|
if (!glyph) continue;
|
|
279
408
|
|
|
280
409
|
if (!charHasUprightVerticalOrientation(codePoint) || writingMode === WritingMode.horizontal) {
|
|
281
|
-
positionedGlyphs.push({
|
|
282
|
-
|
|
410
|
+
positionedGlyphs.push({
|
|
411
|
+
glyph: codePoint,
|
|
412
|
+
x,
|
|
413
|
+
y: y + baselineOffset,
|
|
414
|
+
vertical: false,
|
|
415
|
+
scale: section.scale,
|
|
416
|
+
fontStack: section.fontStack
|
|
417
|
+
});
|
|
418
|
+
x += glyph.metrics.advance * section.scale + spacing;
|
|
283
419
|
} else {
|
|
284
|
-
positionedGlyphs.push({
|
|
285
|
-
|
|
420
|
+
positionedGlyphs.push({
|
|
421
|
+
glyph: codePoint,
|
|
422
|
+
x,
|
|
423
|
+
y: baselineOffset,
|
|
424
|
+
vertical: true,
|
|
425
|
+
scale: section.scale,
|
|
426
|
+
fontStack: section.fontStack
|
|
427
|
+
});
|
|
428
|
+
x += verticalHeight * section.scale + spacing;
|
|
286
429
|
}
|
|
287
430
|
}
|
|
288
431
|
|
|
@@ -291,18 +434,18 @@ function shapeLines(shaping, glyphs, lines, lineHeight, textAnchor, textJustify,
|
|
|
291
434
|
const lineLength = x - spacing;
|
|
292
435
|
maxLineLength = Math.max(lineLength, maxLineLength);
|
|
293
436
|
|
|
294
|
-
justifyLine(positionedGlyphs,
|
|
437
|
+
justifyLine(positionedGlyphs, glyphMap, lineStartIndex, positionedGlyphs.length - 1, justify);
|
|
295
438
|
}
|
|
296
439
|
|
|
297
440
|
x = 0;
|
|
298
|
-
y += lineHeight;
|
|
441
|
+
y += lineHeight * lineMaxScale;
|
|
299
442
|
}
|
|
300
443
|
|
|
301
444
|
const { horizontalAlign, verticalAlign } = getAnchorAlignment(textAnchor);
|
|
302
445
|
align(positionedGlyphs, justify, horizontalAlign, verticalAlign, maxLineLength, lineHeight, lines.length);
|
|
303
446
|
|
|
304
447
|
// Calculate the bounding box
|
|
305
|
-
const height =
|
|
448
|
+
const height = y - yOffset;
|
|
306
449
|
|
|
307
450
|
shaping.top += -verticalAlign * height;
|
|
308
451
|
shaping.bottom = shaping.top + height;
|
|
@@ -311,12 +454,14 @@ function shapeLines(shaping, glyphs, lines, lineHeight, textAnchor, textJustify,
|
|
|
311
454
|
}
|
|
312
455
|
|
|
313
456
|
// justify right = 1, left = 0, center = 0.5
|
|
314
|
-
function justifyLine(positionedGlyphs,
|
|
457
|
+
function justifyLine(positionedGlyphs, glyphMap, start, end, justify) {
|
|
315
458
|
if (!justify) return;
|
|
316
459
|
|
|
317
|
-
const
|
|
460
|
+
const lastPositionedGlyph = positionedGlyphs[end];
|
|
461
|
+
const positions = glyphMap[lastPositionedGlyph.fontStack];
|
|
462
|
+
const glyph = positions?.[lastPositionedGlyph.glyph];
|
|
318
463
|
if (glyph) {
|
|
319
|
-
const lastAdvance = glyph.metrics.advance;
|
|
464
|
+
const lastAdvance = glyph.metrics.advance * lastPositionedGlyph.scale;
|
|
320
465
|
const lineIndent = (positionedGlyphs[end].x + lastAdvance) * justify;
|
|
321
466
|
|
|
322
467
|
for (let j = start; j <= end; j++) {
|
|
@@ -12,6 +12,7 @@ const classifyRings = require('../util/classify_rings');
|
|
|
12
12
|
const EXTENT = require('../data/extent');
|
|
13
13
|
const SymbolBucket = require('../data/bucket/symbol_bucket');
|
|
14
14
|
const EvaluationParameters = require('../style/evaluation_parameters');
|
|
15
|
+
const { Formatted } = require('../style-spec/expression/definitions/formatted');
|
|
15
16
|
|
|
16
17
|
// The symbol layout process needs `text-size` evaluated at up to five different zoom levels, and
|
|
17
18
|
// `icon-size` at up to three:
|
|
@@ -73,18 +74,18 @@ function performSymbolLayout(bucket, glyphMap, glyphPositions, imageMap, imagePo
|
|
|
73
74
|
|
|
74
75
|
for (const feature of bucket.features) {
|
|
75
76
|
const fontstack = layout.get('text-font').evaluate(feature, {}).join(',');
|
|
76
|
-
const
|
|
77
|
-
const glyphPositionMap = glyphPositions[fontstack] || {};
|
|
77
|
+
const glyphPositionMap = glyphPositions;
|
|
78
78
|
|
|
79
79
|
const shapedTextOrientations = {};
|
|
80
80
|
const text = feature.text;
|
|
81
81
|
if (text) {
|
|
82
|
+
const unformattedText = text instanceof Formatted ? text.toString() : text;
|
|
82
83
|
const textOffset = layout
|
|
83
84
|
.get('text-offset')
|
|
84
85
|
.evaluate(feature, {})
|
|
85
86
|
.map(t => t * oneEm);
|
|
86
87
|
const spacing = layout.get('text-letter-spacing').evaluate(feature, {}) * oneEm;
|
|
87
|
-
const spacingIfAllowed = allowsLetterSpacing(
|
|
88
|
+
const spacingIfAllowed = allowsLetterSpacing(unformattedText) ? spacing : 0;
|
|
88
89
|
const textAnchor = layout.get('text-anchor').evaluate(feature, {});
|
|
89
90
|
const textJustify = layout.get('text-justify').evaluate(feature, {});
|
|
90
91
|
const maxWidth =
|
|
@@ -92,7 +93,8 @@ function performSymbolLayout(bucket, glyphMap, glyphPositions, imageMap, imagePo
|
|
|
92
93
|
|
|
93
94
|
shapedTextOrientations.horizontal = shapeText(
|
|
94
95
|
text,
|
|
95
|
-
|
|
96
|
+
glyphMap,
|
|
97
|
+
fontstack,
|
|
96
98
|
maxWidth,
|
|
97
99
|
lineHeight,
|
|
98
100
|
textAnchor,
|
|
@@ -102,10 +104,11 @@ function performSymbolLayout(bucket, glyphMap, glyphPositions, imageMap, imagePo
|
|
|
102
104
|
oneEm,
|
|
103
105
|
WritingMode.horizontal
|
|
104
106
|
);
|
|
105
|
-
if (allowsVerticalWritingMode(
|
|
107
|
+
if (allowsVerticalWritingMode(unformattedText) && textAlongLine && keepUpright) {
|
|
106
108
|
shapedTextOrientations.vertical = shapeText(
|
|
107
109
|
text,
|
|
108
|
-
|
|
110
|
+
glyphMap,
|
|
111
|
+
fontstack,
|
|
109
112
|
maxWidth,
|
|
110
113
|
lineHeight,
|
|
111
114
|
textAnchor,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const { plugin: rtlTextPlugin } = require('../source/rtl_text_plugin');
|
|
2
|
+
const { Formatted } = require('../style-spec/expression/definitions/formatted');
|
|
2
3
|
|
|
3
|
-
|
|
4
|
+
function transformText(text, layer, feature) {
|
|
4
5
|
const transform = layer.layout.get('text-transform').evaluate(feature, {});
|
|
5
6
|
if (transform === 'uppercase') {
|
|
6
7
|
text = text.toLocaleUpperCase();
|
|
@@ -13,4 +14,14 @@ module.exports = function (text, layer, feature) {
|
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
return text;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
module.exports = function (text, layer, feature) {
|
|
20
|
+
if (text instanceof Formatted) {
|
|
21
|
+
text.sections.forEach(section => {
|
|
22
|
+
section.text = transformText(section.text, layer, feature);
|
|
23
|
+
});
|
|
24
|
+
return text;
|
|
25
|
+
}
|
|
26
|
+
return transformText(text, layer, feature);
|
|
16
27
|
};
|
package/src/ui/map.js
CHANGED
|
@@ -289,22 +289,17 @@ class Map extends Camera {
|
|
|
289
289
|
}
|
|
290
290
|
|
|
291
291
|
/**
|
|
292
|
-
* Returns the map's geographical bounds.
|
|
292
|
+
* Returns the map's geographical bounds. When the bearing or pitch is non-zero, the visible region is not
|
|
293
|
+
* an axis-aligned rectangle, and the result is the smallest bounds that encompasses the visible region.
|
|
293
294
|
*
|
|
294
|
-
* @returns {LngLatBounds}
|
|
295
|
+
* @returns {LngLatBounds}
|
|
295
296
|
*/
|
|
296
297
|
getBounds() {
|
|
297
|
-
|
|
298
|
-
this.transform.pointLocation(new Point(0,
|
|
299
|
-
this.transform.pointLocation(new Point(this.transform.width, 0))
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
if (this.transform.angle || this.transform.pitch) {
|
|
303
|
-
bounds.extend(this.transform.pointLocation(new Point(this.transform.size.x, 0)));
|
|
304
|
-
bounds.extend(this.transform.pointLocation(new Point(0, this.transform.size.y)));
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
return bounds;
|
|
298
|
+
return new LngLatBounds()
|
|
299
|
+
.extend(this.transform.pointLocation(new Point(0, 0)))
|
|
300
|
+
.extend(this.transform.pointLocation(new Point(this.transform.width, 0)))
|
|
301
|
+
.extend(this.transform.pointLocation(new Point(this.transform.width, this.transform.height)))
|
|
302
|
+
.extend(this.transform.pointLocation(new Point(0, this.transform.height)));
|
|
308
303
|
}
|
|
309
304
|
|
|
310
305
|
/**
|
|
@@ -772,18 +767,6 @@ class Map extends Camera {
|
|
|
772
767
|
return true;
|
|
773
768
|
}
|
|
774
769
|
|
|
775
|
-
/**
|
|
776
|
-
* Adds a [custom source type](#Custom Sources), making it available for use with
|
|
777
|
-
* {@link Map#addSource}.
|
|
778
|
-
* @private
|
|
779
|
-
* @param {string} name The name of the source type; source definition objects use this name in the `{type: ...}` field.
|
|
780
|
-
* @param {Function} SourceType A {@link Source} constructor.
|
|
781
|
-
* @param {Function} callback Called when the source type is ready or with an error argument if there is an error.
|
|
782
|
-
*/
|
|
783
|
-
addSourceType(name, SourceType, callback) {
|
|
784
|
-
return this.style.addSourceType(name, SourceType, callback);
|
|
785
|
-
}
|
|
786
|
-
|
|
787
770
|
/**
|
|
788
771
|
* Removes a source from the map's style.
|
|
789
772
|
*
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
const { toString, ValueType, BooleanType, CollatorType } = require('../types');
|
|
2
|
-
|
|
3
|
-
function isComparableType(type) {
|
|
4
|
-
return type.kind === 'string' || type.kind === 'number' || type.kind === 'boolean' || type.kind === 'null';
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Special form for ==, !=, implementing the following signatures:
|
|
9
|
-
* - (T1: Comparable, T2: Comparable) => boolean { T1 == T2 }
|
|
10
|
-
* - (Comparable, value) => boolean
|
|
11
|
-
* - (value, Comparable) => boolean
|
|
12
|
-
*
|
|
13
|
-
* Where Comparable = string | number | boolean | null.
|
|
14
|
-
*
|
|
15
|
-
* Evaluation semantics for the value cases are equivalent to Javascript's
|
|
16
|
-
* strict equality (===/!==) -- i.e., when the value argument's type doesn't
|
|
17
|
-
* match that of the Comparable argument, == evaluates to false, != to true.
|
|
18
|
-
*
|
|
19
|
-
* @private
|
|
20
|
-
*/
|
|
21
|
-
function makeComparison(op, negate) {
|
|
22
|
-
return class Comparison {
|
|
23
|
-
constructor(lhs, rhs, collator) {
|
|
24
|
-
this.type = BooleanType;
|
|
25
|
-
this.lhs = lhs;
|
|
26
|
-
this.rhs = rhs;
|
|
27
|
-
this.collator = collator;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
static parse(args, context) {
|
|
31
|
-
if (args.length !== 3 && args.length !== 4) return context.error('Expected two or three arguments.');
|
|
32
|
-
|
|
33
|
-
const lhs = context.parse(args[1], 1, ValueType);
|
|
34
|
-
if (!lhs) return null;
|
|
35
|
-
const rhs = context.parse(args[2], 2, ValueType);
|
|
36
|
-
if (!rhs) return null;
|
|
37
|
-
|
|
38
|
-
if (!isComparableType(lhs.type) && !isComparableType(rhs.type)) {
|
|
39
|
-
return context.error(
|
|
40
|
-
`Expected at least one argument to be a string, number, boolean, or null, but found (${toString(lhs.type)}, ${toString(rhs.type)}) instead.`
|
|
41
|
-
);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
if (lhs.type.kind !== rhs.type.kind && lhs.type.kind !== 'value' && rhs.type.kind !== 'value') {
|
|
45
|
-
return context.error(`Cannot compare ${toString(lhs.type)} and ${toString(rhs.type)}.`);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
let collator = null;
|
|
49
|
-
if (args.length === 4) {
|
|
50
|
-
if (lhs.type.kind !== 'string' && rhs.type.kind !== 'string') {
|
|
51
|
-
return context.error('Cannot use collator to compare non-string types.');
|
|
52
|
-
}
|
|
53
|
-
collator = context.parse(args[3], 3, CollatorType);
|
|
54
|
-
if (!collator) return null;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
return new Comparison(lhs, rhs, collator);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
evaluate(ctx) {
|
|
61
|
-
const equal = this.collator
|
|
62
|
-
? this.collator.evaluate(ctx).compare(this.lhs.evaluate(ctx), this.rhs.evaluate(ctx)) === 0
|
|
63
|
-
: this.lhs.evaluate(ctx) === this.rhs.evaluate(ctx);
|
|
64
|
-
|
|
65
|
-
return negate ? !equal : equal;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
eachChild(fn) {
|
|
69
|
-
fn(this.lhs);
|
|
70
|
-
fn(this.rhs);
|
|
71
|
-
if (this.collator) {
|
|
72
|
-
fn(this.collator);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
possibleOutputs() {
|
|
77
|
-
return [true, false];
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
serialize() {
|
|
81
|
-
const serialized = [op];
|
|
82
|
-
this.eachChild(child => {
|
|
83
|
-
serialized.push(child.serialize());
|
|
84
|
-
});
|
|
85
|
-
return serialized;
|
|
86
|
-
}
|
|
87
|
-
};
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
module.exports = {
|
|
91
|
-
Equals: makeComparison('==', false),
|
|
92
|
-
NotEquals: makeComparison('!=', true)
|
|
93
|
-
};
|