diff --git a/src/render/glyph_atlas.js b/src/render/glyph_atlas.js index bda803322df..fdea1a740e4 100644 --- a/src/render/glyph_atlas.js +++ b/src/render/glyph_atlas.js @@ -5,7 +5,7 @@ import {AlphaImage} from '../util/image.js'; import {register} from '../util/web_worker_transfer.js'; import potpack from 'potpack'; -import type {GlyphMetrics, StyleGlyph} from '../style/style_glyph.js'; +import type {StyleGlyph} from '../style/style_glyph.js'; const glyphPadding = 1; /* @@ -17,34 +17,31 @@ const glyphPadding = 1; */ const localGlyphPadding = glyphPadding * SDF_SCALE; -export type Rect = { +export type GlyphRect = { x: number, y: number, w: number, h: number }; +// {glyphID: glyphRect} +export type GlyphPositionMap = { [_: number]: GlyphRect }; -export type GlyphPosition = { - rect: Rect, - metrics: GlyphMetrics -}; - -export type GlyphPositions = {[_: string]: {[_: number]: GlyphPosition } } +// {fontStack: glyphPoistionMap} +export type GlyphPositions = { [_: string]: GlyphPositionMap }; export default class GlyphAtlas { image: AlphaImage; positions: GlyphPositions; - - constructor(stacks: {[_: string]: {[_: number]: ?StyleGlyph } }) { + constructor(stacks: {[_: string]: {glyphs: {[_: number]: ?StyleGlyph }, ascender?: number, descender?: number }}) { const positions = {}; const bins = []; for (const stack in stacks) { - const glyphs = stacks[stack]; - const stackPositions = positions[stack] = {}; + const glyphData = stacks[stack]; + const glyphPositionMap = positions[stack] = {}; - for (const id in glyphs) { - const src = glyphs[+id]; + for (const id in glyphData.glyphs) { + const src = glyphData.glyphs[+id]; if (!src || src.bitmap.width === 0 || src.bitmap.height === 0) continue; const padding = src.metrics.localGlyph ? localGlyphPadding : glyphPadding; @@ -55,7 +52,7 @@ export default class GlyphAtlas { h: src.bitmap.height + 2 * padding }; bins.push(bin); - stackPositions[id] = {rect: bin, metrics: src.metrics}; + glyphPositionMap[id] = bin; } } @@ -63,12 +60,12 @@ export default class GlyphAtlas { const image = new AlphaImage({width: w || 1, height: h || 1}); for (const stack in stacks) { - const glyphs = stacks[stack]; + const glyphData = stacks[stack]; - for (const id in glyphs) { - const src = glyphs[+id]; + for (const id in glyphData.glyphs) { + const src = glyphData.glyphs[+id]; if (!src || src.bitmap.width === 0 || src.bitmap.height === 0) continue; - const bin = positions[stack][id].rect; + const bin = positions[stack][id]; const padding = src.metrics.localGlyph ? localGlyphPadding : glyphPadding; AlphaImage.copy(src.bitmap, image, {x: 0, y: 0}, {x: bin.x + padding, y: bin.y + padding}, src.bitmap); } diff --git a/src/render/glyph_manager.js b/src/render/glyph_manager.js index 8ec05f02774..ce295d59507 100644 --- a/src/render/glyph_manager.js +++ b/src/render/glyph_manager.js @@ -37,9 +37,11 @@ export const SDF_SCALE = 2; type Entry = { // null means we've requested the range, but the glyph wasn't included in the result. glyphs: {[id: number]: StyleGlyph | null}, - requests: {[range: number]: Array>}, + requests: {[range: number]: Array>}, ranges: {[range: number]: boolean | null}, - tinySDF?: TinySDF + tinySDF?: TinySDF, + ascender?: number, + descender?: number }; export const LocalGlyphMode = { @@ -55,7 +57,7 @@ class GlyphManager { entries: {[_: string]: Entry}; // Multiple fontstacks may share the same local glyphs, so keep an index // into the glyphs based soley on font weight - localGlyphs: {[_: string]: {[id: number]: StyleGlyph | null}}; + localGlyphs: {[_: string]: {glyphs: {[id: number]: StyleGlyph | null}, ascender: ?number, descender: ?number}}; url: ?string; // exposed as statics to enable stubbing in unit tests @@ -80,7 +82,7 @@ class GlyphManager { this.url = url; } - getGlyphs(glyphs: {[stack: string]: Array}, callback: Callback<{[stack: string]: {[id: number]: ?StyleGlyph}}>) { + getGlyphs(glyphs: {[stack: string]: Array}, callback: Callback<{[stack: string]: {glyphs: {[_: number]: ?StyleGlyph}, ascender?: number, descender?: number}}>) { const all = []; for (const stack in glyphs) { @@ -89,37 +91,39 @@ class GlyphManager { } } - asyncAll(all, ({stack, id}, callback: Callback<{stack: string, id: number, glyph: ?StyleGlyph}>) => { + asyncAll(all, ({stack, id}, fnCallback: Callback<{stack: string, id: number, glyph: ?StyleGlyph}>) => { let entry = this.entries[stack]; if (!entry) { entry = this.entries[stack] = { glyphs: {}, requests: {}, - ranges: {} + ranges: {}, + ascender: undefined, + descender: undefined }; } let glyph = entry.glyphs[id]; if (glyph !== undefined) { - callback(null, {stack, id, glyph}); + fnCallback(null, {stack, id, glyph}); return; } glyph = this._tinySDF(entry, stack, id); if (glyph) { entry.glyphs[id] = glyph; - callback(null, {stack, id, glyph}); + fnCallback(null, {stack, id, glyph}); return; } const range = Math.floor(id / 256); if (range * 256 > 65535) { - callback(new Error('glyphs > 65535 not supported')); + fnCallback(new Error('glyphs > 65535 not supported')); return; } if (entry.ranges[range]) { - callback(null, {stack, id, glyph}); + fnCallback(null, {stack, id, glyph}); return; } @@ -127,11 +131,13 @@ class GlyphManager { if (!requests) { requests = entry.requests[range] = []; GlyphManager.loadGlyphRange(stack, range, (this.url: any), this.requestManager, - (err, response: ?{[_: number]: StyleGlyph | null}) => { + (err, response: ?{glyphs: {[_: number]: StyleGlyph | null}, ascender?: number, descender?: number}) => { if (response) { - for (const id in response) { + entry.ascender = response.ascender; + entry.descender = response.descender; + for (const id in response.glyphs) { if (!this._doesCharSupportLocalGlyph(+id)) { - entry.glyphs[+id] = response[+id]; + entry.glyphs[+id] = response.glyphs[+id]; } } entry.ranges[range] = true; @@ -143,11 +149,11 @@ class GlyphManager { }); } - requests.push((err, result: ?{[_: number]: StyleGlyph | null}) => { + requests.push((err, result: ?{glyphs: {[_: number]: StyleGlyph | null}, ascender?: number, descender?: number}) => { if (err) { - callback(err); + fnCallback(err); } else if (result) { - callback(null, {stack, id, glyph: result[id] || null}); + fnCallback(null, {stack, id, glyph: result.glyphs[id] || null}); } }); }, (err, glyphs: ?Array<{stack: string, id: number, glyph: ?StyleGlyph}>) => { @@ -158,11 +164,15 @@ class GlyphManager { for (const {stack, id, glyph} of glyphs) { // Clone the glyph so that our own copy of its ArrayBuffer doesn't get transferred. - (result[stack] || (result[stack] = {}))[id] = glyph && { + if (result[stack] === undefined) result[stack] = {}; + if (result[stack].glyphs === undefined) result[stack].glyphs = {}; + result[stack].glyphs[id] = glyph && { id: glyph.id, bitmap: glyph.bitmap.clone(), metrics: glyph.metrics }; + result[stack].ascender = this.entries[stack].ascender; + result[stack].descender = this.entries[stack].descender; } callback(null, result); @@ -181,7 +191,9 @@ class GlyphManager { (isChar['CJK Unified Ideographs'](id) || isChar['Hangul Syllables'](id) || isChar['Hiragana'](id) || - isChar['Katakana'](id)); + isChar['Katakana'](id)) || + // gl-native parity: Extend Ideographs rasterization range to include CJK symbols and punctuations + isChar['CJK Symbols and Punctuation'](id); /* eslint-enable new-cap */ } } diff --git a/src/source/worker_source.js b/src/source/worker_source.js index 9c33bf574db..781a181b627 100644 --- a/src/source/worker_source.js +++ b/src/source/worker_source.js @@ -59,7 +59,7 @@ export type WorkerTileResult = { rawTileData?: ArrayBuffer, resourceTiming?: Array, // Only used for benchmarking: - glyphMap?: {[_: string]: {[_: number]: ?StyleGlyph}} | null, + glyphMap?: {[_: string]: {glyphs: {[_: number]: ?StyleGlyph}, ascender?: number, descender?: number}} | null, iconMap?: {[_: string]: StyleImage} | null, glyphPositions?: GlyphPositions | null }; diff --git a/src/source/worker_tile.js b/src/source/worker_tile.js index 6aa2fd0312b..4831ddb792f 100644 --- a/src/source/worker_tile.js +++ b/src/source/worker_tile.js @@ -163,7 +163,7 @@ class WorkerTile { lineAtlas.trim(); let error: ?Error; - let glyphMap: ?{[_: string]: {[_: number]: ?StyleGlyph}}; + let glyphMap: ?{[_: string]: {glyphs: {[_: number]: ?StyleGlyph}, ascender?: number, descender?: number}}; let iconMap: ?{[_: string]: StyleImage}; let patternMap: ?{[_: string]: StyleImage}; const taskMetadata = {type: 'maybePrepare', isSymbolTile: this.isSymbolTile, zoom: this.zoom}; diff --git a/src/style/load_glyph_range.js b/src/style/load_glyph_range.js index 3cc9ac9c886..c369ce0751d 100644 --- a/src/style/load_glyph_range.js +++ b/src/style/load_glyph_range.js @@ -12,7 +12,7 @@ export default function (fontstack: string, range: number, urlTemplate: string, requestManager: RequestManager, - callback: Callback<{[_: number]: StyleGlyph | null}>) { + callback: Callback<{glyphs: {[number]: StyleGlyph | null}, ascender?: number, descender?: number}>) { const begin = range * 256; const end = begin + 255; @@ -27,12 +27,11 @@ export default function (fontstack: string, callback(err); } else if (data) { const glyphs = {}; - - for (const glyph of parseGlyphPBF(data)) { + const glyphData = parseGlyphPBF(data); + for (const glyph of glyphData.glyphs) { glyphs[glyph.id] = glyph; } - - callback(null, glyphs); + callback(null, {glyphs, ascender: glyphData.ascender, descender: glyphData.descender}); } }); } diff --git a/src/style/parse_glyph_pbf.js b/src/style/parse_glyph_pbf.js index 9d16db58c49..1319113179a 100644 --- a/src/style/parse_glyph_pbf.js +++ b/src/style/parse_glyph_pbf.js @@ -7,16 +7,17 @@ const border = 3; import type {StyleGlyph} from './style_glyph.js'; -function readFontstacks(tag: number, glyphs: Array, pbf: Protobuf) { +function readFontstacks(tag: number, glyphData: {glyphs: Array, ascender?: number, descender?: number}, pbf: Protobuf) { + glyphData.glyphs = []; if (tag === 1) { - pbf.readMessage(readFontstack, glyphs); + pbf.readMessage(readFontstack, glyphData); } } -function readFontstack(tag: number, glyphs: Array, pbf: Protobuf) { +function readFontstack(tag: number, glyphData: {glyphs: Array, ascender?: number, descender?: number}, pbf: Protobuf) { if (tag === 3) { const {id, bitmap, width, height, left, top, advance} = pbf.readMessage(readGlyph, {}); - glyphs.push({ + glyphData.glyphs.push({ id, bitmap: new AlphaImage({ width: width + 2 * border, @@ -24,6 +25,10 @@ function readFontstack(tag: number, glyphs: Array, pbf: Protobuf) { }, bitmap), metrics: {width, height, left, top, advance} }); + } else if (tag === 4) { + glyphData.ascender = pbf.readSVarint(); + } else if (tag === 5) { + glyphData.descender = pbf.readSVarint(); } } @@ -37,8 +42,8 @@ function readGlyph(tag: number, glyph: Object, pbf: Protobuf) { else if (tag === 7) glyph.advance = pbf.readVarint(); } -export default function (data: ArrayBuffer | Uint8Array): Array { - return new Protobuf(data).readFields(readFontstacks, []); +export default function (data: ArrayBuffer | Uint8Array): {glyphs: Array, ascender?: number, descender?: number} { + return new Protobuf(data).readFields(readFontstacks, {}); } export const GLYPH_PBF_BORDER = border; diff --git a/src/style/style.js b/src/style/style.js index 892b7d02ff0..f39199b6ce8 100644 --- a/src/style/style.js +++ b/src/style/style.js @@ -1723,7 +1723,7 @@ class Style extends Evented { setDependencies(this._symbolSourceCaches[params.source]); } - getGlyphs(mapId: string, params: {stacks: {[_: string]: Array}}, callback: Callback<{[_: string]: {[_: number]: ?StyleGlyph}}>) { + getGlyphs(mapId: string, params: {stacks: {[_: string]: Array}}, callback: Callback<{[_: string]: {glyphs: {[_: number]: ?StyleGlyph}, ascender?: number, descender?: number}}>) { this.glyphManager.getGlyphs(params.stacks, callback); } diff --git a/src/symbol/quads.js b/src/symbol/quads.js index 8595196721b..c82d004a12d 100644 --- a/src/symbol/quads.js +++ b/src/symbol/quads.js @@ -6,12 +6,12 @@ import {GLYPH_PBF_BORDER} from '../style/parse_glyph_pbf.js'; import type Anchor from './anchor.js'; import type {PositionedIcon, Shaping} from './shaping.js'; -import {SHAPING_DEFAULT_OFFSET} from './shaping.js'; import {IMAGE_PADDING} from '../render/image_atlas.js'; import {SDF_SCALE} from '../render/glyph_manager.js'; import type SymbolStyleLayer from '../style/style_layer/symbol_style_layer.js'; import type {Feature} from '../style-spec/expression/index.js'; import type {StyleImage} from '../style/style_image.js'; +import {isVerticalClosePunctuation, isVerticalOpenPunctuation} from '../util/verticalize_punctuation.js'; import ONE_EM from './one_em.js'; /** @@ -216,6 +216,29 @@ function getPxOffset(fixedOffset, fixedSize, stretchOffset, stretchSize) { return fixedOffset - fixedSize * stretchOffset / stretchSize; } +function getRotateOffset(textOffset: [number, number]) { + const x = textOffset[0], y = textOffset[1]; + const product = x * y; + if (product > 0) { + return [x, -y]; + } else if (product < 0) { + return [-x, y]; + } else if (x === 0) { + return [y, x]; + } else { + return [y, -x]; + } +} + +function getMidlineOffset(shaping, lineHeight, previousOffset, lineIndex) { + const currentLineHeight = (lineHeight + shaping.positionedLines[lineIndex].lineOffset); + if (lineIndex === 0) { + return previousOffset + currentLineHeight / 2.0; + } + const aboveLineHeight = (lineHeight + shaping.positionedLines[lineIndex - 1].lineOffset); + return previousOffset + (currentLineHeight + aboveLineHeight) / 2.0; +} + /** * Create the quads used for rendering a text label. * @private @@ -228,11 +251,22 @@ export function getGlyphQuads(anchor: Anchor, feature: Feature, imageMap: {[_: string]: StyleImage}, allowVerticalPlacement: boolean): Array { + const quads = []; + if (shaping.positionedLines.length === 0) return quads; const textRotate = layer.layout.get('text-rotate').evaluate(feature, {}) * Math.PI / 180; - const quads = []; + const rotateOffset = getRotateOffset(textOffset); + let shapingHeight = Math.abs(shaping.top - shaping.bottom); for (const line of shaping.positionedLines) { + shapingHeight -= line.lineOffset; + } + const lineCounts = shaping.positionedLines.length; + const lineHeight = shapingHeight / lineCounts; + let currentOffset = shaping.top - textOffset[1]; + for (let lineIndex = 0; lineIndex < lineCounts; ++lineIndex) { + const line = shaping.positionedLines[lineIndex]; + currentOffset = getMidlineOffset(shaping, lineHeight, currentOffset, lineIndex); for (const positionedGlyph of line.positionedGlyphs) { if (!positionedGlyph.rect) continue; const textureRect = positionedGlyph.rect || {}; @@ -246,16 +280,20 @@ export function getGlyphQuads(anchor: Anchor, const rotateVerticalGlyph = (alongLine || allowVerticalPlacement) && positionedGlyph.vertical; const halfAdvance = positionedGlyph.metrics.advance * positionedGlyph.scale / 2; + const metrics = positionedGlyph.metrics; + const rect = positionedGlyph.rect; + if (rect === null) continue; // Align images and scaled glyphs in the middle of a vertical line. if (allowVerticalPlacement && shaping.verticalizable) { - const scaledGlyphOffset = (positionedGlyph.scale - 1) * ONE_EM; - const imageOffset = (ONE_EM - positionedGlyph.metrics.width * positionedGlyph.scale) / 2; - lineOffset = line.lineOffset / 2 - (positionedGlyph.imageName ? -imageOffset : scaledGlyphOffset); + // image's advance for vertical shaping is its height, so that we have to take the difference into + // account after image glyph is rotated + lineOffset = positionedGlyph.imageName ? halfAdvance - positionedGlyph.metrics.width * positionedGlyph.scale / 2.0 : 0; } if (positionedGlyph.imageName) { const image = imageMap[positionedGlyph.imageName]; + if (!image) continue; isSDF = image.sdf; pixelRatio = image.pixelRatio; rectBuffer = IMAGE_PADDING / pixelRatio; @@ -265,62 +303,122 @@ export function getGlyphQuads(anchor: Anchor, [positionedGlyph.x + halfAdvance, positionedGlyph.y] : [0, 0]; - let builtInOffset = alongLine ? - [0, 0] : - [positionedGlyph.x + halfAdvance + textOffset[0], positionedGlyph.y + textOffset[1] - lineOffset]; - + let builtInOffset = [0, 0]; let verticalizedLabelOffset = [0, 0]; - if (rotateVerticalGlyph) { + let useRotateOffset = false; + if (!alongLine) { + if (rotateVerticalGlyph) { // Vertical POI labels that are rotated 90deg CW and whose glyphs must preserve upright orientation // need to be rotated 90deg CCW. After a quad is rotated, it is translated to the original built-in offset. - verticalizedLabelOffset = builtInOffset; - builtInOffset = [0, 0]; + verticalizedLabelOffset = + [positionedGlyph.x + halfAdvance + rotateOffset[0], positionedGlyph.y + rotateOffset[1] - lineOffset]; + useRotateOffset = true; + } else { + builtInOffset = [positionedGlyph.x + halfAdvance + textOffset[0], positionedGlyph.y + textOffset[1] - lineOffset]; + } } - const x1 = (positionedGlyph.metrics.left - rectBuffer) * positionedGlyph.scale - halfAdvance + builtInOffset[0]; - const y1 = (-positionedGlyph.metrics.top - rectBuffer) * positionedGlyph.scale + builtInOffset[1]; - const x2 = x1 + textureRect.w * positionedGlyph.scale / (pixelRatio * (positionedGlyph.localGlyph ? SDF_SCALE : 1)); - const y2 = y1 + textureRect.h * positionedGlyph.scale / (pixelRatio * (positionedGlyph.localGlyph ? SDF_SCALE : 1)); - - const tl = new Point(x1, y1); - const tr = new Point(x2, y1); - const bl = new Point(x1, y2); - const br = new Point(x2, y2); + const paddedWidth = + rect.w * positionedGlyph.scale / (pixelRatio * (positionedGlyph.localGlyph ? SDF_SCALE : 1)); + const paddedHeight = + rect.h * positionedGlyph.scale / (pixelRatio * (positionedGlyph.localGlyph ? SDF_SCALE : 1)); + + let tl, tr, bl, br; + if (!rotateVerticalGlyph) { + const x1 = (metrics.left - rectBuffer) * positionedGlyph.scale - halfAdvance + builtInOffset[0]; + const y1 = (-metrics.top - rectBuffer) * positionedGlyph.scale + builtInOffset[1]; + const x2 = x1 + paddedWidth; + const y2 = y1 + paddedHeight; + + tl = new Point(x1, y1); + tr = new Point(x2, y1); + bl = new Point(x1, y2); + br = new Point(x2, y2); + } else { + // For vertical glyph placement, follow the steps to put the glyph bitmap in right coordinates: + // 1. Rotate the glyph by using original glyph coordinates instead of padded coordinates, since the + // rotation center and xOffsetCorrection are all based on original glyph's size. + // 2. Do x offset correction so that 'tl' is shifted to the same x coordinate before rotation. + // 3. Adjust glyph positon for 'tl' by applying vertial padding and horizontal shift, now 'tl' is the + // coordinate where we draw the glyph bitmap. + // 4. Calculate other three bitmap coordinates. - if (rotateVerticalGlyph) { // Vertical-supporting glyphs are laid out in 24x24 point boxes (1 square em) - // In horizontal orientation, the y values for glyphs are below the midline - // and we use a "yOffset" of -17 to pull them up to the middle. + // In horizontal orientation, the "yShift" is the negative value of the height that + // the glyph is above the horizontal midline. // By rotating counter-clockwise around the point at the center of the left - // edge of a 24x24 layout box centered below the midline, we align the center - // of the glyphs with the horizontal midline, so the yOffset is no longer + // edge of a 24x24 layout box centered below the midline, we align the midline + // of the rotated glyphs with the horizontal midline, so the yShift is no longer // necessary, but we also pull the glyph to the left along the x axis. - // The y coordinate includes baseline yOffset, thus needs to be accounted - // for when glyph is rotated and translated. - const center = new Point(-halfAdvance, halfAdvance - SHAPING_DEFAULT_OFFSET); + const yShift = (positionedGlyph.y - currentOffset); + const center = new Point(-halfAdvance, halfAdvance - yShift); const verticalRotation = -Math.PI / 2; - - // xHalfWidthOffsetCorrection is a difference between full-width and half-width - // advance, should be 0 for full-width glyphs and will pull up half-width glyphs. - const xHalfWidthOffsetCorrection = ONE_EM / 2 - halfAdvance; - const yImageOffsetCorrection = positionedGlyph.imageName ? xHalfWidthOffsetCorrection : 0.0; - const halfWidthOffsetCorrection = new Point(5 - SHAPING_DEFAULT_OFFSET - xHalfWidthOffsetCorrection, -yImageOffsetCorrection); const verticalOffsetCorrection = new Point(...verticalizedLabelOffset); - tl._rotateAround(verticalRotation, center)._add(halfWidthOffsetCorrection)._add(verticalOffsetCorrection); - tr._rotateAround(verticalRotation, center)._add(halfWidthOffsetCorrection)._add(verticalOffsetCorrection); - bl._rotateAround(verticalRotation, center)._add(halfWidthOffsetCorrection)._add(verticalOffsetCorrection); - br._rotateAround(verticalRotation, center)._add(halfWidthOffsetCorrection)._add(verticalOffsetCorrection); + // Relative position before rotation + // tl ----- tr + // | | + // | | + // bl ----- br + tl = new Point(-halfAdvance + builtInOffset[0], builtInOffset[1]); + tl._rotateAround(verticalRotation, center)._add(verticalOffsetCorrection); + + // Relative position after rotating + // tr ----- br + // | | + // | | + // tl ----- bl + // After rotation, glyph lies on the horizontal midline. + // Shift back to tl's original x coordinate before rotation by applying 'xOffsetCorrection'. + tl.x += -yShift + halfAdvance; + + // Add padding for y coordinate's justification + tl.y -= (metrics.left - rectBuffer) * positionedGlyph.scale; + + // Adjust x coordinate according to glyph bitmap's height and the vectical advance + const verticalAdvance = positionedGlyph.imageName ? metrics.advance * positionedGlyph.scale : + ONE_EM * positionedGlyph.scale; + // Check wether the glyph is generated from server side or locally + const chr = String.fromCharCode(positionedGlyph.glyph); + if (isVerticalClosePunctuation(chr)) { + // Place vertical punctuation in right place, pull down 1 pixel's space for close punctuations + tl.x += (-rectBuffer + 1) * positionedGlyph.scale; + } else if (isVerticalOpenPunctuation(chr)) { + const xOffset = verticalAdvance - metrics.height * positionedGlyph.scale; + // Place vertical punctuation in right place, pull up 1 pixel's space for open punctuations + tl.x += xOffset + (-rectBuffer - 1) * positionedGlyph.scale; + } else if (!positionedGlyph.imageName && + ((metrics.width + rectBuffer * 2) !== rect.w || metrics.height + rectBuffer * 2 !== rect.h)) { + // Locally generated glyphs' bitmap do not have exact 'rectBuffer' padded around the glyphs, + // but the original tl do have distance of rectBuffer padded to the top of the glyph. + const perfectPaddedHeight = (metrics.height + rectBuffer * 2) * positionedGlyph.scale; + const delta = verticalAdvance - perfectPaddedHeight; + tl.x += delta / 2; + } else { + // Place the glyph bitmap right in the center of the 24x24 point boxes + const delta = verticalAdvance - paddedHeight; + tl.x += delta / 2; + } + // Calculate other three points + tr = new Point(tl.x, tl.y - paddedWidth); + bl = new Point(tl.x + paddedHeight, tl.y); + br = new Point(tl.x + paddedHeight, tl.y - paddedWidth); } if (textRotate) { - const sin = Math.sin(textRotate), - cos = Math.cos(textRotate), - matrix = [cos, -sin, sin, cos]; - - tl._matMult(matrix); - tr._matMult(matrix); - bl._matMult(matrix); - br._matMult(matrix); + let center; + if (!alongLine) { + if (useRotateOffset) { + center = new Point(rotateOffset[0], rotateOffset[1]); + } else { + center = new Point(textOffset[0], textOffset[1]); + } + } else { + center = new Point(0, 0); + } + tl._rotateAround(textRotate, center); + tr._rotateAround(textRotate, center); + bl._rotateAround(textRotate, center); + br._rotateAround(textRotate, center); } const pixelOffsetTL = new Point(0, 0); diff --git a/src/symbol/shaping.js b/src/symbol/shaping.js index c430c2c5588..90fecce3197 100644 --- a/src/symbol/shaping.js +++ b/src/symbol/shaping.js @@ -15,7 +15,7 @@ import type {StyleGlyph, GlyphMetrics} from '../style/style_glyph.js'; import {GLYPH_PBF_BORDER} from '../style/parse_glyph_pbf.js'; import type {ImagePosition} from '../render/image_atlas.js'; import {IMAGE_PADDING} from '../render/image_atlas.js'; -import type {Rect, GlyphPosition} from '../render/glyph_atlas.js'; +import type {GlyphRect, GlyphPositions} from '../render/glyph_atlas.js'; import Formatted, {FormattedSection} from '../style-spec/expression/types/formatted.js'; const WritingMode = { @@ -38,7 +38,7 @@ export type PositionedGlyph = { fontStack: string, sectionIndex: number, metrics: GlyphMetrics, - rect: Rect | null, + rect: GlyphRect | null, localGlyph?: boolean }; @@ -57,7 +57,8 @@ export type Shaping = { writingMode: 1 | 2, text: string, iconsInText: boolean, - verticalizable: boolean + verticalizable: boolean, + hasBaseline: boolean }; function isEmpty(positionedLines: Array) { @@ -139,6 +140,10 @@ class TaggedString { return this.sections[this.sectionIndex[index]]; } + getSections(): Array { + return this.sections; + } + getSectionIndex(index: number): number { return this.sectionIndex[index]; } @@ -147,8 +152,8 @@ class TaggedString { return this.text.charCodeAt(index); } - verticalizePunctuation() { - this.text = verticalizePunctuation(this.text); + verticalizePunctuation(skipContextChecking: boolean) { + this.text = verticalizePunctuation(this.text, skipContextChecking); } trim() { @@ -238,8 +243,8 @@ function breakLines(input: TaggedString, lineBreakPoints: Array): Array< } function shapeText(text: Formatted, - glyphMap: {[_: string]: {[_: number]: ?StyleGlyph}}, - glyphPositions: {[_: string]: {[_: number]: GlyphPosition}}, + glyphMap: {[_: string]: {glyphs: {[_: number]: ?StyleGlyph}, ascender?: number, descender?: number}}, + glyphPositions: GlyphPositions, imagePositions: {[_: string]: ImagePosition}, defaultFontStack: string, maxWidth: number, @@ -256,7 +261,7 @@ function shapeText(text: Formatted, const logicalInput = TaggedString.fromFeature(text, defaultFontStack); if (writingMode === WritingMode.vertical) { - logicalInput.verticalizePunctuation(); + logicalInput.verticalizePunctuation(allowVerticalPlacement && symbolPlacement === 'point'); } let lines: Array; @@ -306,7 +311,8 @@ function shapeText(text: Formatted, right: translate[0], writingMode, iconsInText: false, - verticalizable: false + verticalizable: false, + hasBaseline: false }; shapeLines(shaping, glyphMap, glyphPositions, imagePositions, lines, lineHeight, textAnchor, textJustify, writingMode, spacing, allowVerticalPlacement, layoutTextSizeThisZoom); @@ -349,13 +355,13 @@ const breakable: {[_: number]: boolean} = { function getGlyphAdvance(codePoint: number, section: SectionOptions, - glyphMap: {[_: string]: {[_: number]: ?StyleGlyph}}, + glyphMap: {[_: string]: {glyphs: {[_: number]: ?StyleGlyph}, ascender?: number, descender?: number}}, imagePositions: {[_: string]: ImagePosition}, spacing: number, layoutTextSize: number): number { if (!section.imageName) { const positions = glyphMap[section.fontStack]; - const glyph = positions && positions[codePoint]; + const glyph = positions && positions.glyphs[codePoint]; if (!glyph) return 0; return glyph.metrics.advance * section.scale + spacing; } else { @@ -368,7 +374,7 @@ function getGlyphAdvance(codePoint: number, function determineAverageLineWidth(logicalInput: TaggedString, spacing: number, maxWidth: number, - glyphMap: {[_: string]: {[_: number]: ?StyleGlyph}}, + glyphMap: {[_: string]: {glyphs: {[_: number]: ?StyleGlyph}, ascender?: number, descender?: number}}, imagePositions: {[_: string]: ImagePosition}, layoutTextSize: number) { let totalWidth = 0; @@ -472,7 +478,7 @@ function leastBadBreaks(lastLineBreak: ?Break): Array { function determineLineBreaks(logicalInput: TaggedString, spacing: number, maxWidth: number, - glyphMap: {[_: string]: {[_: number]: ?StyleGlyph}}, + glyphMap: {[_: string]: {glyphs: {[_: number]: ?StyleGlyph}, ascender?: number, descender?: number}}, imagePositions: {[_: string]: ImagePosition}, symbolPlacement: string, layoutTextSize: number): Array { @@ -555,8 +561,8 @@ function getAnchorAlignment(anchor: SymbolAnchor) { } function shapeLines(shaping: Shaping, - glyphMap: {[_: string]: {[_: number]: ?StyleGlyph}}, - glyphPositions: {[_: string]: {[_: number]: GlyphPosition}}, + glyphMap: {[_: string]: {glyphs: {[_: number]: ?StyleGlyph}, ascender?: number, descender?: number}}, + glyphPositions: GlyphPositions, imagePositions: {[_: string]: ImagePosition}, lines: Array, lineHeight: number, @@ -568,7 +574,7 @@ function shapeLines(shaping: Shaping, layoutTextSizeThisZoom: number) { let x = 0; - let y = SHAPING_DEFAULT_OFFSET; + let y = 0; let maxLineLength = 0; let maxLineHeight = 0; @@ -577,6 +583,21 @@ function shapeLines(shaping: Shaping, textJustify === 'right' ? 1 : textJustify === 'left' ? 0 : 0.5; + let hasBaseline = false; + for (const line of lines) { + const sections = line.getSections(); + for (const section of sections) { + if (section.imageName) continue; + + const glyphData = glyphMap[section.fontStack]; + if (!glyphData) continue; + + hasBaseline = glyphData.ascender !== undefined && glyphData.descender !== undefined; + if (!hasBaseline) break; + } + if (!hasBaseline) break; + } + let lineIndex = 0; for (const line of lines) { line.trim(); @@ -594,15 +615,20 @@ function shapeLines(shaping: Shaping, continue; } + let biggestHeight = 0; + let baselineOffset = 0; for (let i = 0; i < line.length(); i++) { const section = line.getSection(i); const sectionIndex = line.getSectionIndex(i); const codePoint = line.getCharCode(i); - let baselineOffset = 0.0; + + let sectionScale = section.scale; let metrics = null; let rect = null; let imageName = null; let verticalAdvance = ONE_EM; + let glyphOffset = 0; + const vertical = !(writingMode === WritingMode.horizontal || // Don't verticalize glyphs that have no upright orientation if vertical placement is disabled. (!allowVerticalPlacement && !charHasUprightVerticalOrientation(codePoint)) || @@ -611,22 +637,44 @@ function shapeLines(shaping: Shaping, (allowVerticalPlacement && (whitespace[codePoint] || charInComplexShapingScript(codePoint)))); if (!section.imageName) { - const positions = glyphPositions[section.fontStack]; - const glyphPosition = positions && positions[codePoint]; - if (glyphPosition && glyphPosition.rect) { - rect = glyphPosition.rect; - metrics = glyphPosition.metrics; + // Find glyph position in the glyph atlas, if bitmap is null, + // glyphPosition will not exit in the glyphPosition map + const glyphPositionData = glyphPositions[section.fontStack]; + if (!glyphPositionData) continue; + if (glyphPositionData[codePoint]) { + rect = glyphPositionData[codePoint]; + } + const glyphData = glyphMap[section.fontStack]; + if (!glyphData) continue; + const glyph = glyphData.glyphs[codePoint]; + if (!glyph) continue; + + metrics = glyph.metrics; + verticalAdvance = codePoint !== 0x200b ? ONE_EM : 0; + + // In order to make different fonts aligned, they must share a general baseline that aligns with every + // font's real baseline. Glyph's offset is counted from the top left corner, where the ascender line + // starts. + // First of all, each glyph's baseline lies on the center line of the shaping line. Since ascender + // is above the baseline, the glyphOffset is the negative shift. Then, in order to make glyphs fit in + // the shaping box, for each line, we shift the glyph with biggest height(with scale) to make its center + // lie on the center line of the line, which will lead to a baseline shift. Then adjust the whole line + // with the baseline offset we calculated from the shift. + if (hasBaseline) { + const ascender = glyphData.ascender !== undefined ? Math.abs(glyphData.ascender) : 0; + const descender = glyphData.descender !== undefined ? Math.abs(glyphData.descender) : 0; + const value = (ascender + descender) * sectionScale; + if (biggestHeight < value) { + biggestHeight = value; + baselineOffset = (ascender - descender) / 2 * sectionScale; + } + glyphOffset = -ascender * sectionScale; } else { - const glyphs = glyphMap[section.fontStack]; - const glyph = glyphs && glyphs[codePoint]; - if (!glyph) continue; - metrics = glyph.metrics; + // If font's baseline is not applicable, fall back to use a default baseline offset, see + // Shaping::yOffset. Since we're laying out at 24 points, we need also calculate how much it will + // move when we scale up or down. + glyphOffset = SHAPING_DEFAULT_OFFSET + (lineMaxScale - sectionScale) * ONE_EM; } - - // We don't know the baseline, but since we're laying out - // at 24 points, we can calculate how much it will move when - // we scale up or down. - baselineOffset = (lineMaxScale - section.scale) * ONE_EM; } else { const imagePosition = imagePositions[section.imageName]; if (!imagePosition) continue; @@ -637,7 +685,7 @@ function shapeLines(shaping: Shaping, // If needed, allow to set scale factor for an image using // alias "image-scale" that could be alias for "font-scale" // when FormattedSection is an image section. - section.scale = section.scale * ONE_EM / layoutTextSizeThisZoom; + sectionScale = sectionScale * ONE_EM / layoutTextSizeThisZoom; metrics = {width: size[0], height: size[1], @@ -646,28 +694,33 @@ function shapeLines(shaping: Shaping, advance: vertical ? size[1] : size[0], localGlyph: false}; - // Difference between one EM and an image size. - // Aligns bottom of an image to a baseline level. - const imageOffset = ONE_EM - size[1] * section.scale; - baselineOffset = maxLineOffset + imageOffset; + if (!hasBaseline) { + glyphOffset = SHAPING_DEFAULT_OFFSET + lineMaxScale * ONE_EM - size[1] * sectionScale; + } else { + // Based on node-fontnik: 'top = heightAboveBaseline - Ascender'(it is not valid for locally + // generated glyph). Since the top is a constant: glyph's borderSize. So if we set image glyph with + // 'ascender = height', it means we pull down the glyph under baseline with a distance of glyph's borderSize. + const imageAscender = metrics.height; + glyphOffset = -imageAscender * sectionScale; + + } verticalAdvance = metrics.advance; // Difference between height of an image and one EM at max line scale. // Pushes current line down if an image size is over 1 EM at max line scale. - const offset = vertical ? size[0] * section.scale - ONE_EM * lineMaxScale : - size[1] * section.scale - ONE_EM * lineMaxScale; + const offset = (vertical ? size[0] : size[1]) * sectionScale - ONE_EM * lineMaxScale; if (offset > 0 && offset > lineOffset) { lineOffset = offset; } } if (!vertical) { - positionedGlyphs.push({glyph: codePoint, imageName, x, y: y + baselineOffset, vertical, scale: section.scale, localGlyph: metrics.localGlyph, fontStack: section.fontStack, sectionIndex, metrics, rect}); - x += metrics.advance * section.scale + spacing; + positionedGlyphs.push({glyph: codePoint, imageName, x, y: y + glyphOffset, vertical, scale: sectionScale, localGlyph: metrics.localGlyph, fontStack: section.fontStack, sectionIndex, metrics, rect}); + x += metrics.advance * sectionScale + spacing; } else { shaping.verticalizable = true; - positionedGlyphs.push({glyph: codePoint, imageName, x, y: y + baselineOffset, vertical, scale: section.scale, localGlyph: metrics.localGlyph, fontStack: section.fontStack, sectionIndex, metrics, rect}); - x += verticalAdvance * section.scale + spacing; + positionedGlyphs.push({glyph: codePoint, imageName, x, y: y + glyphOffset, vertical, scale: sectionScale, localGlyph: metrics.localGlyph, fontStack: section.fontStack, sectionIndex, metrics, rect}); + x += verticalAdvance * sectionScale + spacing; } } @@ -675,7 +728,14 @@ function shapeLines(shaping: Shaping, if (positionedGlyphs.length !== 0) { const lineLength = x - spacing; maxLineLength = Math.max(lineLength, maxLineLength); - justifyLine(positionedGlyphs, 0, positionedGlyphs.length - 1, justify, lineOffset); + // Justify the line so that its top is aligned with the current height of y, and its horizontal coordinates + // are justified according to the TextJustifyType + if (hasBaseline) { + justifyLine(positionedGlyphs, justify, lineOffset, baselineOffset, lineHeight * lineMaxScale / 2); + } else { + // Scaled line height offset is counted in glyphOffset, so here just use an unscaled line height + justifyLine(positionedGlyphs, justify, lineOffset, 0, lineHeight / 2); + } } x = 0; @@ -686,33 +746,34 @@ function shapeLines(shaping: Shaping, ++lineIndex; } - // Calculate the bounding box and justify / align text block. - const height = y - SHAPING_DEFAULT_OFFSET; + const height = y; const {horizontalAlign, verticalAlign} = getAnchorAlignment(textAnchor); - align(shaping.positionedLines, justify, horizontalAlign, verticalAlign, maxLineLength, maxLineHeight, lineHeight, height, lines.length); - + align(shaping.positionedLines, justify, horizontalAlign, verticalAlign, maxLineLength, height); + // Calculate the bounding box shaping.top += -verticalAlign * height; shaping.bottom = shaping.top + height; shaping.left += -horizontalAlign * maxLineLength; shaping.right = shaping.left + maxLineLength; + shaping.hasBaseline = hasBaseline; } // justify right = 1, left = 0, center = 0.5 function justifyLine(positionedGlyphs: Array, - start: number, - end: number, justify: 1 | 0 | 0.5, - lineOffset: number) { - if (!justify && !lineOffset) + lineOffset: number, + baselineOffset: number, + halfLineHeight: number) { + if (!justify && !lineOffset && !baselineOffset && !halfLineHeight) { return; + } + const end = positionedGlyphs.length - 1; + const lastGlyph = positionedGlyphs[end]; + const lastAdvance = lastGlyph.metrics.advance * lastGlyph.scale; + const lineIndent = (lastGlyph.x + lastAdvance) * justify; - const lastPositionedGlyph = positionedGlyphs[end]; - const lastAdvance = lastPositionedGlyph.metrics.advance * lastPositionedGlyph.scale; - const lineIndent = (positionedGlyphs[end].x + lastAdvance) * justify; - - for (let j = start; j <= end; j++) { + for (let j = 0; j <= end; j++) { positionedGlyphs[j].x -= lineIndent; - positionedGlyphs[j].y += lineOffset; + positionedGlyphs[j].y += lineOffset + baselineOffset + halfLineHeight; } } @@ -721,19 +782,10 @@ function align(positionedLines: Array, horizontalAlign: number, verticalAlign: number, maxLineLength: number, - maxLineHeight: number, - lineHeight: number, - blockHeight: number, - lineCount: number) { + blockHeight: number) { const shiftX = (justify - horizontalAlign) * maxLineLength; - let shiftY = 0; - - if (maxLineHeight !== lineHeight) { - shiftY = -blockHeight * verticalAlign - SHAPING_DEFAULT_OFFSET; - } else { - shiftY = (-verticalAlign * lineCount + 0.5) * lineHeight; - } + const shiftY = -blockHeight * verticalAlign; for (const line of positionedLines) { for (const positionedGlyph of line.positionedGlyphs) { positionedGlyph.x += shiftX; diff --git a/src/symbol/symbol_layout.js b/src/symbol/symbol_layout.js index f7d48ac18cd..545878a2752 100644 --- a/src/symbol/symbol_layout.js +++ b/src/symbol/symbol_layout.js @@ -26,7 +26,7 @@ import type {StyleImage} from '../style/style_image.js'; import type {StyleGlyph} from '../style/style_glyph.js'; import type SymbolStyleLayer from '../style/style_layer/symbol_style_layer.js'; import type {ImagePosition} from '../render/image_atlas.js'; -import type {GlyphPosition} from '../render/glyph_atlas.js'; +import type {GlyphPositions} from '../render/glyph_atlas.js'; import type {PossiblyEvaluatedPropertyValue} from '../style/properties.js'; import Point from '@mapbox/point-geometry'; @@ -63,6 +63,7 @@ export type TextAnchor = 'center' | 'left' | 'right' | 'top' | 'bottom' | 'top-l // (see "yOffset" in shaping.js) const baselineOffset = 7; const INVALID_TEXT_OFFSET = Number.POSITIVE_INFINITY; +const sqrt2 = Math.sqrt(2); export function evaluateVariableOffset(anchor: TextAnchor, offset: [number, number]) { @@ -70,7 +71,7 @@ export function evaluateVariableOffset(anchor: TextAnchor, offset: [number, numb let x = 0, y = 0; if (radialOffset < 0) radialOffset = 0; // Ignore negative offset. // solve for r where r^2 + r^2 = radialOffset^2 - const hypotenuse = radialOffset / Math.sqrt(2); + const hypotenuse = radialOffset / sqrt2; switch (anchor) { case 'top-right': case 'top-left': @@ -147,8 +148,8 @@ export function evaluateVariableOffset(anchor: TextAnchor, offset: [number, numb } export function performSymbolLayout(bucket: SymbolBucket, - glyphMap: {[_: string]: {[number]: ?StyleGlyph}}, - glyphPositions: {[_: string]: {[number]: GlyphPosition}}, + glyphMap: {[_: string]: {glyphs: {[_: number]: ?StyleGlyph}, ascender?: number, descender?: number}}, + glyphPositions: GlyphPositions, imageMap: {[_: string]: StyleImage}, imagePositions: {[_: string]: ImagePosition}, showCollisionBoxes: boolean, @@ -551,7 +552,8 @@ export function evaluateBoxCollisionFeature(collisionBoxArray: CollisionBoxArray bucketIndex: number, shaped: Object, padding: number, - rotate: number): number { + rotate: number, + textOffset: ?[number, number]): number { let y1 = shaped.top; let y2 = shaped.bottom; let x1 = shaped.left; @@ -576,11 +578,16 @@ export function evaluateBoxCollisionFeature(collisionBoxArray: CollisionBoxArray const br = new Point(x2, y2); const rotateRadians = degToRad(rotate); + let rotateCenter = new Point(0, 0); - tl._rotate(rotateRadians); - tr._rotate(rotateRadians); - bl._rotate(rotateRadians); - br._rotate(rotateRadians); + if (textOffset) { + rotateCenter = new Point(textOffset[0], textOffset[1]); + } + + tl._rotateAround(rotateRadians, rotateCenter); + tr._rotateAround(rotateRadians, rotateCenter); + bl._rotateAround(rotateRadians, rotateCenter); + br._rotateAround(rotateRadians, rotateCenter); // Collision features require an "on-axis" geometry, // so take the envelope of the rotated geometry @@ -671,7 +678,7 @@ function addSymbol(bucket: SymbolBucket, } else { const textRotation = layer.layout.get('text-rotate').evaluate(feature, {}, canonical); const verticalTextRotation = textRotation + 90.0; - verticalTextBoxIndex = evaluateBoxCollisionFeature(collisionBoxArray, anchor, featureIndex, sourceLayerIndex, bucketIndex, verticalShaping, textPadding, verticalTextRotation); + verticalTextBoxIndex = evaluateBoxCollisionFeature(collisionBoxArray, anchor, featureIndex, sourceLayerIndex, bucketIndex, verticalShaping, textPadding, verticalTextRotation, textOffset); if (verticallyShapedIcon) { verticalIconBoxIndex = evaluateBoxCollisionFeature(collisionBoxArray, anchor, featureIndex, sourceLayerIndex, bucketIndex, verticallyShapedIcon, iconPadding, verticalTextRotation); } @@ -758,7 +765,7 @@ function addSymbol(bucket: SymbolBucket, textCircle = evaluateCircleCollisionFeature(shaping); } else { const textRotate = layer.layout.get('text-rotate').evaluate(feature, {}, canonical); - textBoxIndex = evaluateBoxCollisionFeature(collisionBoxArray, anchor, featureIndex, sourceLayerIndex, bucketIndex, shaping, textPadding, textRotate); + textBoxIndex = evaluateBoxCollisionFeature(collisionBoxArray, anchor, featureIndex, sourceLayerIndex, bucketIndex, shaping, textPadding, textRotate, textOffset); } } diff --git a/src/ui/map.js b/src/ui/map.js index b4db01da6d9..e5f98c792c6 100755 --- a/src/ui/map.js +++ b/src/ui/map.js @@ -250,7 +250,7 @@ const defaultOptions = { * map and the other on the left edge of the map) at every zoom level. * @param {number} [options.maxTileCacheSize=null] The maximum number of tiles stored in the tile cache for a given source. If omitted, the cache will be dynamically sized based on the current viewport. * @param {string} [options.localIdeographFontFamily='sans-serif'] Defines a CSS - * font-family for locally overriding generation of glyphs in the 'CJK Unified Ideographs', 'Hiragana', 'Katakana' and 'Hangul Syllables' ranges. + * font-family for locally overriding generation of glyphs in the 'CJK Unified Ideographs', 'Hiragana', 'Katakana', 'Hangul Syllables' and 'CJK Symbols and Punctuation' ranges. * In these ranges, font settings from the map's style will be ignored, except for font-weight keywords (light/regular/medium/bold). * Set to `false`, to enable font settings from the map's style for these glyph ranges. Note that [Mapbox Studio](https://studio.mapbox.com/) sets this value to `false` by default. * The purpose of this option is to avoid bandwidth-intensive glyph server requests. (See [Use locally generated ideographs](https://www.mapbox.com/mapbox-gl-js/example/local-ideographs).) diff --git a/src/util/verticalize_punctuation.js b/src/util/verticalize_punctuation.js index 2fd360ccb56..e9ca2752cda 100644 --- a/src/util/verticalize_punctuation.js +++ b/src/util/verticalize_punctuation.js @@ -88,14 +88,14 @@ export const verticalizedCharacterMap = { '」': '﹂' }; -export default function verticalizePunctuation(input: string) { +export default function verticalizePunctuation(input: string, skipContextChecking: boolean) { let output = ''; for (let i = 0; i < input.length; i++) { const nextCharCode = input.charCodeAt(i + 1) || null; const prevCharCode = input.charCodeAt(i - 1) || null; - const canReplacePunctuation = ( + const canReplacePunctuation = skipContextChecking || ( (!nextCharCode || !charHasRotatedVerticalOrientation(nextCharCode) || verticalizedCharacterMap[input[i + 1]]) && (!prevCharCode || !charHasRotatedVerticalOrientation(prevCharCode) || verticalizedCharacterMap[input[i - 1]]) ); @@ -110,3 +110,13 @@ export default function verticalizePunctuation(input: string) { return output; } +export function isVerticalClosePunctuation(chr: string) { + return chr === '︶' || chr === '﹈' || chr === '︸' || chr === '﹄' || chr === '﹂' || chr === '︾' || + chr === '︼' || chr === '︺' || chr === '︘' || chr === '﹀' || chr === '︐' || chr === '︓' || + chr === '︔' || chr === '`' || chr === ' ̄' || chr === '︑' || chr === '︒'; +} + +export function isVerticalOpenPunctuation(chr: string) { + return chr === '︵' || chr === '﹇' || chr === '︷' || chr === '﹃' || chr === '﹁' || chr === '︽' || + chr === '︻' || chr === '︹' || chr === '︗' || chr === '︿'; +} diff --git a/test/expected/text-shaping-default.json b/test/expected/text-shaping-default.json index 06b10a5c11d..f7ea040548b 100644 --- a/test/expected/text-shaping-default.json +++ b/test/expected/text-shaping-default.json @@ -5,11 +5,11 @@ { "glyph": 97, "imageName": null, - "localGlyph": null, "x": -32.5, "y": -17, "vertical": false, "scale": 1, + "localGlyph": null, "fontStack": "Test", "sectionIndex": 0, "metrics": { @@ -29,11 +29,11 @@ { "glyph": 98, "imageName": null, - "localGlyph": null, "x": -19.5, "y": -17, "vertical": false, "scale": 1, + "localGlyph": null, "fontStack": "Test", "sectionIndex": 0, "metrics": { @@ -53,11 +53,11 @@ { "glyph": 99, "imageName": null, - "localGlyph": null, "x": -5.5, "y": -17, "vertical": false, "scale": 1, + "localGlyph": null, "fontStack": "Test", "sectionIndex": 0, "metrics": { @@ -77,11 +77,11 @@ { "glyph": 100, "imageName": null, - "localGlyph": null, "x": 5.5, "y": -17, "vertical": false, "scale": 1, + "localGlyph": null, "fontStack": "Test", "sectionIndex": 0, "metrics": { @@ -101,11 +101,11 @@ { "glyph": 101, "imageName": null, - "localGlyph": null, "x": 19.5, "y": -17, "vertical": false, "scale": 1, + "localGlyph": null, "fontStack": "Test", "sectionIndex": 0, "metrics": { @@ -133,5 +133,6 @@ "right": 32.5, "writingMode": 1, "iconsInText": false, - "verticalizable": false -} + "verticalizable": false, + "hasBaseline": false +} \ No newline at end of file diff --git a/test/expected/text-shaping-images-horizontal.json b/test/expected/text-shaping-images-horizontal.json index 6d5c3c950a8..3ee3d178238 100644 --- a/test/expected/text-shaping-images-horizontal.json +++ b/test/expected/text-shaping-images-horizontal.json @@ -5,11 +5,11 @@ { "glyph": 70, "imageName": null, - "localGlyph": null, "x": -53, - "y": -34.5, + "y": -39.5, "vertical": false, "scale": 1, + "localGlyph": null, "fontStack": "Test", "sectionIndex": 0, "metrics": { @@ -29,11 +29,11 @@ { "glyph": 111, "imageName": null, - "localGlyph": null, "x": -41, - "y": -34.5, + "y": -39.5, "vertical": false, "scale": 1, + "localGlyph": null, "fontStack": "Test", "sectionIndex": 0, "metrics": { @@ -53,11 +53,11 @@ { "glyph": 111, "imageName": null, - "localGlyph": null, "x": -27, - "y": -34.5, + "y": -39.5, "vertical": false, "scale": 1, + "localGlyph": null, "fontStack": "Test", "sectionIndex": 0, "metrics": { @@ -77,20 +77,20 @@ { "glyph": 57344, "imageName": "square", - "localGlyph": false, "x": -13, - "y": -31.5, + "y": -36.5, "vertical": false, "scale": 1.5, + "localGlyph": false, "fontStack": "", "sectionIndex": 1, "metrics": { "width": 14, "height": 14, "left": 1, - "localGlyph": false, "top": -3, - "advance": 14 + "advance": 14, + "localGlyph": false }, "rect": { "x": 0, @@ -102,20 +102,20 @@ { "glyph": 57345, "imageName": "wide", - "localGlyph": false, "x": 8, - "y": -31.5, + "y": -36.5, "vertical": false, "scale": 1.5, + "localGlyph": false, "fontStack": "", "sectionIndex": 2, "metrics": { "width": 30, "height": 14, "left": 1, - "localGlyph": false, "top": -3, - "advance": 30 + "advance": 30, + "localGlyph": false }, "rect": { "x": 0, @@ -132,20 +132,20 @@ { "glyph": 57346, "imageName": "tall", - "localGlyph": false, "x": -42, - "y": -10.5, + "y": -15.5, "vertical": false, "scale": 1.5, + "localGlyph": false, "fontStack": "", "sectionIndex": 4, "metrics": { "width": 14, "height": 30, "left": 1, - "localGlyph": false, "top": -3, - "advance": 14 + "advance": 14, + "localGlyph": false }, "rect": { "x": 0, @@ -157,20 +157,20 @@ { "glyph": 57347, "imageName": "square", - "localGlyph": false, "x": -21, - "y": 13.5, + "y": 8.5, "vertical": false, "scale": 1.5, + "localGlyph": false, "fontStack": "", "sectionIndex": 5, "metrics": { "width": 14, "height": 14, "left": 1, - "localGlyph": false, "top": -3, - "advance": 14 + "advance": 14, + "localGlyph": false }, "rect": { "x": 0, @@ -182,11 +182,11 @@ { "glyph": 32, "imageName": null, - "localGlyph": null, "x": 0, - "y": 10.5, + "y": 5.5, "vertical": false, "scale": 1, + "localGlyph": null, "fontStack": "Test", "sectionIndex": 6, "metrics": { @@ -206,11 +206,11 @@ { "glyph": 98, "imageName": null, - "localGlyph": null, "x": 6, - "y": 10.5, + "y": 5.5, "vertical": false, "scale": 1, + "localGlyph": null, "fontStack": "Test", "sectionIndex": 6, "metrics": { @@ -230,11 +230,11 @@ { "glyph": 97, "imageName": null, - "localGlyph": null, "x": 20, - "y": 10.5, + "y": 5.5, "vertical": false, "scale": 1, + "localGlyph": null, "fontStack": "Test", "sectionIndex": 6, "metrics": { @@ -254,11 +254,11 @@ { "glyph": 114, "imageName": null, - "localGlyph": null, "x": 33, - "y": 10.5, + "y": 5.5, "vertical": false, "scale": 1, + "localGlyph": null, "fontStack": "Test", "sectionIndex": 6, "metrics": { @@ -286,5 +286,6 @@ "right": 53, "writingMode": 1, "iconsInText": true, - "verticalizable": false -} + "verticalizable": false, + "hasBaseline": false +} \ No newline at end of file diff --git a/test/expected/text-shaping-images-vertical.json b/test/expected/text-shaping-images-vertical.json index eb9480fe808..00f451b1814 100644 --- a/test/expected/text-shaping-images-vertical.json +++ b/test/expected/text-shaping-images-vertical.json @@ -5,11 +5,11 @@ { "glyph": 19977, "imageName": null, - "localGlyph": null, "x": -33, - "y": -13.5, + "y": -18.5, "vertical": true, "scale": 1, + "localGlyph": null, "fontStack": "Test", "sectionIndex": 0, "metrics": { @@ -29,20 +29,20 @@ { "glyph": 57344, "imageName": "square", - "localGlyph": false, "x": -9, - "y": -10.5, + "y": -15.5, "vertical": true, "scale": 1.5, + "localGlyph": false, "fontStack": "", "sectionIndex": 1, "metrics": { "width": 14, "height": 14, "left": 1, - "localGlyph": false, "top": -3, - "advance": 14 + "advance": 14, + "localGlyph": false }, "rect": { "x": 0, @@ -54,20 +54,20 @@ { "glyph": 57345, "imageName": "wide", - "localGlyph": false, "x": 12, - "y": -10.5, + "y": -15.5, "vertical": true, "scale": 1.5, + "localGlyph": false, "fontStack": "", "sectionIndex": 2, "metrics": { "width": 30, "height": 14, "left": 1, - "localGlyph": false, "top": -3, - "advance": 14 + "advance": 14, + "localGlyph": false }, "rect": { "x": 0, @@ -84,20 +84,20 @@ { "glyph": 57346, "imageName": "tall", - "localGlyph": false, "x": -43.5, - "y": -10.5, + "y": -15.5, "vertical": true, "scale": 1.5, + "localGlyph": false, "fontStack": "", "sectionIndex": 4, "metrics": { "width": 14, "height": 30, "left": 1, - "localGlyph": false, "top": -3, - "advance": 30 + "advance": 30, + "localGlyph": false }, "rect": { "x": 0, @@ -109,20 +109,20 @@ { "glyph": 57347, "imageName": "square", - "localGlyph": false, "x": 1.5, - "y": 13.5, + "y": 8.5, "vertical": true, "scale": 1.5, + "localGlyph": false, "fontStack": "", "sectionIndex": 5, "metrics": { "width": 14, "height": 14, "left": 1, - "localGlyph": false, "top": -3, - "advance": 14 + "advance": 14, + "localGlyph": false }, "rect": { "x": 0, @@ -134,11 +134,11 @@ { "glyph": 19977, "imageName": null, - "localGlyph": null, "x": 22.5, - "y": 10.5, + "y": 5.5, "vertical": true, "scale": 1, + "localGlyph": null, "fontStack": "Test", "sectionIndex": 6, "metrics": { @@ -166,5 +166,6 @@ "right": 45, "writingMode": 2, "iconsInText": true, - "verticalizable": true -} + "verticalizable": true, + "hasBaseline": false +} \ No newline at end of file diff --git a/test/expected/text-shaping-linebreak.json b/test/expected/text-shaping-linebreak.json index f516ad39411..ab980794ac6 100644 --- a/test/expected/text-shaping-linebreak.json +++ b/test/expected/text-shaping-linebreak.json @@ -5,11 +5,11 @@ { "glyph": 97, "imageName": null, - "localGlyph": null, "x": -32.5, "y": -29, "vertical": false, "scale": 1, + "localGlyph": null, "fontStack": "Test", "sectionIndex": 0, "metrics": { @@ -29,11 +29,11 @@ { "glyph": 98, "imageName": null, - "localGlyph": null, "x": -19.5, "y": -29, "vertical": false, "scale": 1, + "localGlyph": null, "fontStack": "Test", "sectionIndex": 0, "metrics": { @@ -53,11 +53,11 @@ { "glyph": 99, "imageName": null, - "localGlyph": null, "x": -5.5, "y": -29, "vertical": false, "scale": 1, + "localGlyph": null, "fontStack": "Test", "sectionIndex": 0, "metrics": { @@ -77,11 +77,11 @@ { "glyph": 100, "imageName": null, - "localGlyph": null, "x": 5.5, "y": -29, "vertical": false, "scale": 1, + "localGlyph": null, "fontStack": "Test", "sectionIndex": 0, "metrics": { @@ -101,11 +101,11 @@ { "glyph": 101, "imageName": null, - "localGlyph": null, "x": 19.5, "y": -29, "vertical": false, "scale": 1, + "localGlyph": null, "fontStack": "Test", "sectionIndex": 0, "metrics": { @@ -130,11 +130,11 @@ { "glyph": 97, "imageName": null, - "localGlyph": null, "x": -32.5, "y": -5, "vertical": false, "scale": 1, + "localGlyph": null, "fontStack": "Test", "sectionIndex": 0, "metrics": { @@ -154,11 +154,11 @@ { "glyph": 98, "imageName": null, - "localGlyph": null, "x": -19.5, "y": -5, "vertical": false, "scale": 1, + "localGlyph": null, "fontStack": "Test", "sectionIndex": 0, "metrics": { @@ -178,11 +178,11 @@ { "glyph": 99, "imageName": null, - "localGlyph": null, "x": -5.5, "y": -5, "vertical": false, "scale": 1, + "localGlyph": null, "fontStack": "Test", "sectionIndex": 0, "metrics": { @@ -202,11 +202,11 @@ { "glyph": 100, "imageName": null, - "localGlyph": null, "x": 5.5, "y": -5, "vertical": false, "scale": 1, + "localGlyph": null, "fontStack": "Test", "sectionIndex": 0, "metrics": { @@ -226,11 +226,11 @@ { "glyph": 101, "imageName": null, - "localGlyph": null, "x": 19.5, "y": -5, "vertical": false, "scale": 1, + "localGlyph": null, "fontStack": "Test", "sectionIndex": 0, "metrics": { @@ -258,5 +258,6 @@ "right": 32.5, "writingMode": 1, "iconsInText": false, - "verticalizable": false -} + "verticalizable": false, + "hasBaseline": false +} \ No newline at end of file diff --git a/test/expected/text-shaping-newline.json b/test/expected/text-shaping-newline.json index 2a166c23e6f..d5769216aee 100644 --- a/test/expected/text-shaping-newline.json +++ b/test/expected/text-shaping-newline.json @@ -5,11 +5,11 @@ { "glyph": 97, "imageName": null, - "localGlyph": null, "x": -32.5, "y": -29, "vertical": false, "scale": 1, + "localGlyph": null, "fontStack": "Test", "sectionIndex": 0, "metrics": { @@ -29,11 +29,11 @@ { "glyph": 98, "imageName": null, - "localGlyph": null, "x": -19.5, "y": -29, "vertical": false, "scale": 1, + "localGlyph": null, "fontStack": "Test", "sectionIndex": 0, "metrics": { @@ -53,11 +53,11 @@ { "glyph": 99, "imageName": null, - "localGlyph": null, "x": -5.5, "y": -29, "vertical": false, "scale": 1, + "localGlyph": null, "fontStack": "Test", "sectionIndex": 0, "metrics": { @@ -77,11 +77,11 @@ { "glyph": 100, "imageName": null, - "localGlyph": null, "x": 5.5, "y": -29, "vertical": false, "scale": 1, + "localGlyph": null, "fontStack": "Test", "sectionIndex": 0, "metrics": { @@ -101,11 +101,11 @@ { "glyph": 101, "imageName": null, - "localGlyph": null, "x": 19.5, "y": -29, "vertical": false, "scale": 1, + "localGlyph": null, "fontStack": "Test", "sectionIndex": 0, "metrics": { @@ -130,11 +130,11 @@ { "glyph": 97, "imageName": null, - "localGlyph": null, "x": -32.5, "y": -5, "vertical": false, "scale": 1, + "localGlyph": null, "fontStack": "Test", "sectionIndex": 0, "metrics": { @@ -154,11 +154,11 @@ { "glyph": 98, "imageName": null, - "localGlyph": null, "x": -19.5, "y": -5, "vertical": false, "scale": 1, + "localGlyph": null, "fontStack": "Test", "sectionIndex": 0, "metrics": { @@ -178,11 +178,11 @@ { "glyph": 99, "imageName": null, - "localGlyph": null, "x": -5.5, "y": -5, "vertical": false, "scale": 1, + "localGlyph": null, "fontStack": "Test", "sectionIndex": 0, "metrics": { @@ -202,11 +202,11 @@ { "glyph": 100, "imageName": null, - "localGlyph": null, "x": 5.5, "y": -5, "vertical": false, "scale": 1, + "localGlyph": null, "fontStack": "Test", "sectionIndex": 0, "metrics": { @@ -226,11 +226,11 @@ { "glyph": 101, "imageName": null, - "localGlyph": null, "x": 19.5, "y": -5, "vertical": false, "scale": 1, + "localGlyph": null, "fontStack": "Test", "sectionIndex": 0, "metrics": { @@ -258,5 +258,6 @@ "right": 32.5, "writingMode": 1, "iconsInText": false, - "verticalizable": false -} + "verticalizable": false, + "hasBaseline": false +} \ No newline at end of file diff --git a/test/expected/text-shaping-newlines-in-middle.json b/test/expected/text-shaping-newlines-in-middle.json index c3ef0a14207..04987ff8c9d 100644 --- a/test/expected/text-shaping-newlines-in-middle.json +++ b/test/expected/text-shaping-newlines-in-middle.json @@ -5,11 +5,11 @@ { "glyph": 97, "imageName": null, - "localGlyph": null, "x": -32.5, "y": -41, "vertical": false, "scale": 1, + "localGlyph": null, "fontStack": "Test", "sectionIndex": 0, "metrics": { @@ -29,11 +29,11 @@ { "glyph": 98, "imageName": null, - "localGlyph": null, "x": -19.5, "y": -41, "vertical": false, "scale": 1, + "localGlyph": null, "fontStack": "Test", "sectionIndex": 0, "metrics": { @@ -53,11 +53,11 @@ { "glyph": 99, "imageName": null, - "localGlyph": null, "x": -5.5, "y": -41, "vertical": false, "scale": 1, + "localGlyph": null, "fontStack": "Test", "sectionIndex": 0, "metrics": { @@ -77,11 +77,11 @@ { "glyph": 100, "imageName": null, - "localGlyph": null, "x": 5.5, "y": -41, "vertical": false, "scale": 1, + "localGlyph": null, "fontStack": "Test", "sectionIndex": 0, "metrics": { @@ -101,11 +101,11 @@ { "glyph": 101, "imageName": null, - "localGlyph": null, "x": 19.5, "y": -41, "vertical": false, "scale": 1, + "localGlyph": null, "fontStack": "Test", "sectionIndex": 0, "metrics": { @@ -134,11 +134,11 @@ { "glyph": 97, "imageName": null, - "localGlyph": null, "x": -32.5, "y": 7, "vertical": false, "scale": 1, + "localGlyph": null, "fontStack": "Test", "sectionIndex": 0, "metrics": { @@ -158,11 +158,11 @@ { "glyph": 98, "imageName": null, - "localGlyph": null, "x": -19.5, "y": 7, "vertical": false, "scale": 1, + "localGlyph": null, "fontStack": "Test", "sectionIndex": 0, "metrics": { @@ -182,11 +182,11 @@ { "glyph": 99, "imageName": null, - "localGlyph": null, "x": -5.5, "y": 7, "vertical": false, "scale": 1, + "localGlyph": null, "fontStack": "Test", "sectionIndex": 0, "metrics": { @@ -206,11 +206,11 @@ { "glyph": 100, "imageName": null, - "localGlyph": null, "x": 5.5, "y": 7, "vertical": false, "scale": 1, + "localGlyph": null, "fontStack": "Test", "sectionIndex": 0, "metrics": { @@ -230,11 +230,11 @@ { "glyph": 101, "imageName": null, - "localGlyph": null, "x": 19.5, "y": 7, "vertical": false, "scale": 1, + "localGlyph": null, "fontStack": "Test", "sectionIndex": 0, "metrics": { @@ -262,5 +262,6 @@ "right": 32.5, "writingMode": 1, "iconsInText": false, - "verticalizable": false -} + "verticalizable": false, + "hasBaseline": false +} \ No newline at end of file diff --git a/test/expected/text-shaping-null.json b/test/expected/text-shaping-null.json index 3422152fb8c..23a32f6ef4a 100644 --- a/test/expected/text-shaping-null.json +++ b/test/expected/text-shaping-null.json @@ -5,11 +5,11 @@ { "glyph": 104, "imageName": null, - "localGlyph": null, "x": -10, "y": -17, "vertical": false, "scale": 1, + "localGlyph": null, "fontStack": "Test", "sectionIndex": 0, "metrics": { @@ -29,11 +29,11 @@ { "glyph": 105, "imageName": null, - "localGlyph": null, "x": 4, "y": -17, "vertical": false, "scale": 1, + "localGlyph": null, "fontStack": "Test", "sectionIndex": 0, "metrics": { @@ -61,5 +61,6 @@ "right": 10, "writingMode": 1, "iconsInText": false, - "verticalizable": false -} + "verticalizable": false, + "hasBaseline": false +} \ No newline at end of file diff --git a/test/expected/text-shaping-spacing.json b/test/expected/text-shaping-spacing.json index 7639e2bded6..3ccdae3f27c 100644 --- a/test/expected/text-shaping-spacing.json +++ b/test/expected/text-shaping-spacing.json @@ -5,11 +5,11 @@ { "glyph": 97, "imageName": null, - "localGlyph": null, "x": -38.5, "y": -17, "vertical": false, "scale": 1, + "localGlyph": null, "fontStack": "Test", "sectionIndex": 0, "metrics": { @@ -29,11 +29,11 @@ { "glyph": 98, "imageName": null, - "localGlyph": null, "x": -22.5, "y": -17, "vertical": false, "scale": 1, + "localGlyph": null, "fontStack": "Test", "sectionIndex": 0, "metrics": { @@ -53,11 +53,11 @@ { "glyph": 99, "imageName": null, - "localGlyph": null, "x": -5.5, "y": -17, "vertical": false, "scale": 1, + "localGlyph": null, "fontStack": "Test", "sectionIndex": 0, "metrics": { @@ -77,11 +77,11 @@ { "glyph": 100, "imageName": null, - "localGlyph": null, "x": 8.5, "y": -17, "vertical": false, "scale": 1, + "localGlyph": null, "fontStack": "Test", "sectionIndex": 0, "metrics": { @@ -101,11 +101,11 @@ { "glyph": 101, "imageName": null, - "localGlyph": null, "x": 25.5, "y": -17, "vertical": false, "scale": 1, + "localGlyph": null, "fontStack": "Test", "sectionIndex": 0, "metrics": { @@ -133,5 +133,6 @@ "right": 38.5, "writingMode": 1, "iconsInText": false, - "verticalizable": false -} + "verticalizable": false, + "hasBaseline": false +} \ No newline at end of file diff --git a/test/expected/text-shaping-zero-width-space.json b/test/expected/text-shaping-zero-width-space.json index 9aa35f5f08f..8fb1436a667 100644 --- a/test/expected/text-shaping-zero-width-space.json +++ b/test/expected/text-shaping-zero-width-space.json @@ -5,11 +5,11 @@ { "glyph": 19977, "imageName": null, - "localGlyph": null, "x": -63, "y": -41, "vertical": false, "scale": 1, + "localGlyph": null, "fontStack": "Test", "sectionIndex": 0, "metrics": { @@ -29,11 +29,11 @@ { "glyph": 19977, "imageName": null, - "localGlyph": null, "x": -42, "y": -41, "vertical": false, "scale": 1, + "localGlyph": null, "fontStack": "Test", "sectionIndex": 0, "metrics": { @@ -53,11 +53,11 @@ { "glyph": 19977, "imageName": null, - "localGlyph": null, "x": -21, "y": -41, "vertical": false, "scale": 1, + "localGlyph": null, "fontStack": "Test", "sectionIndex": 0, "metrics": { @@ -77,11 +77,11 @@ { "glyph": 19977, "imageName": null, - "localGlyph": null, "x": 0, "y": -41, "vertical": false, "scale": 1, + "localGlyph": null, "fontStack": "Test", "sectionIndex": 0, "metrics": { @@ -101,11 +101,11 @@ { "glyph": 19977, "imageName": null, - "localGlyph": null, "x": 21, "y": -41, "vertical": false, "scale": 1, + "localGlyph": null, "fontStack": "Test", "sectionIndex": 0, "metrics": { @@ -125,11 +125,11 @@ { "glyph": 19977, "imageName": null, - "localGlyph": null, "x": 42, "y": -41, "vertical": false, "scale": 1, + "localGlyph": null, "fontStack": "Test", "sectionIndex": 0, "metrics": { @@ -154,11 +154,11 @@ { "glyph": 19977, "imageName": null, - "localGlyph": null, "x": -63, "y": -17, "vertical": false, "scale": 1, + "localGlyph": null, "fontStack": "Test", "sectionIndex": 0, "metrics": { @@ -178,11 +178,11 @@ { "glyph": 19977, "imageName": null, - "localGlyph": null, "x": -42, "y": -17, "vertical": false, "scale": 1, + "localGlyph": null, "fontStack": "Test", "sectionIndex": 0, "metrics": { @@ -202,11 +202,11 @@ { "glyph": 19977, "imageName": null, - "localGlyph": null, "x": -21, "y": -17, "vertical": false, "scale": 1, + "localGlyph": null, "fontStack": "Test", "sectionIndex": 0, "metrics": { @@ -226,11 +226,11 @@ { "glyph": 19977, "imageName": null, - "localGlyph": null, "x": 0, "y": -17, "vertical": false, "scale": 1, + "localGlyph": null, "fontStack": "Test", "sectionIndex": 0, "metrics": { @@ -250,11 +250,11 @@ { "glyph": 19977, "imageName": null, - "localGlyph": null, "x": 21, "y": -17, "vertical": false, "scale": 1, + "localGlyph": null, "fontStack": "Test", "sectionIndex": 0, "metrics": { @@ -274,11 +274,11 @@ { "glyph": 19977, "imageName": null, - "localGlyph": null, "x": 42, "y": -17, "vertical": false, "scale": 1, + "localGlyph": null, "fontStack": "Test", "sectionIndex": 0, "metrics": { @@ -303,11 +303,11 @@ { "glyph": 19977, "imageName": null, - "localGlyph": null, "x": -21, "y": 7, "vertical": false, "scale": 1, + "localGlyph": null, "fontStack": "Test", "sectionIndex": 0, "metrics": { @@ -327,11 +327,11 @@ { "glyph": 19977, "imageName": null, - "localGlyph": null, "x": 0, "y": 7, "vertical": false, "scale": 1, + "localGlyph": null, "fontStack": "Test", "sectionIndex": 0, "metrics": { @@ -359,5 +359,6 @@ "right": 63, "writingMode": 1, "iconsInText": false, - "verticalizable": false -} + "verticalizable": false, + "hasBaseline": false +} \ No newline at end of file diff --git a/test/fixtures/fontstack-glyphs.json b/test/fixtures/fontstack-glyphs.json index bb8c7967f28..ee6156be659 100644 --- a/test/fixtures/fontstack-glyphs.json +++ b/test/fixtures/fontstack-glyphs.json @@ -1,4 +1,5 @@ { + "glyphs": { "32": { "id": 32, "metrics": { @@ -2121,5 +2122,5 @@ "advance": 21 }, "rect": {"x": 0, "y": 0, "w": 32, "h": 32} - } + }} } diff --git a/test/ignores.json b/test/ignores.json index c3a536134dc..6cdca5868ba 100644 --- a/test/ignores.json +++ b/test/ignores.json @@ -20,20 +20,6 @@ "render-tests/symbol-placement/line-center-buffer-tile-map-mode": "skip - mapbox-gl-js does not support tile-mode", "render-tests/symbol-placement/line-center-tile-map-mode": "skip - mapbox-gl-js does not support tile-mode", "render-tests/symbol-sort-key/text-ignore-placement": "skip - text drawn over icons", - "render-tests/text-font-metrics/font-with-baseline-font-without-baseline": "mapbox-gl-js-internal#74", - "render-tests/text-font-metrics/font-with-image-vertical": "mapbox-gl-js-internal#75", - "render-tests/text-font-metrics/latin-alphabets-vertical": "mapbox-gl-js-internal#75", - "render-tests/text-font-metrics/latin-alphabets-vertical-no-ascender-descender": "mapbox-gl-js-internal#75", - "render-tests/text-font-metrics/line-placement-vertical-shaping-with-punctuations": "mapbox-gl-js-internal#76", - "render-tests/text-font-metrics/mixed-fonts-both-with-baseline": "mapbox-gl-js-internal#74", - "render-tests/text-font-metrics/mixed-fonts-with-image": "mapbox-gl-js-internal#74", - "render-tests/text-font-metrics/mixed-fonts-with-image-vertical": "mapbox-gl-js-internal#74", - "render-tests/text-font-metrics/mixed-fonts-with-images-vertical": "mapbox-gl-js-internal#75", - "render-tests/text-font-metrics/mixed-fonts-with-scales": "mapbox-gl-js-internal#74", - "render-tests/text-font-metrics/multiline-point-placement-vertical-shaping-with-punctuations": "mapbox-gl-js-internal#76", - "render-tests/text-font-metrics/point-placement-vertical-shaping-with-punctuations": "mapbox-gl-js-internal#76", - "render-tests/text-font-metrics/punctuations-vertical": "mapbox-gl-js-internal#75", - "render-tests/text-font-metrics/vertical-shaping-with-ZWSP": "mapbox-gl-js-internal#76", "render-tests/text-variable-anchor/all-anchors-labels-priority-tile-map-mode": "skip - mapbox-gl-js does not support tile-mode", "render-tests/text-variable-anchor/all-anchors-tile-map-mode": "skip - mapbox-gl-js does not support tile-mode", "render-tests/text-variable-anchor/avoid-edges-tile-map-mode": "skip - mapbox-gl-js does not support tile-mode", diff --git a/test/integration/glyphs/ArialAscenderDescender/0-255.pbf b/test/integration/glyphs/ArialAscenderDescender/0-255.pbf new file mode 100644 index 00000000000..76598023931 Binary files /dev/null and b/test/integration/glyphs/ArialAscenderDescender/0-255.pbf differ diff --git a/test/integration/glyphs/ArialAscenderDescender/12288-12543.pbf b/test/integration/glyphs/ArialAscenderDescender/12288-12543.pbf new file mode 100644 index 00000000000..96da25d34cb Binary files /dev/null and b/test/integration/glyphs/ArialAscenderDescender/12288-12543.pbf differ diff --git a/test/integration/glyphs/ArialAscenderDescender/37120-37375.pbf b/test/integration/glyphs/ArialAscenderDescender/37120-37375.pbf new file mode 100644 index 00000000000..fdee9f9cffb Binary files /dev/null and b/test/integration/glyphs/ArialAscenderDescender/37120-37375.pbf differ diff --git a/test/integration/glyphs/ArialAscenderDescender/65024-65279.pbf b/test/integration/glyphs/ArialAscenderDescender/65024-65279.pbf new file mode 100644 index 00000000000..29b9791858f Binary files /dev/null and b/test/integration/glyphs/ArialAscenderDescender/65024-65279.pbf differ diff --git a/test/integration/glyphs/NotoCJKAscenderDescender/0-255.pbf b/test/integration/glyphs/NotoCJKAscenderDescender/0-255.pbf new file mode 100644 index 00000000000..9812e9389dc Binary files /dev/null and b/test/integration/glyphs/NotoCJKAscenderDescender/0-255.pbf differ diff --git a/test/integration/glyphs/NotoCJKAscenderDescender/12288-12543.pbf b/test/integration/glyphs/NotoCJKAscenderDescender/12288-12543.pbf new file mode 100644 index 00000000000..9e4fd3fcba4 Binary files /dev/null and b/test/integration/glyphs/NotoCJKAscenderDescender/12288-12543.pbf differ diff --git a/test/integration/glyphs/NotoCJKAscenderDescender/37120-37375.pbf b/test/integration/glyphs/NotoCJKAscenderDescender/37120-37375.pbf new file mode 100644 index 00000000000..d457e7dcae4 Binary files /dev/null and b/test/integration/glyphs/NotoCJKAscenderDescender/37120-37375.pbf differ diff --git a/test/integration/glyphs/NotoCJKAscenderDescender/65024-65279.pbf b/test/integration/glyphs/NotoCJKAscenderDescender/65024-65279.pbf new file mode 100644 index 00000000000..6c885d048ae Binary files /dev/null and b/test/integration/glyphs/NotoCJKAscenderDescender/65024-65279.pbf differ diff --git a/test/integration/render-tests/regressions/mapbox-gl-js#5546/expected.png b/test/integration/render-tests/regressions/mapbox-gl-js#5546/expected.png index e1a8189c6bc..690f2988dc4 100644 Binary files a/test/integration/render-tests/regressions/mapbox-gl-js#5546/expected.png and b/test/integration/render-tests/regressions/mapbox-gl-js#5546/expected.png differ diff --git a/test/integration/render-tests/symbol-cross-fade/chinese-disable-fade-in/expected.png b/test/integration/render-tests/symbol-cross-fade/chinese-disable-fade-in/expected.png index 42f2d8d0a8d..59c0d320b04 100644 Binary files a/test/integration/render-tests/symbol-cross-fade/chinese-disable-fade-in/expected.png and b/test/integration/render-tests/symbol-cross-fade/chinese-disable-fade-in/expected.png differ diff --git a/test/integration/render-tests/symbol-cross-fade/chinese-fade-in/expected.png b/test/integration/render-tests/symbol-cross-fade/chinese-fade-in/expected.png index c09b8b80479..be62c67a900 100644 Binary files a/test/integration/render-tests/symbol-cross-fade/chinese-fade-in/expected.png and b/test/integration/render-tests/symbol-cross-fade/chinese-fade-in/expected.png differ diff --git a/test/integration/render-tests/text-field/formatted-arabic/expected.png b/test/integration/render-tests/text-field/formatted-arabic/expected.png index cbb7afa2252..dbfd2befe58 100644 Binary files a/test/integration/render-tests/text-field/formatted-arabic/expected.png and b/test/integration/render-tests/text-field/formatted-arabic/expected.png differ diff --git a/test/integration/render-tests/text-field/formatted-images-multiline/expected.png b/test/integration/render-tests/text-field/formatted-images-multiline/expected.png index 1d2c7701d60..f2b579ccca6 100644 Binary files a/test/integration/render-tests/text-field/formatted-images-multiline/expected.png and b/test/integration/render-tests/text-field/formatted-images-multiline/expected.png differ diff --git a/test/integration/render-tests/text-field/formatted-images-vertical/expected.png b/test/integration/render-tests/text-field/formatted-images-vertical/expected.png index db1901df9e2..fe9401bd9b4 100644 Binary files a/test/integration/render-tests/text-field/formatted-images-vertical/expected.png and b/test/integration/render-tests/text-field/formatted-images-vertical/expected.png differ diff --git a/test/integration/render-tests/text-field/formatted-line/expected.png b/test/integration/render-tests/text-field/formatted-line/expected.png index a5c6a684a08..49e7329e84f 100644 Binary files a/test/integration/render-tests/text-field/formatted-line/expected.png and b/test/integration/render-tests/text-field/formatted-line/expected.png differ diff --git a/test/integration/render-tests/text-field/formatted-text-color/expected.png b/test/integration/render-tests/text-field/formatted-text-color/expected.png index 03a96d89cde..837102904e9 100644 Binary files a/test/integration/render-tests/text-field/formatted-text-color/expected.png and b/test/integration/render-tests/text-field/formatted-text-color/expected.png differ diff --git a/test/integration/render-tests/text-field/formatted/expected.png b/test/integration/render-tests/text-field/formatted/expected.png index c120b5a0662..257e0213d1e 100644 Binary files a/test/integration/render-tests/text-field/formatted/expected.png and b/test/integration/render-tests/text-field/formatted/expected.png differ diff --git a/test/integration/render-tests/text-field/terrain/formatted-images-vertical/expected.png b/test/integration/render-tests/text-field/terrain/formatted-images-vertical/expected.png index db1901df9e2..663062c11ff 100644 Binary files a/test/integration/render-tests/text-field/terrain/formatted-images-vertical/expected.png and b/test/integration/render-tests/text-field/terrain/formatted-images-vertical/expected.png differ diff --git a/test/integration/render-tests/text-font-metrics/font-with-baseline-font-without-baseline/expected.png b/test/integration/render-tests/text-font-metrics/font-with-baseline-font-without-baseline/expected.png index 09b3914fd7c..c39f4649549 100644 Binary files a/test/integration/render-tests/text-font-metrics/font-with-baseline-font-without-baseline/expected.png and b/test/integration/render-tests/text-font-metrics/font-with-baseline-font-without-baseline/expected.png differ diff --git a/test/integration/render-tests/text-font-metrics/font-with-baseline-font-without-baseline/style.json b/test/integration/render-tests/text-font-metrics/font-with-baseline-font-without-baseline/style.json index 823b479ffc5..dab45f5755b 100644 --- a/test/integration/render-tests/text-font-metrics/font-with-baseline-font-without-baseline/style.json +++ b/test/integration/render-tests/text-font-metrics/font-with-baseline-font-without-baseline/style.json @@ -2,7 +2,6 @@ "version": 8, "metadata": { "test": { - "collisionDebug": true, "height": 100, "width": 100 } diff --git a/test/integration/render-tests/text-font-metrics/font-with-image-vertical/expected.png b/test/integration/render-tests/text-font-metrics/font-with-image-vertical/expected.png index 2107dce7ebf..24b767ca9f7 100644 Binary files a/test/integration/render-tests/text-font-metrics/font-with-image-vertical/expected.png and b/test/integration/render-tests/text-font-metrics/font-with-image-vertical/expected.png differ diff --git a/test/integration/render-tests/text-font-metrics/font-with-image-vertical/style.json b/test/integration/render-tests/text-font-metrics/font-with-image-vertical/style.json index c029a4e690b..98f0caca164 100644 --- a/test/integration/render-tests/text-font-metrics/font-with-image-vertical/style.json +++ b/test/integration/render-tests/text-font-metrics/font-with-image-vertical/style.json @@ -2,7 +2,7 @@ "version": 8, "metadata": { "test": { - "collisionDebug": true, + "height": 180, "width": 150 } diff --git a/test/integration/render-tests/text-font-metrics/latin-alphabets-vertical-no-ascender-descender/expected.png b/test/integration/render-tests/text-font-metrics/latin-alphabets-vertical-no-ascender-descender/expected.png index 047dc41920d..ff506241794 100644 Binary files a/test/integration/render-tests/text-font-metrics/latin-alphabets-vertical-no-ascender-descender/expected.png and b/test/integration/render-tests/text-font-metrics/latin-alphabets-vertical-no-ascender-descender/expected.png differ diff --git a/test/integration/render-tests/text-font-metrics/latin-alphabets-vertical-no-ascender-descender/style.json b/test/integration/render-tests/text-font-metrics/latin-alphabets-vertical-no-ascender-descender/style.json index 335439039aa..d142b2d59c4 100644 --- a/test/integration/render-tests/text-font-metrics/latin-alphabets-vertical-no-ascender-descender/style.json +++ b/test/integration/render-tests/text-font-metrics/latin-alphabets-vertical-no-ascender-descender/style.json @@ -2,7 +2,6 @@ "version": 8, "metadata": { "test": { - "collisionDebug": true, "height": 170, "width": 120 } diff --git a/test/integration/render-tests/text-font-metrics/latin-alphabets-vertical/expected.png b/test/integration/render-tests/text-font-metrics/latin-alphabets-vertical/expected.png index 1963bce4948..6e39aa04122 100644 Binary files a/test/integration/render-tests/text-font-metrics/latin-alphabets-vertical/expected.png and b/test/integration/render-tests/text-font-metrics/latin-alphabets-vertical/expected.png differ diff --git a/test/integration/render-tests/text-font-metrics/latin-alphabets-vertical/style.json b/test/integration/render-tests/text-font-metrics/latin-alphabets-vertical/style.json index 5d81ba07150..e6857ce29a6 100644 --- a/test/integration/render-tests/text-font-metrics/latin-alphabets-vertical/style.json +++ b/test/integration/render-tests/text-font-metrics/latin-alphabets-vertical/style.json @@ -2,7 +2,6 @@ "version": 8, "metadata": { "test": { - "collisionDebug": true, "height": 170, "width": 120 } diff --git a/test/integration/render-tests/text-font-metrics/mixed-fonts-with-image-vertical/expected.png b/test/integration/render-tests/text-font-metrics/mixed-fonts-with-image-vertical/expected.png index d55bdd4feeb..8b80a2cacbc 100644 Binary files a/test/integration/render-tests/text-font-metrics/mixed-fonts-with-image-vertical/expected.png and b/test/integration/render-tests/text-font-metrics/mixed-fonts-with-image-vertical/expected.png differ diff --git a/test/integration/render-tests/text-font-metrics/mixed-fonts-with-image-vertical/style.json b/test/integration/render-tests/text-font-metrics/mixed-fonts-with-image-vertical/style.json index d106f167e8d..87d4b2d604a 100644 --- a/test/integration/render-tests/text-font-metrics/mixed-fonts-with-image-vertical/style.json +++ b/test/integration/render-tests/text-font-metrics/mixed-fonts-with-image-vertical/style.json @@ -2,7 +2,6 @@ "version": 8, "metadata": { "test": { - "collisionDebug": true, "height": 100, "width": 100 } diff --git a/test/integration/render-tests/text-font-metrics/mixed-fonts-with-image/expected.png b/test/integration/render-tests/text-font-metrics/mixed-fonts-with-image/expected.png index c836f6073a0..5b2d54adac1 100644 Binary files a/test/integration/render-tests/text-font-metrics/mixed-fonts-with-image/expected.png and b/test/integration/render-tests/text-font-metrics/mixed-fonts-with-image/expected.png differ diff --git a/test/integration/render-tests/text-font-metrics/mixed-fonts-with-image/style.json b/test/integration/render-tests/text-font-metrics/mixed-fonts-with-image/style.json index b2dc5afce4c..44dab14e144 100644 --- a/test/integration/render-tests/text-font-metrics/mixed-fonts-with-image/style.json +++ b/test/integration/render-tests/text-font-metrics/mixed-fonts-with-image/style.json @@ -2,7 +2,6 @@ "version": 8, "metadata": { "test": { - "collisionDebug": true, "height": 100, "width": 200 } diff --git a/test/integration/render-tests/text-font-metrics/mixed-fonts-with-images-vertical/expected.png b/test/integration/render-tests/text-font-metrics/mixed-fonts-with-images-vertical/expected.png index f9a34db54c3..52c2240ef47 100644 Binary files a/test/integration/render-tests/text-font-metrics/mixed-fonts-with-images-vertical/expected.png and b/test/integration/render-tests/text-font-metrics/mixed-fonts-with-images-vertical/expected.png differ diff --git a/test/integration/render-tests/text-font-metrics/mixed-fonts-with-images-vertical/style.json b/test/integration/render-tests/text-font-metrics/mixed-fonts-with-images-vertical/style.json index 87f8f7f014b..252e412c576 100644 --- a/test/integration/render-tests/text-font-metrics/mixed-fonts-with-images-vertical/style.json +++ b/test/integration/render-tests/text-font-metrics/mixed-fonts-with-images-vertical/style.json @@ -2,7 +2,6 @@ "version": 8, "metadata": { "test": { - "collisionDebug": true, "height": 250, "width": 200 } diff --git a/test/integration/render-tests/text-font-metrics/point-placement-vertical-shaping-with-punctuations/expected.png b/test/integration/render-tests/text-font-metrics/point-placement-vertical-shaping-with-punctuations/expected.png index 6e7bca6306e..20bb02ae5b1 100644 Binary files a/test/integration/render-tests/text-font-metrics/point-placement-vertical-shaping-with-punctuations/expected.png and b/test/integration/render-tests/text-font-metrics/point-placement-vertical-shaping-with-punctuations/expected.png differ diff --git a/test/integration/render-tests/text-font-metrics/point-placement-vertical-shaping-with-punctuations/style.json b/test/integration/render-tests/text-font-metrics/point-placement-vertical-shaping-with-punctuations/style.json index c4482638fa7..9d42b4d630a 100644 --- a/test/integration/render-tests/text-font-metrics/point-placement-vertical-shaping-with-punctuations/style.json +++ b/test/integration/render-tests/text-font-metrics/point-placement-vertical-shaping-with-punctuations/style.json @@ -2,7 +2,6 @@ "version": 8, "metadata": { "test": { - "collisionDebug": true, "height": 200, "width": 120 } diff --git a/test/integration/render-tests/text-font-metrics/punctuations-vertical/expected.png b/test/integration/render-tests/text-font-metrics/punctuations-vertical/expected.png index 6f1b6f4adc9..1ff02a4b453 100644 Binary files a/test/integration/render-tests/text-font-metrics/punctuations-vertical/expected.png and b/test/integration/render-tests/text-font-metrics/punctuations-vertical/expected.png differ diff --git a/test/integration/render-tests/text-font-metrics/punctuations-vertical/style.json b/test/integration/render-tests/text-font-metrics/punctuations-vertical/style.json index b8b0b7f7f8f..cde58e13fb9 100644 --- a/test/integration/render-tests/text-font-metrics/punctuations-vertical/style.json +++ b/test/integration/render-tests/text-font-metrics/punctuations-vertical/style.json @@ -2,7 +2,6 @@ "version": 8, "metadata": { "test": { - "collisionDebug": true, "height": 200, "width": 150 } diff --git a/test/integration/render-tests/text-font-metrics/vertical-shaping-with-ZWSP/expected.png b/test/integration/render-tests/text-font-metrics/vertical-shaping-with-ZWSP/expected.png index 9dda2fdeb78..9c31d3082d1 100644 Binary files a/test/integration/render-tests/text-font-metrics/vertical-shaping-with-ZWSP/expected.png and b/test/integration/render-tests/text-font-metrics/vertical-shaping-with-ZWSP/expected.png differ diff --git a/test/integration/render-tests/text-font-metrics/vertical-shaping-with-ZWSP/style.json b/test/integration/render-tests/text-font-metrics/vertical-shaping-with-ZWSP/style.json index 7499c1fa4d5..5b0660dd360 100644 --- a/test/integration/render-tests/text-font-metrics/vertical-shaping-with-ZWSP/style.json +++ b/test/integration/render-tests/text-font-metrics/vertical-shaping-with-ZWSP/style.json @@ -2,7 +2,6 @@ "version": 8, "metadata": { "test": { - "collisionDebug": true, "height": 150, "width": 100 } diff --git a/test/integration/render-tests/text-keep-upright/line-placement-flip-retain-mixed-CJK/expected.png b/test/integration/render-tests/text-keep-upright/line-placement-flip-retain-mixed-CJK/expected.png index 5cf785e22c3..4a67bd70d2f 100644 Binary files a/test/integration/render-tests/text-keep-upright/line-placement-flip-retain-mixed-CJK/expected.png and b/test/integration/render-tests/text-keep-upright/line-placement-flip-retain-mixed-CJK/expected.png differ diff --git a/test/integration/render-tests/text-rotate/with-offset/expected.png b/test/integration/render-tests/text-rotate/with-offset/expected.png index f1ae274732a..9c905e7d05a 100644 Binary files a/test/integration/render-tests/text-rotate/with-offset/expected.png and b/test/integration/render-tests/text-rotate/with-offset/expected.png differ diff --git a/test/integration/render-tests/text-rotate/with-offset/style.json b/test/integration/render-tests/text-rotate/with-offset/style.json index 26bd59f4f8a..b60fd0eac63 100644 --- a/test/integration/render-tests/text-rotate/with-offset/style.json +++ b/test/integration/render-tests/text-rotate/with-offset/style.json @@ -2,7 +2,7 @@ "version": 8, "metadata": { "test": { - "width": 128, + "width": 150, "height": 256, "allowed": 0.005, "collisionDebug": true diff --git a/test/integration/render-tests/text-writing-mode/line_label/chinese-punctuation/expected.png b/test/integration/render-tests/text-writing-mode/line_label/chinese-punctuation/expected.png index 74bea29b802..e66a1270cf8 100644 Binary files a/test/integration/render-tests/text-writing-mode/line_label/chinese-punctuation/expected.png and b/test/integration/render-tests/text-writing-mode/line_label/chinese-punctuation/expected.png differ diff --git a/test/integration/render-tests/text-writing-mode/line_label/chinese/expected.png b/test/integration/render-tests/text-writing-mode/line_label/chinese/expected.png index 249041857fc..4a0c36037a1 100644 Binary files a/test/integration/render-tests/text-writing-mode/line_label/chinese/expected.png and b/test/integration/render-tests/text-writing-mode/line_label/chinese/expected.png differ diff --git a/test/integration/render-tests/text-writing-mode/point_label/cjk-arabic-vertical-mode/expected.png b/test/integration/render-tests/text-writing-mode/point_label/cjk-arabic-vertical-mode/expected.png index b2d3a24d59b..0983c750c82 100644 Binary files a/test/integration/render-tests/text-writing-mode/point_label/cjk-arabic-vertical-mode/expected.png and b/test/integration/render-tests/text-writing-mode/point_label/cjk-arabic-vertical-mode/expected.png differ diff --git a/test/integration/render-tests/text-writing-mode/point_label/cjk-horizontal-vertical-mode/expected.png b/test/integration/render-tests/text-writing-mode/point_label/cjk-horizontal-vertical-mode/expected.png index ca20330459a..ccddec60b5a 100644 Binary files a/test/integration/render-tests/text-writing-mode/point_label/cjk-horizontal-vertical-mode/expected.png and b/test/integration/render-tests/text-writing-mode/point_label/cjk-horizontal-vertical-mode/expected.png differ diff --git a/test/integration/render-tests/text-writing-mode/point_label/cjk-multiline-vertical-horizontal-mode/expected.png b/test/integration/render-tests/text-writing-mode/point_label/cjk-multiline-vertical-horizontal-mode/expected.png index 4e4a10c5dfa..6dd3e944c73 100644 Binary files a/test/integration/render-tests/text-writing-mode/point_label/cjk-multiline-vertical-horizontal-mode/expected.png and b/test/integration/render-tests/text-writing-mode/point_label/cjk-multiline-vertical-horizontal-mode/expected.png differ diff --git a/test/integration/render-tests/text-writing-mode/point_label/cjk-punctuation-vertical-mode/expected.png b/test/integration/render-tests/text-writing-mode/point_label/cjk-punctuation-vertical-mode/expected.png index 5714c413f5b..e35dbbdb985 100644 Binary files a/test/integration/render-tests/text-writing-mode/point_label/cjk-punctuation-vertical-mode/expected.png and b/test/integration/render-tests/text-writing-mode/point_label/cjk-punctuation-vertical-mode/expected.png differ diff --git a/test/integration/render-tests/text-writing-mode/point_label/cjk-variable-anchors-vertical-horizontal-mode-icon-text-fit-terrain/expected.png b/test/integration/render-tests/text-writing-mode/point_label/cjk-variable-anchors-vertical-horizontal-mode-icon-text-fit-terrain/expected.png index 8923aee2f53..b78a03b3366 100644 Binary files a/test/integration/render-tests/text-writing-mode/point_label/cjk-variable-anchors-vertical-horizontal-mode-icon-text-fit-terrain/expected.png and b/test/integration/render-tests/text-writing-mode/point_label/cjk-variable-anchors-vertical-horizontal-mode-icon-text-fit-terrain/expected.png differ diff --git a/test/integration/render-tests/text-writing-mode/point_label/cjk-variable-anchors-vertical-horizontal-mode-icon-text-fit/expected.png b/test/integration/render-tests/text-writing-mode/point_label/cjk-variable-anchors-vertical-horizontal-mode-icon-text-fit/expected.png index 3e3ce112ba2..a3d7064ad96 100644 Binary files a/test/integration/render-tests/text-writing-mode/point_label/cjk-variable-anchors-vertical-horizontal-mode-icon-text-fit/expected.png and b/test/integration/render-tests/text-writing-mode/point_label/cjk-variable-anchors-vertical-horizontal-mode-icon-text-fit/expected.png differ diff --git a/test/integration/render-tests/text-writing-mode/point_label/cjk-variable-anchors-vertical-horizontal-mode/expected.png b/test/integration/render-tests/text-writing-mode/point_label/cjk-variable-anchors-vertical-horizontal-mode/expected.png index f1ef07dea79..31727da49a3 100644 Binary files a/test/integration/render-tests/text-writing-mode/point_label/cjk-variable-anchors-vertical-horizontal-mode/expected.png and b/test/integration/render-tests/text-writing-mode/point_label/cjk-variable-anchors-vertical-horizontal-mode/expected.png differ diff --git a/test/integration/render-tests/text-writing-mode/point_label/cjk-variable-anchors-vertical-mode/expected.png b/test/integration/render-tests/text-writing-mode/point_label/cjk-variable-anchors-vertical-mode/expected.png index 402d88f3c76..c98c0221b28 100644 Binary files a/test/integration/render-tests/text-writing-mode/point_label/cjk-variable-anchors-vertical-mode/expected.png and b/test/integration/render-tests/text-writing-mode/point_label/cjk-variable-anchors-vertical-mode/expected.png differ diff --git a/test/integration/render-tests/text-writing-mode/point_label/cjk-vertical-horizontal-mode/expected.png b/test/integration/render-tests/text-writing-mode/point_label/cjk-vertical-horizontal-mode/expected.png index 504173962fb..d1ef56be59e 100644 Binary files a/test/integration/render-tests/text-writing-mode/point_label/cjk-vertical-horizontal-mode/expected.png and b/test/integration/render-tests/text-writing-mode/point_label/cjk-vertical-horizontal-mode/expected.png differ diff --git a/test/integration/render-tests/text-writing-mode/point_label/cjk-vertical-mode/expected.png b/test/integration/render-tests/text-writing-mode/point_label/cjk-vertical-mode/expected.png index 43b2d37adf8..c4b6add5d56 100644 Binary files a/test/integration/render-tests/text-writing-mode/point_label/cjk-vertical-mode/expected.png and b/test/integration/render-tests/text-writing-mode/point_label/cjk-vertical-mode/expected.png differ diff --git a/test/integration/render-tests/text-writing-mode/point_label/mixed-multiline-vertical-horizontal-mode-icon-text-fit/expected.png b/test/integration/render-tests/text-writing-mode/point_label/mixed-multiline-vertical-horizontal-mode-icon-text-fit/expected.png index 374bd2ad0a8..3a0f29357fe 100644 Binary files a/test/integration/render-tests/text-writing-mode/point_label/mixed-multiline-vertical-horizontal-mode-icon-text-fit/expected.png and b/test/integration/render-tests/text-writing-mode/point_label/mixed-multiline-vertical-horizontal-mode-icon-text-fit/expected.png differ diff --git a/test/integration/render-tests/text-writing-mode/point_label/mixed-multiline-vertical-horizontal-mode/expected.png b/test/integration/render-tests/text-writing-mode/point_label/mixed-multiline-vertical-horizontal-mode/expected.png index 6894f4c3b4e..e68e68e6f84 100644 Binary files a/test/integration/render-tests/text-writing-mode/point_label/mixed-multiline-vertical-horizontal-mode/expected.png and b/test/integration/render-tests/text-writing-mode/point_label/mixed-multiline-vertical-horizontal-mode/expected.png differ diff --git a/test/unit/data/symbol_bucket.test.js b/test/unit/data/symbol_bucket.test.js index 71f7e2530a3..a73cfffcf63 100644 --- a/test/unit/data/symbol_bucket.test.js +++ b/test/unit/data/symbol_bucket.test.js @@ -20,7 +20,7 @@ const __dirname = fileURLToPath(new URL('.', import.meta.url)); // Load a point feature from fixture tile. const vt = new VectorTile(new Protobuf(fs.readFileSync(path.join(__dirname, '/../../fixtures/mbsv5-6-18-23.vector.pbf')))); const feature = vt.layers.place_label.feature(10); -const glyphs = JSON.parse(fs.readFileSync(path.join(__dirname, '/../../fixtures/fontstack-glyphs.json'))); +const glyphData = JSON.parse(fs.readFileSync(path.join(__dirname, '/../../fixtures/fontstack-glyphs.json'))); /*eslint new-cap: 0*/ const collisionBoxArray = new CollisionBoxArray(); @@ -29,7 +29,12 @@ transform.width = 100; transform.height = 100; transform.cameraToCenterDistance = 100; -const stacks = {'Test': glyphs}; +const stacks = {'Test': glyphData}; +const glyphPositions = {'Test' : {}}; +const glyphPositonMap = glyphPositions['Test']; +for (const id in glyphData.glyphs) { + glyphPositonMap[id] = glyphData.glyphs[id].rect; +} function bucketSetup(text = 'abcde') { return createSymbolBucket('test', 'Test', text, collisionBoxArray); @@ -45,7 +50,7 @@ test('SymbolBucket', (t) => { // add feature from bucket A bucketA.populate([{feature}], options); - performSymbolLayout(bucketA, stacks, {}); + performSymbolLayout(bucketA, stacks, glyphPositions); const tileA = new Tile(tileID, 512); tileA.latestFeatureIndex = new FeatureIndex(tileID); tileA.buckets = {test: bucketA}; @@ -53,7 +58,7 @@ test('SymbolBucket', (t) => { // add same feature from bucket B bucketB.populate([{feature}], options); - performSymbolLayout(bucketB, stacks, {}); + performSymbolLayout(bucketB, stacks, glyphPositions); const tileB = new Tile(tileID, 512); tileB.buckets = {test: bucketB}; tileB.collisionBoxArray = collisionBoxArray; @@ -87,8 +92,8 @@ test('SymbolBucket integer overflow', (t) => { const options = {iconDependencies: {}, glyphDependencies: {}}; bucket.populate([{feature}], options); - const fakeGlyph = {rect: {w: 10, h: 10}, metrics: {left: 10, top: 10, advance: 10}}; - performSymbolLayout(bucket, stacks, {'Test': {97: fakeGlyph, 98: fakeGlyph, 99: fakeGlyph, 100: fakeGlyph, 101: fakeGlyph, 102: fakeGlyph}}); + const fakeRect = {w: 10, h: 10}; + performSymbolLayout(bucket, stacks, {'Test': {97: fakeRect, 98: fakeRect, 99: fakeRect, 100: fakeRect, 101: fakeRect, 102: fakeRect}}); t.ok(console.warn.calledOnce); t.ok(console.warn.getCall(0).calledWithMatch(/Too many glyphs being rendered in a tile./)); diff --git a/test/unit/render/glyph_manager.test.js b/test/unit/render/glyph_manager.test.js index 8c52fe3d1d1..652c1fe9ee7 100644 --- a/test/unit/render/glyph_manager.test.js +++ b/test/unit/render/glyph_manager.test.js @@ -3,9 +3,13 @@ import parseGlyphPBF from '../../../src/style/parse_glyph_pbf.js'; import GlyphManager, {LocalGlyphMode} from '../../../src/render/glyph_manager.js'; import fs from 'fs'; -const glyphs = {}; -for (const glyph of parseGlyphPBF(fs.readFileSync('./test/fixtures/0-255.pbf'))) { - glyphs[glyph.id] = glyph; +const glyphData = {}; +glyphData.glyphs = []; +const data = parseGlyphPBF(fs.readFileSync('./test/fixtures/0-255.pbf')); +glyphData.ascender = data.ascender; +glyphData.descender = data.descender; +for (const glyph of data.glyphs) { + glyphData.glyphs[glyph.id] = glyph; } const identityTransform = (url) => ({url}); @@ -29,7 +33,7 @@ const createLoadGlyphRangeStub = (t) => { t.equal(range, 0); t.equal(urlTemplate, 'https://localhost/fonts/v1/{fontstack}/{range}.pbf'); t.equal(transform, identityTransform); - setImmediate(() => callback(null, glyphs)); + setImmediate(() => callback(null, glyphData)); }); }; @@ -45,9 +49,9 @@ test('GlyphManager requests 0-255 PBF', (t) => { createLoadGlyphRangeStub(t); const manager = createGlyphManager(); - manager.getGlyphs({'Arial Unicode MS': [55]}, (err, glyphs) => { + manager.getGlyphs({'Arial Unicode MS': [55]}, (err, result) => { t.ifError(err); - t.equal(glyphs['Arial Unicode MS']['55'].metrics.advance, 12); + t.equal(result['Arial Unicode MS'].glyphs['55'].metrics.advance, 12); t.end(); }); }); @@ -75,14 +79,14 @@ test('GlyphManager doesn\'t request twice 0-255 PBF if a glyph is missing', (t) test('GlyphManager requests remote CJK PBF', (t) => { t.stub(GlyphManager, 'loadGlyphRange').callsFake((stack, range, urlTemplate, transform, callback) => { - setImmediate(() => callback(null, glyphs)); + setImmediate(() => callback(null, glyphData)); }); const manager = createGlyphManager(); - manager.getGlyphs({'Arial Unicode MS': [0x5e73]}, (err, glyphs) => { + manager.getGlyphs({'Arial Unicode MS': [0x5e73]}, (err, results) => { t.ifError(err); - t.equal(glyphs['Arial Unicode MS'][0x5e73], null); // The fixture returns a PBF without the glyph we requested + t.equal(results['Arial Unicode MS'].glyphs[0x5e73], null); // The fixture returns a PBF without the glyph we requested t.end(); }); }); @@ -90,10 +94,13 @@ test('GlyphManager requests remote CJK PBF', (t) => { test('GlyphManager does not cache CJK chars that should be rendered locally', (t) => { t.stub(GlyphManager, 'loadGlyphRange').callsFake((stack, range, urlTemplate, transform, callback) => { const overlappingGlyphs = {}; + overlappingGlyphs.glyphs = []; + overlappingGlyphs.ascender = glyphData.ascender; + overlappingGlyphs.descender = glyphData.descender; const start = range * 256; const end = start + 256; for (let i = start, j = 0; i < end; i++, j++) { - overlappingGlyphs[i] = glyphs[j]; + overlappingGlyphs.glyphs[i] = glyphData.glyphs[j]; } setImmediate(() => callback(null, overlappingGlyphs)); }); @@ -101,13 +108,13 @@ test('GlyphManager does not cache CJK chars that should be rendered locally', (t const manager = createGlyphManager('sans-serif'); //Request char that overlaps Katakana range - manager.getGlyphs({'Arial Unicode MS': [0x3005]}, (err, glyphs) => { + manager.getGlyphs({'Arial Unicode MS': [0x3005]}, (err, result) => { t.ifError(err); - t.notEqual(glyphs['Arial Unicode MS'][0x3005], null); + t.notEqual(result['Arial Unicode MS'].glyphs[0x3005], null); //Request char from Katakana range (te) - manager.getGlyphs({'Arial Unicode MS': [0x30C6]}, (err, glyphs) => { + manager.getGlyphs({'Arial Unicode MS': [0x30C6]}, (err, result) => { t.ifError(err); - const glyph = glyphs['Arial Unicode MS'][0x30c6]; + const glyph = result['Arial Unicode MS'].glyphs[0x30c6]; //Ensure that te is locally generated. t.equal(glyph.bitmap.height, 30); t.equal(glyph.bitmap.width, 30); @@ -121,9 +128,9 @@ test('GlyphManager generates CJK PBF locally', (t) => { const manager = createGlyphManager('sans-serif'); - manager.getGlyphs({'Arial Unicode MS': [0x5e73]}, (err, glyphs) => { + manager.getGlyphs({'Arial Unicode MS': [0x5e73]}, (err, result) => { t.ifError(err); - t.equal(glyphs['Arial Unicode MS'][0x5e73].metrics.advance, 24); + t.equal(result['Arial Unicode MS'].glyphs[0x5e73].metrics.advance, 24); t.end(); }); }); @@ -134,9 +141,9 @@ test('GlyphManager generates Katakana PBF locally', (t) => { const manager = createGlyphManager('sans-serif'); // Katakana letter te - manager.getGlyphs({'Arial Unicode MS': [0x30c6]}, (err, glyphs) => { + manager.getGlyphs({'Arial Unicode MS': [0x30c6]}, (err, result) => { t.ifError(err); - t.equal(glyphs['Arial Unicode MS'][0x30c6].metrics.advance, 24); + t.equal(result['Arial Unicode MS'].glyphs[0x30c6].metrics.advance, 24); t.end(); }); }); @@ -147,9 +154,9 @@ test('GlyphManager generates Hiragana PBF locally', (t) => { const manager = createGlyphManager('sans-serif'); //Hiragana letter te - manager.getGlyphs({'Arial Unicode MS': [0x3066]}, (err, glyphs) => { + manager.getGlyphs({'Arial Unicode MS': [0x3066]}, (err, result) => { t.ifError(err); - t.equal(glyphs['Arial Unicode MS'][0x3066].metrics.advance, 24); + t.equal(result['Arial Unicode MS'].glyphs[0x3066].metrics.advance, 24); t.end(); }); }); @@ -173,9 +180,9 @@ test('GlyphManager caches locally generated glyphs', (t) => { const manager = createGlyphManager('sans-serif'); // Katakana letter te - manager.getGlyphs({'Arial Unicode MS': [0x30c6]}, (err, glyphs) => { + manager.getGlyphs({'Arial Unicode MS': [0x30c6]}, (err, result) => { t.ifError(err); - t.equal(glyphs['Arial Unicode MS'][0x30c6].metrics.advance, 24); + t.equal(result['Arial Unicode MS'].glyphs[0x30c6].metrics.advance, 24); manager.getGlyphs({'Arial Unicode MS': [0x30c6]}, () => { t.equal(drawCallCount, 1); t.end(); @@ -199,11 +206,12 @@ test('GlyphManager locally generates latin glyphs', (t) => { const manager = createGlyphManager('sans-serif', true); - manager.getGlyphs({'Arial Unicode MS': ['A']}, (err, glyphs) => { + manager.getGlyphs({'Arial Unicode MS': ['A']}, (err, result) => { t.ifError(err); - t.equal(glyphs['Arial Unicode MS']['A'].metrics.advance, 10); - t.equal(glyphs['Arial Unicode MS']['A'].metrics.width, 14); - t.equal(glyphs['Arial Unicode MS']['A'].metrics.height, 18); + const glyphs = result['Arial Unicode MS'].glyphs; + t.equal(glyphs['A'].metrics.advance, 10); + t.equal(glyphs['A'].metrics.width, 14); + t.equal(glyphs['A'].metrics.height, 18); t.end(); }); }); diff --git a/test/unit/style/load_glyph_range.test.js b/test/unit/style/load_glyph_range.test.js index 69b7e92c09f..64c29ef9524 100644 --- a/test/unit/style/load_glyph_range.test.js +++ b/test/unit/style/load_glyph_range.test.js @@ -30,11 +30,14 @@ test('loadGlyphRange', (t) => { t.deepEqual(transform.getCall(0).args, ['https://localhost/fonts/v1/Arial Unicode MS/0-255.pbf', 'Glyphs']); if (!result) return t.fail(); // appease flow - - t.equal(Object.keys(result).length, 223); - for (const key in result) { + t.equal(typeof result.ascender, 'undefined'); + t.equal(typeof result.descender, 'undefined'); + t.equal(result.ascender, undefined); + t.equal(result.descender, undefined); + t.equal(Object.keys(result.glyphs).length, 223); + for (const key in result.glyphs) { const id = Number(key); - const glyph = result[id]; + const glyph = result.glyphs[id]; if (!glyph) return t.fail(); // appease flow t.equal(glyph.id, Number(id)); t.ok(glyph.metrics); diff --git a/test/unit/symbol/shaping.test.js b/test/unit/symbol/shaping.test.js index c2ba04c8a2b..b2c86446e62 100644 --- a/test/unit/symbol/shaping.test.js +++ b/test/unit/symbol/shaping.test.js @@ -5,6 +5,7 @@ import * as shaping from '../../../src/symbol/shaping.js'; import Formatted, {FormattedSection} from '../../../src/style-spec/expression/types/formatted.js'; import ResolvedImage from '../../../src/style-spec/expression/types/resolved_image.js'; import {ImagePosition} from '../../../src/render/image_atlas.js'; + const WritingMode = shaping.WritingMode; import {fileURLToPath} from 'url'; @@ -20,10 +21,16 @@ test('shaping', (t) => { const layoutTextSize = 16; const layoutTextSizeThisZoom = 16; const fontStack = 'Test'; - const glyphs = { + const glyphMap = { 'Test': JSON.parse(fs.readFileSync(path.join(__dirname, '/../../fixtures/fontstack-glyphs.json'))) }; - const glyphPositions = glyphs; + + const glyphPositions = {'Test' : {}}; + const glyphPositonMap = glyphPositions['Test']; + const glyphData = glyphMap['Test'].glyphs; + for (const id in glyphData) { + glyphPositonMap[id] = glyphData[id].rect; + } const images = { 'square': new ImagePosition({x: 0, y: 0, w: 16, h: 16}, {pixelRatio: 1, version: 1}), @@ -43,37 +50,37 @@ test('shaping', (t) => { JSON.parse('{}'); - shaped = shaping.shapeText(Formatted.fromString(`hi${String.fromCharCode(0)}`), glyphs, glyphPositions, images, fontStack, 15 * oneEm, oneEm, 'center', 'center', 0 * oneEm, [0, 0], WritingMode.horizontal, false, 'point', layoutTextSize, layoutTextSizeThisZoom); + shaped = shaping.shapeText(Formatted.fromString(`hi${String.fromCharCode(0)}`), glyphMap, glyphPositions, images, fontStack, 15 * oneEm, oneEm, 'center', 'center', 0 * oneEm, [0, 0], WritingMode.horizontal, false, 'point', layoutTextSize, layoutTextSizeThisZoom); if (UPDATE) fs.writeFileSync(path.join(__dirname, '/../../expected/text-shaping-null.json'), JSON.stringify(shaped, null, 2)); t.deepEqual(shaped, JSON.parse(fs.readFileSync(path.join(__dirname, '/../../expected/text-shaping-null.json')))); // Default shaping. - shaped = shaping.shapeText(Formatted.fromString('abcde'), glyphs, glyphPositions, images, fontStack, 15 * oneEm, oneEm, 'center', 'center', 0 * oneEm, [0, 0], WritingMode.horizontal, false, 'point', layoutTextSize, layoutTextSizeThisZoom); + shaped = shaping.shapeText(Formatted.fromString('abcde'), glyphMap, glyphPositions, images, fontStack, 15 * oneEm, oneEm, 'center', 'center', 0 * oneEm, [0, 0], WritingMode.horizontal, false, 'point', layoutTextSize, layoutTextSizeThisZoom); if (UPDATE) fs.writeFileSync(path.join(__dirname, '/../../expected/text-shaping-default.json'), JSON.stringify(shaped, null, 2)); t.deepEqual(shaped, JSON.parse(fs.readFileSync(path.join(__dirname, '/../../expected/text-shaping-default.json')))); // Letter spacing. - shaped = shaping.shapeText(Formatted.fromString('abcde'), glyphs, glyphPositions, images, fontStack, 15 * oneEm, oneEm, 'center', 'center', 0.125 * oneEm, [0, 0], WritingMode.horizontal, false, 'point', layoutTextSize, layoutTextSizeThisZoom); + shaped = shaping.shapeText(Formatted.fromString('abcde'), glyphMap, glyphPositions, images, fontStack, 15 * oneEm, oneEm, 'center', 'center', 0.125 * oneEm, [0, 0], WritingMode.horizontal, false, 'point', layoutTextSize, layoutTextSizeThisZoom); if (UPDATE) fs.writeFileSync(path.join(__dirname, '/../../expected/text-shaping-spacing.json'), JSON.stringify(shaped, null, 2)); t.deepEqual(shaped, JSON.parse(fs.readFileSync(path.join(__dirname, '/../../expected/text-shaping-spacing.json')))); // Line break. - shaped = shaping.shapeText(Formatted.fromString('abcde abcde'), glyphs, glyphPositions, images, fontStack, 4 * oneEm, oneEm, 'center', 'center', 0 * oneEm, [0, 0], WritingMode.horizontal, false, 'point', layoutTextSize, layoutTextSizeThisZoom); + shaped = shaping.shapeText(Formatted.fromString('abcde abcde'), glyphMap, glyphPositions, images, fontStack, 4 * oneEm, oneEm, 'center', 'center', 0 * oneEm, [0, 0], WritingMode.horizontal, false, 'point', layoutTextSize, layoutTextSizeThisZoom); if (UPDATE) fs.writeFileSync(path.join(__dirname, '/../../expected/text-shaping-linebreak.json'), JSON.stringify(shaped, null, 2)); t.deepEqual(shaped, JSON.parse(fs.readFileSync(path.join(__dirname, '../../expected/text-shaping-linebreak.json')))); const expectedNewLine = JSON.parse(fs.readFileSync(path.join(__dirname, '/../../expected/text-shaping-newline.json'))); - shaped = shaping.shapeText(Formatted.fromString('abcde\nabcde'), glyphs, glyphPositions, images, fontStack, 15 * oneEm, oneEm, 'center', 'center', 0, [0, 0], WritingMode.horizontal, false, 'point', layoutTextSize, layoutTextSizeThisZoom); + shaped = shaping.shapeText(Formatted.fromString('abcde\nabcde'), glyphMap, glyphPositions, images, fontStack, 15 * oneEm, oneEm, 'center', 'center', 0, [0, 0], WritingMode.horizontal, false, 'point', layoutTextSize, layoutTextSizeThisZoom); if (UPDATE) fs.writeFileSync(path.join(__dirname, '/../../expected/text-shaping-newline.json'), JSON.stringify(shaped, null, 2)); t.deepEqual(shaped, expectedNewLine); - shaped = shaping.shapeText(Formatted.fromString('abcde\r\nabcde'), glyphs, glyphPositions, images, fontStack, 15 * oneEm, oneEm, 'center', 'center', 0, [0, 0], WritingMode.horizontal, false, 'point', layoutTextSize, layoutTextSizeThisZoom); + shaped = shaping.shapeText(Formatted.fromString('abcde\r\nabcde'), glyphMap, glyphPositions, images, fontStack, 15 * oneEm, oneEm, 'center', 'center', 0, [0, 0], WritingMode.horizontal, false, 'point', layoutTextSize, layoutTextSizeThisZoom); t.deepEqual(shaped.positionedLines, expectedNewLine.positionedLines); const expectedNewLinesInMiddle = JSON.parse(fs.readFileSync(path.join(__dirname, '/../../expected/text-shaping-newlines-in-middle.json'))); - shaped = shaping.shapeText(Formatted.fromString('abcde\n\nabcde'), glyphs, glyphPositions, images, fontStack, 15 * oneEm, oneEm, 'center', 'center', 0, [0, 0], WritingMode.horizontal, false, 'point', layoutTextSize, layoutTextSizeThisZoom); + shaped = shaping.shapeText(Formatted.fromString('abcde\n\nabcde'), glyphMap, glyphPositions, images, fontStack, 15 * oneEm, oneEm, 'center', 'center', 0, [0, 0], WritingMode.horizontal, false, 'point', layoutTextSize, layoutTextSizeThisZoom); if (UPDATE) fs.writeFileSync(path.join(__dirname, '/../../expected/text-shaping-newlines-in-middle.json'), JSON.stringify(shaped, null, 2)); t.deepEqual(shaped, expectedNewLinesInMiddle); @@ -81,24 +88,24 @@ test('shaping', (t) => { // a position is ideal for breaking. const expectedZeroWidthSpaceBreak = JSON.parse(fs.readFileSync(path.join(__dirname, '/../../expected/text-shaping-zero-width-space.json'))); - shaped = shaping.shapeText(Formatted.fromString('三三\u200b三三\u200b三三\u200b三三三三三三\u200b三三'), glyphs, glyphPositions, images, fontStack, 5 * oneEm, oneEm, 'center', 'center', 0, [0, 0], WritingMode.horizontal, false, 'point', layoutTextSize, layoutTextSizeThisZoom); + shaped = shaping.shapeText(Formatted.fromString('三三\u200b三三\u200b三三\u200b三三三三三三\u200b三三'), glyphMap, glyphPositions, images, fontStack, 5 * oneEm, oneEm, 'center', 'center', 0, [0, 0], WritingMode.horizontal, false, 'point', layoutTextSize, layoutTextSizeThisZoom); if (UPDATE) fs.writeFileSync(path.join(__dirname, '/../../expected/text-shaping-zero-width-space.json'), JSON.stringify(shaped, null, 2)); t.deepEqual(shaped, expectedZeroWidthSpaceBreak); // Null shaping. - shaped = shaping.shapeText(Formatted.fromString(''), glyphs, glyphPositions, images, fontStack, 15 * oneEm, oneEm, 'center', 'center', 0 * oneEm, [0, 0], WritingMode.horizontal, false, 'point', layoutTextSize, layoutTextSizeThisZoom); + shaped = shaping.shapeText(Formatted.fromString(''), glyphMap, glyphPositions, images, fontStack, 15 * oneEm, oneEm, 'center', 'center', 0 * oneEm, [0, 0], WritingMode.horizontal, false, 'point', layoutTextSize, layoutTextSizeThisZoom); t.equal(false, shaped); - shaped = shaping.shapeText(Formatted.fromString(String.fromCharCode(0)), glyphs, glyphPositions, images, fontStack, 15 * oneEm, oneEm, 'center', 'center', 0 * oneEm, [0, 0], WritingMode.horizontal, false, 'point', layoutTextSize, layoutTextSizeThisZoom); + shaped = shaping.shapeText(Formatted.fromString(String.fromCharCode(0)), glyphMap, glyphPositions, images, fontStack, 15 * oneEm, oneEm, 'center', 'center', 0 * oneEm, [0, 0], WritingMode.horizontal, false, 'point', layoutTextSize, layoutTextSizeThisZoom); t.equal(false, shaped); // https://github.com/mapbox/mapbox-gl-js/issues/3254 - shaped = shaping.shapeText(Formatted.fromString(' foo bar\n'), glyphs, glyphPositions, images, fontStack, 15 * oneEm, oneEm, 'center', 'center', 0 * oneEm, [0, 0], WritingMode.horizontal, false, 'point', layoutTextSize, layoutTextSizeThisZoom); - const shaped2 = shaping.shapeText(Formatted.fromString('foo bar'), glyphs, glyphPositions, images, fontStack, 15 * oneEm, oneEm, 'center', 'center', 0 * oneEm, [0, 0], WritingMode.horizontal, false, 'point', layoutTextSize, layoutTextSizeThisZoom); + shaped = shaping.shapeText(Formatted.fromString(' foo bar\n'), glyphMap, glyphPositions, images, fontStack, 15 * oneEm, oneEm, 'center', 'center', 0 * oneEm, [0, 0], WritingMode.horizontal, false, 'point', layoutTextSize, layoutTextSizeThisZoom); + const shaped2 = shaping.shapeText(Formatted.fromString('foo bar'), glyphMap, glyphPositions, images, fontStack, 15 * oneEm, oneEm, 'center', 'center', 0 * oneEm, [0, 0], WritingMode.horizontal, false, 'point', layoutTextSize, layoutTextSizeThisZoom); t.same(shaped.positionedLines, shaped2.positionedLines); t.test('basic image shaping', (t) => { - const shaped = shaping.shapeText(new Formatted([sectionForImage('square')]), glyphs, glyphPositions, images, fontStack, 5 * oneEm, oneEm, 'center', 'center', 0, [0, 0], WritingMode.horizontal, false, 'point', layoutTextSize, layoutTextSizeThisZoom); + const shaped = shaping.shapeText(new Formatted([sectionForImage('square')]), glyphMap, glyphPositions, images, fontStack, 5 * oneEm, oneEm, 'center', 'center', 0, [0, 0], WritingMode.horizontal, false, 'point', layoutTextSize, layoutTextSizeThisZoom); t.same(shaped.top, -12); // 1em line height t.same(shaped.left, -10.5); // 16 - 2px border * 1.5 scale factor t.end(); @@ -115,7 +122,7 @@ test('shaping', (t) => { sectionForImage('square'), sectionForText(' bar'), ]); - const shaped = shaping.shapeText(horizontalFormatted, glyphs, glyphPositions, images, fontStack, 5 * oneEm, oneEm, 'center', 'center', 0, [0, 0], WritingMode.horizontal, false, 'point', layoutTextSize, layoutTextSizeThisZoom); + const shaped = shaping.shapeText(horizontalFormatted, glyphMap, glyphPositions, images, fontStack, 5 * oneEm, oneEm, 'center', 'center', 0, [0, 0], WritingMode.horizontal, false, 'point', layoutTextSize, layoutTextSizeThisZoom); if (UPDATE) fs.writeFileSync(path.join(__dirname, '/../../expected/text-shaping-images-horizontal.json'), JSON.stringify(shaped, null, 2)); t.deepEqual(shaped, expectedImagesHorizontal); t.end(); @@ -132,7 +139,7 @@ test('shaping', (t) => { sectionForImage('square'), sectionForText('三'), ]); - const shaped = shaping.shapeText(horizontalFormatted, glyphs, glyphPositions, images, fontStack, 5 * oneEm, oneEm, 'center', 'center', 0, [0, 0], WritingMode.vertical, true, 'point', layoutTextSize, layoutTextSizeThisZoom); + const shaped = shaping.shapeText(horizontalFormatted, glyphMap, glyphPositions, images, fontStack, 5 * oneEm, oneEm, 'center', 'center', 0, [0, 0], WritingMode.vertical, true, 'point', layoutTextSize, layoutTextSizeThisZoom); if (UPDATE) fs.writeFileSync(path.join(__dirname, '/../../expected/text-shaping-images-vertical.json'), JSON.stringify(shaped, null, 2)); t.deepEqual(shaped, expectedImagesVertical); t.end();