0 | ||| Measuring the visible bounds of text is incredibly hard
1 | ||| and tons of material have been written about this.
2 | |||
3 | ||| In this module, we take a pragmatic approach that produces
4 | ||| reasonable results without having to load and parse font files.
5 | ||| The drawback of this: The steps described below have to be repeated
6 | ||| for every new font we'd like to support.
7 | |||
8 | ||| A detailed introduction to typography and how fonts are specified
9 | ||| can be found [here](https://learn.microsoft.com/en-us/typography/opentype/spec/otff).
10 | |||
11 | ||| In general, we need to know the height and width of a piece of printed text
12 | ||| to properly align it with the rest of the drawing.
13 | |||
14 | ||| Text height:
15 | ||| there are different types of "height" when it comes to text, and I won't go
16 | ||| into the details here. Suffice to say that we are interested in vertically aligning
17 | ||| atom labels, charges, implicit hydrogen count and mass numbers in a
18 | ||| way that feels natural. For this, wir are mostly interested in "capHeight" of
19 | ||| a font: The height of capital letters (without descenders). This, together
20 | ||| with the Em-square size can be read from font files.
21 | |||
22 | ||| Text width:
23 | ||| While computing the height of a piece of text is non-trivial, computing its
24 | ||| width is insane. Every glyph has its own specific width, sometimes depending
25 | ||| on its neighbouring glyphs (see ligatures and kerning). Fortunately, there
26 | ||| is a quite simple method to get good approximations without being overly
27 | ||| complicated. It is described on [stackoverflow](https://stackoverflow.com/questions/16007743/roughly-approximate-the-width-of-a-string-of-text-in-python)
28 | ||| We use Python (because it has support for almost everything) to parse
29 | ||| the true type font file we are interested in and generate a dictionary
30 | ||| of the glyphs and their widths we are interested in. Using this to compute
31 | ||| the width of a piece of text at a given font size is efficient and simple
32 | ||| but not perfectly exact because it ignores kerning. It also requires large
33 | ||| dictionaries if we want to support lots of unicode characters.
34 | |||
35 | ||| Note: The Python script used to extract the glyph widths can be found in the
36 | ||| `resources` directory.
43 | -- based on [stackoverflow](https://stackoverflow.com/questions/16007743/roughly-approximate-the-width-of-a-string-of-text-in-python)
45 | widths = [('0',278),('1',278),('2',278),('3',278),('4',278),('5',278),('6',278),('7',278),('8',278),('9',278),('a',279),('b',278),('c',250),('d',278),('e',278),('f',140),('g',278),('h',278),('i',111),('j',124),('k',251),('l',111),('m',417),('n',278),('o',278),('p',278),('q',278),('r',167),('s',250),('t',139),('u',278),('v',250),('w',364),('x',250),('y',250),('z',250),('A',334),('B',334),('C',361),('D',361),('E',334),('F',305),('G',389),('H',361),('I',139),('J',250),('K',334),('L',278),('M',417),('N',361),('O',389),('P',334),('Q',389),('R',361),('S',334),('T',305),('U',361),('V',334),('W',472),('X',334),('Y',334),('Z',305),('!',139),('"',177),('#',278),('$',278),('%',445),('&',334),('\'',95),('(',167),(')',167),('*',195),('+',292),(',',139),('-',167),('.',139),('/',139),(':',139),(';',139),('<',292),('=',292),('>',292),('?',278),('@',508),('[',139),('\\',139),(']',139),('^',235),('_',292),('`',167),('{',167),('|',130),('}',167),('~',292),(' ',139)]
56 | -- text was measure at a font size of 500, so we divide by that
57 | -- when computing the width at a different font size.
61 | ||| Metrics of a piece of text.
69 | ||| Utility for measuring text metrics.
72 | [noHints]
76 | ||| This is a primitive but efficient implementation of `Measure`, which
77 | ||| is described in the module docs. A more exact implementation could
78 | ||| make use of a browser canvas and use text metrics from the DOM, but
79 | ||| this is not available when we are not drawing molecules in the browser.
80 | |||
81 | ||| This implementation assumes a typical roman font similar to Arial.
82 | ||| It is based on the metrics of "Liberation Sans", which should have the
83 | ||| same layout as Arial or Helvetica.
84 | |||
85 | ||| About magic numbers: 2048 is the Em square size, 1409 the cap height,
86 | ||| 307 the line height. These have to be multiplied with the font size.
87 | ||| Text widths are approxiamted by summing up glyph width stored in a
88 | ||| dictionary.
89 | export