pixi-glyphs 4.1.8 → 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 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
 
@@ -38,24 +38,19 @@ const assoc = key => value => object => ({
38
38
  const flatReduce = (f, acc) => nested => [nested].flat(255).reduce(f, acc);
39
39
  const flatEvery = p => flatReduce((acc, t) => acc && p(t), true);
40
40
 
41
- const log = type => function (handler, supressConsole, target) {
42
- if (supressConsole === void 0) {
43
- supressConsole = false;
44
- }
45
- return (code, message) => {
46
- if (supressConsole !== true) {
47
- const method = type === "warning" ? console.warn : console.error;
48
- method(`[${code}] ${message}`);
49
- }
50
- if (handler) {
51
- handler({
52
- target,
53
- code,
54
- message,
55
- type
56
- });
57
- }
58
- };
41
+ const log = type => (handler, supressConsole = false, target) => (code, message) => {
42
+ if (supressConsole !== true) {
43
+ const method = type === "warning" ? console.warn : console.error;
44
+ method(`[${code}] ${message}`);
45
+ }
46
+ if (handler) {
47
+ handler({
48
+ target,
49
+ code,
50
+ message,
51
+ type
52
+ });
53
+ }
59
54
  };
60
55
  const logWarning = log("warning");
61
56
 
@@ -147,10 +142,7 @@ var RGI_Emoji = () => {
147
142
  };
148
143
 
149
144
  const defaultLogWarning = logWarning();
150
- const getTagRegex = function (tagNamesToMatch) {
151
- if (tagNamesToMatch === void 0) {
152
- tagNamesToMatch = ["\\w+"];
153
- }
145
+ const getTagRegex = (tagNamesToMatch = ["\\w+"]) => {
154
146
  const matchingTagNames = tagNamesToMatch.join("|");
155
147
  const captureGroup = a => `(${a})`;
156
148
  const noCaptureGroup = a => `(?:${a})`;
@@ -166,10 +158,7 @@ const getTagRegex = function (tagNamesToMatch) {
166
158
  return new RegExp(pattern, "g");
167
159
  };
168
160
  const EMOJI_TAG = "__EMOJI__";
169
- const parseAttributes = function (attributesString) {
170
- if (attributesString === void 0) {
171
- attributesString = "";
172
- }
161
+ const parseAttributes = (attributesString = "") => {
173
162
  if (attributesString === "") {
174
163
  return {};
175
164
  }
@@ -249,10 +238,7 @@ const wrapEmoji = input => {
249
238
  return `<${EMOJI_TAG}>${match}</${EMOJI_TAG}>`;
250
239
  });
251
240
  };
252
- const replaceSelfClosingTags = input => input.replace(selfClosingTagSearch, function (_, tag, attributes) {
253
- if (attributes === void 0) {
254
- attributes = "";
255
- }
241
+ const replaceSelfClosingTags = input => input.replace(selfClosingTagSearch, (_, tag, attributes = "") => {
256
242
  let output = `<${tag}${attributes}></${tag}>`;
257
243
  output = output.replace(/\s+/g, " ");
258
244
  output = output.replace(/\s>/g, ">");
@@ -268,10 +254,7 @@ const tagMatchToTagToken = tag => {
268
254
  })
269
255
  };
270
256
  };
271
- const createTokensNew = function (segments, tags, logWarningFunction) {
272
- if (logWarningFunction === void 0) {
273
- logWarningFunction = defaultLogWarning;
274
- }
257
+ const createTokensNew = (segments, tags, logWarningFunction = defaultLogWarning) => {
275
258
  const rootTokens = {
276
259
  children: []
277
260
  };
@@ -305,16 +288,7 @@ const createTokensNew = function (segments, tags, logWarningFunction) {
305
288
  return rootTokens.children;
306
289
  };
307
290
  const containsEmoji = input => RGI_Emoji().test(input);
308
- const parseTagsNew = function (input, tagNamesToMatch, shouldWrapEmoji, logWarningFunction) {
309
- if (tagNamesToMatch === void 0) {
310
- tagNamesToMatch = [];
311
- }
312
- if (shouldWrapEmoji === void 0) {
313
- shouldWrapEmoji = false;
314
- }
315
- if (logWarningFunction === void 0) {
316
- logWarningFunction = defaultLogWarning;
317
- }
291
+ const parseTagsNew = (input, tagNamesToMatch = [], shouldWrapEmoji = false, logWarningFunction = defaultLogWarning) => {
318
292
  if (shouldWrapEmoji && containsEmoji(input)) {
319
293
  input = wrapEmoji(input);
320
294
  }
@@ -347,7 +321,7 @@ const isOnlyWhitespace = s => s.search(/^\s+$/) === 0;
347
321
  const PX_PER_EM = 16;
348
322
  const PX_PER_PERCENT = 16 / 100;
349
323
  const PX_PER_PT = 1.3281472327365;
350
- const getFontPropertiesOfText = function (textField, forceUpdate) {
324
+ const getFontPropertiesOfText = (textField, forceUpdate = false) => {
351
325
  const text = textField.text;
352
326
  const style = textField.style;
353
327
  const measureText = text && text.trim().length > 0 ? text : 'M';
@@ -410,31 +384,19 @@ const convertAttributeValues = attributes => {
410
384
  }
411
385
  return convertedAttributes;
412
386
  };
413
- const injectAttributes = function (attributes, style) {
414
- if (attributes === void 0) {
415
- attributes = {};
416
- }
417
- if (style === void 0) {
418
- style = {};
419
- }
387
+ const injectAttributes = (attributes = {}, style = {}) => {
420
388
  if (isEmptyObject(style) && isEmptyObject(attributes)) return undefined;
421
389
  return combineRecords(style, convertAttributeValues(attributes));
422
390
  };
423
- const getStyleForTag = function (tagName, tagStyles, attributes) {
424
- if (attributes === void 0) {
425
- attributes = {};
426
- }
391
+ const getStyleForTag = (tagName, tagStyles, attributes = {}) => {
427
392
  const style = injectAttributes(attributes, tagStyles[tagName]) ?? {};
428
393
  if (Object.values(style).length === 0) return undefined;
429
394
  return style;
430
395
  };
431
- const tagWithAttributesToStyle = (_ref, tagStyles) => {
432
- let {
433
- tagName,
434
- attributes
435
- } = _ref;
436
- return getStyleForTag(tagName, tagStyles, attributes);
437
- };
396
+ const tagWithAttributesToStyle = ({
397
+ tagName,
398
+ attributes
399
+ }, tagStyles) => getStyleForTag(tagName, tagStyles, attributes);
438
400
  const getStyleForTags = (tags, tagStyles, styleCache) => {
439
401
  const tagHash = JSON.stringify(tags);
440
402
  if (styleCache[tagHash] === undefined) {
@@ -538,10 +500,7 @@ const convertDecorationToLineProps = style => {
538
500
  const defaultColor = decorationColor || style.fill || DEFAULT_STYLE.fill;
539
501
  const defaultThickness = decorationThickness || 1;
540
502
  const defaultOffset = 0;
541
- function mergeDecoration(decorationLineType, decorationLineTypeCamelCase) {
542
- if (decorationLineTypeCamelCase === void 0) {
543
- decorationLineTypeCamelCase = decorationLineType;
544
- }
503
+ function mergeDecoration(decorationLineType, decorationLineTypeCamelCase = decorationLineType) {
545
504
  if (style.textDecoration?.includes(decorationLineType)) {
546
505
  return {
547
506
  [`${decorationLineTypeCamelCase}Color`]: style[`${decorationLineTypeCamelCase}Color`] ?? defaultColor,
@@ -621,13 +580,10 @@ const ICON_SCALE_BASE = 0.8;
621
580
  new PIXI__namespace.Text({
622
581
  text: ""
623
582
  });
624
- const rectFromContainer = function (container, offset) {
625
- if (offset === void 0) {
626
- offset = {
627
- x: 0,
628
- y: 0
629
- };
630
- }
583
+ const rectFromContainer = (container, offset = {
584
+ x: 0,
585
+ y: 0
586
+ }) => {
631
587
  const w = container.width;
632
588
  const h = container.height;
633
589
  const x = offset.x + container.x;
@@ -696,23 +652,17 @@ const positionWordX = x => word => {
696
652
  };
697
653
  return positionRecursive(word);
698
654
  };
699
- const concatBounds = function (originalBounds, bounds) {
700
- if (originalBounds === void 0) {
701
- originalBounds = {
702
- x: NaN,
703
- y: NaN,
704
- width: NaN,
705
- height: NaN
706
- };
707
- }
708
- if (bounds === void 0) {
709
- bounds = {
710
- x: NaN,
711
- y: NaN,
712
- width: NaN,
713
- height: NaN
714
- };
715
- }
655
+ const concatBounds = (originalBounds = {
656
+ x: NaN,
657
+ y: NaN,
658
+ width: NaN,
659
+ height: NaN
660
+ }, bounds = {
661
+ x: NaN,
662
+ y: NaN,
663
+ width: NaN,
664
+ height: NaN
665
+ }) => {
716
666
  if (isNaN(originalBounds.x)) {
717
667
  return bounds;
718
668
  }
@@ -789,12 +739,9 @@ const alignJustify = maxLineWidth => line => {
789
739
  if (count === 0) {
790
740
  return [];
791
741
  }
792
- const nonZeroWidthWords = line.filter(_ref => {
793
- let {
794
- width
795
- } = _ref;
796
- return width > 0;
797
- });
742
+ const nonZeroWidthWords = line.filter(({
743
+ width
744
+ }) => width > 0);
798
745
  const countNonZeroWidthWords = nonZeroWidthWords.length;
799
746
  if (countNonZeroWidthWords === 1) {
800
747
  const [first, ...rest] = line;
@@ -953,6 +900,10 @@ const verticalAlignInLines = (lines, lineSpacing, overrideValign) => {
953
900
  if (strokeThickness && strokeThickness > 0) {
954
901
  segAscent += strokeThickness / 2;
955
902
  }
903
+ const topTrim = segment.style.topTrim ?? 0;
904
+ if (topTrim !== 0) {
905
+ segAscent = Math.max(0, segAscent - topTrim);
906
+ }
956
907
  const isNewline = isNewlineToken(segment);
957
908
  isWhitespaceToken(segment);
958
909
  const skipNewline = isNewlineToken(segment) && hasRealContent;
@@ -1222,13 +1173,7 @@ const splitText = (s, splitStyle) => {
1222
1173
  throw new Error(`Unsupported split style "${splitStyle}". ${suggestion}`);
1223
1174
  }
1224
1175
  };
1225
- const calculateTokens = function (styledTokens, splitStyle, scaleIcons, adjustFontBaseline) {
1226
- if (splitStyle === void 0) {
1227
- splitStyle = "words";
1228
- }
1229
- if (scaleIcons === void 0) {
1230
- scaleIcons = true;
1231
- }
1176
+ const calculateTokens = (styledTokens, splitStyle = "words", scaleIcons = true, adjustFontBaseline) => {
1232
1177
  const defaultStyle = styledTokens.style;
1233
1178
  let fontProperties;
1234
1179
  const generateTokensFormStyledToken = (style, tags) => token => {
@@ -1273,7 +1218,7 @@ const calculateTokens = function (styledTokens, splitStyle, scaleIcons, adjustFo
1273
1218
  localSizer.text = str;
1274
1219
  }
1275
1220
  fontProperties = {
1276
- ...getFontPropertiesOfText(localSizer)
1221
+ ...getFontPropertiesOfText(localSizer, true)
1277
1222
  };
1278
1223
  const sw = style.fontScaleWidth ?? 1.0;
1279
1224
  const sh = style.fontScaleHeight ?? 1.0;
@@ -1342,7 +1287,7 @@ const calculateTokens = function (styledTokens, splitStyle, scaleIcons, adjustFo
1342
1287
  localSizer.text = "Mg";
1343
1288
  }
1344
1289
  fontProperties = {
1345
- ...getFontPropertiesOfText(localSizer)
1290
+ ...getFontPropertiesOfText(localSizer, true)
1346
1291
  };
1347
1292
  if (isIcon) {
1348
1293
  const h = Math.max(sprite.height, 1);
@@ -1406,10 +1351,7 @@ const calculateTokens = function (styledTokens, splitStyle, scaleIcons, adjustFo
1406
1351
  const lines = layout(finalTokens, maxWidth, lineSpacing, align);
1407
1352
  return lines;
1408
1353
  };
1409
- const getBaselineAdjustment = function (style, fontBaselineMap, ascent) {
1410
- if (fontBaselineMap === void 0) {
1411
- fontBaselineMap = {};
1412
- }
1354
+ const getBaselineAdjustment = (style, fontBaselineMap = {}, ascent) => {
1413
1355
  const fontFamily = style.fontFamily?.toString() ?? "";
1414
1356
  const adjustBaseline = style.adjustBaseline ?? 0;
1415
1357
  const adjustFontBaseline = fontBaselineMap[fontFamily] ?? null;
@@ -1523,27 +1465,18 @@ class Glyphs extends PIXI__namespace.Container {
1523
1465
  this.setTagStyles(styles);
1524
1466
  }
1525
1467
  setTagStyles(styles, skipUpdate) {
1526
- Object.entries(styles).forEach(_ref => {
1527
- let [tag, style] = _ref;
1528
- return this.setStyleForTag(tag, style, true);
1529
- });
1468
+ Object.entries(styles).forEach(([tag, style]) => this.setStyleForTag(tag, style, true));
1530
1469
  this._needsUpdate = true;
1531
1470
  this.updateIfShould(skipUpdate);
1532
1471
  }
1533
- getStyleForTag(tag, attributes) {
1534
- if (attributes === void 0) {
1535
- attributes = {};
1536
- }
1472
+ getStyleForTag(tag, attributes = {}) {
1537
1473
  return getStyleForTag(tag, this.tagStyles, attributes);
1538
1474
  }
1539
1475
  getStyleForTags(tags) {
1540
- const styles = tags.map(_ref2 => {
1541
- let {
1542
- tagName,
1543
- attributes
1544
- } = _ref2;
1545
- return this.getStyleForTag(tagName, attributes);
1546
- });
1476
+ const styles = tags.map(({
1477
+ tagName,
1478
+ attributes
1479
+ }) => this.getStyleForTag(tagName, attributes));
1547
1480
  return combineAllStyles(styles);
1548
1481
  }
1549
1482
  setStyleForTag(tag, styles, skipUpdate) {
@@ -1601,16 +1534,7 @@ class Glyphs extends PIXI__namespace.Container {
1601
1534
  get debugContainer() {
1602
1535
  return this._debugContainer;
1603
1536
  }
1604
- constructor(text, tagStyles, options) {
1605
- if (text === void 0) {
1606
- text = "";
1607
- }
1608
- if (tagStyles === void 0) {
1609
- tagStyles = {};
1610
- }
1611
- if (options === void 0) {
1612
- options = {};
1613
- }
1537
+ constructor(text = "", tagStyles = {}, options = {}) {
1614
1538
  super();
1615
1539
  this._options = void 0;
1616
1540
  this._needsUpdate = true;
@@ -1749,8 +1673,7 @@ class Glyphs extends PIXI__namespace.Container {
1749
1673
  }
1750
1674
  createSpriteTemplatesFromSourceMap(imgMap) {
1751
1675
  this._spriteTemplates = {};
1752
- Object.entries(imgMap).forEach(_ref3 => {
1753
- let [key, spriteSource] = _ref3;
1676
+ Object.entries(imgMap).forEach(([key, spriteSource]) => {
1754
1677
  const wrongFormatError = new TypeError(`The spriteSource provided for key ${key} was not in a valid format. Please use a Sprite, Texture, BaseTexture, string, HTMLImageElement, HTMLVideoElement, HTMLCanvasElement, or SVGElement`);
1755
1678
  const destroyedError = new Error(`The spriteSource provided for key ${key} appears to be a Sprite or Texture that has been destroyed or removed from PIXI.TextureCache probably using \`destroy()\` with aggressive options or \`destroyImgMap()\`.`);
1756
1679
  let error = null;
@@ -2118,9 +2041,31 @@ class Glyphs extends PIXI__namespace.Container {
2118
2041
  }
2119
2042
  for (let lineNumber = 0; lineNumber < paragraph.length; lineNumber++) {
2120
2043
  const line = paragraph[lineNumber];
2121
- const lineBounds = getBoundsNested(line);
2122
- let lineBoxY = lineBounds.y + 2;
2123
- let lineBoxHeight = lineBounds.height;
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;
2124
2069
  if (this.defaultStyle.wordWrap) {
2125
2070
  const w = this.defaultStyle.wordWrapWidth ?? this.width;
2126
2071
  g.rect(0, lineBoxY, w, lineBoxHeight).stroke({
@@ -2153,8 +2098,12 @@ class Glyphs extends PIXI__namespace.Container {
2153
2098
  let boxY = y;
2154
2099
  let boxHeight = segmentToken.bounds.height;
2155
2100
  if (!isSprite) {
2156
- const ascent = segmentToken.fontProperties.ascent;
2101
+ let ascent = segmentToken.fontProperties.ascent;
2157
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
+ }
2158
2107
  boxY = baseline - ascent;
2159
2108
  boxHeight = ascent + descent;
2160
2109
  } else {