Skip to content

canvas.measureText disagrees with DOM on Shantell Sans weight 700 in Chrome and Firefox #195

@arnaud-secondlayer

Description

@arnaud-secondlayer

For Shantell Sans at weight 700, prepareWithSegments + layoutWithLines chooses a different break position than the browser's <div> wrap in Chrome and Firefox.

The DOM shows 3 rows of 15 characters and the last row is 11 characters.
Pretext returns a different layout with 3 rows of 16 characters and the last row is 8 characters.

== macOS Chrome ==

xxxxxxxxxxxxxxx
xxxxxxxxxxxxxxx
xxxxxxxxxxxxxxx
xxxxxxxxxxx

DOM
0: width=134.04
1: width=134.04
2: width=134.04
3: width=98.20

Pretext
0: width=137.28 for 16 chars
1: width=137.28 for 16 chars
2: width=137.28 for 16 chars
3: width=68.64 for 8 chars

== macOS Firefox ==

xxxxxxxxxxxxxxx
xxxxxxxxxxxxxxx
xxxxxxxxxxxxxxx
xxxxxxxxxxx

DOM
0: width=134.50
1: width=134.50
2: width=134.50
3: width=98.25

Pretext
0: width=137.33 for 16 chars
1: width=137.33 for 16 chars
2: width=137.33 for 16 chars
3: width=68.67 for 8 chars

HTML file:

<!doctype html>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Shantell+Sans:wght@400;700&display=swap" />
<style>
    .box {
        width: 140px;
        font: bold 15px "Shantell Sans", cursive;
        line-height: 18px;
        white-space: pre-wrap;
        overflow-wrap: break-word;
        border: 1px dashed #aaa;
    }
</style>
<div class="box" id="dom"></div>
<p>DOM</p>
<pre id="dom-measure"></pre>
<p>Pretext</p>
<pre id="pretext"></pre>
<script type="module">
   import { prepareWithSegments, layoutWithLines } from 'https://esm.sh/@chenglou/pretext';

   const TEXT = 'x'.repeat(56);
   const FONT = 'bold 15px "Shantell Sans", cursive';
   const MAX_WIDTH = 140;
   const LINE_HEIGHT = 18;

   const div = document.getElementById('dom');
   div.textContent = TEXT;
   await document.fonts.load(FONT);

   // DOM measurement: one ClientRect per laid-out line.
   const range = document.createRange();
   range.selectNodeContents(div);
   const domLines = [...range.getClientRects()];
   document.getElementById('dom-measure').textContent = domLines.map((r, i) => `${i}: width=${r.width.toFixed(2)}`).join('\n');

   // Pretext measurement.
   const prepared = prepareWithSegments(TEXT, FONT, { whiteSpace: 'pre-wrap' });
   const { lines } = layoutWithLines(prepared, MAX_WIDTH, LINE_HEIGHT);
   document.getElementById('pretext').textContent = lines.map((l, i) => `${i}: width=${l.width.toFixed(2)} for ${l.text.length} chars`).join('\n');
</script>

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions