@trebco/treb 30.16.0 → 31.1.1

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.
Files changed (58) hide show
  1. package/api-generator/api-generator.ts +3 -1
  2. package/api-generator/package.json +2 -1
  3. package/dist/languages/treb-i18n-da.mjs +1 -1
  4. package/dist/languages/treb-i18n-de.mjs +1 -1
  5. package/dist/languages/treb-i18n-es.mjs +1 -1
  6. package/dist/languages/treb-i18n-fr.mjs +1 -1
  7. package/dist/languages/treb-i18n-it.mjs +1 -1
  8. package/dist/languages/treb-i18n-nl.mjs +1 -1
  9. package/dist/languages/treb-i18n-no.mjs +1 -1
  10. package/dist/languages/treb-i18n-pl.mjs +1 -1
  11. package/dist/languages/treb-i18n-pt.mjs +1 -1
  12. package/dist/languages/treb-i18n-sv.mjs +1 -1
  13. package/dist/treb-export-worker.mjs +2 -2
  14. package/dist/treb-spreadsheet.mjs +13 -13
  15. package/dist/treb.d.ts +19 -2
  16. package/package.json +8 -7
  17. package/treb-base-types/src/font-stack.ts +144 -0
  18. package/treb-base-types/src/style.ts +121 -11
  19. package/treb-base-types/src/theme.ts +53 -8
  20. package/treb-calculator/src/calculator.ts +13 -13
  21. package/treb-calculator/src/descriptors.ts +12 -4
  22. package/treb-calculator/src/expression-calculator.ts +17 -4
  23. package/treb-calculator/src/functions/base-functions.ts +57 -4
  24. package/treb-calculator/src/functions/statistics-functions.ts +9 -6
  25. package/treb-calculator/tsconfig.json +11 -0
  26. package/treb-charts/style/charts.scss +7 -1
  27. package/treb-data-model/src/annotation.ts +6 -0
  28. package/treb-data-model/src/data_model.ts +14 -3
  29. package/treb-data-model/src/language-model.ts +11 -2
  30. package/treb-data-model/src/sheet.ts +57 -56
  31. package/treb-embed/markup/toolbar.html +15 -1
  32. package/treb-embed/src/custom-element/spreadsheet-constructor.ts +38 -0
  33. package/treb-embed/src/embedded-spreadsheet.ts +139 -32
  34. package/treb-embed/src/options.ts +3 -0
  35. package/treb-embed/src/selection-state.ts +1 -0
  36. package/treb-embed/src/toolbar-message.ts +6 -0
  37. package/treb-embed/src/types.ts +9 -0
  38. package/treb-embed/style/defaults.scss +12 -1
  39. package/treb-embed/style/font-stacks.scss +105 -0
  40. package/treb-embed/style/layout.scss +1 -0
  41. package/treb-embed/style/theme-defaults.scss +12 -2
  42. package/treb-embed/style/toolbar.scss +16 -0
  43. package/treb-grid/src/editors/overlay_editor.ts +36 -3
  44. package/treb-grid/src/layout/base_layout.ts +52 -37
  45. package/treb-grid/src/layout/grid_layout.ts +7 -0
  46. package/treb-grid/src/render/tile_renderer.ts +156 -148
  47. package/treb-grid/src/types/grid.ts +188 -54
  48. package/treb-grid/src/types/grid_events.ts +1 -1
  49. package/treb-grid/src/types/grid_options.ts +3 -0
  50. package/treb-grid/src/util/fontmetrics.ts +134 -0
  51. package/treb-parser/src/parser.ts +12 -3
  52. package/treb-utils/src/measurement.ts +2 -3
  53. package/tsproject.json +1 -1
  54. package/treb-calculator/modern.tsconfig.json +0 -11
  55. package/treb-grid/src/util/fontmetrics2.ts +0 -182
  56. package/treb-parser/src/parser.test.ts +0 -298
  57. /package/treb-embed/{modern.tsconfig.json → tsconfig.json} +0 -0
  58. /package/treb-export/{modern.tsconfig.json → tsconfig.json} +0 -0
@@ -377,6 +377,11 @@ export abstract class BaseLayout {
377
377
 
378
378
  }
