pixi-glyphs 4.1.7 → 4.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/pixi-glyphs.js +98 -90
- package/dist/pixi-glyphs.js.map +1 -1
- package/dist/pixi-glyphs.m.js +98 -90
- package/dist/pixi-glyphs.m.js.map +1 -1
- package/dist/pixi-glyphs.modern.js +103 -95
- package/dist/pixi-glyphs.modern.js.map +1 -1
- package/dist/pixi-glyphs.umd.js +98 -90
- package/dist/pixi-glyphs.umd.js.map +1 -1
- package/dist/types.d.ts +1 -0
- package/package.json +5 -3
package/README.md
CHANGED
|
@@ -107,6 +107,7 @@ The style objects are modified versions (supersets) of `PIXI.TextStyle` (referre
|
|
|
107
107
|
- `lineThroughThickness` - Sets the thickness of the line-through. Default is `1`.
|
|
108
108
|
- `lineThroughOffset` - Positions the line-through above or below the default location. Default is `0`.
|
|
109
109
|
- `adjustBaseline` - Adjusts the position of the text above or below the baseline. Default is `0`. Also see the `adjustFontBaseline` property in the options.
|
|
110
|
+
- `topTrim` - Adjusts the line height of text by trimming (or expanding) from the top of the text. Positive values reduce the line height by trimming from the top (useful for reducing gaps above large text), while negative values increase the line height. This property only affects the segments that have it set, and the overall line height is determined by the tallest effective segment after all individual `topTrim` adjustments. Default is `0`. Example: `<big topTrim="50">Big Text</big>` reduces the line height of "Big Text" by 50 pixels from the top.
|
|
110
111
|
- `highlightColor` - Adds a background highlight color behind the text. Can be a hex number like `0xFFEB3B` or a hex string like `"#FFEB3B"`. The highlight appears as a continuous solid color box behind the text with no borders, spanning across spaces between words for a seamless highlight effect. When the same highlight color is applied to consecutive text segments, they will be rendered as a single continuous highlight box. Perfect for emphasizing important text, creating visual hierarchy, inline code blocks, or implementing syntax highlighting. Example: `<highlight>This entire phrase is highlighted</highlight>` will create one continuous highlight box.
|
|
111
112
|
- `color` - An alias for `fill`. It's recommended you just use either `fill` or `color`, but if both are set, `fill` will be used. If tags are nested, `color` on an inner tag can override `fill` in an outer tag.
|
|
112
113
|
|
package/dist/pixi-glyphs.js
CHANGED
|
@@ -321,34 +321,17 @@ const isOnlyWhitespace = s => s.search(/^\s+$/) === 0;
|
|
|
321
321
|
const PX_PER_EM = 16;
|
|
322
322
|
const PX_PER_PERCENT = 16 / 100;
|
|
323
323
|
const PX_PER_PT = 1.3281472327365;
|
|
324
|
-
const fontMetricsCache = new Map();
|
|
325
|
-
const measureFont = font => {
|
|
326
|
-
if (fontMetricsCache.has(font)) {
|
|
327
|
-
return fontMetricsCache.get(font);
|
|
328
|
-
}
|
|
329
|
-
const canvas = document.createElement('canvas');
|
|
330
|
-
const context = canvas.getContext('2d');
|
|
331
|
-
if (!context) throw new Error('Cannot get 2D context');
|
|
332
|
-
context.font = font;
|
|
333
|
-
const measureString = "Mgpjy";
|
|
334
|
-
const metrics = context.measureText(measureString);
|
|
335
|
-
const fontSize = parseInt(font.match(/(\d+)px/)?.['1'] || '16');
|
|
336
|
-
const result = {
|
|
337
|
-
ascent: metrics.actualBoundingBoxAscent || fontSize * 0.88,
|
|
338
|
-
descent: metrics.actualBoundingBoxDescent || fontSize * 0.12,
|
|
339
|
-
fontSize: fontSize
|
|
340
|
-
};
|
|
341
|
-
fontMetricsCache.set(font, result);
|
|
342
|
-
return result;
|
|
343
|
-
};
|
|
344
324
|
const getFontPropertiesOfText = (textField, forceUpdate = false) => {
|
|
325
|
+
const text = textField.text;
|
|
345
326
|
const style = textField.style;
|
|
346
|
-
const
|
|
347
|
-
const
|
|
348
|
-
const
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
327
|
+
const measureText = text && text.trim().length > 0 ? text : 'M';
|
|
328
|
+
const metrics = PIXI__namespace.CanvasTextMetrics.measureText(measureText, style);
|
|
329
|
+
const fontSize = metrics.fontProperties?.fontSize || (typeof style.fontSize === 'number' ? style.fontSize : 16);
|
|
330
|
+
return {
|
|
331
|
+
ascent: metrics.fontProperties?.ascent || fontSize * 0.88,
|
|
332
|
+
descent: metrics.fontProperties?.descent || fontSize * 0.12,
|
|
333
|
+
fontSize: fontSize
|
|
334
|
+
};
|
|
352
335
|
};
|
|
353
336
|
const cloneSprite = sprite => new PIXI__namespace.Sprite(sprite.texture);
|
|
354
337
|
const fontSizeStringToNumber = size => {
|
|
@@ -872,7 +855,7 @@ const getTallestToken = line => {
|
|
|
872
855
|
}
|
|
873
856
|
}
|
|
874
857
|
const relevantWords = lastNonWhitespaceIndex >= 0 ? line.slice(0, lastNonWhitespaceIndex + 1) : line;
|
|
875
|
-
|
|
858
|
+
const tallest = flatReduce((tallest, current) => {
|
|
876
859
|
if (isWhitespaceToken(current) || isNewlineToken(current)) {
|
|
877
860
|
return tallest;
|
|
878
861
|
}
|
|
@@ -886,6 +869,14 @@ const getTallestToken = line => {
|
|
|
886
869
|
}
|
|
887
870
|
return tallest;
|
|
888
871
|
}, createEmptySegmentToken())(relevantWords);
|
|
872
|
+
if ((tallest.bounds?.height ?? 0) === 0) {
|
|
873
|
+
const allTokens = line.flat(2);
|
|
874
|
+
const firstNewline = allTokens.find(t => isNewlineToken(t));
|
|
875
|
+
if (firstNewline) {
|
|
876
|
+
return firstNewline;
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
return tallest;
|
|
889
880
|
};
|
|
890
881
|
const verticalAlignInLines = (lines, lineSpacing, overrideValign) => {
|
|
891
882
|
let previousTallestToken = createEmptySegmentToken();
|
|
@@ -898,12 +889,29 @@ const verticalAlignInLines = (lines, lineSpacing, overrideValign) => {
|
|
|
898
889
|
let tallestToken = getTallestToken(line);
|
|
899
890
|
let baseHeight = tallestToken.bounds?.height ?? 0;
|
|
900
891
|
let baseTallestAscent = 0;
|
|
892
|
+
const hasRealContent = line.flat(2).some(seg => !isWhitespaceToken(seg) && !isNewlineToken(seg));
|
|
901
893
|
for (const word of line) {
|
|
902
894
|
for (const segment of word) {
|
|
903
|
-
|
|
895
|
+
let segAscent = segment.fontProperties?.ascent ?? 0;
|
|
896
|
+
const style = segment.style;
|
|
897
|
+
const strokeWidth = typeof style?.stroke === 'object' ? style.stroke.width : 0;
|
|
898
|
+
const legacyStrokeThickness = style.strokeThickness || 0;
|
|
899
|
+
const strokeThickness = strokeWidth || legacyStrokeThickness;
|
|
900
|
+
if (strokeThickness && strokeThickness > 0) {
|
|
901
|
+
segAscent += strokeThickness / 2;
|
|
902
|
+
}
|
|
903
|
+
const topTrim = segment.style.topTrim ?? 0;
|
|
904
|
+
if (topTrim !== 0) {
|
|
905
|
+
segAscent = Math.max(0, segAscent - topTrim);
|
|
906
|
+
}
|
|
907
|
+
const isNewline = isNewlineToken(segment);
|
|
908
|
+
isWhitespaceToken(segment);
|
|
909
|
+
const skipNewline = isNewlineToken(segment) && hasRealContent;
|
|
910
|
+
const skipWhitespace = isWhitespaceToken(segment) && !isNewline && !(strokeThickness && strokeThickness > 0);
|
|
911
|
+
const isEmptyLineNewline = isNewline && !hasRealContent;
|
|
912
|
+
if ((skipNewline || skipWhitespace) && !isEmptyLineNewline) {
|
|
904
913
|
continue;
|
|
905
914
|
}
|
|
906
|
-
const segAscent = segment.fontProperties?.ascent ?? 0;
|
|
907
915
|
if (segAscent > baseTallestAscent) {
|
|
908
916
|
baseTallestAscent = segAscent;
|
|
909
917
|
}
|
|
@@ -947,13 +955,17 @@ const verticalAlignInLines = (lines, lineSpacing, overrideValign) => {
|
|
|
947
955
|
...bounds
|
|
948
956
|
};
|
|
949
957
|
const valign = overrideValign ?? style.valign;
|
|
958
|
+
const strokeWidth = typeof style?.stroke === 'object' ? style.stroke.width : 0;
|
|
959
|
+
const legacyStrokeThickness = style.strokeThickness || 0;
|
|
960
|
+
const strokeThickness = strokeWidth || legacyStrokeThickness;
|
|
961
|
+
const hasStroke = style?.stroke && strokeThickness > 0;
|
|
950
962
|
let {
|
|
951
963
|
ascent
|
|
952
964
|
} = fontProperties;
|
|
953
965
|
if (isSpriteToken(segment)) {
|
|
954
966
|
const imgDisplay = segment.style[IMG_DISPLAY_PROPERTY];
|
|
955
967
|
if (imgDisplay === 'icon') {
|
|
956
|
-
ascent =
|
|
968
|
+
ascent = fontProperties.ascent;
|
|
957
969
|
} else {
|
|
958
970
|
ascent = segment.bounds.height;
|
|
959
971
|
}
|
|
@@ -967,10 +979,6 @@ const verticalAlignInLines = (lines, lineSpacing, overrideValign) => {
|
|
|
967
979
|
continue;
|
|
968
980
|
}
|
|
969
981
|
let newY = previousLineBottom;
|
|
970
|
-
const strokeWidth = typeof style?.stroke === 'object' ? style.stroke.width : 0;
|
|
971
|
-
const legacyStrokeThickness = style.strokeThickness || 0;
|
|
972
|
-
const strokeThickness = strokeWidth || legacyStrokeThickness;
|
|
973
|
-
const hasStroke = style?.stroke && strokeThickness > 0;
|
|
974
982
|
switch (valign) {
|
|
975
983
|
case "bottom":
|
|
976
984
|
newY += tallestHeight - height;
|
|
@@ -1212,14 +1220,6 @@ const calculateTokens = (styledTokens, splitStyle = "words", scaleIcons = true,
|
|
|
1212
1220
|
fontProperties = {
|
|
1213
1221
|
...getFontPropertiesOfText(localSizer, true)
|
|
1214
1222
|
};
|
|
1215
|
-
if (isOnlyWhitespace(token) === false) {
|
|
1216
|
-
const stroke = localSizer.style.stroke ?? 0;
|
|
1217
|
-
if (stroke > 0) {
|
|
1218
|
-
fontProperties.descent += stroke / 2;
|
|
1219
|
-
fontProperties.ascent += stroke / 2;
|
|
1220
|
-
fontProperties.fontSize = fontProperties.ascent + fontProperties.descent;
|
|
1221
|
-
}
|
|
1222
|
-
}
|
|
1223
1223
|
const sw = style.fontScaleWidth ?? 1.0;
|
|
1224
1224
|
const sh = style.fontScaleHeight ?? 1.0;
|
|
1225
1225
|
const scaleWidth = isNaN(sw) || sw < 0 ? 0.0 : sw;
|
|
@@ -1232,7 +1232,10 @@ const calculateTokens = (styledTokens, splitStyle = "words", scaleIcons = true,
|
|
|
1232
1232
|
if (isOnlyWhitespace(str)) {
|
|
1233
1233
|
const fontSize = typeof localSizer.style.fontSize === 'string' ? parseInt(localSizer.style.fontSize) : localSizer.style.fontSize || 24;
|
|
1234
1234
|
const spaceWidth = fontSize * 0.3 * str.length;
|
|
1235
|
-
|
|
1235
|
+
let height = fontProperties.fontSize;
|
|
1236
|
+
if (strokeThickness && strokeThickness > 0) {
|
|
1237
|
+
height += strokeThickness;
|
|
1238
|
+
}
|
|
1236
1239
|
bounds = new PIXI__namespace.Rectangle(0, 0, spaceWidth, height);
|
|
1237
1240
|
} else {
|
|
1238
1241
|
const measureSizer = new PIXI__namespace.Text({
|
|
@@ -1286,11 +1289,6 @@ const calculateTokens = (styledTokens, splitStyle = "words", scaleIcons = true,
|
|
|
1286
1289
|
fontProperties = {
|
|
1287
1290
|
...getFontPropertiesOfText(localSizer, true)
|
|
1288
1291
|
};
|
|
1289
|
-
if (strokeThickness && strokeThickness > 0) {
|
|
1290
|
-
fontProperties.ascent += strokeThickness / 2;
|
|
1291
|
-
fontProperties.descent += strokeThickness / 2;
|
|
1292
|
-
fontProperties.fontSize = fontProperties.ascent + fontProperties.descent;
|
|
1293
|
-
}
|
|
1294
1292
|
if (isIcon) {
|
|
1295
1293
|
const h = Math.max(sprite.height, 1);
|
|
1296
1294
|
if (h > 1 && sprite.scale.y === 1) {
|
|
@@ -1906,6 +1904,8 @@ class Glyphs extends PIXI__namespace.Container {
|
|
|
1906
1904
|
color: cleanedStyle.stroke,
|
|
1907
1905
|
width: cleanedStyle.strokeThickness || 0
|
|
1908
1906
|
};
|
|
1907
|
+
}
|
|
1908
|
+
if (cleanedStyle.strokeThickness !== undefined) {
|
|
1909
1909
|
delete cleanedStyle.strokeThickness;
|
|
1910
1910
|
}
|
|
1911
1911
|
const textField = new PIXI__namespace.Text({
|
|
@@ -2020,11 +2020,11 @@ class Glyphs extends PIXI__namespace.Container {
|
|
|
2020
2020
|
const g = this._debugGraphics;
|
|
2021
2021
|
g.clear();
|
|
2022
2022
|
let lineHeightGraphics = new PIXI__namespace.Graphics();
|
|
2023
|
-
lineHeightGraphics.
|
|
2023
|
+
lineHeightGraphics.label = 'lineHeightGraphics';
|
|
2024
2024
|
debugContainer.addChild(lineHeightGraphics);
|
|
2025
2025
|
lineHeightGraphics.clear();
|
|
2026
2026
|
let baselineGraphics = new PIXI__namespace.Graphics();
|
|
2027
|
-
baselineGraphics.
|
|
2027
|
+
baselineGraphics.label = 'baselineGraphics';
|
|
2028
2028
|
baselineGraphics.visible = true;
|
|
2029
2029
|
baselineGraphics.alpha = 1;
|
|
2030
2030
|
debugContainer.addChild(baselineGraphics);
|
|
@@ -2041,13 +2041,38 @@ class Glyphs extends PIXI__namespace.Container {
|
|
|
2041
2041
|
}
|
|
2042
2042
|
for (let lineNumber = 0; lineNumber < paragraph.length; lineNumber++) {
|
|
2043
2043
|
const line = paragraph[lineNumber];
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2044
|
+
getBoundsNested(line);
|
|
2045
|
+
for (const word of line) {
|
|
2046
|
+
for (const segment of word) {
|
|
2047
|
+
}
|
|
2048
|
+
}
|
|
2049
|
+
let tallestEffectiveHeight = 0;
|
|
2050
|
+
let tallestEffectiveY = Number.POSITIVE_INFINITY;
|
|
2051
|
+
for (const word of line) {
|
|
2052
|
+
for (const segment of word) {
|
|
2053
|
+
const topTrim = segment.style.topTrim ?? 0;
|
|
2054
|
+
let segmentAscent = segment.fontProperties.ascent;
|
|
2055
|
+
const segmentDescent = segment.fontProperties.descent;
|
|
2056
|
+
if (topTrim !== 0) {
|
|
2057
|
+
segmentAscent = Math.max(0, segmentAscent - topTrim);
|
|
2058
|
+
}
|
|
2059
|
+
const effectiveHeight = segmentAscent + segmentDescent;
|
|
2060
|
+
const effectiveY = segment.bounds.y + segment.fontProperties.ascent - segmentAscent;
|
|
2061
|
+
if (effectiveHeight > tallestEffectiveHeight) {
|
|
2062
|
+
tallestEffectiveHeight = effectiveHeight;
|
|
2063
|
+
tallestEffectiveY = effectiveY;
|
|
2064
|
+
}
|
|
2065
|
+
}
|
|
2066
|
+
}
|
|
2067
|
+
let lineBoxY = tallestEffectiveY;
|
|
2068
|
+
let lineBoxHeight = tallestEffectiveHeight;
|
|
2047
2069
|
if (this.defaultStyle.wordWrap) {
|
|
2048
2070
|
const w = this.defaultStyle.wordWrapWidth ?? this.width;
|
|
2049
|
-
g.
|
|
2050
|
-
|
|
2071
|
+
g.rect(0, lineBoxY, w, lineBoxHeight).stroke({
|
|
2072
|
+
width: 0.5,
|
|
2073
|
+
color: DEBUG.LINE_COLOR,
|
|
2074
|
+
alpha: 0.2
|
|
2075
|
+
});
|
|
2051
2076
|
}
|
|
2052
2077
|
for (let wordNumber = 0; wordNumber < line.length; wordNumber++) {
|
|
2053
2078
|
const word = line[wordNumber];
|
|
@@ -2058,49 +2083,31 @@ class Glyphs extends PIXI__namespace.Container {
|
|
|
2058
2083
|
y,
|
|
2059
2084
|
width
|
|
2060
2085
|
} = segmentToken.bounds;
|
|
2061
|
-
let adjustedY = y;
|
|
2062
|
-
const fontSize = segmentToken.fontProperties?.fontSize || 24;
|
|
2063
|
-
let adjustment = 0;
|
|
2064
|
-
if (fontSize <= 24) {
|
|
2065
|
-
adjustment = -2;
|
|
2066
|
-
} else if (fontSize <= 28) {
|
|
2067
|
-
adjustment = -3;
|
|
2068
|
-
} else if (fontSize === 36) {
|
|
2069
|
-
adjustment = 5;
|
|
2070
|
-
} else if (fontSize > 36 && fontSize <= 47) {
|
|
2071
|
-
adjustment = 5 + (fontSize - 36) * 0.2;
|
|
2072
|
-
} else if (fontSize > 47) {
|
|
2073
|
-
adjustment = 7 + (fontSize - 47) * 0.1;
|
|
2074
|
-
}
|
|
2075
|
-
adjustedY -= adjustment;
|
|
2076
2086
|
let baseline;
|
|
2077
2087
|
if (isSprite) {
|
|
2078
|
-
baseline =
|
|
2088
|
+
baseline = y + segmentToken.bounds.height;
|
|
2079
2089
|
} else {
|
|
2080
|
-
baseline =
|
|
2081
|
-
const
|
|
2082
|
-
const
|
|
2083
|
-
const
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
baseline += 15;
|
|
2087
|
-
} else if (hasStroke && fontSize <= 28) {
|
|
2088
|
-
baseline += 6;
|
|
2089
|
-
} else if (fontSize <= 24) {
|
|
2090
|
-
baseline += 4;
|
|
2091
|
-
} else {
|
|
2092
|
-
baseline += 4;
|
|
2090
|
+
baseline = y + segmentToken.fontProperties.ascent;
|
|
2091
|
+
const actualHeight = segmentToken.bounds.height;
|
|
2092
|
+
const metricsHeight = segmentToken.fontProperties.ascent + segmentToken.fontProperties.descent;
|
|
2093
|
+
const heightDifference = actualHeight - metricsHeight;
|
|
2094
|
+
if (heightDifference > 1) {
|
|
2095
|
+
baseline += heightDifference / 2;
|
|
2093
2096
|
}
|
|
2094
2097
|
}
|
|
2095
|
-
let boxY =
|
|
2098
|
+
let boxY = y;
|
|
2096
2099
|
let boxHeight = segmentToken.bounds.height;
|
|
2097
2100
|
if (!isSprite) {
|
|
2098
|
-
|
|
2101
|
+
let ascent = segmentToken.fontProperties.ascent;
|
|
2099
2102
|
const descent = segmentToken.fontProperties.descent;
|
|
2103
|
+
const segmentTopTrim = segmentToken.style.topTrim ?? 0;
|
|
2104
|
+
if (segmentTopTrim !== 0) {
|
|
2105
|
+
ascent = Math.max(0, ascent - segmentTopTrim);
|
|
2106
|
+
}
|
|
2100
2107
|
boxY = baseline - ascent;
|
|
2101
2108
|
boxHeight = ascent + descent;
|
|
2102
2109
|
} else {
|
|
2103
|
-
boxHeight
|
|
2110
|
+
boxHeight = segmentToken.bounds.height + segmentToken.fontProperties.descent;
|
|
2104
2111
|
}
|
|
2105
2112
|
const strokeColor = isWhitespaceToken(segmentToken) && this.options.drawWhitespace === false ? DEBUG.WHITESPACE_STROKE_COLOR : DEBUG.WORD_STROKE_COLOR;
|
|
2106
2113
|
const fillColor = isWhitespaceToken(segmentToken) && this.options.drawWhitespace === false ? DEBUG.WHITESPACE_COLOR : DEBUG.WORD_FILL_COLOR;
|
|
@@ -2141,15 +2148,16 @@ class Glyphs extends PIXI__namespace.Container {
|
|
|
2141
2148
|
}
|
|
2142
2149
|
}
|
|
2143
2150
|
if (baselines.length > 0) {
|
|
2144
|
-
baselineGraphics.beginFill(DEBUG.BASELINE_COLOR, 0.8);
|
|
2145
2151
|
for (const {
|
|
2146
2152
|
x,
|
|
2147
2153
|
baseline,
|
|
2148
2154
|
width
|
|
2149
2155
|
} of baselines) {
|
|
2150
|
-
baselineGraphics.
|
|
2156
|
+
baselineGraphics.rect(x, baseline - 1, width, 2).fill({
|
|
2157
|
+
color: DEBUG.BASELINE_COLOR,
|
|
2158
|
+
alpha: 0.8
|
|
2159
|
+
});
|
|
2151
2160
|
}
|
|
2152
|
-
baselineGraphics.endFill();
|
|
2153
2161
|
}
|
|
2154
2162
|
}
|
|
2155
2163
|
}
|