UNPKG

mapbox-gl

Version:
158 lines (116 loc) 4.86 kB
'use strict'; module.exports = { shapeText: shapeText, shapeIcon: shapeIcon }; // The position of a glyph relative to the text's anchor point. function PositionedGlyph(codePoint, x, y, glyph) { this.codePoint = codePoint; this.x = x; this.y = y; this.glyph = glyph; } // A collection of positioned glyphs and some metadata function Shaping(positionedGlyphs, text, top, bottom, left, right) { this.positionedGlyphs = positionedGlyphs; this.text = text; this.top = top; this.bottom = bottom; this.left = left; this.right = right; } function shapeText(text, glyphs, maxWidth, lineHeight, horizontalAlign, verticalAlign, justify, spacing, translate) { var positionedGlyphs = []; var shaping = new Shaping(positionedGlyphs, text, translate[1], translate[1], translate[0], translate[0]); // the y offset *should* be part of the font metadata var yOffset = -17; var x = translate[0]; var y = translate[1] + yOffset; for (var i = 0; i < text.length; i++) { var codePoint = text.charCodeAt(i); var glyph = glyphs[codePoint]; if (!glyph) continue; positionedGlyphs.push(new PositionedGlyph(codePoint, x, y, glyph)); x += glyph.advance + spacing; } if (!positionedGlyphs.length) return false; linewrap(shaping, glyphs, lineHeight, maxWidth, horizontalAlign, verticalAlign, justify); return shaping; } var breakable = { 32: true }; // Currently only breaks at regular spaces function linewrap(shaping, glyphs, lineHeight, maxWidth, horizontalAlign, verticalAlign, justify) { var lastSafeBreak = null; var lengthBeforeCurrentLine = 0; var lineStartIndex = 0; var line = 0; var maxLineLength = 0; var positionedGlyphs = shaping.positionedGlyphs; if (maxWidth) { for (var i = 0; i < positionedGlyphs.length; i++) { var positionedGlyph = positionedGlyphs[i]; positionedGlyph.x -= lengthBeforeCurrentLine; positionedGlyph.y += lineHeight * line; if (positionedGlyph.x > maxWidth && lastSafeBreak !== null) { var lineLength = positionedGlyphs[lastSafeBreak + 1].x; maxLineLength = Math.max(lineLength, maxLineLength); for (var k = lastSafeBreak + 1; k <= i; k++) { positionedGlyphs[k].y += lineHeight; positionedGlyphs[k].x -= lineLength; } if (justify) { justifyLine(positionedGlyphs, glyphs, lineStartIndex, lastSafeBreak - 1, justify); } lineStartIndex = lastSafeBreak + 1; lastSafeBreak = null; lengthBeforeCurrentLine += lineLength; line++; } if (breakable[positionedGlyph.codePoint]) { lastSafeBreak = i; } } } var lastPositionedGlyph = positionedGlyphs[positionedGlyphs.length - 1]; var lastLineLength = lastPositionedGlyph.x + glyphs[lastPositionedGlyph.codePoint].advance; maxLineLength = Math.max(maxLineLength, lastLineLength); var height = (line + 1) * lineHeight; justifyLine(positionedGlyphs, glyphs, lineStartIndex, positionedGlyphs.length - 1, justify); align(positionedGlyphs, justify, horizontalAlign, verticalAlign, maxLineLength, lineHeight, line); // Calculate the bounding box shaping.top += -verticalAlign * height; shaping.bottom = shaping.top + height; shaping.left += -horizontalAlign * maxLineLength; shaping.right = shaping.left + maxLineLength; } function justifyLine(positionedGlyphs, glyphs, start, end, justify) { var lastAdvance = glyphs[positionedGlyphs[end].codePoint].advance; var lineIndent = (positionedGlyphs[end].x + lastAdvance) * justify; for (var j = start; j <= end; j++) { positionedGlyphs[j].x -= lineIndent; } } function align(positionedGlyphs, justify, horizontalAlign, verticalAlign, maxLineLength, lineHeight, line) { var shiftX = (justify - horizontalAlign) * maxLineLength; var shiftY = (-verticalAlign * (line + 1) + 0.5) * lineHeight; for (var j = 0; j < positionedGlyphs.length; j++) { positionedGlyphs[j].x += shiftX; positionedGlyphs[j].y += shiftY; } } function shapeIcon(image, layout) { if (!image || !image.rect) return null; var dx = layout['icon-offset'][0]; var dy = layout['icon-offset'][1]; var x1 = dx - image.width / 2; var x2 = x1 + image.width; var y1 = dy - image.height / 2; var y2 = y1 + image.height; return new PositionedIcon(image, y1, y2, x1, x2); } function PositionedIcon(image, top, bottom, left, right) { this.image = image; this.top = top; this.bottom = bottom; this.left = left; this.right = right; }