379
379
 
380
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
381
+ public FocusInLayout(target?: EventTarget): boolean {
382
+ return false;
383
+ }
384
+
380
385
  /**
381
386
  * if the DPR has changed, update it and return true. otherwise return
382
387
  * false. this is used on resize events: if the scale has changed, we
@@ -672,7 +677,7 @@ export abstract class BaseLayout {
672
677
 
673
678
  }
674
679
 
675
- public UpdateAnnotation(elements: Annotation | Annotation[]): void {
680
+ public UpdateAnnotation(elements: Annotation | Annotation[], theme: Theme): void {
676
681
  if (!Array.isArray(elements)) elements = [elements];
677
682
  for (const annotation of elements) {
678
683
  const view = annotation.view[this.view.view_index] || {};
@@ -684,8 +689,48 @@ export abstract class BaseLayout {
684
689
  }
685
690
  */
686
691
 
692
+ let font_size_pt = 10 * this.scale;
693
+
694
+ // console.info(annotation.data.style);
695
+
696
+ if (annotation.data.style?.font_size?.unit === 'em') {
697
+
698
+ font_size_pt *= annotation.data.style.font_size.value;
699
+
700
+ // console.info("scaling up...", font_size_pt)
701
+
702
+ }
703
+
687
704
  view.node.dataset.scale = this.scale.toString();
688
- view.node.style.fontSize = `${10 * this.scale}pt`;
705
+
706
+ // maybe we should just reset and then apply?
707
+
708
+ if (annotation.data.style?.font_face && annotation.data.style.font_face.startsWith('stack:')) {
709
+ const name = annotation.data.style.font_face.substring(6);
710
+ const stack = theme.font_stacks[name];
711
+ if(stack) {
712
+ view.node.classList.add('treb-inherit-font');
713
+ view.node.style.fontFamily = stack.font || '';
714
+
715
+ // font_size_pt *= stack.scale;
716
+
717
+ if (stack.variants) {
718
+ view.node.style.fontVariant = stack.variants;
719
+ }
720
+ }
721
+ else {
722
+ view.node.classList.remove('treb-inherit-font');
723
+ view.node.style.fontFamily = '';
724
+ view.node.style.fontVariant = '';
725
+ }
726
+ }
727
+ else {
728
+ view.node.classList.remove('treb-inherit-font');
729
+ view.node.style.fontFamily = '';
730
+ view.node.style.fontVariant = '';
731
+ }
732
+
733
+ view.node.style.fontSize = `${font_size_pt}pt`;
689
734
 
690
735
  // update the layout here if necessary. after that it should
691
736
  // be persisted (assuming it's saved). eventually this should
@@ -848,13 +893,13 @@ export abstract class BaseLayout {
848
893
 
849
894
  }
850
895
 
851
- public AddAnnotation(annotation: Annotation): void {
896
+ public AddAnnotation(annotation: Annotation, theme: Theme): void {
852
897
  const view = annotation.view[this.view.view_index] || {};
853
898
  if (!view.node) {
854
899
  throw new Error('annotation view/node missing');
855
900
  }
856
901
  this.annotation_container.appendChild(view.node);
857
- this.UpdateAnnotation(annotation);
902
+ this.UpdateAnnotation(annotation, theme);
858
903
  }
859
904
 
860
905
  // testing moving this here...
@@ -1246,38 +1291,6 @@ export abstract class BaseLayout {
1246
1291
 
1247
1292
  parent.appendChild(tile);
1248
1293
 
1249
- // NOTE re: text rendering. you can't use baseline = top, because that's
1250
- // inconsistent among browsers. in fact of all baselines, the only ones that
1251
- // are even close are alphabetic and bottom -- bottom is slightly different
1252
- // in ffx compared to chrome and edge, but that could be because of different
1253
- // font rendering schemes. alphabetic is the closest, but requires offset for
1254
- // ascender (or descender).
1255
-
1256
- // actually it looks like there's a 1px difference in bottom baseline...
1257
- // alphabetic is the only one that's consistent.
1258
-
1259
- // FIXME: why not just offset on a per-browser basis? it might be ugly
1260
- // but it's simpler.
1261
-
1262
- // for the time being we will use bottom.
1263
-
1264
- // why were we prepainting (because firefox, below?) and why was this so
1265
- // slow? do we need to preset the context text parameters?
1266
-
1267
- /*
1268
- const context = tile.getContext('2d', {alpha: false});
1269
-
1270
- if (context) {
1271
- context.textAlign = 'left';
1272
- context.textBaseline = 'alphabetic';
1273
-
1274
- // prepaint -- firefox is a little slow so flashes empty tiles sometimes
1275
-
1276
- // context.fillStyle = '#fff'; // FIXME: use theme color
1277
- // context.fillRect(0, 0, tile.width, tile.height);
1278
- }
1279
- */
1280
-
1281
1294
  return tile;
1282
1295
 
1283
1296
  }
@@ -1319,7 +1332,9 @@ export abstract class BaseLayout {
1319
1332
 
1320
1333
  // TODO: dropdown caret
1321
1334
 
1322
- this.dropdown_list.style.font = Style.Font(theme.grid_cell || {});
1335
+ this.dropdown_list.style.font = Style.CompositeFont(theme.grid_cell_font_size, {
1336
+ font_face: 'stack:default',
1337
+ }, this.scale, theme).font;
1323
1338
 
1324
1339
  // testing
1325
1340
 
@@ -99,6 +99,13 @@ export class GridLayout extends BaseLayout {
99
99
 
100
100
  }
101
101
 
102
+ public FocusInLayout(target?: EventTarget): boolean {
103
+ if (target && target instanceof Element && this.container?.contains(target)) {
104
+ return true;
105
+ }
106
+ return false;
107
+ }
108
+
102
109
  public ResizeCursor(resize?: 'row'|'column'): void {
103
110
  switch (resize) {
104
111
  case 'row':
@@ -28,16 +28,18 @@ import type { ICellAddress,
28
28
  import { TextPartFlag, Style, ValueType, Area, Rectangle, ResolveThemeColor, IsDefinedColor } from 'treb-base-types';
29
29
 
30
30
  import type { Tile } from '../types/tile';
31
- import { FontMetricsCache as FontMetricsCache2 } from '../util/fontmetrics2';
31
+ // import { FontMetricsCache as FontMetricsCache2 } from '../util/fontmetrics2';
32
32
  import type { FormattedString} from 'treb-parser';
33
33
  import { MDParser } from 'treb-parser';
34
- import type { BaseLayout, TileRange } from '../layout/base_layout';
34
+ import { BaseLayout, TileRange } from '../layout/base_layout';
35
35
  import type { DataModel, ViewModel } from 'treb-data-model';
36
36
  import type { GridOptions } from '../types/grid_options';
37
37
 
38
+ import { Get as GetFontMetrics } from '../util/fontmetrics';
39
+
38
40
  const DEFAULT_INDENT = ' '; // two spaces in the current font
39
- const BASELINE = 'bottom';
40
- const WK = /webkit/i.test(typeof navigator === 'undefined' ? '' : navigator?.userAgent || '') ? 1 : 0;
41
+ // const BASELINE = 'bottom';
42
+ // const WK = /webkit/i.test(typeof navigator === 'undefined' ? '' : navigator?.userAgent || '') ? 1 : 0;
41
43
 
42
44
  interface FontSet {
43
45
  base: string,
@@ -118,7 +120,8 @@ export class TileRenderer {
118
120
  this.buffer_context = context;
119
121
  this.buffer_context.setTransform(scale, 0, 0, scale, 0, 0);
120
122
  this.buffer_context.textAlign = 'left';
121
- this.buffer_context.textBaseline = BASELINE; // 'alphabetic';
123
+ this.buffer_context.textBaseline = // BASELINE; //
124
+ 'alphabetic';
122
125
  }
123
126
 
124
127
  // this.UpdateTheme();
@@ -173,6 +176,52 @@ export class TileRenderer {
173
176
  }
174
177
 
175
178
  /**
179
+ * new method for measuring text, intended to take into account
180
+ * indentation and wrapping, scale, and all the other stuff.
181
+ */
182
+ public MeasureText(cell: Cell, current_width: number, override_scale?: number) {
183
+
184
+ // we need a canvas. I guess we can just randomly use one?
185
+
186
+ const tile = this.layout.grid_tiles[0][0];
187
+
188
+ // we need fonts, and check if we need variants
189
+
190
+ const scale = override_scale ?? this.layout.scale;
191
+ const style: CellStyle = cell.style || {};
192
+ const base_font = Style.CompositeFont(this.theme.grid_cell_font_size, style, scale, this.theme);
193
+ const metrics = GetFontMetrics(base_font.font, base_font.variants);
194
+
195
+ const fonts: FontSet = {
196
+ base: base_font.font,
197
+ strong: Style.CompositeFont(this.theme.grid_cell_font_size, {...style, bold: true}, scale, this.theme).font,
198
+ emphasis: Style.CompositeFont(this.theme.grid_cell_font_size, {...style, italic: true}, scale, this.theme).font,
199
+ strong_emphasis: Style.CompositeFont(this.theme.grid_cell_font_size, {...style, bold: true, italic: true }, scale, this.theme).font,
200
+ };
201
+
202
+ if (base_font.variants) {
203
+ tile.style.fontVariant = base_font.variants;
204
+ }
205
+ else {
206
+ tile.style.fontVariant = '';
207
+ }
208
+
209
+ const context = tile.getContext('2d', { alpha: false });
210
+
211
+ if (!context) {
212
+ throw new Error('context failed');
213
+ }
214
+
215
+ const prepped = this.PrepText(context, fonts, cell, current_width);
216
+
217
+ const width = prepped.width;
218
+ const height = metrics.height * prepped.strings.length;
219
+
220
+ return { width, height };
221
+
222
+ }
223
+
224
+ /* *
176
225
  * use one of the tile contexts to measure text. we are using the tile
177
226
  * context because it's attached to the DOM, and style is applied. we need
178
227
  * that for the root font size, in case font size in the style is relative
@@ -188,7 +237,7 @@ export class TileRenderer {
188
237
  *
189
238
  * @param text
190
239
  * @param font
191
- */
240
+ * /
192
241
  public MeasureText(text: string, font?: string): TextMetrics {
193
242
 
194
243
  const context = this.layout.grid_tiles[0][0].getContext('2d', { alpha: false });
@@ -203,6 +252,7 @@ export class TileRenderer {
203
252
 
204
253
  return context.measureText(text);
205
254
  }
255
+ */
206
256
 
207
257
  /**
208
258
  * when drawing to the buffered canvas, (1) ensure it's large enough,
@@ -233,7 +283,8 @@ export class TileRenderer {
233
283
  if (context) {
234
284
  this.buffer_context = context;
235
285
  this.buffer_context.textAlign = 'left';
236
- this.buffer_context.textBaseline = BASELINE;
286
+ this.buffer_context.textBaseline = // BASELINE;
287
+ 'alphabetic';
237
288
  }
238
289
 
239
290
  }
@@ -291,7 +342,8 @@ export class TileRenderer {
291
342
  throw new Error('invalid context');
292
343
  }
293
344
 
294
- const m2 = FontMetricsCache2.Get(Style.Font(this.theme.headers || {}, this.layout.scale));
345
+ const font_info = Style.CompositeFont(this.theme.grid_cell_font_size, this.theme.headers||{}, this.layout.scale, this.theme);
346
+ const m2 = GetFontMetrics(font_info.font, font_info.variants);
295
347
 
296
348
  const scale = this.layout.dpr;
297
349
  const header_size = this.layout.header_offset;
@@ -351,8 +403,8 @@ export class TileRenderer {
351
403
  // 0 or 1 pixel) we don't want to render them here.
352
404
 
353
405
  context.textAlign = 'center';
354
- context.textBaseline = 'middle';
355
- context.font = Style.Font(this.theme.headers||{}, this.layout.scale);
406
+ context.textBaseline = 'middle'; // FUXME
407
+ context.font = font_info.font; // Style.Font(this.theme.headers||{}, this.layout.scale);
356
408
 
357
409
  context.fillStyle = ResolveThemeColor(this.theme, this.theme.headers?.text);
358
410
 
@@ -365,7 +417,8 @@ export class TileRenderer {
365
417
  context.lineTo(header_size.x, 0 - 0.5);
366
418
  context.stroke();
367
419
 
368
- this.RenderRowLabels(context, 0, this.view.active_sheet.freeze.rows - 1, m2.block);
420
+ this.RenderRowLabels(context, 0, this.view.active_sheet.freeze.rows - 1, // m2.block);
421
+ m2.height);
369
422
 
370
423
  }
371
424
 
@@ -458,7 +511,10 @@ export class TileRenderer {
458
511
 
459
512
  const scale = this.layout.dpr;
460
513
  const header_size = this.layout.header_offset;
461
- const m2 = FontMetricsCache2.Get(Style.Font(this.theme.headers || {}, this.layout.scale));
514
+
515
+ const font_info = Style.CompositeFont(this.theme.grid_cell_font_size, this.theme.headers||{}, this.layout.scale, this.theme);
516
+ const m2 = GetFontMetrics(font_info.font, font_info.variants);
517
+
462
518
 
463
519
  for (let column = tiles.start.column; column <= tiles.end.column; column++) {
464
520
 
@@ -470,8 +526,15 @@ export class TileRenderer {
470
526
  context.setTransform(scale, 0, 0, scale, 0, 0);
471
527
 
472
528
  context.textAlign = 'center';
473
- context.textBaseline = 'middle';
474
- context.font = Style.Font(this.theme.headers||{}, this.layout.scale);
529
+ context.textBaseline = 'middle'; // FIXME
530
+
531
+ context.font = font_info.font; // Style.Font(this.theme.headers||{}, this.layout.scale);
532
+ if (font_info.variants) {
533
+ tile.style.fontVariant = font_info.variants;
534
+ }
535
+ else {
536
+ tile.style.fontVariant = '';
537
+ }
475
538
 
476
539
  context.fillStyle = this.theme.headers?.fill ? ResolveThemeColor(this.theme, this.theme.headers.fill) : '';
477
540
  context.fillRect(0, 0, tile.logical_size.width, this.layout.header_offset.y);
@@ -512,8 +575,16 @@ export class TileRenderer {
512
575
  context.setTransform(scale, 0, 0, scale, 0, 0);
513
576
 
514
577
  context.textAlign = 'center';
515
- context.textBaseline = 'middle';
516
- context.font = Style.Font(this.theme.headers||{}, this.layout.scale);
578
+ context.textBaseline = 'middle'; // FIXME
579
+ // context.font = Style.Font(this.theme.headers||{}, this.layout.scale);
580
+
581
+ context.font = font_info.font; // Style.Font(this.theme.headers||{}, this.layout.scale);
582
+ if (font_info.variants) {
583
+ tile.style.fontVariant = font_info.variants;
584
+ }
585
+ else {
586
+ tile.style.fontVariant = '';
587
+ }
517
588
 
518
589
  context.fillRect(0, 0, this.layout.header_offset.x, tile.logical_size.height);
519
590
 
@@ -527,7 +598,8 @@ export class TileRenderer {
527
598
 
528
599
  context.strokeStyle = this.theme.headers_grid_color || '';
529
600
 
530
- this.RenderRowLabels(context, tile.first_cell.row, tile.last_cell.row, m2.block);
601
+ this.RenderRowLabels(context, tile.first_cell.row, tile.last_cell.row, // m2.block);
602
+ m2.height);
531
603
 
532
604
  tile.dirty = false;
533
605
  }
@@ -587,10 +659,18 @@ export class TileRenderer {
587
659
  /** render a tile */
588
660
  public Render(tile: Tile): void {
589
661
 
662
+ // can we assume this is always set? this feels sloppy, and
663
+ // it feels like something we'd like to turn off... can we
664
+ // fetch the canvas more than once? not sure what the impact of
665
+ // that would be.
666
+
667
+ // tile.style.fontVariantNumeric = 'lining-nums tabular-nums';
668
+
590
669
  const context = tile.getContext('2d', { alpha: false });
591
670
  if (!context) { return; } // should throw
592
671
 
593
- context.textBaseline = BASELINE;
672
+ context.textBaseline = // BASELINE;
673
+ 'alphabetic';
594
674
 
595
675
  const scale = this.layout.dpr;
596
676
 
@@ -1654,13 +1734,28 @@ export class TileRenderer {
1654
1734
  // (eventually) painting to the buffer context. just remember to set
1655
1735
  // font in the buffer context.
1656
1736
 
1737
+ const base_font = Style.CompositeFont(this.theme.grid_cell_font_size, style, this.layout.scale, this.theme);
1738
+
1739
+ /*
1740
+ if (cell.value) {
1741
+ console.info(base_font);
1742
+ }
1743
+ */
1744
+
1657
1745
  const fonts: FontSet = {
1658
- base: Style.Font(style, this.layout.scale),
1659
- strong: Style.Font({...style, bold: true}, this.layout.scale),
1660
- emphasis: Style.Font({...style, italic: true}, this.layout.scale),
1661
- strong_emphasis: Style.Font({...style, bold: true, italic: true}, this.layout.scale),
1746
+ base: base_font.font,
1747
+ strong: Style.CompositeFont(this.theme.grid_cell_font_size, {...style, bold: true}, this.layout.scale, this.theme).font,
1748
+ emphasis: Style.CompositeFont(this.theme.grid_cell_font_size, {...style, italic: true}, this.layout.scale, this.theme).font,
1749
+ strong_emphasis: Style.CompositeFont(this.theme.grid_cell_font_size, {...style, bold: true, italic: true }, this.layout.scale, this.theme).font,
1662
1750
  };
1663
1751
 
1752
+ if (base_font.variants) {
1753
+ tile.style.fontVariant = base_font.variants;
1754
+ }
1755
+ else {
1756
+ tile.style.fontVariant = '';
1757
+ }
1758
+
1664
1759
  context.font = fonts.base;
1665
1760
 
1666
1761
  //
@@ -1927,8 +2022,17 @@ export class TileRenderer {
1927
2022
  // and bold variants). this should be OK because we use it for height, mostly.
1928
2023
  // not sure about invisible text (FIXME)
1929
2024
 
1930
- const m2 = FontMetricsCache2.Get(fonts.base, this.theme.grid_cell?.font_size?.value);
1931
- // console.info("FB", fonts.base, m2);
2025
+ // const m2 = FontMetricsCache2.Get(fonts.base, // this.theme.grid_cell?.font_size?.value);
2026
+ // this.theme.grid_cell_font_size);
2027
+
2028
+
2029
+ const m2 = GetFontMetrics(fonts.base, base_font.variants);
2030
+
2031
+ /*
2032
+ if (cell.value) {
2033
+ console.info(fonts.base, {m2});
2034
+ }
2035
+ */
1932
2036
 
1933
2037
  // set stroke for underline
1934
2038
 
@@ -1936,7 +2040,7 @@ export class TileRenderer {
1936
2040
  // because our default style happens to be the default color. that applies
1937
2041
  // to text color, background color and border color.
1938
2042
 
1939
- context.lineWidth = 1;
2043
+ context.lineWidth = 1; // 1.5; // FIXME: scale? font scale?
1940
2044
 
1941
2045
  context.strokeStyle = context.fillStyle =
1942
2046
  text_data.format ? text_data.format : ResolveThemeColor(this.theme, style.text, 1);
@@ -1945,11 +2049,12 @@ export class TileRenderer {
1945
2049
 
1946
2050
  let left = this.cell_edge_buffer;
1947
2051
 
1948
- const line_height = 1.25;
2052
+ const line_height = 1; // 1.25;
1949
2053
 
1950
2054
  //const line_count = text_data.single ? 1 : text_data.strings.length;
1951
2055
  const line_count = text_data.strings.length;
1952
- const text_height = (line_count * m2.block * line_height);
2056
+ // const text_height = (line_count * m2.block * line_height);
2057
+ const text_height = (line_count * m2.height * line_height);
1953
2058
 
1954
2059
  // we stopped clipping initially because it was expensive -- but then
1955
2060
  // we were doing it on every cell. it's hard to imagine that clipping
@@ -1970,20 +2075,23 @@ export class TileRenderer {
1970
2075
  context.clip();
1971
2076
  }
1972
2077
 
1973
- // path for underline. if there's no underline, it won't do anything.
2078
+ // path for underline (and strike). if there's no underline (or strike),
2079
+ // it won't do anything.
1974
2080
 
1975
2081
  context.beginPath();
1976
2082
 
1977
- // baseline looks OK, if you account for descenders.
1978
-
1979
- let original_baseline = Math.round(height - 2 - (m2.block * line_height * (line_count - 1)) + WK); // switched baseline to "bottom"
2083
+ let original_baseline = Math.round(
2084
+ (height - m2.descent - 2) + // baseline for first line of text
2085
+ (-line_height * m2.height * (line_count - 1))); // adjust for multiple lines
1980
2086
 
1981
2087
  switch (style.vertical_align) {
1982
- case 'top': // Style.VerticalAlign.Top:
1983
- original_baseline = Math.round(m2.block * line_height) + 1;
2088
+ case 'top':
2089
+ original_baseline = Math.round(2 + m2.ascent);
1984
2090
  break;
1985
- case 'middle': // Style.VerticalAlign.Middle:
1986
- original_baseline = Math.round((height - text_height) / 2 + m2.block * line_height);
2091
+
2092
+ case 'middle':
2093
+ original_baseline = Math.round((height - text_height) / 2 + m2.ascent) - 1.5;
2094
+
1987
2095
  break;
1988
2096
  }
1989
2097
 
@@ -2017,7 +2125,7 @@ export class TileRenderer {
2017
2125
  // may have different formatting.
2018
2126
 
2019
2127
  let baseline = original_baseline;
2020
- let index = 0;
2128
+ // let index = 0;
2021
2129
 
2022
2130
  for (const line of text_data.strings) {
2023
2131
 
@@ -2026,29 +2134,17 @@ export class TileRenderer {
2026
2134
  let line_width = 0;
2027
2135
  for (const part of line) { line_width += part.width; }
2028
2136
 
2029
- if (horizontal_align === 'center' /* Style.HorizontalAlign.Center */ ) {
2137
+ if (horizontal_align === 'center') {
2030
2138
  left = Math.round((width - line_width) / 2);
2031
2139
  }
2032
- else if (horizontal_align === 'right' /* Style.HorizontalAlign.Right */ ) {
2140
+ else if (horizontal_align === 'right') {
2033
2141
  left = width - this.cell_edge_buffer - line_width;
2034
2142
  }
2035
2143
 
2036
- /*
2037
- if (style.font_underline) {
2038
- const underline_y = Math.floor(baseline + 1.5 - m2.descender - WK) + .5; // metrics.block - 3.5 - metrics.ascent - 3;
2039
- context.moveTo(left, underline_y);
2040
- context.lineTo(left + line_width, underline_y);
2041
- }
2042
-
2043
- if (style.font_strike) {
2044
- const strike_y = Math.floor(baseline - m2.descender - m2.ascender / 2) + .5;
2045
- context.moveTo(left, strike_y);
2046
- context.lineTo(left + line_width, strike_y);
2047
- }
2048
- */
2144
+ // still tinkering with these. surely we need to apply scale? FIXME
2049
2145
 
2050
- const underline_y = Math.floor(baseline + 1.5 - m2.descender - WK) + .5; // metrics.block - 3.5 - metrics.ascent - 3;
2051
- const strike_y = Math.floor(baseline - m2.descender - m2.ascender / 2) + .5;
2146
+ const underline_y = baseline + 2.5;
2147
+ const strike_y = Math.floor(baseline - m2.ascent * 1 / 3) + .5;
2052
2148
 
2053
2149
  let x = left;
2054
2150
  for (const part of line) {
@@ -2089,8 +2185,10 @@ export class TileRenderer {
2089
2185
 
2090
2186
  if (preserve_layout_info) {
2091
2187
  part.left = x;
2092
- part.top = baseline - m2.block;
2093
- part.height = m2.block;
2188
+ part.top = baseline - // m2.block;
2189
+ m2.ascent;
2190
+ part.height = // m2.block;
2191
+ m2.height;
2094
2192
  }
2095
2193
 
2096
2194
  }
@@ -2099,106 +2197,16 @@ export class TileRenderer {
2099
2197
 
2100
2198
  }
2101
2199
 
2102
- index++;
2103
- baseline = Math.round(original_baseline + index * m2.block * line_height);
2104
-
2105
- }
2106
-
2107
-
2108
- }
2109
-
2110
- /*
2111
- else if (text_data.single) {
2112
-
2113
- // const cached_font = context.font;
2114
- // const italic_font = /italic/i.test(cached_font) ? cached_font : 'italic ' + cached_font;
2115
-
2116
- // single refers to single-line text that has multiple components,
2117
- // including spacing or hidden text. single line text (not formatted)
2118
- // probably doesn't have this flag set, it will use the next block.
2119
- // these could (should?) be consolidated
2120
-
2121
- if (horizontal_align === Style.HorizontalAlign.Center) {
2122
- left = Math.round((width - text_data.width) / 2);
2123
- }
2124
- else if (horizontal_align === Style.HorizontalAlign.Right) {
2125
- left = width - this.cell_edge_buffer - text_data.width;
2126
- }
2127
-
2128
- const underline_y = Math.floor(original_baseline + 1.5 - m2.descender - WK) + .5; // metrics.block - 3.5 - metrics.ascent - 3;
2129
- const strike_y = Math.floor(original_baseline - m2.descender - m2.ascender / 2) + .5;
2130
-
2131
- // we want a single underline, possibly spanning hidden elements,
2132
- // but not starting or stopping on a hidden element (usually invisible
2133
- // parentheses).
2200
+ // height tends to be an integer, or maybe at worst 1/2 integer,
2201
+ // so stepping is probably ok (we used to index and multiply).
2134
2202
 
2135
- for (const part of text_data.strings) {
2136
- if (!part.hidden) {
2203
+ baseline += line_height * m2.height;
2137
2204
 
2138
- context.fillText(part.text, left, original_baseline);
2139
2205
 
2140
- if (style.font_underline) {
2141
- context.moveTo(left, underline_y);
2142
- context.lineTo(left + part.width, underline_y);
2143
- }
2144
- if (style.font_strike) {
2145
- context.moveTo(left, strike_y);
2146
- context.lineTo(left + part.width, strike_y);
2147
- }
2148
- }
2149
-
2150
- if (preserve_layout_info) {
2151
- part.left = left;
2152
- part.top = original_baseline - m2.block;
2153
- part.height = m2.block;
2154
- }
2155
-
2156
- left += part.width;
2157
2206
  }
2158
2207
 
2159
- }
2160
- else {
2161
-
2162
- let baseline = original_baseline;
2163
- let index = 0;
2164
-
2165
- for (const part of text_data.strings) {
2166
-
2167
- // here we justify based on part, each line might have different width
2168
-
2169
- if (horizontal_align === Style.HorizontalAlign.Center) {
2170
- left = Math.round((width - part.width) / 2);
2171
- }
2172
- else if (horizontal_align === Style.HorizontalAlign.Right) {
2173
- left = width - this.cell_edge_buffer - part.width;
2174
- }
2175
-
2176
- if (style.font_underline) {
2177
- const underline_y = Math.floor(baseline + 1.5 - m2.descender - WK) + .5; // metrics.block - 3.5 - metrics.ascent - 3;
2178
- context.moveTo(left, underline_y);
2179
- context.lineTo(left + part.width, underline_y);
2180
- }
2181
-
2182
- if (style.font_strike) {
2183
- const strike_y = Math.floor(baseline - m2.descender - m2.ascender / 2) + .5;
2184
- context.moveTo(left, strike_y);
2185
- context.lineTo(left + part.width, strike_y);
2186
- }
2187
-
2188
- context.fillText(part.text, left, baseline);
2189
-
2190
- if (preserve_layout_info) {
2191
- part.left = left;
2192
- part.top = baseline - m2.block;
2193
- part.height = m2.block;
2194
- }
2195
-
2196
- index++;
2197
- baseline = Math.round(original_baseline + index * m2.block * line_height);
2198
- }
2199
2208
 
2200
2209
  }
2201
- */
2202
2210
 
2203
2211
  context.stroke();
2204
2212