-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Description
When a Terminal is opened in an element subject to CSS transformations such as scale or rotate, the computation of the character size is affected by that transformation, which is wrong because the rendering should be transform-agnostic.
As a result, this makes the integration of Xterm.js into reveal.js slides render incorrectly when the slides are scaled down to fit the browser viewport.
Changing the measurement method in CharSizeService to use getComputedStyle instead of getBoundingClientRect makes the computation independent of CSS transforms.
Details
- Browser and browser version: Firefox 69.0 and Chromium 77.0
- OS version: Arch Linux
- xterm.js version: current master (6311076)
Steps to reproduce
- Add
style="transform: rotate(90deg);"to the<body>tag indemo/index.html - Notice that character boxes in the demo have the wrong dimensions
Workaround
With the rotate(90deg) CSS transform Xterm.js renders character cells with the width and height swapped around:

The cause is in the measurement methods in CharSizeService: it uses getBoundingClientRect, which returns the size of the bounding box after CSS transforms.
xterm.js/src/browser/services/CharSizeService.ts
Lines 71 to 86 in 6311076
| public measure(): IReadonlyMeasureResult { | |
| this._measureElement.style.fontFamily = this._optionsService.options.fontFamily; | |
| this._measureElement.style.fontSize = `${this._optionsService.options.fontSize}px`; | |
| // Note that this triggers a synchronous layout | |
| const geometry = this._measureElement.getBoundingClientRect(); | |
| // If values are 0 then the element is likely currently display:none, in which case we should | |
| // retain the previous value. | |
| if (geometry.width !== 0 && geometry.height !== 0) { | |
| this._result.width = geometry.width; | |
| this._result.height = Math.ceil(geometry.height); | |
| } | |
| return this._result; | |
| } |
My understanding is that the rendering of the terminal should not be affected by said transforms. And indeed, replacing the geometry definition in the code above with (POC)
const computedStyle = getComputedStyle(this._measureElement);
const geometry = {
height: Number(computedStyle.getPropertyValue('height').replace('px', '')),
width: Number(computedStyle.getPropertyValue('width').replace('px', '')),
}Note that my proof-of-concept fix above may be insufficient in cases where the element or its parent are display: none.
